diff --git a/.gitmodules b/.gitmodules index 0b60f9814..319d02892 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "Frameworks/mGBA/mGBA/mgba"] path = Frameworks/mGBA/mGBA/mgba - url = https://g.losno.co/chris/mgba.git + url = https://git.lopez-snowhill.net/chris/mgba.git [submodule "Frameworks/AdPlug/AdPlug/adplug"] path = Frameworks/AdPlug/AdPlug/adplug url = https://github.com/adplug/adplug.git @@ -12,7 +12,7 @@ url = https://github.com/adplug/database.git [submodule "Frameworks/libsidplay/sidplay-residfp-code"] path = Frameworks/libsidplay/sidplay-residfp-code - url = https://g.losno.co/chris/sidplay-residfp.git + url = https://git.lopez-snowhill.net/chris/sidplay-residfp.git [submodule "Frameworks/libatrac9/libatrac9"] path = Frameworks/libatrac9/libatrac9 url = https://github.com/Thealexbarney/LibAtrac9.git diff --git a/Application/AppController.h b/Application/AppController.h index ccc61798e..10f25e4ac 100644 --- a/Application/AppController.h +++ b/Application/AppController.h @@ -74,8 +74,12 @@ - (IBAction)delEntries:(id)sender; - (IBAction)savePlaylist:(id)sender; -- (IBAction)donate:(id)sender; -- (IBAction)patreon:(id)sender; +- (IBAction)openLiberapayPage:(id)sender; +- (IBAction)openPaypalPage:(id)sender; +- (IBAction)openBitcoinPage:(id)sender; +- (IBAction)openPatreonPage:(id)sender; +- (IBAction)openKofiPage:(id)sender; + - (IBAction)feedback:(id)sender; - (IBAction)toggleInfoDrawer:(id)sender; diff --git a/Application/AppController.m b/Application/AppController.m index fead99688..05d52cd64 100644 --- a/Application/AppController.m +++ b/Application/AppController.m @@ -12,6 +12,7 @@ #import "SpotlightWindowController.h" #import "StringToURLTransformer.h" #import "FontSizetoLineHeightTransformer.h" +#import "Cog-Swift.h" #import "PathNode.h" #import @@ -36,6 +37,9 @@ NSValueTransformer *miniModeMenuTitleTransformer = [[MiniModeMenuTitleTransformer alloc] init]; [NSValueTransformer setValueTransformer:miniModeMenuTitleTransformer forName:@"MiniModeMenuTitleTransformer"]; + + NSValueTransformer *playbackStatusToHiddenTransformer = [[PlaybackStatusToHiddenTransformer alloc] init]; + [NSValueTransformer setValueTransformer:playbackStatusToHiddenTransformer forName:@"PlaybackStatusToHiddenTransformer"]; } @@ -330,16 +334,32 @@ } } -- (IBAction)donate:(id)sender +- (IBAction)openLiberapayPage:(id)sender { - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.paypal.me/kode54"]]; + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://liberapay.com/kode54"]]; } -- (IBAction)patreon:(id)sender +- (IBAction)openPaypalPage:(id)sender { - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.patreon.com/kode54"]]; + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.paypal.com/paypalme/kode54"]]; } +- (IBAction)openBitcoinPage:(id)sender +{ + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://kode54.net/donateBitcoin"]]; +} + +- (IBAction)openPatreonPage:(id)sender +{ + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://patreon.com/kode54"]]; +} + +- (IBAction)openKofiPage:(id)sender +{ + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://ko-fi.com/kode54"]]; +} + + - (IBAction)feedback:(id)sender { NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]; diff --git a/Audio/CogAudio.xcodeproj/project.pbxproj b/Audio/CogAudio.xcodeproj/project.pbxproj index 7c52103b5..9d6bff301 100644 --- a/Audio/CogAudio.xcodeproj/project.pbxproj +++ b/Audio/CogAudio.xcodeproj/project.pbxproj @@ -365,8 +365,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8DC2EF4F0486A6940098B216 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -431,8 +431,11 @@ 1DEB91AE08733DA50010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_SEARCH_PATHS = ""; @@ -448,6 +451,7 @@ OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.cogaudio; PRODUCT_NAME = CogAudio; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WARNING_LDFLAGS = ""; @@ -459,7 +463,10 @@ 1DEB91AF08733DA50010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_SEARCH_PATHS = ""; @@ -473,6 +480,7 @@ OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.cogaudio; PRODUCT_NAME = CogAudio; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WARNING_LDFLAGS = ""; @@ -514,7 +522,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYMROOT = ../build; @@ -553,7 +561,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SYMROOT = ../build; }; diff --git a/Audio/CogPluginMulti.m b/Audio/CogPluginMulti.m index 7efe2f63b..f4ad98cb5 100644 --- a/Audio/CogPluginMulti.m +++ b/Audio/CogPluginMulti.m @@ -78,7 +78,10 @@ NSArray * sortClassesByPriority(NSArray * theClasses) Class decoder = NSClassFromString(classString); theDecoder = [[decoder alloc] init]; for (NSDictionary *obsItem in cachedObservers) { - [theDecoder addObserver:[obsItem objectForKey:@"observer"] forKeyPath:[obsItem objectForKey:@"keyPath"] options:[obsItem objectForKey:@"options"] context:(__bridge void *)([obsItem objectForKey:@"context"])]; + [theDecoder addObserver:[obsItem objectForKey:@"observer"] + forKeyPath:[obsItem objectForKey:@"keyPath"] + options:[[obsItem objectForKey:@"options"] unsignedIntegerValue] + context:(__bridge void *)([obsItem objectForKey:@"context"])]; } if ([theDecoder open:source]) return YES; @@ -118,7 +121,11 @@ NSArray * sortClassesByPriority(NSArray * theClasses) /* By the current design, the core adds its observers to decoders before they are opened */ - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context { - [cachedObservers addObject:[NSDictionary dictionaryWithObjectsAndKeys:observer, @"observer", keyPath, @"keyPath", options, @"options", context, @"context", nil]]; + if(context != nil) { + [cachedObservers addObject:[NSDictionary dictionaryWithObjectsAndKeys:observer, @"observer", keyPath, @"keyPath", @(options), @"options", context, @"context", nil]]; + } else { + [cachedObservers addObject:[NSDictionary dictionaryWithObjectsAndKeys:observer, @"observer", keyPath, @"keyPath", @(options), @"options", nil]]; + } } /* And this is currently called after the decoder is closed */ diff --git a/Base.lproj/MainMenu.xib b/Base.lproj/MainMenu.xib index ffc14c092..ae8548b2f 100644 --- a/Base.lproj/MainMenu.xib +++ b/Base.lproj/MainMenu.xib @@ -1,8 +1,8 @@ - + - + @@ -14,52 +14,51 @@ - + - + - + - - - - - - - - - + + - - + - + - + - + - + @@ -97,7 +96,7 @@ - + @@ -118,7 +117,7 @@ - + @@ -139,7 +138,7 @@ - + @@ -160,7 +159,7 @@ - + @@ -180,7 +179,7 @@ - + @@ -196,7 +195,7 @@ - + @@ -213,7 +212,7 @@ - + @@ -295,7 +294,7 @@ - + @@ -308,26 +307,51 @@ - - + + + + + + + + + + + + + Total Duration: %{value1}@ + + + + + + + + + + + + + + + + - + - - - - - + + + - + @@ -339,13 +363,11 @@ - + - - - + @@ -391,13 +413,11 @@ - + - - - - + + @@ -408,13 +428,11 @@ - + - - - - + + @@ -422,13 +440,11 @@ - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - + - + @@ -57,7 +57,6 @@ DQ - @@ -69,17 +68,16 @@ DQ - + - @@ -91,17 +89,16 @@ DQ - + - @@ -113,17 +110,16 @@ DQ - + - @@ -199,7 +192,6 @@ DQ - @@ -216,6 +208,7 @@ DQ + @@ -231,25 +224,25 @@ DQ - + - - + + - - + + - + @@ -262,7 +255,7 @@ DQ - + @@ -271,7 +264,7 @@ DQ - + @@ -293,6 +286,7 @@ DQ + diff --git a/Cog-Bridging-Header.h b/Cog-Bridging-Header.h new file mode 100644 index 000000000..fb62b4b20 --- /dev/null +++ b/Cog-Bridging-Header.h @@ -0,0 +1,5 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import diff --git a/Cog.xcodeproj/project.pbxproj b/Cog.xcodeproj/project.pbxproj index 682536ca7..897b799da 100644 --- a/Cog.xcodeproj/project.pbxproj +++ b/Cog.xcodeproj/project.pbxproj @@ -143,8 +143,6 @@ 838491231807F38A00E7332D /* NowPlayingBarController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8384911F1807F38A00E7332D /* NowPlayingBarController.m */; }; 8384913C1808217300E7332D /* randomize.png in Resources */ = {isa = PBXBuildFile; fileRef = 8384913B1808217300E7332D /* randomize.png */; }; 8384914018083E4E00E7332D /* filetype.icns in Resources */ = {isa = PBXBuildFile; fileRef = 8384913D18083E4E00E7332D /* filetype.icns */; }; - 8384914118083E4E00E7332D /* icon_blank.icns in Resources */ = {isa = PBXBuildFile; fileRef = 8384913E18083E4E00E7332D /* icon_blank.icns */; }; - 8384914218083E4E00E7332D /* icon_main.icns in Resources */ = {isa = PBXBuildFile; fileRef = 8384913F18083E4E00E7332D /* icon_main.icns */; }; 8384915918083EAB00E7332D /* infoTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 8384914318083EAB00E7332D /* infoTemplate.pdf */; }; 8384915A18083EAB00E7332D /* missingArt@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8384914418083EAB00E7332D /* missingArt@2x.png */; }; 8384915B18083EAB00E7332D /* navigatorTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 8384914518083EAB00E7332D /* navigatorTemplate.pdf */; }; @@ -172,8 +170,10 @@ 83849174180843B200E7332D /* stopDockBadgeColorful.png in Resources */ = {isa = PBXBuildFile; fileRef = 83849171180843B200E7332D /* stopDockBadgeColorful.png */; }; 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, ); }; }; - 8398033A240B4AED003C54FA /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83980339240B4AED003C54FA /* Sparkle.framework */; }; - 8398033B240B4B0C003C54FA /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83980339240B4AED003C54FA /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 838F850125687C5C00C3E614 /* PlaybackStatusToHiddenTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 838F850025687C5C00C3E614 /* PlaybackStatusToHiddenTransformer.swift */; }; + 838F851C256B4AC400C3E614 /* icon_blank.icns in Resources */ = {isa = PBXBuildFile; fileRef = 838F851B256B4AC400C3E614 /* icon_blank.icns */; }; + 838F851E256B4E5E00C3E614 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 838F851D256B4E5E00C3E614 /* Sparkle.framework */; }; + 838F851F256B4E8B00C3E614 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 838F851D256B4E5E00C3E614 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 8399D4E21805A55000B503B1 /* XmlContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8399D4E01805A55000B503B1 /* XmlContainer.m */; }; 83A360B220E4E81D00192DAB /* Flac.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8303A30C20E4E3D000951EF8 /* Flac.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 83B06704180D579E008E3612 /* MIDI.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83B066A1180D5669008E3612 /* MIDI.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; @@ -185,6 +185,7 @@ 83BC5AC320E4CE8D00631CD4 /* SpotlightPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = 178456C00F6320B5007E8021 /* SpotlightPanel.xib */; }; 83BC5AC420E4CE9000631CD4 /* Feedback.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17D1B1DA0F6330D400694C57 /* Feedback.xib */; }; 83BCB8DE17FC971300760340 /* FFMPEG.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = B09E94350D747F7B0064F138 /* FFMPEG.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 83D0380F24A40DFB004CF90F /* CogAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 83D0380E24A40DF2004CF90F /* CogAssets.xcassets */; }; 83E5E54C18087CA5001F3284 /* miniModeOffTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 83E5E54A18087CA5001F3284 /* miniModeOffTemplate.pdf */; }; 83E5E54D18087CA5001F3284 /* miniModeOnTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 83E5E54B18087CA5001F3284 /* miniModeOnTemplate.pdf */; }; 83EEAB241C965C56002761C5 /* Syntrax.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83EEAAFA1C9651D8002761C5 /* Syntrax.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -669,7 +670,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 8398033B240B4B0C003C54FA /* Sparkle.framework in CopyFiles */, + 838F851F256B4E8B00C3E614 /* Sparkle.framework in CopyFiles */, 838491881808593200E7332D /* NDHotKey.framework in CopyFiles */, 17F561400C3BD4F30019975C /* CogAudio.framework in CopyFiles */, ); @@ -683,7 +684,6 @@ 07E18DF10D62B38400BB0E11 /* NSArray+ShuffleUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+ShuffleUtils.h"; sourceTree = ""; }; 07E18DF20D62B38400BB0E11 /* NSArray+ShuffleUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+ShuffleUtils.m"; sourceTree = ""; }; 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; - 170680620B950158006BA573 /* Growl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Growl.framework; path = ThirdParty/Frameworks/Growl.framework; sourceTree = ""; }; 170B55920D6E5E7B006B9E92 /* StatusImageTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StatusImageTransformer.h; sourceTree = ""; }; 170B55930D6E5E7B006B9E92 /* StatusImageTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StatusImageTransformer.m; sourceTree = ""; }; 171B57D90C091F2B00F6AFAF /* flac.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = flac.icns; sourceTree = ""; }; @@ -896,8 +896,6 @@ 8384912518080F2D00E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = ""; }; 8384913B1808217300E7332D /* randomize.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = randomize.png; path = Images/randomize.png; sourceTree = ""; }; 8384913D18083E4E00E7332D /* filetype.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = filetype.icns; sourceTree = ""; }; - 8384913E18083E4E00E7332D /* icon_blank.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = icon_blank.icns; sourceTree = ""; }; - 8384913F18083E4E00E7332D /* icon_main.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = icon_main.icns; sourceTree = ""; }; 8384914318083EAB00E7332D /* infoTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = infoTemplate.pdf; path = Images/infoTemplate.pdf; sourceTree = ""; }; 8384914418083EAB00E7332D /* missingArt@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "missingArt@2x.png"; path = "Images/missingArt@2x.png"; sourceTree = ""; }; 8384914518083EAB00E7332D /* navigatorTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = navigatorTemplate.pdf; path = Images/navigatorTemplate.pdf; sourceTree = ""; }; @@ -925,7 +923,10 @@ 83849171180843B200E7332D /* stopDockBadgeColorful.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = stopDockBadgeColorful.png; path = Images/stopDockBadgeColorful.png; sourceTree = ""; }; 838491791808585C00E7332D /* NDHotKey.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = NDHotKey.xcodeproj; path = Frameworks/NDHotKey/NDHotKey.xcodeproj; sourceTree = ""; }; 83859520234FEB35004E9946 /* Cog.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Cog.entitlements; sourceTree = ""; }; - 83980339240B4AED003C54FA /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = ThirdParty/Sparkle/Sparkle.framework; sourceTree = ""; }; + 838F84FF25687C5C00C3E614 /* Cog-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Cog-Bridging-Header.h"; sourceTree = ""; }; + 838F850025687C5C00C3E614 /* PlaybackStatusToHiddenTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PlaybackStatusToHiddenTransformer.swift; path = Transformers/PlaybackStatusToHiddenTransformer.swift; sourceTree = ""; }; + 838F851B256B4AC400C3E614 /* icon_blank.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = icon_blank.icns; sourceTree = ""; }; + 838F851D256B4E5E00C3E614 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = ThirdParty/Frameworks/Sparkle.framework; 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 = ""; }; 83AB9031237CEFD300A433D5 /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; }; @@ -950,6 +951,7 @@ 83BC5AD820E4D0D900631CD4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/SpotlightPanel.strings; sourceTree = ""; }; 83BC5ADA20E4D0E900631CD4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Feedback.strings; sourceTree = ""; }; 83BC5ADC20E4D0EC00631CD4 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Feedback.strings; sourceTree = ""; }; + 83D0380E24A40DF2004CF90F /* CogAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = CogAssets.xcassets; 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 = ""; }; @@ -994,7 +996,7 @@ 17BB5CED0B8A86010009ACB1 /* AudioToolbox.framework in Frameworks */, 17BB5CF90B8A86350009ACB1 /* AudioUnit.framework in Frameworks */, 17BB5CFA0B8A86350009ACB1 /* CoreAudio.framework in Frameworks */, - 8398033A240B4AED003C54FA /* Sparkle.framework in Frameworks */, + 838F851E256B4E5E00C3E614 /* Sparkle.framework in Frameworks */, 17BB5CFB0B8A86350009ACB1 /* CoreAudioKit.framework in Frameworks */, 17BB5EA60B8A87850009ACB1 /* IOKit.framework in Frameworks */, ); @@ -1035,9 +1037,9 @@ 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { isa = PBXGroup; children = ( + 838F851D256B4E5E00C3E614 /* Sparkle.framework */, 838491791808585C00E7332D /* NDHotKey.xcodeproj */, 17F5612A0C3BD4DC0019975C /* CogAudio.xcodeproj */, - 170680620B950158006BA573 /* Growl.framework */, 8E6889230AAA403C00AD3950 /* Carbon.framework */, 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, ); @@ -1047,7 +1049,6 @@ 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { isa = PBXGroup; children = ( - 83980339240B4AED003C54FA /* Sparkle.framework */, 17BB5EA50B8A87850009ACB1 /* IOKit.framework */, 17BB5CF60B8A86350009ACB1 /* AudioUnit.framework */, 17BB5CF70B8A86350009ACB1 /* CoreAudio.framework */, @@ -1387,6 +1388,8 @@ 17E0D6130F520F87005B6FED /* FontSizetoLineHeightTransformer.m */, 17E0D6140F520F87005B6FED /* StringToURLTransformer.h */, 17E0D6150F520F87005B6FED /* StringToURLTransformer.m */, + 838F850025687C5C00C3E614 /* PlaybackStatusToHiddenTransformer.swift */, + 838F84FF25687C5C00C3E614 /* Cog-Bridging-Header.h */, ); name = Transformers; sourceTree = ""; @@ -1426,6 +1429,7 @@ 29B97314FDCFA39411CA2CEA /* Cog */ = { isa = PBXGroup; children = ( + 83D0380E24A40DF2004CF90F /* CogAssets.xcassets */, 83859520234FEB35004E9946 /* Cog.entitlements */, 080E96DDFE201D6D7F000001 /* Classes */, 29B97315FDCFA39411CA2CEA /* Other Sources */, @@ -1736,9 +1740,8 @@ 8E75758E09F31D800080F1EE /* Icons */ = { isa = PBXGroup; children = ( + 838F851B256B4AC400C3E614 /* icon_blank.icns */, 8384913D18083E4E00E7332D /* filetype.icns */, - 8384913E18083E4E00E7332D /* icon_blank.icns */, - 8384913F18083E4E00E7332D /* icon_main.icns */, 17D1B2760CF8B2830028F5B5 /* cue.icns */, 17D1B2770CF8B2830028F5B5 /* it.icns */, 17D1B2780CF8B2830028F5B5 /* pls.icns */, @@ -1849,6 +1852,7 @@ TargetAttributes = { 8D1107260486CEB800E47090 = { DevelopmentTeam = 4S876G9VCD; + LastSwiftMigration = 1220; ProvisioningStyle = Automatic; }; }; @@ -2221,7 +2225,6 @@ 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, 17E41E070C130DFF00AC744D /* Credits.html in Resources */, 8384916618083EAB00E7332D /* repeatModeOneTemplate.pdf in Resources */, - 8384914118083E4E00E7332D /* icon_blank.icns in Resources */, 8E7575DB09F31E930080F1EE /* Localizable.strings in Resources */, 177EC04B0B8BC2FF0000BC8C /* next.png in Resources */, 177EC04D0B8BC2FF0000BC8C /* pause.png in Resources */, @@ -2241,7 +2244,6 @@ 8384916018083EAB00E7332D /* playTemplate.pdf in Resources */, 1766C8940B912FB4004A7AE4 /* info_off.png in Resources */, 1766C8950B912FB4004A7AE4 /* info_on.png in Resources */, - 8384914218083E4E00E7332D /* icon_main.icns in Resources */, 8384915E18083EAB00E7332D /* pauseTemplate.pdf in Resources */, 1766C8980B912FB4004A7AE4 /* shuffle_off.png in Resources */, 8384916D18083EAB00E7332D /* volume1Template.pdf in Resources */, @@ -2260,6 +2262,7 @@ 17818A980C0B27AC001C4916 /* mpc.icns in Resources */, 83E5E54D18087CA5001F3284 /* miniModeOnTemplate.pdf in Resources */, 17818A990C0B27AC001C4916 /* shn.icns in Resources */, + 838F851C256B4AC400C3E614 /* icon_blank.icns in Resources */, 17818A9A0C0B27AC001C4916 /* wav.icns in Resources */, 17818A9B0C0B27AC001C4916 /* wv.icns in Resources */, 1791005E0CB44D6D0070BC5C /* Cog.scriptSuite in Resources */, @@ -2269,6 +2272,7 @@ 8384913C1808217300E7332D /* randomize.png in Resources */, 8384915A18083EAB00E7332D /* missingArt@2x.png in Resources */, 17D1B27E0CF8B2830028F5B5 /* it.icns in Resources */, + 83D0380F24A40DFB004CF90F /* CogAssets.xcassets in Resources */, 8384914018083E4E00E7332D /* filetype.icns in Resources */, 8384916918083EAB00E7332D /* shuffleOnTemplate.pdf in Resources */, 17D1B27F0CF8B2830028F5B5 /* pls.icns in Resources */, @@ -2307,7 +2311,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PROJECT_DIR}/Scripts/genversion.sh\"\nif [ \"${EXPANDED_CODE_SIGN_IDENTITY_NAME}\" != \"\" ]; then codesign --verbose --force --deep -o runtime --sign \"${EXPANDED_CODE_SIGN_IDENTITY_NAME}\" \"${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/AutoUpdate.app\"; fi\n"; + shellScript = "\"${PROJECT_DIR}/Scripts/genversion.sh\"\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -2349,6 +2353,7 @@ 07E18DF30D62B38400BB0E11 /* NSArray+ShuffleUtils.m in Sources */, 56C63D910D647DF300EAE25A /* NSComparisonPredicate+CogPredicate.m in Sources */, 56DB084C0D6717DC00453B6A /* NSNumber+CogSort.m in Sources */, + 838F850125687C5C00C3E614 /* PlaybackStatusToHiddenTransformer.swift in Sources */, 56DB08550D67185300453B6A /* NSArray+CogSort.m in Sources */, 170B55940D6E5E7B006B9E92 /* StatusImageTransformer.m in Sources */, 17249F0F0D82E17700F33392 /* ToggleQueueTitleTransformer.m in Sources */, @@ -2641,6 +2646,9 @@ C01FCF4B08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "Cog Icon Precomposed"; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = "Cog color"; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Cog.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; @@ -2659,7 +2667,7 @@ INSTALL_PATH = "$(HOME)/Applications"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; OTHER_CFLAGS = ( "-D__MACOSX__", "-DHAVE_CONFIG_H", @@ -2679,6 +2687,9 @@ PRODUCT_NAME = Cog; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; + SWIFT_OBJC_BRIDGING_HEADER = "Cog-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; WRAPPER_EXTENSION = app; }; name = Debug; @@ -2686,6 +2697,9 @@ C01FCF4C08A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "Cog Icon Precomposed"; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = "Cog color"; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Cog.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; @@ -2701,7 +2715,7 @@ INSTALL_PATH = "$(HOME)/Applications"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; OTHER_CFLAGS = ( "-D__MACOSX__", "-DHAVE_CONFIG_H", @@ -2721,6 +2735,8 @@ PRODUCT_NAME = Cog; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; + SWIFT_OBJC_BRIDGING_HEADER = "Cog-Bridging-Header.h"; + SWIFT_VERSION = 5.0; WRAPPER_EXTENSION = app; }; name = Release; @@ -2760,7 +2776,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.7; + MACOSX_DEPLOYMENT_TARGET = 10.9; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -2799,7 +2815,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.7; + MACOSX_DEPLOYMENT_TARGET = 10.9; SDKROOT = macosx; }; name = Release; diff --git a/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_128.png b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_128.png new file mode 100644 index 000000000..1c2d39724 Binary files /dev/null and b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_128.png differ diff --git a/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_128@2x.png b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_128@2x.png new file mode 100644 index 000000000..20712b3e1 Binary files /dev/null and b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_128@2x.png differ diff --git a/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_16.png b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_16.png new file mode 100644 index 000000000..f640c12a4 Binary files /dev/null and b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_16.png differ diff --git a/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_16@2x.png b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_16@2x.png new file mode 100644 index 000000000..38bdb1429 Binary files /dev/null and b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_16@2x.png differ diff --git a/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_256.png b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_256.png new file mode 100644 index 000000000..a2b6fc251 Binary files /dev/null and b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_256.png differ diff --git a/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_256@2x.png b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_256@2x.png new file mode 100644 index 000000000..0376c166a Binary files /dev/null and b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_256@2x.png differ diff --git a/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_32.png b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_32.png new file mode 100644 index 000000000..09958620f Binary files /dev/null and b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_32.png differ diff --git a/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_32@2x.png b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_32@2x.png new file mode 100644 index 000000000..2a3701e19 Binary files /dev/null and b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_32@2x.png differ diff --git a/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_512.png b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_512.png new file mode 100644 index 000000000..83b2ec2ed Binary files /dev/null and b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_512.png differ diff --git a/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_512@2x.png b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_512@2x.png new file mode 100644 index 000000000..6ddfd4d38 Binary files /dev/null and b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Cog Icon Precomposed_512@2x.png differ diff --git a/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Contents.json b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Contents.json new file mode 100644 index 000000000..ea0bf2b22 --- /dev/null +++ b/CogAssets.xcassets/Cog Icon Precomposed.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "filename" : "Cog Icon Precomposed_16.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "filename" : "Cog Icon Precomposed_16@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "filename" : "Cog Icon Precomposed_32.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "filename" : "Cog Icon Precomposed_32@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "filename" : "Cog Icon Precomposed_128.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "filename" : "Cog Icon Precomposed_128@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "filename" : "Cog Icon Precomposed_256.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "filename" : "Cog Icon Precomposed_256@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "filename" : "Cog Icon Precomposed_512.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "filename" : "Cog Icon Precomposed_512@2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CogAssets.xcassets/Cog color.colorset/Contents.json b/CogAssets.xcassets/Cog color.colorset/Contents.json new file mode 100644 index 000000000..cf905c425 --- /dev/null +++ b/CogAssets.xcassets/Cog color.colorset/Contents.json @@ -0,0 +1,56 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.003", + "green" : "0.506", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "light" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.003", + "green" : "0.431", + "red" : "0.869" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.003", + "green" : "0.620", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CogAssets.xcassets/Contents.json b/CogAssets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/CogAssets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Frameworks/AdPlug/libAdPlug.xcodeproj/project.pbxproj b/Frameworks/AdPlug/libAdPlug.xcodeproj/project.pbxproj index b8779531d..ffad57a29 100644 --- a/Frameworks/AdPlug/libAdPlug.xcodeproj/project.pbxproj +++ b/Frameworks/AdPlug/libAdPlug.xcodeproj/project.pbxproj @@ -566,7 +566,7 @@ TargetAttributes = { 83D3C4D3201C654F005564CB = { CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Manual; + ProvisioningStyle = Automatic; }; }; }; @@ -742,7 +742,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -796,7 +796,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; VERSIONING_SYSTEM = "apple-generic"; @@ -807,10 +807,10 @@ 83D3C4DD201C6550005564CB /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -836,10 +836,10 @@ 83D3C4DE201C6550005564CB /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; diff --git a/Frameworks/FLAC/flac.xcodeproj/project.pbxproj b/Frameworks/FLAC/flac.xcodeproj/project.pbxproj index 4cdde4b9f..4614815a5 100644 --- a/Frameworks/FLAC/flac.xcodeproj/project.pbxproj +++ b/Frameworks/FLAC/flac.xcodeproj/project.pbxproj @@ -424,8 +424,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8DC2EF4F0486A6940098B216 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -489,8 +489,11 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -520,6 +523,7 @@ PRIVATE_HEADERS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/PrivateHeaders"; PRODUCT_BUNDLE_IDENTIFIER = org.xiph.libflac; PRODUCT_NAME = FLAC; + PROVISIONING_PROFILE_SPECIFIER = ""; PUBLIC_HEADERS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/Headers"; SKIP_INSTALL = YES; USER_HEADER_SEARCH_PATHS = ""; @@ -533,7 +537,10 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -561,6 +568,7 @@ PRIVATE_HEADERS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/PrivateHeaders"; PRODUCT_BUNDLE_IDENTIFIER = org.xiph.libflac; PRODUCT_NAME = FLAC; + PROVISIONING_PROFILE_SPECIFIER = ""; PUBLIC_HEADERS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/Headers"; SKIP_INSTALL = YES; USER_HEADER_SEARCH_PATHS = ""; @@ -602,7 +610,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYMROOT = ../../build; @@ -641,7 +649,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SYMROOT = ../../build; USE_SEPERATE_HEADERMAPS = YES; diff --git a/Frameworks/File_Extractor/File_Extractor.xcodeproj/project.pbxproj b/Frameworks/File_Extractor/File_Extractor.xcodeproj/project.pbxproj index a7fd8881b..f931d80cd 100644 --- a/Frameworks/File_Extractor/File_Extractor.xcodeproj/project.pbxproj +++ b/Frameworks/File_Extractor/File_Extractor.xcodeproj/project.pbxproj @@ -623,8 +623,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 8359FF3B17FEF39F0060F3ED = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -791,7 +791,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -837,7 +837,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; }; name = Release; @@ -845,7 +845,10 @@ 8359FF6517FEF39F0060F3ED /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -859,6 +862,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.fileextractor; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; @@ -868,7 +872,10 @@ 8359FF6617FEF39F0060F3ED /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -882,6 +889,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.fileextractor; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; diff --git a/Frameworks/GME/GME.xcodeproj/project.pbxproj b/Frameworks/GME/GME.xcodeproj/project.pbxproj index fa129a3e3..2cd6d447a 100644 --- a/Frameworks/GME/GME.xcodeproj/project.pbxproj +++ b/Frameworks/GME/GME.xcodeproj/project.pbxproj @@ -1284,8 +1284,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8DC2EF4F0486A6940098B216 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -1492,8 +1492,11 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -1512,6 +1515,7 @@ PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.lib.gme; PRODUCT_NAME = GME; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SHARED_PRECOMPS_DIR = ""; SKIP_INSTALL = YES; @@ -1526,7 +1530,10 @@ buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -1543,6 +1550,7 @@ PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.lib.gme; PRODUCT_NAME = GME; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SHARED_PRECOMPS_DIR = ""; SKIP_INSTALL = YES; @@ -1585,7 +1593,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; OBJROOT = ../../build; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -1625,7 +1633,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; OBJROOT = ../../build; SDKROOT = macosx; SYMROOT = ../../build; diff --git a/Frameworks/GME/vgmplay/chips/yam.c b/Frameworks/GME/vgmplay/chips/yam.c index 9ee132ac9..700c52c26 100644 --- a/Frameworks/GME/vgmplay/chips/yam.c +++ b/Frameworks/GME/vgmplay/chips/yam.c @@ -32,8 +32,12 @@ #ifndef _WIN32 #define __cdecl +#ifdef __aarch64__ +#define __fastcall +#else #define __fastcall __attribute__((regparm(3))) #endif +#endif /* No dynarec for x86_64 yet */ #if defined(_WIN32) || defined(__i386__) diff --git a/Frameworks/HighlyAdvanced/HighlyAdvanced.xcodeproj/project.pbxproj b/Frameworks/HighlyAdvanced/HighlyAdvanced.xcodeproj/project.pbxproj index 816e37af2..85d8691c2 100644 --- a/Frameworks/HighlyAdvanced/HighlyAdvanced.xcodeproj/project.pbxproj +++ b/Frameworks/HighlyAdvanced/HighlyAdvanced.xcodeproj/project.pbxproj @@ -239,8 +239,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 8343793417F97BDB00584396 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -353,7 +353,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -398,7 +398,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; }; name = Release; @@ -406,7 +406,10 @@ 8343795E17F97BDB00584396 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -419,6 +422,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.highlyadvanced; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; @@ -428,7 +432,10 @@ 8343795F17F97BDB00584396 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -438,6 +445,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.highlyadvanced; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; diff --git a/Frameworks/HighlyExperimental/HighlyExperimental.xcodeproj/project.pbxproj b/Frameworks/HighlyExperimental/HighlyExperimental.xcodeproj/project.pbxproj index 878557fe8..bbfe64a0d 100644 --- a/Frameworks/HighlyExperimental/HighlyExperimental.xcodeproj/project.pbxproj +++ b/Frameworks/HighlyExperimental/HighlyExperimental.xcodeproj/project.pbxproj @@ -178,8 +178,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 8360EF0F17F92C91005208A4 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -290,7 +290,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; }; name = Debug; @@ -334,14 +334,17 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; }; name = Release; }; 8360EF3917F92C91005208A4 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -357,6 +360,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.highlyexperimental; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; }; @@ -365,7 +369,10 @@ 8360EF3A17F92C91005208A4 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -380,6 +387,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.highlyexperimental; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; }; diff --git a/Frameworks/HighlyQuixotic/HighlyQuixotic.xcodeproj/project.pbxproj b/Frameworks/HighlyQuixotic/HighlyQuixotic.xcodeproj/project.pbxproj index fa60f2d87..94d8a1091 100644 --- a/Frameworks/HighlyQuixotic/HighlyQuixotic.xcodeproj/project.pbxproj +++ b/Frameworks/HighlyQuixotic/HighlyQuixotic.xcodeproj/project.pbxproj @@ -150,8 +150,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 834378DD17F96E2600584396 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -260,7 +260,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -311,7 +311,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SKIP_INSTALL = YES; }; @@ -320,7 +320,10 @@ 8343790717F96E2600584396 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -329,6 +332,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.highlyquixotic; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; WRAPPER_EXTENSION = framework; }; @@ -337,7 +341,10 @@ 8343790817F96E2600584396 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -346,6 +353,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.highlyquixotic; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; WRAPPER_EXTENSION = framework; }; diff --git a/Frameworks/HighlyTheoretical/HighlyTheoretical.xcodeproj/project.pbxproj b/Frameworks/HighlyTheoretical/HighlyTheoretical.xcodeproj/project.pbxproj index 00cade9ea..94e47b1fa 100644 --- a/Frameworks/HighlyTheoretical/HighlyTheoretical.xcodeproj/project.pbxproj +++ b/Frameworks/HighlyTheoretical/HighlyTheoretical.xcodeproj/project.pbxproj @@ -192,8 +192,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 8343786D17F9658E00584396 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -308,7 +308,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -361,7 +361,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; }; name = Release; @@ -369,7 +369,10 @@ 8343789717F9658E00584396 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -378,6 +381,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.highlytheoretical; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; @@ -387,7 +391,10 @@ 8343789817F9658E00584396 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -396,6 +403,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.highlytheoretical; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; diff --git a/Frameworks/HighlyTheoretical/HighlyTheoretical/Core/yam.c b/Frameworks/HighlyTheoretical/HighlyTheoretical/Core/yam.c index e09c04933..031b7ee30 100644 --- a/Frameworks/HighlyTheoretical/HighlyTheoretical/Core/yam.c +++ b/Frameworks/HighlyTheoretical/HighlyTheoretical/Core/yam.c @@ -24,8 +24,12 @@ #ifndef _WIN32 #define __cdecl +#ifdef __aarch64__ +#define __fastcall +#else #define __fastcall __attribute__((regparm(3))) #endif +#endif /* No dynarec for x86_64 yet */ #if defined(_WIN32) || defined(__i386__) diff --git a/Frameworks/HivelyPlayer/HivelyPlayer.xcodeproj/project.pbxproj b/Frameworks/HivelyPlayer/HivelyPlayer.xcodeproj/project.pbxproj index 8a2b8559e..beeb8ac7f 100644 --- a/Frameworks/HivelyPlayer/HivelyPlayer.xcodeproj/project.pbxproj +++ b/Frameworks/HivelyPlayer/HivelyPlayer.xcodeproj/project.pbxproj @@ -132,8 +132,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 836FB555182053D700B3AD2D = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -238,7 +238,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -285,7 +285,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SKIP_INSTALL = YES; }; @@ -294,7 +294,10 @@ 836FB57F182053D700B3AD2D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -302,6 +305,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.hivelyplayer; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; WRAPPER_EXTENSION = framework; }; @@ -310,7 +314,10 @@ 836FB580182053D700B3AD2D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -318,6 +325,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.hivelyplayer; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; WRAPPER_EXTENSION = framework; }; diff --git a/Frameworks/HivelyPlayer/HivelyPlayer/hvl_replay.h b/Frameworks/HivelyPlayer/HivelyPlayer/hvl_replay.h index c3716085e..5599cae34 100644 --- a/Frameworks/HivelyPlayer/HivelyPlayer/hvl_replay.h +++ b/Frameworks/HivelyPlayer/HivelyPlayer/hvl_replay.h @@ -18,8 +18,13 @@ typedef char TEXT; #ifdef _WIN32 typedef int BOOL; #else +#ifdef __aarch64__ +#include +typedef bool BOOL; +#else typedef signed char BOOL; #endif +#endif #define TRUE 1 #define FALSE 0 diff --git a/Frameworks/MPCDec/MPCDec.xcodeproj/project.pbxproj b/Frameworks/MPCDec/MPCDec.xcodeproj/project.pbxproj index 584fe36bf..923909044 100644 --- a/Frameworks/MPCDec/MPCDec.xcodeproj/project.pbxproj +++ b/Frameworks/MPCDec/MPCDec.xcodeproj/project.pbxproj @@ -232,8 +232,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8DC2EF4F0486A6940098B216 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -306,8 +306,11 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -321,6 +324,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.lib.mpcdec; PRODUCT_NAME = mpcdec; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; @@ -332,7 +336,10 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -343,6 +350,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.lib.mpcdec; PRODUCT_NAME = mpcdec; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; USER_HEADER_SEARCH_PATHS = Files/include/; @@ -383,7 +391,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYMROOT = ../../build; @@ -421,7 +429,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SYMROOT = ../../build; }; diff --git a/Frameworks/NDHotKey/NDHotKey.xcodeproj/project.pbxproj b/Frameworks/NDHotKey/NDHotKey.xcodeproj/project.pbxproj index 1d4192cea..88d18abb5 100644 --- a/Frameworks/NDHotKey/NDHotKey.xcodeproj/project.pbxproj +++ b/Frameworks/NDHotKey/NDHotKey.xcodeproj/project.pbxproj @@ -155,8 +155,8 @@ ORGANIZATIONNAME = "dmitry.promsky@gmail.com"; TargetAttributes = { 32F1615514E6BB3B00D6AB2F = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -260,7 +260,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYMROOT = ../../build; @@ -303,7 +303,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SYMROOT = ../../build; }; @@ -312,7 +312,10 @@ 32F1616C14E6BB3B00D6AB2F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -322,6 +325,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "ND.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; @@ -331,7 +335,10 @@ 32F1616D14E6BB3B00D6AB2F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -341,6 +348,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "ND.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; diff --git a/Frameworks/Ogg/macosx/Ogg.xcodeproj/project.pbxproj b/Frameworks/Ogg/macosx/Ogg.xcodeproj/project.pbxproj index ae3e66d44..6c56d692a 100644 --- a/Frameworks/Ogg/macosx/Ogg.xcodeproj/project.pbxproj +++ b/Frameworks/Ogg/macosx/Ogg.xcodeproj/project.pbxproj @@ -176,8 +176,8 @@ DevelopmentTeam = N6E749HJ2X; }; 8D07F2BC0486CC7A007CD1D0 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -259,11 +259,11 @@ 730F235509181A3A00AB638C /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - DEVELOPMENT_TEAM = N6E749HJ2X; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -273,7 +273,7 @@ GCC_PREFIX_HEADER = Ogg_Prefix.pch; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_BUNDLE_IDENTIFIER = org.xiph.ogg; PRODUCT_NAME = Ogg; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -286,11 +286,11 @@ 730F235609181A3A00AB638C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = YES; - DEVELOPMENT_TEAM = N6E749HJ2X; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -299,7 +299,7 @@ GCC_PREFIX_HEADER = Ogg_Prefix.pch; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_BUNDLE_IDENTIFIER = org.xiph.ogg; PRODUCT_NAME = Ogg; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/Frameworks/OpenMPT/OpenMPT/LICENSE b/Frameworks/OpenMPT/OpenMPT/LICENSE index 834603bf0..e81fc3ff8 100644 --- a/Frameworks/OpenMPT/OpenMPT/LICENSE +++ b/Frameworks/OpenMPT/OpenMPT/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2019, OpenMPT contributors +Copyright (c) 2004-2020, OpenMPT contributors Copyright (c) 1997-2003, Olivier Lapicque All rights reserved. diff --git a/Frameworks/OpenMPT/OpenMPT/Makefile b/Frameworks/OpenMPT/OpenMPT/Makefile index 54d039d50..9d77ab7cd 100644 --- a/Frameworks/OpenMPT/OpenMPT/Makefile +++ b/Frameworks/OpenMPT/OpenMPT/Makefile @@ -54,7 +54,7 @@ # ONLY_TEST=0 Only build the test suite. # STRICT=0 Treat warnings as errors. # MODERN=0 Pass more modern compiler options. -# STDCXX=c++11 C++ standard version (only for GCC and clang) +# STDCXX=c++14 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 @@ -69,6 +69,11 @@ # NO_VORBIS=1 Avoid using libvorbis, even if found # NO_VORBISFILE=1 Avoid using libvorbisfile, even if found # +# LOCAL_ZLIB=1 Build local copy of zlib, even if found +# LOCAL_MPG123=1 Build local copy of libmpg123, even if found +# LOCAL_OGG=1 Build local copy of libogg, even if found +# LOCAL_VORBIS=1 Build local copy of libvorbis, even if found +# # NO_MINIMP3=1 Do not fallback to minimp3 # NO_STBVORBIS=1 Do not fallback to stb_vorbis # @@ -86,7 +91,6 @@ # (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 @@ -158,6 +162,7 @@ TEST=1 ONLY_TEST=0 SOSUFFIX=.so SOSUFFIXWINDOWS=0 +NO_SHARED_LINKER_FLAG=0 OPENMPT123=1 MODERN=0 STRICT=0 @@ -290,7 +295,7 @@ INSTALL_LIB = $(INSTALL) -m 0644 INSTALL_DATA_DIR = $(INSTALL_DIR) INSTALL_MAKE_DIR += -m 0755 -CPPFLAGS += -Icommon -I. -Iinclude/modplug/include -Iinclude +CPPFLAGS += -Icommon -I. -Iinclude ifeq ($(MPT_COMPILER_GENERIC),1) @@ -407,6 +412,30 @@ ifeq ($(HACK_ARCHIVE_SUPPORT),1) NO_ZLIB:=1 endif +ifeq ($(LOCAL_ZLIB),1) +CPPFLAGS_ZLIB := -DMPT_WITH_ZLIB +LDFLAGS_ZLIB := +LDLIBS_ZLIB := +CPPFLAGS_ZLIB += -Iinclude/zlib/ +LOCAL_ZLIB_SOURCES := +LOCAL_ZLIB_SOURCES += include/zlib/adler32.c +LOCAL_ZLIB_SOURCES += include/zlib/compress.c +LOCAL_ZLIB_SOURCES += include/zlib/crc32.c +LOCAL_ZLIB_SOURCES += include/zlib/deflate.c +LOCAL_ZLIB_SOURCES += include/zlib/gzclose.c +LOCAL_ZLIB_SOURCES += include/zlib/gzlib.c +LOCAL_ZLIB_SOURCES += include/zlib/gzread.c +LOCAL_ZLIB_SOURCES += include/zlib/gzwrite.c +LOCAL_ZLIB_SOURCES += include/zlib/infback.c +LOCAL_ZLIB_SOURCES += include/zlib/inffast.c +LOCAL_ZLIB_SOURCES += include/zlib/inflate.c +LOCAL_ZLIB_SOURCES += include/zlib/inftrees.c +LOCAL_ZLIB_SOURCES += include/zlib/trees.c +LOCAL_ZLIB_SOURCES += include/zlib/uncompr.c +LOCAL_ZLIB_SOURCES += include/zlib/zutil.c +include/zlib/%.o : CFLAGS+=$(CFLAGS_SILENT) -DSTDC -DZ_HAVE_UNISTD_H +include/zlib/%.test.o : CFLAGS+=$(CFLAGS_SILENT) -DSTDC -DZ_HAVE_UNISTD_H +else ifeq ($(NO_ZLIB),1) else #LDLIBS += -lz @@ -424,7 +453,44 @@ endif NO_ZLIB:=1 endif endif +endif +ifeq ($(LOCAL_MPG123),1) +CPPFLAGS_MPG123 := -DMPT_WITH_MPG123 +LDFLAGS_MPG123 := +LDLIBS_MPG123 := +CPPFLAGS_MPG123 += -Iinclude/mpg123/src/libmpg123/ -Iinclude/mpg123/src/compat/ -Iinclude/mpg123/src/ -Iinclude/mpg123/ports/makefile/ +LOCAL_MPG123_SOURCES := +LOCAL_MPG123_SOURCES += include/mpg123/src/compat/compat.c +LOCAL_MPG123_SOURCES += include/mpg123/src/compat/compat_str.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/dct64.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/equalizer.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/feature.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/format.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/frame.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/icy.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/icy2utf8.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/id3.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/index.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/layer1.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/layer2.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/layer3.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/libmpg123.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/ntom.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/optimize.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/parse.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/readers.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/stringbuf.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/synth.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/synth_8bit.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/synth_real.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/synth_s32.c +LOCAL_MPG123_SOURCES += include/mpg123/src/libmpg123/tabinit.c +include/mpg123/src/compat/%.o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC +include/mpg123/src/compat/%.test.o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC +include/mpg123/src/libmpg123/%.o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC +include/mpg123/src/libmpg123/%.test.o : CFLAGS+=$(CFLAGS_SILENT) -DOPT_GENERIC +else ifeq ($(NO_MPG123),1) else #LDLIBS += -lmpg123 @@ -442,7 +508,19 @@ endif NO_MPG123:=1 endif endif +endif +ifeq ($(LOCAL_OGG),1) +CPPFLAGS_OGG := -DMPT_WITH_OGG +LDFLAGS_OGG := +LDLIBS_OGG := +CPPFLAGS_OGG += -Iinclude/ogg/include/ -Iinclude/ogg/ports/makefile/ +LOCAL_OGG_SOURCES := +LOCAL_OGG_SOURCES += include/ogg/src/bitwise.c +LOCAL_OGG_SOURCES += include/ogg/src/framing.c +include/ogg/src/%.o : CFLAGS+=$(CFLAGS_SILENT) +include/ogg/src/%.test.o : CFLAGS+=$(CFLAGS_SILENT) +else ifeq ($(NO_OGG),1) else #LDLIBS += -logg @@ -460,7 +538,39 @@ endif NO_OGG:=1 endif endif +endif +ifeq ($(LOCAL_VORBIS),1) +CPPFLAGS_VORBIS := -DMPT_WITH_VORBIS +LDFLAGS_VORBIS := +LDLIBS_VORBIS := +CPPFLAGS_VORBIS += -Iinclude/vorbis/include/ -Iinclude/vorbis/lib/ -DHAVE_ALLOCA_H +LOCAL_VORBIS_SOURCES := +LOCAL_VORBIS_SOURCES += include/vorbis/lib/analysis.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/bitrate.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/block.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/codebook.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/envelope.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/floor0.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/floor1.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/info.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/lookup.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/lpc.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/lsp.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/mapping0.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/mdct.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/psy.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/registry.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/res0.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/sharedbook.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/smallft.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/synthesis.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/vorbisenc.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/vorbisfile.c +LOCAL_VORBIS_SOURCES += include/vorbis/lib/window.c +include/vorbis/lib/%.o : CFLAGS+=$(CFLAGS_SILENT) +include/vorbis/lib/%.test.o : CFLAGS+=$(CFLAGS_SILENT) +else ifeq ($(NO_VORBIS),1) else #LDLIBS += -lvorbis @@ -478,7 +588,13 @@ endif NO_VORBIS:=1 endif endif +endif +ifeq ($(LOCAL_VORBIS),1) +CPPFLAGS_VORBISFILE := -DMPT_WITH_VORBISFILE +LDFLAGS_VORBISFILE := +LDLIBS_VORBISFILE := +else ifeq ($(NO_VORBISFILE),1) else #LDLIBS += -lvorbisfile @@ -496,15 +612,15 @@ endif NO_VORBISFILE:=1 endif endif +endif ifeq ($(NO_SDL2),1) else #LDLIBS += -lsdl2 -ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists sdl2 && echo yes),yes) -CPPFLAGS_SDL := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I sdl2 ) -DMPT_WITH_SDL2 -LDFLAGS_SDL := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L sdl2 ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other sdl2 ) -LDLIBS_SDL := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l sdl2 ) -NO_SDL:=1 +ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists 'sdl2 >= 2.0.4' && echo yes),yes) +CPPFLAGS_SDL2 := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I 'sdl2 >= 2.0.4' ) -DMPT_WITH_SDL2 +LDFLAGS_SDL2 := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L 'sdl2 >= 2.0.4' ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other 'sdl2 >= 2.0.4' ) +LDLIBS_SDL2 := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-l 'sdl2 >= 2.0.4' ) else ifeq ($(FORCE_DEPS),1) $(error sdl2 not found) @@ -515,23 +631,6 @@ NO_SDL2:=1 endif endif -ifeq ($(NO_SDL),1) -else -#LDLIBS += -lsdl -ifeq ($(shell pkg-config$(TOOLCHAIN_SUFFIX) --exists sdl && echo yes),yes) -CPPFLAGS_SDL := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --cflags-only-I sdl ) -DMPT_WITH_SDL -LDFLAGS_SDL := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-L sdl ) $(shell pkg-config$(TOOLCHAIN_SUFFIX) --libs-only-other sdl ) -LDLIBS_SDL := $(shell pkg-config$(TOOLCHAIN_SUFFIX) --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 @@ -629,7 +728,7 @@ CPPFLAGS += -DMPT_BUILD_HACK_ARCHIVE_SUPPORT endif CPPCHECK_FLAGS += -j $(NUMTHREADS) -CPPCHECK_FLAGS += --std=c99 --std=c++11 +CPPCHECK_FLAGS += --std=c99 --std=c++14 CPPCHECK_FLAGS += --quiet CPPCHECK_FLAGS += --enable=warning --inline-suppr --template='{file}:{line}: warning: {severity}: {message} [{id}]' CPPCHECK_FLAGS += --suppress=missingIncludeSystem @@ -639,9 +738,9 @@ CPPFLAGS += $(CPPFLAGS_ZLIB) $(CPPFLAGS_MPG123) $(CPPFLAGS_OGG) $(CPPFLAGS_VORBI 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) $(CPPFLAGS_ALLEGRO42) -LDFLAGS_OPENMPT123 += $(LDFLAGS_SDL2) $(LDFLAGS_SDL) $(LDFLAGS_PORTAUDIO) $(LDFLAGS_PULSEAUDIO) $(LDFLAGS_FLAC) $(LDFLAGS_SNDFILE) $(LDFLAGS_ALLEGRO42) -LDLIBS_OPENMPT123 += $(LDLIBS_SDL2) $(LDLIBS_SDL) $(LDLIBS_PORTAUDIO) $(LDLIBS_PULSEAUDIO) $(LDLIBS_FLAC) $(LDLIBS_SNDFILE) $(LDLIBS_ALLEGRO42) +CPPFLAGS_OPENMPT123 += $(CPPFLAGS_SDL2) $(CPPFLAGS_PORTAUDIO) $(CPPFLAGS_PULSEAUDIO) $(CPPFLAGS_FLAC) $(CPPFLAGS_SNDFILE) $(CPPFLAGS_ALLEGRO42) +LDFLAGS_OPENMPT123 += $(LDFLAGS_SDL2) $(LDFLAGS_PORTAUDIO) $(LDFLAGS_PULSEAUDIO) $(LDFLAGS_FLAC) $(LDFLAGS_SNDFILE) $(LDFLAGS_ALLEGRO42) +LDLIBS_OPENMPT123 += $(LDLIBS_SDL2) $(LDLIBS_PORTAUDIO) $(LDLIBS_PULSEAUDIO) $(LDLIBS_FLAC) $(LDLIBS_SNDFILE) $(LDLIBS_ALLEGRO42) %: %.o @@ -761,14 +860,23 @@ LIBOPENMPT_CXX_SOURCES += \ include/miniz/miniz.o : CFLAGS+=$(CFLAGS_SILENT) include/miniz/miniz.test.o : CFLAGS+=$(CFLAGS_SILENT) +ifeq ($(LOCAL_ZLIB),1) +LIBOPENMPT_C_SOURCES += $(LOCAL_ZLIB_SOURCES) +LIBOPENMPTTEST_C_SOURCES += $(LOCAL_ZLIB_SOURCES) +else ifeq ($(NO_ZLIB),1) LIBOPENMPT_C_SOURCES += include/miniz/miniz.c LIBOPENMPTTEST_C_SOURCES += include/miniz/miniz.c CPPFLAGS += -DMPT_WITH_MINIZ endif +endif include/minimp3/minimp3.o : CFLAGS+=$(CFLAGS_SILENT) include/minimp3/minimp3.test.o : CFLAGS+=$(CFLAGS_SILENT) +ifeq ($(LOCAL_MPG123),1) +LIBOPENMPT_C_SOURCES += $(LOCAL_MPG123_SOURCES) +LIBOPENMPTTEST_C_SOURCES += $(LOCAL_MPG123_SOURCES) +else ifeq ($(NO_MPG123),1) ifeq ($(NO_MINIMP3),1) else @@ -777,9 +885,18 @@ LIBOPENMPTTEST_C_SOURCES += include/minimp3/minimp3.c CPPFLAGS += -DMPT_WITH_MINIMP3 endif endif +endif include/stb_vorbis/stb_vorbis.o : CFLAGS+=$(CFLAGS_SILENT) include/stb_vorbis/stb_vorbis.test.o : CFLAGS+=$(CFLAGS_SILENT) +ifeq ($(LOCAL_VORBIS),1) +ifeq ($(LOCAL_OGG),1) +LIBOPENMPT_C_SOURCES += $(LOCAL_OGG_SOURCES) +LIBOPENMPTTEST_C_SOURCES += $(LOCAL_OGG_SOURCES) +endif +LIBOPENMPT_C_SOURCES += $(LOCAL_VORBIS_SOURCES) +LIBOPENMPTTEST_C_SOURCES += $(LOCAL_VORBIS_SOURCES) +else ifeq ($(NO_OGG),1) ifeq ($(NO_STBVORBIS),1) else @@ -807,6 +924,7 @@ else endif endif endif +endif LIBOPENMPT_OBJECTS += $(LIBOPENMPT_CXX_SOURCES:.cpp=.o) $(LIBOPENMPT_C_SOURCES:.c=.o) LIBOPENMPT_DEPENDS = $(LIBOPENMPT_OBJECTS:.o=.d) @@ -820,18 +938,6 @@ 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)) \ @@ -879,7 +985,6 @@ all: ifeq ($(DYNLINK),1) OUTPUTS += bin/libopenmpt$(SOSUFFIX) -OUTPUTS += bin/libopenmpt_modplug$(SOSUFFIX) endif ifeq ($(SHARED_LIB),1) OUTPUTS += bin/libopenmpt$(SOSUFFIX) @@ -937,22 +1042,30 @@ MISC_OUTPUTS += libopenmpt$(SOSUFFIX) MISC_OUTPUTS += bin/.docs MISC_OUTPUTS += bin/libopenmpt_test$(EXESUFFIX) MISC_OUTPUTS += bin/libopenmpt_test.wasm +MISC_OUTPUTS += bin/libopenmpt_test.wasm.js MISC_OUTPUTS += bin/libopenmpt_test.js.mem MISC_OUTPUTS += bin/made.docs MISC_OUTPUTS += bin/$(LIBOPENMPT_SONAME) MISC_OUTPUTS += bin/libopenmpt.wasm +MISC_OUTPUTS += bin/libopenmpt.wasm.js MISC_OUTPUTS += bin/libopenmpt.js.mem MISC_OUTPUTS += bin/libopenmpt_example_c.wasm +MISC_OUTPUTS += bin/libopenmpt_example_c.wasm.js MISC_OUTPUTS += bin/libopenmpt_example_c.js.mem MISC_OUTPUTS += bin/libopenmpt_example_c_mem.wasm +MISC_OUTPUTS += bin/libopenmpt_example_c_mem.wasm.js MISC_OUTPUTS += bin/libopenmpt_example_c_mem.js.mem MISC_OUTPUTS += bin/libopenmpt_example_c_pipe.wasm +MISC_OUTPUTS += bin/libopenmpt_example_c_pipe.wasm.js MISC_OUTPUTS += bin/libopenmpt_example_c_pipe.js.mem MISC_OUTPUTS += bin/libopenmpt_example_c_probe.wasm +MISC_OUTPUTS += bin/libopenmpt_example_c_probe.wasm.js MISC_OUTPUTS += bin/libopenmpt_example_c_probe.js.mem MISC_OUTPUTS += bin/libopenmpt_example_c_stdout.wasm +MISC_OUTPUTS += bin/libopenmpt_example_c_stdout.wasm.js MISC_OUTPUTS += bin/libopenmpt_example_c_stdout.js.mem MISC_OUTPUTS += bin/libopenmpt_example_c_unsafe.wasm +MISC_OUTPUTS += bin/libopenmpt_example_c_unsafe.wasm.js MISC_OUTPUTS += bin/libopenmpt_example_c_unsafe.js.mem MISC_OUTPUTS += bin/openmpt.a #old @@ -1095,24 +1208,6 @@ ifeq ($(MPT_WITH_DOXYGEN),1) $(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 @@ -1209,6 +1304,7 @@ bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION).makefile.tar: bin/dist.mk bin 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 ./.clang-format bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/.clang-format 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 @@ -1229,7 +1325,6 @@ bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION).makefile.tar: bin/dist.mk bin svn export ./include/cwsdpmi bin/dist-tar/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/include/cwsdpmi 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 @@ -1248,6 +1343,7 @@ bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip: bin/dist.mk bin/svn 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 ./.clang-format bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/.clang-format --native-eol CRLF svn export ./bin bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin --native-eol CRLF svn export ./build/genie/def bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/genie/def --native-eol CRLF svn export ./build/premake/def bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/premake/def --native-eol CRLF @@ -1257,13 +1353,14 @@ bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip: bin/dist.mk bin/svn 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/vs2017win10 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017win10 - 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/vs2017win7 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017win7 --native-eol CRLF + svn export ./build/vs2017win81 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017win81 --native-eol CRLF + svn export ./build/vs2017win10 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017win10 --native-eol CRLF + svn export ./build/vs2017uwp bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2017uwp --native-eol CRLF + svn export ./build/vs2019win7 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2019win7 --native-eol CRLF + svn export ./build/vs2019win81 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2019win81 --native-eol CRLF + svn export ./build/vs2019win10 bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2019win10 --native-eol CRLF + svn export ./build/vs2019uwp bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/build/vs2019uwp --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 ./doc/contributing.md bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/doc/contributing.md --native-eol CRLF @@ -1282,8 +1379,6 @@ bin/dist-zip/libopenmpt-$(DIST_LIBOPENMPT_VERSION).msvc.zip: bin/dist.mk bin/svn 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 @@ -1314,15 +1409,14 @@ bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION).dev.js.tar: 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 mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin + mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/all + cp bin/stage/all/libopenmpt.js bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/all/libopenmpt.js + cp bin/stage/all/libopenmpt.js.mem bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/all/libopenmpt.js.mem + cp bin/stage/all/libopenmpt.wasm bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/all/libopenmpt.wasm + cp bin/stage/all/libopenmpt.wasm.js bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/all/libopenmpt.wasm.js mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/wasm cp bin/stage/wasm/libopenmpt.js bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/wasm/libopenmpt.js cp bin/stage/wasm/libopenmpt.wasm bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/wasm/libopenmpt.wasm - mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/asmjs128m - cp bin/stage/asmjs128m/libopenmpt.js bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/asmjs128m/libopenmpt.js - cp bin/stage/asmjs128m/libopenmpt.js.mem bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/asmjs128m/libopenmpt.js.mem - mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/asmjs - cp bin/stage/asmjs/libopenmpt.js bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/asmjs/libopenmpt.js - cp bin/stage/asmjs/libopenmpt.js.mem bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/asmjs/libopenmpt.js.mem mkdir -p bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/js cp bin/stage/js/libopenmpt.js bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/js/libopenmpt.js cp bin/stage/js/libopenmpt.js.mem bin/dist-js/libopenmpt-$(DIST_LIBOPENMPT_VERSION)/bin/js/libopenmpt.js.mem @@ -1361,16 +1455,16 @@ bin/libopenmpt.a: $(LIBOPENMPT_OBJECTS) bin/libopenmpt$(SOSUFFIX): $(LIBOPENMPT_OBJECTS) $(INFO) [LD] $@ +ifeq ($(NO_SHARED_LINKER_FLAG),1) + $(SILENT)$(LINK.cc) $(LIBOPENMPT_LDFLAGS) $(SO_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ +else $(SILENT)$(LINK.cc) -shared $(LIBOPENMPT_LDFLAGS) $(SO_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ +endif 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 $(SO_LDFLAGS) $(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 $< > $@ diff --git a/Frameworks/OpenMPT/OpenMPT/README.md b/Frameworks/OpenMPT/OpenMPT/README.md index a9d8d0a18..94c32e0fb 100644 --- a/Frameworks/OpenMPT/OpenMPT/README.md +++ b/Frameworks/OpenMPT/OpenMPT/README.md @@ -35,44 +35,19 @@ How to compile - Supported Visual Studio versions: - - Visual Studio 2015 Update 3 Community/Professional/Enterprise + - Visual Studio 2017 and 2019 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. + To compile the project, open `build/vsVERSIONwin7/OpenMPT.sln` (VERSION + being 2017 or 2019) and hit the compile button. Other target systems can + be found in the `vs2017*` and `vs2019*` sibling folders. - OpenMPT requires the compile host system to be 64bit x86-64. - - 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 Windows 8.1 SDK is required to build OpenMPT with Visual Studio 2017 + (this is included with Visual Studio, however may need to be selected + explicitly during setup). - - The ASIO SDK is needed for compiling with ASIO support. - - If you don't want this, comment out `#define MPT_WITH_ASIO` in the file - `common/BuildSettings.h`. - - The ASIO SDK can be downloaded automatically on Windows 7 or later 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: - - - Visit - [steinberg.net](https://www.steinberg.net/en/company/developers.html) to - download the SDK. - - - 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. - - If you need further help with the ASIO SDK, get in touch with the - main OpenMPT developers. + - Microsoft Foundation Classes (MFC) are required to build OpenMPT. ### libopenmpt and openmpt123 @@ -104,15 +79,13 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`. - 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/`. - Projects that target Windows 10 1709 Desktop (10.0.16299.0, including - ARM and ARM64) or later versions are available in - `build/vsVERSIONwin10/`. + - You will find solutions for Visual Studio 2017 and 2019 in the + corresponding `build/vsVERSIONwin7/` folder. + Projects that target Windows 10 Desktop (including ARM and ARM64) are + available in `build/vsVERSIONwin10/` (Visual Studio 2017 requires SDK + version 10.0.16299). Minimal projects that target Windows 10 UWP are available in - `build/winstore82/`. + `build/vsVERSIONuwp/`. Most projects are supported with any of the mentioned Visual Studio verions, with the following exceptions: @@ -161,7 +134,7 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`. - mingw-w64: - The required version is at least 4.8. + The required compiler version is at least GCC 7. make CONFIG=mingw64-win32 # for win32 @@ -172,9 +145,9 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`. The minimum required compiler versions are: - - gcc 4.8 + - gcc 7 - - clang 3.6 + - clang 5 The Makefile requires pkg-config for native builds. For sound output in openmpt123, PortAudio or SDL is required. @@ -256,7 +229,7 @@ For detailed requirements, see `libopenmpt/dox/quickstart.md`. - other compilers: - To compile libopenmpt with other C++11 compliant compilers, run: + To compile libopenmpt with other C++14 compliant compilers, run: make CONFIG=generic diff --git a/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Android.mk b/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Android.mk index a6b6f2187..4661aef61 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Android.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Android.mk @@ -7,7 +7,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := openmpt LOCAL_CFLAGS +=#-std=c99 -LOCAL_CPPFLAGS += -std=c++11 -fexceptions -frtti +LOCAL_CPPFLAGS += -std=c++17 -fexceptions -frtti LOCAL_CPP_FEATURES += exceptions rtti @@ -111,12 +111,12 @@ LOCAL_SRC_FILES += \ libopenmpt/libopenmpt_cxx.cpp \ libopenmpt/libopenmpt_impl.cpp \ libopenmpt/libopenmpt_ext_impl.cpp \ + soundbase/Dither.cpp \ soundlib/AudioCriticalSection.cpp \ soundlib/ContainerMMCMP.cpp \ soundlib/ContainerPP20.cpp \ soundlib/ContainerUMX.cpp \ soundlib/ContainerXPK.cpp \ - soundlib/Dither.cpp \ soundlib/Dlsbank.cpp \ soundlib/Fastmix.cpp \ soundlib/InstrumentExtensions.cpp \ @@ -178,10 +178,12 @@ LOCAL_SRC_FILES += \ soundlib/RowVisitor.cpp \ soundlib/S3MTools.cpp \ soundlib/SampleFormats.cpp \ + soundlib/SampleFormatBRR.cpp \ soundlib/SampleFormatFLAC.cpp \ soundlib/SampleFormatMediaFoundation.cpp \ soundlib/SampleFormatMP3.cpp \ soundlib/SampleFormatOpus.cpp \ + soundlib/SampleFormatSFZ.cpp \ soundlib/SampleFormatVorbis.cpp \ soundlib/SampleIO.cpp \ soundlib/Sndfile.cpp \ @@ -193,7 +195,7 @@ LOCAL_SRC_FILES += \ soundlib/UpgradeModule.cpp \ soundlib/Tables.cpp \ soundlib/Tagging.cpp \ - soundlib/tuningbase.cpp \ + soundlib/TinyFFT.cpp \ soundlib/tuningCollection.cpp \ soundlib/tuning.cpp \ soundlib/WAVTools.cpp \ diff --git a/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Application.mk b/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Application.mk index 7ca8cd0df..6b0c65285 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Application.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/android_ndk/Application.mk @@ -1,5 +1,5 @@ APP_CFLAGS :=#-std=c99 -APP_CPPFLAGS := -std=c++11 -fexceptions -frtti +APP_CPPFLAGS := -std=c++17 -fexceptions -frtti APP_LDFLAGS := APP_STL := c++_shared diff --git a/Frameworks/OpenMPT/OpenMPT/build/android_ndk/README.AndroidNDK.txt b/Frameworks/OpenMPT/OpenMPT/build/android_ndk/README.AndroidNDK.txt index 54453bc02..248c36c78 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/android_ndk/README.AndroidNDK.txt +++ b/Frameworks/OpenMPT/OpenMPT/build/android_ndk/README.AndroidNDK.txt @@ -3,7 +3,7 @@ This is preliminary documentation. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - 0. The minimum required Android NDK version is r16b. + 0. The minimum required Android NDK version is r18b. 1. Copy the whole libopenmpt source tree below your jni directory. 2. Copy build/android_ndk/* into the root of libopenmpt, i.e. also into the jni directory and adjust as needed. diff --git a/Frameworks/OpenMPT/OpenMPT/build/dist.mk b/Frameworks/OpenMPT/OpenMPT/build/dist.mk index 8088a8c8f..2d038bdac 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/dist.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/dist.mk @@ -1,4 +1,4 @@ -MPT_SVNVERSION=12263 -MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.4.10 -MPT_SVNDATE=2019-10-30T10:43:15.521271Z +MPT_SVNVERSION=13775 +MPT_SVNURL=https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.3 +MPT_SVNDATE=2020-10-25T14:02:16.624929Z diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-afl.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-afl.mk index c1064d103..fd073cd26 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-afl.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-afl.mk @@ -9,14 +9,6 @@ CXXFLAGS_STDCXX = -std=$(STDCXX) else ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++17 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++17' ; fi ), c++17) CXXFLAGS_STDCXX = -std=c++17 -else -ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++14 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++14' ; fi ), c++14) -CXXFLAGS_STDCXX = -std=c++14 -else -ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++11 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++11' ; fi ), c++11) -CXXFLAGS_STDCXX = -std=c++11 -endif -endif endif endif CFLAGS_STDC = -std=c99 diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-clang.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-clang.mk index f8283f5d8..cb5e4fe3e 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-clang.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-clang.mk @@ -12,10 +12,6 @@ CXXFLAGS_STDCXX = -std=c++17 else ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++14 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++14' ; fi ), c++14) CXXFLAGS_STDCXX = -std=c++14 -else -ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++11 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++11' ; fi ), c++11) -CXXFLAGS_STDCXX = -std=c++11 -endif endif endif endif @@ -43,20 +39,27 @@ endif CXXFLAGS_WARNINGS += -Wmissing-declarations -Wshift-count-negative -Wshift-count-overflow -Wshift-overflow -Wshift-sign-overflow -Wshift-op-parentheses CFLAGS_WARNINGS += -Wmissing-prototypes -Wshift-count-negative -Wshift-count-overflow -Wshift-overflow -Wshift-sign-overflow -Wshift-op-parentheses +CXXFLAGS_WARNINGS += -Wdeprecated -Wextra-semi -Wnon-virtual-dtor -Wreserved-id-macro -Wglobal-constructors -Wimplicit-fallthrough + #CXXFLAGS_WARNINGS += -Wdocumentation #CXXFLAGS_WARNINGS += -Wconversion #CXXFLAGS_WARNINGS += -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-shadow -Wno-sign-conversion -Wno-weak-vtables ifeq ($(MODERN),1) LDFLAGS += -fuse-ld=lld -CXXFLAGS_WARNINGS += -Wpedantic -Wframe-larger-than=20000 -#CXXFLAGS_WARNINGS += -Wdouble-promotion -Wframe-larger-than=16000 +CXXFLAGS_WARNINGS += -Wpedantic -Wframe-larger-than=16000 CFLAGS_WARNINGS += -Wpedantic -Wframe-larger-than=4000 -#CFLAGS_WARNINGS += -Wdouble-promotion LDFLAGS_WARNINGS += -Wl,-no-undefined -Wl,--detect-odr-violations -CXXFLAGS_WARNINGS += -Wdeprecated -Wextra-semi -Wnon-virtual-dtor -Wreserved-id-macro +# re-renable after 1.29 branch +#CXXFLAGS_WARNINGS += -Wdouble-promotion +#CFLAGS_WARNINGS += -Wdouble-promotion endif -CFLAGS_SILENT += -Wno-unused-parameter -Wno-unused-function -Wno-cast-qual +CFLAGS_SILENT += -Wno-cast-align +CFLAGS_SILENT += -Wno-cast-qual +CFLAGS_SILENT += -Wno-missing-prototypes +CFLAGS_SILENT += -Wno-sign-compare +CFLAGS_SILENT += -Wno-unused-function +CFLAGS_SILENT += -Wno-unused-parameter EXESUFFIX= diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-defaults.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-defaults.mk index 18acb5a3b..b876e777b 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-defaults.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-defaults.mk @@ -7,9 +7,6 @@ include build/make/config-clang.mk # Mac OS X overrides DYNLINK=0 SHARED_SONAME=0 -# when using iconv -#CPPFLAGS += -DMPT_WITH_ICONV -#LDLIBS += -liconv else ifeq ($(HOST_FLAVOUR),LINUX) diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-djgpp.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-djgpp.mk index ea582894f..ae0afeaeb 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-djgpp.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-djgpp.mk @@ -1,19 +1,19 @@ -CC = i586-pc-msdosdjgpp-gcc -CXX = i586-pc-msdosdjgpp-g++ -LD = i586-pc-msdosdjgpp-g++ -AR = i586-pc-msdosdjgpp-ar +CC = i386-pc-msdosdjgpp-gcc +CXX = i386-pc-msdosdjgpp-g++ +LD = i386-pc-msdosdjgpp-g++ +AR = i386-pc-msdosdjgpp-ar # Note that we are using GNU extensions instead of 100% standards-compliant # mode, because otherwise DJGPP-specific headers/functions are unavailable. -CXXFLAGS_STDCXX = -std=gnu++11 +CXXFLAGS_STDCXX = -std=gnu++17 CFLAGS_STDC = -std=gnu99 CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) CPPFLAGS += -CXXFLAGS += -march=pentium -mtune=pentium -CFLAGS += -march=pentium -mtune=pentium +CXXFLAGS += -march=i386 -m80387 -mtune=pentium +CFLAGS += -march=i386 -m80387 -mtune=pentium LDFLAGS += LDLIBS += -lm ARFLAGS := rcs diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-emscripten.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-emscripten.mk index aaf08715b..3a9670d78 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-emscripten.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-emscripten.mk @@ -1,8 +1,9 @@ -CC = emcc -CXX = em++ +CC = emcc -c +CXX = em++ -c LD = em++ AR = emar +LINK.cc = em++ $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) EMSCRIPTEN_TARGET?=default @@ -11,14 +12,6 @@ CXXFLAGS_STDCXX = -std=$(STDCXX) else ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++17 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++17' ; fi ), c++17) CXXFLAGS_STDCXX = -std=c++17 -else -ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++14 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++14' ; fi ), c++14) -CXXFLAGS_STDCXX = -std=c++14 -else -ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++11 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++11' ; fi ), c++11) -CXXFLAGS_STDCXX = -std=c++11 -endif -endif endif endif CFLAGS_STDC = -std=c99 @@ -45,50 +38,49 @@ LDFLAGS += LDFLAGS += -s ALLOW_MEMORY_GROWTH=1 -else ifeq ($(EMSCRIPTEN_TARGET),wasm) -# emits native wasm AND an emulator for running wasm in asmjs/js with full wasm optimizations. +else ifeq ($(EMSCRIPTEN_TARGET),all) +# emits native wasm AND javascript with full wasm optimizations. # as of emscripten 1.38, this is equivalent to default. CPPFLAGS += -DMPT_BUILD_WASM -CXXFLAGS += -s WASM=1 -s BINARYEN_METHOD='native-wasm' -CFLAGS += -s WASM=1 -s BINARYEN_METHOD='native-wasm' -LDFLAGS += -s WASM=1 -s BINARYEN_METHOD='native-wasm' +CXXFLAGS += -s WASM=2 -s LEGACY_VM_SUPPORT=1 +CFLAGS += -s WASM=2 -s LEGACY_VM_SUPPORT=1 +LDFLAGS += -s WASM=2 -s LEGACY_VM_SUPPORT=1 LDFLAGS += -s ALLOW_MEMORY_GROWTH=1 -else ifeq ($(EMSCRIPTEN_TARGET),asmjs128m) -# emits only asmjs -CPPFLAGS += -DMPT_BUILD_ASMJS -CXXFLAGS += -s WASM=0 -s ASM_JS=1 -CFLAGS += -s WASM=0 -s ASM_JS=1 -LDFLAGS += -s WASM=0 -s ASM_JS=1 +else ifeq ($(EMSCRIPTEN_TARGET),wasm) +# emits native wasm. +CPPFLAGS += -DMPT_BUILD_WASM +CXXFLAGS += -s WASM=1 +CFLAGS += -s WASM=1 +LDFLAGS += -s WASM=1 -LDFLAGS += -s ALLOW_MEMORY_GROWTH=0 -s ABORTING_MALLOC=0 -s TOTAL_MEMORY=134217728 - -else ifeq ($(EMSCRIPTEN_TARGET),asmjs) -# emits only asmjs -CPPFLAGS += -DMPT_BUILD_ASMJS -CXXFLAGS += -s WASM=0 -s ASM_JS=1 -CFLAGS += -s WASM=0 -s ASM_JS=1 -LDFLAGS += -s WASM=0 -s ASM_JS=1 - -LDFLAGS += -s ALLOW_MEMORY_GROWTH=0 -s ABORTING_MALLOC=0 +LDFLAGS += -s ALLOW_MEMORY_GROWTH=1 else ifeq ($(EMSCRIPTEN_TARGET),js) # emits only plain javascript with plain javascript focused optimizations. CPPFLAGS += -DMPT_BUILD_ASMJS -CXXFLAGS += -s WASM=0 -s ASM_JS=2 -s LEGACY_VM_SUPPORT=1 -CFLAGS += -s WASM=0 -s ASM_JS=2 -s LEGACY_VM_SUPPORT=1 -LDFLAGS += -s WASM=0 -s ASM_JS=2 -s LEGACY_VM_SUPPORT=1 +CXXFLAGS += -s WASM=0 -s LEGACY_VM_SUPPORT=1 +CFLAGS += -s WASM=0 -s LEGACY_VM_SUPPORT=1 +LDFLAGS += -s WASM=0 -s LEGACY_VM_SUPPORT=1 LDFLAGS += -s ALLOW_MEMORY_GROWTH=1 endif -CXXFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -ffast-math -CFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -ffast-math -fno-strict-aliasing -LDFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s EXPORT_NAME="'libopenmpt'" +CXXFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s ERROR_ON_MISSING_LIBRARIES=1 -ffast-math +CFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s ERROR_ON_MISSING_LIBRARIES=1 -ffast-math -fno-strict-aliasing +LDFLAGS += -s DISABLE_EXCEPTION_CATCHING=0 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s ERROR_ON_MISSING_LIBRARIES=1 -s EXPORT_NAME="'libopenmpt'" -CFLAGS_SILENT += -Wno-unused-parameter -Wno-unused-function -Wno-cast-qual +CFLAGS_SILENT += -Wno-\#warnings +CFLAGS_SILENT += -Wno-cast-align +CFLAGS_SILENT += -Wno-cast-qual +CFLAGS_SILENT += -Wno-format +CFLAGS_SILENT += -Wno-missing-prototypes +CFLAGS_SILENT += -Wno-sign-compare +CFLAGS_SILENT += -Wno-unused-function +CFLAGS_SILENT += -Wno-unused-parameter +CFLAGS_SILENT += -Wno-unused-variable CXXFLAGS_WARNINGS += -Wmissing-declarations CFLAGS_WARNINGS += -Wmissing-prototypes @@ -98,7 +90,7 @@ REQUIRES_RUNPREFIX=1 EXESUFFIX=.js SOSUFFIX=.js RUNPREFIX=node -TEST_LDFLAGS= --pre-js build/make/test-pre.js +TEST_LDFLAGS= --pre-js build/make/test-pre.js -lnodefs.js DYNLINK=0 SHARED_LIB=1 @@ -106,6 +98,7 @@ STATIC_LIB=0 EXAMPLES=1 OPENMPT123=0 SHARED_SONAME=0 +NO_SHARED_LINKER_FLAG=1 # Disable the generic compiler optimization flags as emscripten is sufficiently different. # Optimization flags are hard-coded for emscripten in this file. diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-gcc.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-gcc.mk index e92d26fc7..4c763bd51 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-gcc.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-gcc.mk @@ -9,14 +9,6 @@ CXXFLAGS_STDCXX = -std=$(STDCXX) else ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++17 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++17' ; fi ), c++17) CXXFLAGS_STDCXX = -std=c++17 -else -ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++14 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++14' ; fi ), c++14) -CXXFLAGS_STDCXX = -std=c++14 -else -ifeq ($(shell printf '\n' > bin/empty.cpp ; if $(CXX) -std=c++11 -c bin/empty.cpp -o bin/empty.out > /dev/null 2>&1 ; then echo 'c++11' ; fi ), c++11) -CXXFLAGS_STDCXX = -std=c++11 -endif -endif endif endif CFLAGS_STDC = -std=c99 @@ -40,17 +32,27 @@ CXXFLAGS += -fsanitize=undefined CFLAGS += -fsanitize=undefined endif +CXXFLAGS_WARNINGS += -Wsuggest-override -Wno-psabi + ifeq ($(MODERN),1) LDFLAGS += -fuse-ld=gold CXXFLAGS_WARNINGS += -Wpedantic -Wlogical-op -Wframe-larger-than=16000 -#CXXFLAGS_WARNINGS += -Wdouble-promotion CFLAGS_WARNINGS += -Wpedantic -Wlogical-op -Wframe-larger-than=4000 -#CFLAGS_WARNINGS += -Wdouble-promotion LDFLAGS_WARNINGS += -Wl,-no-undefined -Wl,--detect-odr-violations -CXXFLAGS_WARNINGS += -Wsuggest-override +# re-renable after 1.29 branch +#CXXFLAGS_WARNINGS += -Wdouble-promotion +#CFLAGS_WARNINGS += -Wdouble-promotion endif -CFLAGS_SILENT += -Wno-unused-parameter -Wno-unused-function -Wno-cast-qual -Wno-old-style-declaration -Wno-type-limits -Wno-unused-but-set-variable +CFLAGS_SILENT += -Wno-cast-qual +CFLAGS_SILENT += -Wno-empty-body +CFLAGS_SILENT += -Wno-implicit-fallthrough +CFLAGS_SILENT += -Wno-old-style-declaration +CFLAGS_SILENT += -Wno-sign-compare +CFLAGS_SILENT += -Wno-type-limits +CFLAGS_SILENT += -Wno-unused-but-set-variable +CFLAGS_SILENT += -Wno-unused-function +CFLAGS_SILENT += -Wno-unused-parameter EXESUFFIX= diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-generic.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-generic.mk index ea7066285..fc1ebbec6 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-generic.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-generic.mk @@ -4,7 +4,7 @@ CXX ?= c++ LD ?= c++ AR = ar -CXXFLAGS_STDCXX = -std=c++11 +CXXFLAGS_STDCXX = -std=c++17 CFLAGS_STDC = -std=c99 CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win32.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win32.mk index e5c68df8f..a991f1a09 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win32.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win32.mk @@ -4,7 +4,7 @@ CXX = i686-w64-mingw32-g++$(MINGW_FLAVOUR) LD = i686-w64-mingw32-g++$(MINGW_FLAVOUR) AR = i686-w64-mingw32-ar$(MINGW_FLAVOUR) -CXXFLAGS_STDCXX = -std=c++11 +CXXFLAGS_STDCXX = -std=c++17 CFLAGS_STDC = -std=c99 CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win64.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win64.mk index c0ac0a824..0b6f50001 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win64.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-win64.mk @@ -4,7 +4,7 @@ CXX = x86_64-w64-mingw32-g++$(MINGW_FLAVOUR) LD = x86_64-w64-mingw32-g++$(MINGW_FLAVOUR) AR = x86_64-w64-mingw32-ar$(MINGW_FLAVOUR) -CXXFLAGS_STDCXX = -std=c++11 +CXXFLAGS_STDCXX = -std=c++17 CFLAGS_STDC = -std=c99 CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-amd64.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-amd64.mk index 01cb215d8..dd848ce3c 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-amd64.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-amd64.mk @@ -4,7 +4,7 @@ CXX = x86_64-w64-mingw32-g++$(MINGW_FLAVOUR) LD = x86_64-w64-mingw32-g++$(MINGW_FLAVOUR) AR = x86_64-w64-mingw32-ar$(MINGW_FLAVOUR) -CXXFLAGS_STDCXX = -std=c++11 +CXXFLAGS_STDCXX = -std=c++17 CFLAGS_STDC = -std=c99 CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-x86.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-x86.mk index 776023a18..62cc0e210 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-x86.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-mingw64-winrt-x86.mk @@ -4,7 +4,7 @@ CXX = i686-w64-mingw32-g++$(MINGW_FLAVOUR) LD = i686-w64-mingw32-g++$(MINGW_FLAVOUR) AR = i686-w64-mingw32-ar$(MINGW_FLAVOUR) -CXXFLAGS_STDCXX = -std=c++11 +CXXFLAGS_STDCXX = -std=c++17 CFLAGS_STDC = -std=c99 CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) diff --git a/Frameworks/OpenMPT/OpenMPT/build/make/config-standard.mk b/Frameworks/OpenMPT/OpenMPT/build/make/config-standard.mk index 57e3fd811..d9757fae2 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/make/config-standard.mk +++ b/Frameworks/OpenMPT/OpenMPT/build/make/config-standard.mk @@ -4,7 +4,7 @@ CXX ?= c++ LD ?= c++ AR = ar -CXXFLAGS_STDCXX = -std=c++11 +CXXFLAGS_STDCXX = -std=c++17 CFLAGS_STDC = -std=c99 CXXFLAGS += $(CXXFLAGS_STDCXX) CFLAGS += $(CFLAGS_STDC) diff --git a/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h b/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h index 07bcdfaa4..ffe4e8be4 100644 --- a/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h +++ b/Frameworks/OpenMPT/OpenMPT/build/svn_version/svn_version.h @@ -1,10 +1,10 @@ #pragma once -#define OPENMPT_VERSION_SVNVERSION "12263" -#define OPENMPT_VERSION_REVISION 12263 +#define OPENMPT_VERSION_SVNVERSION "13775" +#define OPENMPT_VERSION_REVISION 13775 #define OPENMPT_VERSION_DIRTY 0 #define OPENMPT_VERSION_MIXEDREVISIONS 0 -#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.4.10" -#define OPENMPT_VERSION_DATE "2019-10-30T10:43:15.521271Z" +#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.5.3" +#define OPENMPT_VERSION_DATE "2020-10-25T14:02:16.624929Z" #define OPENMPT_VERSION_IS_PACKAGE 1 diff --git a/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h b/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h index ce631f794..ea7fc264e 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h +++ b/Frameworks/OpenMPT/OpenMPT/common/BuildSettings.h @@ -20,36 +20,10 @@ #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 @@ -98,8 +72,15 @@ #if defined(MODPLUG_TRACKER) +#if MPT_OS_WINDOWS +#if !defined(MPT_BUILD_WINESUPPORT) +#define MPT_WITH_MFC +#endif // !MPT_BUILD_WINESUPPORT +#endif // MPT_OS_WINDOWS + // OpenMPT-only dependencies #define MPT_WITH_ASIO +#define MPT_WITH_DMO #define MPT_WITH_LAME #define MPT_WITH_LHASA #define MPT_WITH_MINIZIP @@ -118,13 +99,10 @@ // 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 @@ -132,6 +110,11 @@ //#define MPT_WITH_STBVORBIS #define MPT_WITH_VORBIS #define MPT_WITH_VORBISFILE +#if MPT_OS_WINDOWS +#if (_WIN32_WINNT >= 0x0A00) +#define MPT_WITH_WINDOWS10 +#endif +#endif #define MPT_WITH_ZLIB #endif // MODPLUG_TRACKER @@ -147,7 +130,6 @@ //#define MPT_WITH_DL //#define MPT_WITH_FLAC -//#define MPT_WITH_ICONV //#define MPT_WITH_LTDL //#define MPT_WITH_MEDIAFOUNDATION #define MPT_WITH_MINIMP3 @@ -163,7 +145,6 @@ //#define MPT_WITH_DL //#define MPT_WITH_FLAC -//#define MPT_WITH_ICONV //#define MPT_WITH_LTDL //#define MPT_WITH_MEDIAFOUNDATION //#define MPT_WITH_MINIMP3 @@ -194,7 +175,6 @@ //#define MPT_WITH_DL //#define MPT_WITH_FLAC -//#define MPT_WITH_ICONV //#define MPT_WITH_LTDL //#define MPT_WITH_MEDIAFOUNDATION //#define MPT_WITH_MINIMP3 @@ -229,14 +209,14 @@ #define MPT_LOG_GLOBAL_LEVEL 0 #endif +// Enable all individual logging macros and MPT_LOG calls +//#define MPT_ALL_LOGGING + // Disable all runtime asserts #if !defined(MPT_BUILD_DEBUG) && !defined(MPT_BUILD_CHECKED) && !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 @@ -267,9 +247,6 @@ // 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 @@ -300,7 +277,7 @@ #define NO_ASSERTS #endif //#define NO_LOGGING -#define MPT_FILEREADER_STD_ISTREAM +//#define MPT_ALL_LOGGING #define MPT_FILEREADER_CALLBACK_STREAM //#define MPT_EXTERNAL_SAMPLES #if defined(ENABLE_TESTS) || defined(MPT_BUILD_HACK_ARCHIVE_SUPPORT) @@ -320,9 +297,6 @@ #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 @@ -333,51 +307,28 @@ #if MPT_OS_WINDOWS - #define MPT_CHARSET_WIN32 #ifndef MPT_ENABLE_CHARSET_LOCALE #define MPT_ENABLE_CHARSET_LOCALE #endif #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 + #define MPT_LOCALE_ASSUME_CHARSET Charset::UTF8 #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 MPT_OS_DJGPP - #define MPT_CHARSET_INTERNAL #ifndef MPT_LOCALE_ASSUME_CHARSET - #define MPT_LOCALE_ASSUME_CHARSET CharsetCP437 + #define MPT_LOCALE_ASSUME_CHARSET Charset::CP437 #endif -#elif defined(MPT_WITH_ICONV) - - #define MPT_CHARSET_ICONV - #endif @@ -457,6 +408,7 @@ #if defined(ENABLE_ASM) #if MPT_COMPILER_MSVC && defined(_M_IX86) +#define ENABLE_CPUID // Generate general x86 inline assembly and intrinsics. #define ENABLE_X86 // Generate MMX instructions (only used when the CPU supports it). @@ -469,14 +421,14 @@ #define ENABLE_SSE3 // Generate SSE4 instructions (only used when the CPU supports it). #define ENABLE_SSE4 - -#if defined(MPT_BUILD_TARGET_XP) -// Generate AMD specific instruction set extensions (only used when the CPU supports it). -#define ENABLE_X86_AMD -#endif +// Generate AVX instructions (only used when the CPU supports it). +#define ENABLE_AVX +// Generate AVX2 instructions (only used when the CPU supports it). +#define ENABLE_AVX2 #elif MPT_COMPILER_MSVC && defined(_M_X64) +#define ENABLE_CPUID // Generate general x64 intrinsics. #define ENABLE_X64 // Generate SSE instructions (only used when the CPU supports it). @@ -487,18 +439,14 @@ #define ENABLE_SSE3 // Generate SSE4 instructions (only used when the CPU supports it). #define ENABLE_SSE4 +// Generate AVX instructions (only used when the CPU supports it). +#define ENABLE_AVX +// Generate AVX2 instructions (only used when the CPU supports it). +#define ENABLE_AVX2 #endif // arch #endif // ENABLE_ASM -#if defined(MPT_WITH_LAME) && defined(MPT_BUILD_MSVC) && defined(MPT_BUILD_MSVC_STATIC) && defined(MODPLUG_TRACKER) && !MPT_OS_WINDOWS_WINRT -#define MPT_ENABLE_LAME_DELAYLOAD -#endif - -#if defined(MPT_WITH_MPG123) && defined(MPT_BUILD_MSVC) && defined(MPT_BUILD_MSVC_STATIC) && defined(MODPLUG_TRACKER) && !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 @@ -524,26 +472,10 @@ #define MPT_ENABLE_TEMPFILE #endif -#if MPT_CXX_AT_LEAST(17) && defined(MPT_CHARSET_CODECVTUTF8) -#undef MPT_CHARSET_CODECVTUTF8 // std::codecvt_utf8 is deprecated in c++17 -#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_LAME_DELAYLOAD) && !defined(MPT_ENABLE_DYNBIND) -#define MPT_ENABLE_DYNBIND // static MSVC builds require dynbind to load delay-loaded DLLs -#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 @@ -556,10 +488,6 @@ #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 @@ -575,7 +503,6 @@ #if defined(NO_PLUGINS) // Any plugin type requires NO_PLUGINS to not be defined. #define NO_VST -#define NO_DMO #endif #if defined(ENABLE_ASM) || !defined(NO_VST) @@ -626,6 +553,10 @@ // platform configuration +#ifdef MPT_WITH_MFC +//#define MPT_MFC_FULL // use full MFC, including MFC controls +#endif // MPT_WITH_MFC + #if defined(MODPLUG_TRACKER) #if MPT_OS_WINDOWS #if !defined(MPT_BUILD_WINESUPPORT) @@ -721,11 +652,13 @@ #pragma warning(disable:4512) // assignment operator could not be generated #pragma warning(error:4309) // Treat "truncation of constant value"-warning as error. +#pragma warning(error:4463) // Treat overflow; assigning value to bit-field that can only hold values from low_value to high_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:6297) // 32-bit value is shifted, then cast to 64-bit value. Results might not be an expected value. #pragma warning(disable:6326) // Potential comparison of a constant with another constant //#pragma warning(disable:6385) //#pragma warning(disable:6386) @@ -733,32 +666,30 @@ #endif // MPT_COMPILER_MSVC +#if MPT_COMPILER_CLANG + +#if defined(MPT_BUILD_MSVC) +#pragma clang diagnostic warning "-Wimplicit-fallthrough" +#endif // MPT_BUILD_MSVC + +#if defined(MODPLUG_TRACKER) +#pragma clang diagnostic ignored "-Wunused-local-typedef" +#endif // MODPLUG_TRACKER + +#endif // MPT_COMPILER_CLANG + // standard library quirks -#if MPT_CXX_AT_LEAST(17) -#if (MPT_COMPILER_GCC || MPT_COMPILER_CLANG) -// we need to detect the standard library via macro __GLIBCXX__ -#include -#endif -#if MPT_COMPILER_MSVC || MPT_GCC_BEFORE(8,1,0) || MPT_CLANG_BEFORE(5,0,0) || (MPT_COMPILER_GCC && defined(__GLIBCXX__) && (defined(__MINGW32__) || defined(__MINGW64__))) || (MPT_COMPILER_CLANG && defined(__GLIBCXX__)) || (MPT_COMPILER_CLANG && MPT_OS_MACOSX_OR_IOS) || MPT_OS_OPENBSD || MPT_OS_EMSCRIPTEN || MPT_OS_HAIKU || (defined(__clang__) && defined(_MSC_VER)) -#define MPT_COMPILER_QUIRK_NO_ALIGNEDALLOC -#endif -#endif - // third-party library configuration -#if defined(MODPLUG_TRACKER) -//#define MPT_MFC_FULL // use full MFC, including MFC controls -#endif - #ifdef MPT_WITH_FLAC #ifdef MPT_BUILD_MSVC_STATIC #define FLAC__NO_DLL diff --git a/Frameworks/OpenMPT/OpenMPT/common/CompilerDetect.h b/Frameworks/OpenMPT/OpenMPT/common/CompilerDetect.h index 5087163c3..3b1d75951 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/CompilerDetect.h +++ b/Frameworks/OpenMPT/OpenMPT/common/CompilerDetect.h @@ -34,8 +34,8 @@ #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,6,0) -#error "clang version 3.6 required" +#if MPT_CLANG_BEFORE(5,0,0) +#error "clang version 5 required" #endif #if defined(__clang_analyzer__) @@ -51,14 +51,22 @@ #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" +#if MPT_GCC_BEFORE(7,1,0) +#error "GCC version 7.1 required" #endif #elif defined(_MSC_VER) #define MPT_COMPILER_MSVC 1 -#if (_MSC_VER >= 1922) +#if (_MSC_VER >= 1926) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019,6) +#elif (_MSC_VER >= 1925) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019,5) +#elif (_MSC_VER >= 1924) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019,4) +#elif (_MSC_VER >= 1923) +#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019,3) +#elif (_MSC_VER >= 1922) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019,2) #elif (_MSC_VER >= 1921) #define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019,1) @@ -96,8 +104,8 @@ #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" +#if MPT_MSVC_BEFORE(2017,9) +#error "MSVC version 2017 15.9 required" #endif #if defined(_PREFAST_) @@ -139,29 +147,21 @@ #if (__cplusplus >= 201703) #define MPT_CXX 17 -#elif (__cplusplus >= 201402) -#define MPT_CXX 14 #else -#define MPT_CXX 11 +#define MPT_CXX 17 #endif #elif MPT_COMPILER_MSVC -#if MPT_MSVC_AT_LEAST(2015,3) #if (_MSVC_LANG >= 201703) #define MPT_CXX 17 -#elif (_MSVC_LANG >= 201402) -#define MPT_CXX 14 #else -#define MPT_CXX 11 -#endif -#else -#define MPT_CXX 11 +#define MPT_CXX 17 #endif #else -#define MPT_CXX 11 +#define MPT_CXX 17 #endif @@ -205,11 +205,13 @@ #define MPT_OS_EMSCRIPTEN 1 #if defined(__EMSCRIPTEN_major__) && defined(__EMSCRIPTEN_minor__) #if (__EMSCRIPTEN_major__ > 1) - // ok - #elif (__EMSCRIPTEN_major__ == 1) && (__EMSCRIPTEN_minor__ >= 38) - // ok + // ok + #elif (__EMSCRIPTEN_major__ == 1) && (__EMSCRIPTEN_minor__ > 39) + // ok + #elif (__EMSCRIPTEN_major__ == 1) && (__EMSCRIPTEN_minor__ == 39) && (__EMSCRIPTEN_tiny__ >= 7) + // ok #else - #error "Emscripten >= 1.38 is required." + #error "Emscripten >= 1.39.7 is required." #endif #endif #elif defined(_WIN32) @@ -306,13 +308,59 @@ #define MPT_PLATFORM_MULTITHREADED 0 #endif + #if MPT_OS_DJGPP #define MPT_COMPILER_QUIRK_NO_WCHAR #endif -#if MPT_MSVC_BEFORE(2017,8) -// fixed in VS2017 15.8 -// see -#define MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM + +#if defined(__arm__) + +#if defined(__SOFTFP__) +#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 1 +#else +#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 0 +#endif +#if defined(__VFP_FP__) +// native-endian IEEE754 +#define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 0 +#define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 0 +#elif defined(__MAVERICK__) +// little-endian IEEE754, we assume native-endian though +#define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 1 +#define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 0 +#else +// not IEEE754 +#define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 1 +#define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 1 #endif +#elif defined(__mips__) + +#if defined(__mips_soft_float) +#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 1 +#else +#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 0 +#endif + +#endif + +#if MPT_OS_EMSCRIPTEN +#define MPT_COMPILER_QUIRK_FLOAT_PREFER64 1 +#endif + +#ifndef MPT_COMPILER_QUIRK_FLOAT_PREFER32 +#define MPT_COMPILER_QUIRK_FLOAT_PREFER32 0 +#endif +#ifndef MPT_COMPILER_QUIRK_FLOAT_PREFER64 +#define MPT_COMPILER_QUIRK_FLOAT_PREFER64 0 +#endif +#ifndef MPT_COMPILER_QUIRK_FLOAT_EMULATED +#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 0 +#endif +#ifndef MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN +#define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 0 +#endif +#ifndef MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 +#define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 0 +#endif diff --git a/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.cpp b/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.cpp index 5ba5a4d5b..ace006272 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.cpp @@ -191,7 +191,7 @@ void ComponentFactoryBase::PreConstruct() const { MPT_LOG(LogInformation, "Components", mpt::format(U_("Constructing Component %1")) - ( mpt::ToUnicode(mpt::CharsetASCII, m_ID) + ( mpt::ToUnicode(mpt::Charset::ASCII, m_ID) ) ); } @@ -233,7 +233,7 @@ static ComponentListEntry * & ComponentListHead() bool ComponentListPush(ComponentListEntry *entry) { - MPT_LOCK_GUARD guard(ComponentListMutex()); + mpt::lock_guard guard(ComponentListMutex()); entry->next = ComponentListHead(); ComponentListHead() = entry; return true; @@ -267,7 +267,7 @@ std::shared_ptr ComponentManager::Instance() ComponentManager::ComponentManager(const IComponentManagerSettings &settings) : m_Settings(settings) { - MPT_LOCK_GUARD guard(ComponentListMutex()); + mpt::lock_guard guard(ComponentListMutex()); for(ComponentListEntry *entry = ComponentListHead(); entry; entry = entry->next) { entry->reg(*this); diff --git a/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.h b/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.h index 6586cae61..f8c555fca 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.h +++ b/Frameworks/OpenMPT/OpenMPT/common/ComponentManager.h @@ -318,6 +318,8 @@ public: virtual bool KeepLoaded() const = 0; virtual bool IsBlocked(const std::string &key) const = 0; virtual mpt::PathString Path() const = 0; +protected: + virtual ~IComponentManagerSettings() = default; }; @@ -440,7 +442,7 @@ std::shared_ptr GetComponent() { static std::weak_ptr cache; static mpt::mutex m; - MPT_LOCK_GUARD l(m); + mpt::lock_guard l(m); std::shared_ptr component = cache.lock(); if(!component) { diff --git a/Frameworks/OpenMPT/OpenMPT/common/Endianness.h b/Frameworks/OpenMPT/OpenMPT/common/Endianness.h index 1960f1884..b2a80612c 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/Endianness.h +++ b/Frameworks/OpenMPT/OpenMPT/common/Endianness.h @@ -13,6 +13,9 @@ #include "BuildSettings.h" #include +#if MPT_CXX_AT_LEAST(20) +#include +#endif // C++20 #include #include @@ -27,15 +30,43 @@ +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt { + + + #if MPT_CXX_AT_LEAST(20) -// nothing +using std::endian; -#elif MPT_COMPILER_GENERIC +static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); -// rely on runtime detection instead of using non-standard macros +static constexpr mpt::endian get_endian() noexcept +{ + return mpt::endian::native; +} -#else +static constexpr bool endian_is_little() noexcept +{ + return get_endian() == mpt::endian::little; +} + +static constexpr bool endian_is_big() noexcept +{ + return get_endian() == mpt::endian::big; +} + +static constexpr bool endian_is_weird() noexcept +{ + return !endian_is_little() && !endian_is_big(); +} + +#else // !C++20 + +#if !MPT_COMPILER_GENERIC #if MPT_COMPILER_MSVC #define MPT_PLATFORM_LITTLE_ENDIAN @@ -75,111 +106,36 @@ #endif #endif -#endif +#endif // !MPT_COMPILER_GENERIC -#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 - - - -#if MPT_PLATFORM_ENDIAN_KNOWN && MPT_CXX_AT_LEAST(14) -//#define MPT_ENDIAN_IS_CONSTEXPR 1 -// For now, we do not want to use constexpr endianness functions and types. -// It bloats the binary size somewhat (possibly because of either the zeroing -// constructor or because of not being able to use byteswap intrinsics) and has -// currently no compelling benefit for us -#define MPT_ENDIAN_IS_CONSTEXPR 0 -#else -#define MPT_ENDIAN_IS_CONSTEXPR 0 -#endif - -#if MPT_ENDIAN_IS_CONSTEXPR -#define MPT_ENDIAN_CONSTEXPR_FUN MPT_CONSTEXPR14_FUN -#define MPT_ENDIAN_CONSTEXPR_VAR MPT_CONSTEXPR14_VAR -#else -#define MPT_ENDIAN_CONSTEXPR_FUN MPT_FORCEINLINE -#define MPT_ENDIAN_CONSTEXPR_VAR const -#endif - - - -OPENMPT_NAMESPACE_BEGIN - - - -namespace mpt { - - - -// C++20 std::endian -#if MPT_CXX_AT_LEAST(20) -using std::endian; -#else // !C++20 enum class endian { little = 0x78563412u, big = 0x12345678u, weird = 1u, -#if MPT_PLATFORM_ENDIAN_KNOWN && defined(MPT_PLATFORM_LITTLE_ENDIAN) - native = little -#elif MPT_PLATFORM_ENDIAN_KNOWN && defined(MPT_PLATFORM_BIG_ENDIAN) - native = big +#if MPT_COMPILER_GENERIC + native = 0u, +#elif defined(MPT_PLATFORM_LITTLE_ENDIAN) + native = little, +#elif defined(MPT_PLATFORM_BIG_ENDIAN) + native = big, #else - native = 0u + native = 0u, #endif }; -#endif // C++20 -MPT_CONSTEXPR11_FUN bool endian_known() noexcept -{ - return ((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)); -} - -MPT_CONSTEXPR11_FUN bool endian_unknown() noexcept -{ - return ((mpt::endian::native != mpt::endian::little) && (mpt::endian::native != mpt::endian::big)); -} - - - -#if MPT_CXX_AT_LEAST(20) - -static MPT_CONSTEXPR11_FUN mpt::endian get_endian() noexcept -{ - return mpt::endian::native; -} - -static MPT_CONSTEXPR11_FUN bool endian_is_little() noexcept -{ - return get_endian() == mpt::endian::little; -} - -static MPT_CONSTEXPR11_FUN bool endian_is_big() noexcept -{ - return get_endian() == mpt::endian::big; -} - -static MPT_CONSTEXPR11_FUN bool endian_is_weird() noexcept -{ - return !endian_is_little() && !endian_is_big(); -} - -#else // !C++20 +static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); namespace detail { static MPT_FORCEINLINE mpt::endian endian_probe() noexcept { - typedef uint32 endian_probe_type; - MPT_STATIC_ASSERT(sizeof(endian_probe_type) == 4); + using endian_probe_type = uint32; + static_assert(sizeof(endian_probe_type) == 4); constexpr endian_probe_type endian_probe_big = 0x12345678u; constexpr endian_probe_type endian_probe_little = 0x78563412u; - const mpt::byte probe[sizeof(endian_probe_type)] = { mpt::as_byte(0x12), mpt::as_byte(0x34), mpt::as_byte(0x56), mpt::as_byte(0x78) }; - endian_probe_type test; - std::memcpy(&test, probe, sizeof(endian_probe_type)); + const std::array probe{ {mpt::as_byte(0x12), mpt::as_byte(0x34), mpt::as_byte(0x56), mpt::as_byte(0x78)} }; + const endian_probe_type test = mpt::bit_cast(probe); mpt::endian result = mpt::endian::native; switch(test) { @@ -196,17 +152,24 @@ namespace detail { return result; } -} +} // namespace detail static MPT_FORCEINLINE mpt::endian get_endian() noexcept { - MPT_CONSTANT_IF(mpt::endian_known()) +#if MPT_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable:6285) // false-positive: ( || ) is always a non-zero constant. +#endif // MPT_COMPILER_MSVC + if constexpr((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) { return mpt::endian::native; } else { return detail::endian_probe(); } +#if MPT_COMPILER_MSVC +#pragma warning(pop) +#endif // MPT_COMPILER_MSVC } static MPT_FORCEINLINE bool endian_is_little() noexcept @@ -234,12 +197,12 @@ static MPT_FORCEINLINE bool endian_is_weird() noexcept struct BigEndian_tag { - static MPT_CONSTEXPR11_VAR mpt::endian endian = mpt::endian::big; + static constexpr mpt::endian endian = mpt::endian::big; }; struct LittleEndian_tag { - static MPT_CONSTEXPR11_VAR mpt::endian endian = mpt::endian::little; + static constexpr mpt::endian endian = mpt::endian::little; }; @@ -249,7 +212,7 @@ namespace mpt { template inline void SwapBufferEndian(std::size_t elementSize, Tbyte * buffer, std::size_t elements) { - MPT_STATIC_ASSERT(sizeof(Tbyte) == 1); + static_assert(sizeof(Tbyte) == 1); for(std::size_t element = 0; element < elements; ++element) { std::reverse(&buffer[0], &buffer[elementSize]); @@ -261,7 +224,32 @@ inline void SwapBufferEndian(std::size_t elementSize, Tbyte * buffer, std::size_ -#if !MPT_ENDIAN_IS_CONSTEXPR +#define MPT_constexpr_bswap16(x) \ + ( uint16(0) \ + | ((static_cast(x) >> 8) & 0x00FFu) \ + | ((static_cast(x) << 8) & 0xFF00u) \ + ) \ +/**/ +#define MPT_constexpr_bswap32(x) \ + ( uint32(0) \ + | ((static_cast(x) & 0x000000FFu) << 24) \ + | ((static_cast(x) & 0x0000FF00u) << 8) \ + | ((static_cast(x) & 0x00FF0000u) >> 8) \ + | ((static_cast(x) & 0xFF000000u) >> 24) \ + ) \ +/**/ +#define MPT_constexpr_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) \ + ) \ +/**/ #if MPT_COMPILER_GCC #define MPT_bswap16 __builtin_bswap16 @@ -295,85 +283,60 @@ static MPT_FORCEINLINE uint64 mpt_bswap64(uint64 x) { return bswap64(x); } #endif } } // namespace mpt::detail -#endif // !MPT_ENDIAN_IS_CONSTEXPR - - // No intrinsics available #ifndef MPT_bswap16 -#define MPT_bswap16(x) \ - ( uint16(0) \ - | ((static_cast(x) >> 8) & 0x00FFu) \ - | ((static_cast(x) << 8) & 0xFF00u) \ - ) \ -/**/ +#define MPT_bswap16(x) MPT_constexpr_bswap16(x) #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) \ - ) \ -/**/ +#define MPT_bswap32(x) MPT_constexpr_bswap32(x) #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) \ - ) \ -/**/ +#define MPT_bswap64(x) MPT_constexpr_bswap64(x) #endif + template -static MPT_CONSTEXPR17_FUN std::array EndianEncode(T val) noexcept +static MPT_CONSTEXPR17_FUN std::array EndianEncode(T val) noexcept { - MPT_STATIC_ASSERT(Tendian::endian == mpt::endian::little || Tendian::endian == mpt::endian::big); - 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; + static_assert(Tendian::endian == mpt::endian::little || Tendian::endian == mpt::endian::big); + static_assert(std::numeric_limits::is_integer); + static_assert(!std::numeric_limits::is_signed); + static_assert(sizeof(T) == size); + using base_type = T; + using unsigned_base_type = typename std::make_unsigned::type; + using endian_type = Tendian; unsigned_base_type uval = static_cast(val); - std::array data; - MPT_CONSTANT_IF(endian_type::endian == mpt::endian::little) + std::array data{}; + if constexpr(endian_type::endian == mpt::endian::little) { for(std::size_t i = 0; i < sizeof(base_type); ++i) { - data[i] = static_cast(static_cast((uval >> (i*8)) & 0xffu)); + 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)); + data[(sizeof(base_type)-1) - i] = static_cast(static_cast((uval >> (i*8)) & 0xffu)); } } return data; } template -static MPT_CONSTEXPR17_FUN T EndianDecode(std::array data) noexcept +static MPT_CONSTEXPR17_FUN T EndianDecode(std::array data) noexcept { - MPT_STATIC_ASSERT(Tendian::endian == mpt::endian::little || Tendian::endian == mpt::endian::big); - 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; + static_assert(Tendian::endian == mpt::endian::little || Tendian::endian == mpt::endian::big); + static_assert(std::numeric_limits::is_integer); + static_assert(!std::numeric_limits::is_signed); + static_assert(sizeof(T) == size); + using base_type = T; + using unsigned_base_type = typename std::make_unsigned::type; + using endian_type = Tendian; base_type val = base_type(); unsigned_base_type uval = unsigned_base_type(); - MPT_CONSTANT_IF(endian_type::endian == mpt::endian::little) + if constexpr(endian_type::endian == mpt::endian::little) { for(std::size_t i = 0; i < sizeof(base_type); ++i) { @@ -396,24 +359,28 @@ namespace mpt namespace detail { -static MPT_ENDIAN_CONSTEXPR_FUN uint64 SwapBytes(uint64 value) noexcept { return MPT_bswap64(value); } -static MPT_ENDIAN_CONSTEXPR_FUN uint32 SwapBytes(uint32 value) noexcept { return MPT_bswap32(value); } -static MPT_ENDIAN_CONSTEXPR_FUN uint16 SwapBytes(uint16 value) noexcept { return MPT_bswap16(value); } -static MPT_ENDIAN_CONSTEXPR_FUN int64 SwapBytes(int64 value) noexcept { return MPT_bswap64(value); } -static MPT_ENDIAN_CONSTEXPR_FUN int32 SwapBytes(int32 value) noexcept { return MPT_bswap32(value); } -static MPT_ENDIAN_CONSTEXPR_FUN int16 SwapBytes(int16 value) noexcept { return MPT_bswap16(value); } +static MPT_CONSTEXPR20_FUN uint64 SwapBytes(uint64 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap64(value); } else { return MPT_bswap64(value); } } +static MPT_CONSTEXPR20_FUN uint32 SwapBytes(uint32 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap32(value); } else { return MPT_bswap32(value); } } +static MPT_CONSTEXPR20_FUN uint16 SwapBytes(uint16 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap16(value); } else { return MPT_bswap16(value); } } +static MPT_CONSTEXPR20_FUN int64 SwapBytes(int64 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap64(value); } else { return MPT_bswap64(value); } } +static MPT_CONSTEXPR20_FUN int32 SwapBytes(int32 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap32(value); } else { return MPT_bswap32(value); } } +static MPT_CONSTEXPR20_FUN int16 SwapBytes(int16 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap16(value); } else { return MPT_bswap16(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_ENDIAN_CONSTEXPR_FUN uint8 SwapBytes(uint8 value) noexcept { return value; } -static MPT_ENDIAN_CONSTEXPR_FUN int8 SwapBytes(int8 value) noexcept { return value; } -static MPT_ENDIAN_CONSTEXPR_FUN char SwapBytes(char value) noexcept { return value; } +static MPT_CONSTEXPR20_FUN uint8 SwapBytes(uint8 value) noexcept { return value; } +static MPT_CONSTEXPR20_FUN int8 SwapBytes(int8 value) noexcept { return value; } +static MPT_CONSTEXPR20_FUN char SwapBytes(char value) noexcept { return value; } } // namespace detail } // namespace mpt +#undef MPT_constexpr_bswap16 +#undef MPT_constexpr_bswap32 +#undef MPT_constexpr_bswap64 + #undef MPT_bswap16 #undef MPT_bswap32 #undef MPT_bswap64 @@ -422,7 +389,7 @@ static MPT_ENDIAN_CONSTEXPR_FUN char SwapBytes(char value) noexcept { return // 1.0f --> 0x3f800000u static MPT_FORCEINLINE uint32 EncodeIEEE754binary32(float32 f) { - MPT_CONSTANT_IF(std::numeric_limits::is_iec559 && mpt::endian_known()) + if constexpr(mpt::float_traits::is_ieee754_binary32ne) { return mpt::bit_cast(f); } else @@ -454,7 +421,7 @@ static MPT_FORCEINLINE uint32 EncodeIEEE754binary32(float32 f) } static MPT_FORCEINLINE uint64 EncodeIEEE754binary64(float64 f) { - MPT_CONSTANT_IF(std::numeric_limits::is_iec559 && mpt::endian_known()) + if constexpr(mpt::float_traits::is_ieee754_binary64ne) { return mpt::bit_cast(f); } else @@ -488,7 +455,7 @@ static MPT_FORCEINLINE uint64 EncodeIEEE754binary64(float64 f) // 0x3f800000u --> 1.0f static MPT_FORCEINLINE float32 DecodeIEEE754binary32(uint32 i) { - MPT_CONSTANT_IF(std::numeric_limits::is_iec559 && mpt::endian_known()) + if constexpr(mpt::float_traits::is_ieee754_binary32ne) { return mpt::bit_cast(i); } else @@ -514,7 +481,7 @@ static MPT_FORCEINLINE float32 DecodeIEEE754binary32(uint32 i) } static MPT_FORCEINLINE float64 DecodeIEEE754binary64(uint64 i) { - MPT_CONSTANT_IF(std::numeric_limits::is_iec559 && mpt::endian_known()) + if constexpr(mpt::float_traits::is_ieee754_binary64ne) { return mpt::bit_cast(i); } else @@ -545,14 +512,14 @@ template struct IEEE754binary32Emulated { public: - typedef IEEE754binary32Emulated self_t; - mpt::byte bytes[4]; + using self_t = IEEE754binary32Emulated; + std::byte bytes[4]; public: - MPT_FORCEINLINE mpt::byte GetByte(std::size_t i) const + MPT_FORCEINLINE std::byte GetByte(std::size_t i) const { return bytes[i]; } - MPT_FORCEINLINE IEEE754binary32Emulated() { } + IEEE754binary32Emulated() = default; MPT_FORCEINLINE explicit IEEE754binary32Emulated(float32 f) { SetInt32(EncodeIEEE754binary32(f)); @@ -560,7 +527,7 @@ public: // 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) + MPT_FORCEINLINE explicit IEEE754binary32Emulated(std::byte b0, std::byte b1, std::byte b2, std::byte b3) { bytes[0] = b0; bytes[1] = b1; @@ -573,10 +540,10 @@ public: } 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); + 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 @@ -606,19 +573,19 @@ template self_t; - mpt::byte bytes[8]; + using self_t = IEEE754binary64Emulated; + std::byte bytes[8]; public: - MPT_FORCEINLINE mpt::byte GetByte(std::size_t i) const + MPT_FORCEINLINE std::byte GetByte(std::size_t i) const { return bytes[i]; } - MPT_FORCEINLINE IEEE754binary64Emulated() { } + IEEE754binary64Emulated() = default; 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) + MPT_FORCEINLINE explicit IEEE754binary64Emulated(std::byte b0, std::byte b1, std::byte b2, std::byte b3, std::byte b4, std::byte b5, std::byte b6, std::byte b7) { bytes[0] = b0; bytes[1] = b1; @@ -635,14 +602,14 @@ public: } 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); + 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 @@ -677,10 +644,10 @@ public: } }; -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; +using IEEE754binary32EmulatedBE = IEEE754binary32Emulated<0,1,2,3>; +using IEEE754binary32EmulatedLE = IEEE754binary32Emulated<3,2,1,0>; +using IEEE754binary64EmulatedBE = IEEE754binary64Emulated<0,1,2,3,4,5,6,7>; +using IEEE754binary64EmulatedLE = IEEE754binary64Emulated<7,6,5,4,3,2,1,0>; MPT_BINARY_STRUCT(IEEE754binary32EmulatedBE, 4) MPT_BINARY_STRUCT(IEEE754binary32EmulatedLE, 4) @@ -693,19 +660,19 @@ struct IEEE754binary32Native public: float32 value; public: - MPT_FORCEINLINE mpt::byte GetByte(std::size_t i) const + MPT_FORCEINLINE std::byte GetByte(std::size_t i) const { - MPT_STATIC_ASSERT(endian == mpt::endian::little || endian == mpt::endian::big); - MPT_CONSTANT_IF(endian == mpt::endian::little) + static_assert(endian == mpt::endian::little || endian == mpt::endian::big); + if constexpr(endian == mpt::endian::little) { - return static_cast(EncodeIEEE754binary32(value) >> (i*8)); + return static_cast(EncodeIEEE754binary32(value) >> (i*8)); } - MPT_CONSTANT_IF(endian == mpt::endian::big) + if constexpr(endian == mpt::endian::big) { - return static_cast(EncodeIEEE754binary32(value) >> ((4-1-i)*8)); + return static_cast(EncodeIEEE754binary32(value) >> ((4-1-i)*8)); } } - MPT_FORCEINLINE IEEE754binary32Native() { } + IEEE754binary32Native() = default; MPT_FORCEINLINE explicit IEEE754binary32Native(float32 f) { value = f; @@ -713,10 +680,10 @@ public: // 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) + MPT_FORCEINLINE explicit IEEE754binary32Native(std::byte b0, std::byte b1, std::byte b2, std::byte b3) { - MPT_STATIC_ASSERT(endian == mpt::endian::little || endian == mpt::endian::big); - MPT_CONSTANT_IF(endian == mpt::endian::little) + static_assert(endian == mpt::endian::little || endian == mpt::endian::big); + if constexpr(endian == mpt::endian::little) { value = DecodeIEEE754binary32(0u | (static_cast(b0) << 0) @@ -725,7 +692,7 @@ public: | (static_cast(b3) << 24) ); } - MPT_CONSTANT_IF(endian == mpt::endian::big) + if constexpr(endian == mpt::endian::big) { value = DecodeIEEE754binary32(0u | (static_cast(b0) << 24) @@ -764,27 +731,27 @@ struct IEEE754binary64Native public: float64 value; public: - MPT_FORCEINLINE mpt::byte GetByte(std::size_t i) const + MPT_FORCEINLINE std::byte GetByte(std::size_t i) const { - MPT_STATIC_ASSERT(endian == mpt::endian::little || endian == mpt::endian::big); - MPT_CONSTANT_IF(endian == mpt::endian::little) + static_assert(endian == mpt::endian::little || endian == mpt::endian::big); + if constexpr(endian == mpt::endian::little) { - return mpt::byte_cast(static_cast(EncodeIEEE754binary64(value) >> (i*8))); + return mpt::byte_cast(static_cast(EncodeIEEE754binary64(value) >> (i*8))); } - MPT_CONSTANT_IF(endian == mpt::endian::big) + if constexpr(endian == mpt::endian::big) { - return mpt::byte_cast(static_cast(EncodeIEEE754binary64(value) >> ((8-1-i)*8))); + return mpt::byte_cast(static_cast(EncodeIEEE754binary64(value) >> ((8-1-i)*8))); } } - MPT_FORCEINLINE IEEE754binary64Native() { } + IEEE754binary64Native() = default; 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) + MPT_FORCEINLINE explicit IEEE754binary64Native(std::byte b0, std::byte b1, std::byte b2, std::byte b3, std::byte b4, std::byte b5, std::byte b6, std::byte b7) { - MPT_STATIC_ASSERT(endian == mpt::endian::little || endian == mpt::endian::big); - MPT_CONSTANT_IF(endian == mpt::endian::little) + static_assert(endian == mpt::endian::little || endian == mpt::endian::big); + if constexpr(endian == mpt::endian::little) { value = DecodeIEEE754binary64(0ull | (static_cast(b0) << 0) @@ -797,7 +764,7 @@ public: | (static_cast(b7) << 56) ); } - MPT_CONSTANT_IF(endian == mpt::endian::big) + if constexpr(endian == mpt::endian::big) { value = DecodeIEEE754binary64(0ull | (static_cast(b0) << 56) @@ -834,68 +801,68 @@ public: } }; -MPT_STATIC_ASSERT((sizeof(IEEE754binary32Native<>) == 4)); -MPT_STATIC_ASSERT((sizeof(IEEE754binary64Native<>) == 8)); +static_assert((sizeof(IEEE754binary32Native<>) == 4)); +static_assert((sizeof(IEEE754binary64Native<>) == 8)); namespace mpt { template <> struct is_binary_safe< IEEE754binary32Native<> > : public std::true_type { }; template <> struct is_binary_safe< IEEE754binary64Native<> > : public std::true_type { }; } -template struct IEEE754binary_types { - typedef IEEE754binary32EmulatedLE IEEE754binary32LE; - typedef IEEE754binary32EmulatedBE IEEE754binary32BE; - typedef IEEE754binary64EmulatedLE IEEE754binary64LE; - typedef IEEE754binary64EmulatedBE IEEE754binary64BE; +template struct IEEE754binary_types { + using IEEE754binary32LE = IEEE754binary32EmulatedLE; + using IEEE754binary32BE = IEEE754binary32EmulatedBE; + using IEEE754binary64LE = IEEE754binary64EmulatedLE; + using IEEE754binary64BE = IEEE754binary64EmulatedBE; }; template <> struct IEEE754binary_types { - typedef IEEE754binary32Native<> IEEE754binary32LE; - typedef IEEE754binary32EmulatedBE IEEE754binary32BE; - typedef IEEE754binary64Native<> IEEE754binary64LE; - typedef IEEE754binary64EmulatedBE IEEE754binary64BE; + using IEEE754binary32LE = IEEE754binary32Native<>; + using IEEE754binary32BE = IEEE754binary32EmulatedBE; + using IEEE754binary64LE = IEEE754binary64Native<>; + using IEEE754binary64BE = IEEE754binary64EmulatedBE; }; template <> struct IEEE754binary_types { - typedef IEEE754binary32EmulatedLE IEEE754binary32LE; - typedef IEEE754binary32Native<> IEEE754binary32BE; - typedef IEEE754binary64EmulatedLE IEEE754binary64LE; - typedef IEEE754binary64Native<> IEEE754binary64BE; + using IEEE754binary32LE = IEEE754binary32EmulatedLE; + using IEEE754binary32BE = IEEE754binary32Native<>; + using IEEE754binary64LE = IEEE754binary64EmulatedLE; + using IEEE754binary64BE = IEEE754binary64Native<>; }; -typedef IEEE754binary_types::is_iec559 && std::numeric_limits::is_iec559, mpt::endian::native>::IEEE754binary32LE IEEE754binary32LE; -typedef IEEE754binary_types::is_iec559 && std::numeric_limits::is_iec559, mpt::endian::native>::IEEE754binary32BE IEEE754binary32BE; -typedef IEEE754binary_types::is_iec559 && std::numeric_limits::is_iec559, mpt::endian::native>::IEEE754binary64LE IEEE754binary64LE; -typedef IEEE754binary_types::is_iec559 && std::numeric_limits::is_iec559, mpt::endian::native>::IEEE754binary64BE IEEE754binary64BE; +using IEEE754binary32LE = IEEE754binary_types::is_ieee754_binary32ne, mpt::endian::native>::IEEE754binary32LE; +using IEEE754binary32BE = IEEE754binary_types::is_ieee754_binary32ne, mpt::endian::native>::IEEE754binary32BE; +using IEEE754binary64LE = IEEE754binary_types::is_ieee754_binary64ne, mpt::endian::native>::IEEE754binary64LE; +using IEEE754binary64BE = IEEE754binary_types::is_ieee754_binary64ne, mpt::endian::native>::IEEE754binary64BE; -STATIC_ASSERT(sizeof(IEEE754binary32LE) == 4); -STATIC_ASSERT(sizeof(IEEE754binary32BE) == 4); -STATIC_ASSERT(sizeof(IEEE754binary64LE) == 8); -STATIC_ASSERT(sizeof(IEEE754binary64BE) == 8); +static_assert(sizeof(IEEE754binary32LE) == 4); +static_assert(sizeof(IEEE754binary32BE) == 4); +static_assert(sizeof(IEEE754binary64LE) == 8); +static_assert(sizeof(IEEE754binary64BE) == 8); // unaligned -typedef IEEE754binary32EmulatedLE float32le; -typedef IEEE754binary32EmulatedBE float32be; -typedef IEEE754binary64EmulatedLE float64le; -typedef IEEE754binary64EmulatedBE float64be; +using float32le = IEEE754binary32EmulatedLE; +using float32be = IEEE754binary32EmulatedBE; +using float64le = IEEE754binary64EmulatedLE; +using float64be = IEEE754binary64EmulatedBE; -STATIC_ASSERT(sizeof(float32le) == 4); -STATIC_ASSERT(sizeof(float32be) == 4); -STATIC_ASSERT(sizeof(float64le) == 8); -STATIC_ASSERT(sizeof(float64be) == 8); +static_assert(sizeof(float32le) == 4); +static_assert(sizeof(float32be) == 4); +static_assert(sizeof(float64le) == 8); +static_assert(sizeof(float64be) == 8); // potentially aligned -typedef IEEE754binary32LE float32le_fast; -typedef IEEE754binary32BE float32be_fast; -typedef IEEE754binary64LE float64le_fast; -typedef IEEE754binary64BE float64be_fast; +using float32le_fast = IEEE754binary32LE; +using float32be_fast = IEEE754binary32BE; +using float64le_fast = IEEE754binary64LE; +using float64be_fast = IEEE754binary64BE; -STATIC_ASSERT(sizeof(float32le_fast) == 4); -STATIC_ASSERT(sizeof(float32be_fast) == 4); -STATIC_ASSERT(sizeof(float64le_fast) == 8); -STATIC_ASSERT(sizeof(float64be_fast) == 8); +static_assert(sizeof(float32le_fast) == 4); +static_assert(sizeof(float32be_fast) == 4); +static_assert(sizeof(float64le_fast) == 8); +static_assert(sizeof(float64be_fast) == 8); @@ -908,54 +875,53 @@ template struct packed { public: - typedef T base_type; - typedef Tendian endian_type; + using base_type = T; + using endian_type = Tendian; public: -#if MPT_ENDIAN_IS_CONSTEXPR - mpt::byte data[sizeof(base_type)]{}; -#else // !MPT_ENDIAN_IS_CONSTEXPR - std::array data; -#endif // MPT_ENDIAN_IS_CONSTEXPR + std::array data; public: - MPT_ENDIAN_CONSTEXPR_FUN void set(base_type val) noexcept + MPT_CONSTEXPR20_FUN void set(base_type val) noexcept { - STATIC_ASSERT(std::numeric_limits::is_integer); - #if MPT_ENDIAN_IS_CONSTEXPR - MPT_CONSTANT_IF(endian_type::endian == mpt::endian::big) + static_assert(std::numeric_limits::is_integer); + MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) + { + if constexpr(endian_type::endian == mpt::endian::big) { typename std::make_unsigned::type uval = val; for(std::size_t i = 0; i < sizeof(base_type); ++i) { - data[i] = static_cast((uval >> (8*(sizeof(base_type)-1-i))) & 0xffu); + data[i] = static_cast((uval >> (8*(sizeof(base_type)-1-i))) & 0xffu); } } else { typename std::make_unsigned::type uval = val; for(std::size_t i = 0; i < sizeof(base_type); ++i) { - data[i] = static_cast((uval >> (8*i)) & 0xffu); + data[i] = static_cast((uval >> (8*i)) & 0xffu); } } - #else // !MPT_ENDIAN_IS_CONSTEXPR - MPT_CONSTANT_IF(mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) + } else + { + if constexpr(mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) { - MPT_CONSTANT_IF(mpt::endian::native != endian_type::endian) + if constexpr(mpt::endian::native != endian_type::endian) { val = mpt::detail::SwapBytes(val); } std::memcpy(data.data(), &val, sizeof(val)); } else { - typedef typename std::make_unsigned::type unsigned_base_type; + using unsigned_base_type = typename std::make_unsigned::type; data = EndianEncode(val); } - #endif // MPT_ENDIAN_IS_CONSTEXPR + } } - MPT_ENDIAN_CONSTEXPR_FUN base_type get() const noexcept + MPT_CONSTEXPR20_FUN base_type get() const noexcept { - STATIC_ASSERT(std::numeric_limits::is_integer); - #if MPT_ENDIAN_IS_CONSTEXPR - MPT_CONSTANT_IF(endian_type::endian == mpt::endian::big) + static_assert(std::numeric_limits::is_integer); + MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) + { + if constexpr(endian_type::endian == mpt::endian::big) { typename std::make_unsigned::type uval = 0; for(std::size_t i = 0; i < sizeof(base_type); ++i) @@ -972,57 +938,58 @@ public: } return static_cast(uval); } - #else // !MPT_ENDIAN_IS_CONSTEXPR - MPT_CONSTANT_IF(mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) + } else + { + if constexpr(mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) { base_type val = base_type(); std::memcpy(&val, data.data(), sizeof(val)); - MPT_CONSTANT_IF(mpt::endian::native != endian_type::endian) + if constexpr(mpt::endian::native != endian_type::endian) { val = mpt::detail::SwapBytes(val); } return val; } else { - typedef typename std::make_unsigned::type unsigned_base_type; + using unsigned_base_type = typename std::make_unsigned::type; return EndianDecode(data); } - #endif // MPT_ENDIAN_IS_CONSTEXPR + } } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator = (const base_type & val) noexcept { set(val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN operator base_type () const noexcept { return get(); } + MPT_CONSTEXPR20_FUN packed & operator = (const base_type & val) noexcept { set(val); return *this; } + MPT_CONSTEXPR20_FUN operator base_type () const noexcept { return get(); } public: - MPT_ENDIAN_CONSTEXPR_FUN packed & operator &= (base_type val) noexcept { set(get() & val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator |= (base_type val) noexcept { set(get() | val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator ^= (base_type val) noexcept { set(get() ^ val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator += (base_type val) noexcept { set(get() + val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator -= (base_type val) noexcept { set(get() - val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator *= (base_type val) noexcept { set(get() * val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator /= (base_type val) noexcept { set(get() / val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator %= (base_type val) noexcept { set(get() % val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator ++ () noexcept { set(get() + 1); return *this; } // prefix - MPT_ENDIAN_CONSTEXPR_FUN packed & operator -- () noexcept { set(get() - 1); return *this; } // prefix - MPT_ENDIAN_CONSTEXPR_FUN base_type operator ++ (int) noexcept { base_type old = get(); set(old + 1); return old; } // postfix - MPT_ENDIAN_CONSTEXPR_FUN base_type operator -- (int) noexcept { base_type old = get(); set(old - 1); return old; } // postfix + MPT_CONSTEXPR20_FUN packed & operator &= (base_type val) noexcept { set(get() & val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator |= (base_type val) noexcept { set(get() | val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator ^= (base_type val) noexcept { set(get() ^ val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator += (base_type val) noexcept { set(get() + val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator -= (base_type val) noexcept { set(get() - val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator *= (base_type val) noexcept { set(get() * val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator /= (base_type val) noexcept { set(get() / val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator %= (base_type val) noexcept { set(get() % val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator ++ () noexcept { set(get() + 1); return *this; } // prefix + MPT_CONSTEXPR20_FUN packed & operator -- () noexcept { set(get() - 1); return *this; } // prefix + MPT_CONSTEXPR20_FUN base_type operator ++ (int) noexcept { base_type old = get(); set(old + 1); return old; } // postfix + MPT_CONSTEXPR20_FUN base_type operator -- (int) noexcept { 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; +using int64le = packed< int64, LittleEndian_tag>; +using int32le = packed< int32, LittleEndian_tag>; +using int16le = packed< int16, LittleEndian_tag>; +using int8le = packed< int8 , LittleEndian_tag>; +using uint64le = packed; +using uint32le = packed; +using uint16le = packed; +using uint8le = packed; -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; +using int64be = packed< int64, BigEndian_tag>; +using int32be = packed< int32, BigEndian_tag>; +using int16be = packed< int16, BigEndian_tag>; +using int8be = packed< int8 , BigEndian_tag>; +using uint64be = packed; +using uint32be = packed; +using uint16be = packed; +using uint8be = packed; namespace mpt { template struct limits> : mpt::limits {}; @@ -1048,28 +1015,28 @@ MPT_BINARY_STRUCT(uint8be , 1) namespace mpt { -template struct make_le { typedef packed::type, LittleEndian_tag> type; }; -template struct make_be { typedef packed::type, BigEndian_tag> type; }; +template struct make_le { using type = packed::type, LittleEndian_tag>; }; +template struct make_be { using type = packed::type, BigEndian_tag>; }; template -MPT_ENDIAN_CONSTEXPR_FUN auto as_le(T v) noexcept -> typename mpt::make_le::type>::type +MPT_CONSTEXPR20_FUN auto as_le(T v) noexcept -> typename mpt::make_le::type>::type { - typename mpt::make_le::type>::type res; + typename mpt::make_le::type>::type res{}; res = v; return res; } template -MPT_ENDIAN_CONSTEXPR_FUN auto as_be(T v) noexcept -> typename mpt::make_be::type>::type +MPT_CONSTEXPR20_FUN auto as_be(T v) noexcept -> typename mpt::make_be::type>::type { - typename mpt::make_be::type>::type res; + typename mpt::make_be::type>::type res{}; res = v; return res; } template -MPT_ENDIAN_CONSTEXPR_FUN Tpacked as_endian(typename Tpacked::base_type v) noexcept +MPT_CONSTEXPR20_FUN Tpacked as_endian(typename Tpacked::base_type v) noexcept { - Tpacked res; + Tpacked res{}; res = v; return res; } @@ -1111,108 +1078,11 @@ struct int24 } } }; -MPT_STATIC_ASSERT(sizeof(int24) == 3); -#define int24_min (0-0x00800000) -#define int24_max (0+0x007fffff) +static_assert(sizeof(int24) == 3); +static constexpr int32 int24_min = (0 - 0x00800000); +static constexpr int32 int24_max = (0 + 0x007fffff); -// 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 - { - typename mpt::make_le::type val; - std::memcpy(&val, mem, 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; } - template explicit const_unaligned_ptr_le(const Tbyte *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 - { - typename mpt::make_be::type val; - std::memcpy(&val, mem, 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; } - template explicit const_unaligned_ptr_be(const Tbyte *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; } - template explicit const_unaligned_ptr(const Tbyte *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.h b/Frameworks/OpenMPT/OpenMPT/common/FileReader.h index d85fce384..e6c3118a2 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/FileReader.h +++ b/Frameworks/OpenMPT/OpenMPT/common/FileReader.h @@ -29,7 +29,7 @@ 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 [[deprecated]] #define FILEREADER_DEPRECATED @@ -38,12 +38,12 @@ class FileReaderTraitsMemory public: - typedef FileDataContainerMemory::off_t off_t; + using off_t = FileDataContainerMemory::off_t; - typedef FileDataContainerMemory data_type; - typedef const FileDataContainerMemory & ref_data_type; - typedef const FileDataContainerMemory & shared_data_type; - typedef FileDataContainerMemory value_data_type; + using data_type = FileDataContainerMemory; + using ref_data_type = const FileDataContainerMemory &; + using shared_data_type = const FileDataContainerMemory &; + using value_data_type = FileDataContainerMemory; static shared_data_type get_shared(const data_type & data) { return data; } static ref_data_type get_ref(const data_type & data) { return data; } @@ -58,19 +58,17 @@ public: }; -#if defined(MPT_FILEREADER_STD_ISTREAM) - class FileReaderTraitsStdStream { public: - typedef IFileDataContainer::off_t off_t; + using off_t = IFileDataContainer::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; + using data_type = std::shared_ptr; + using ref_data_type = const IFileDataContainer &; + using shared_data_type = std::shared_ptr; + using value_data_type = std::shared_ptr; static shared_data_type get_shared(const data_type & data) { return data; } static ref_data_type get_ref(const data_type & data) { return *data; } @@ -85,13 +83,629 @@ public: }; -typedef FileReaderTraitsStdStream FileReaderTraitsDefault; +using FileReaderTraitsDefault = FileReaderTraitsStdStream; -#else // !MPT_FILEREADER_STD_ISTREAM +namespace mpt +{ +namespace FileReader +{ -typedef FileReaderTraitsMemory FileReaderTraitsDefault; + // 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(TFileCursor &f, T &target) + { + // cppcheck false-positive + // cppcheck-suppress uninitvar + mpt::byte_span dst = mpt::as_raw_memory(target); + if(dst.size() != f.GetRaw(dst)) + { + return false; + } + f.Skip(dst.size()); + return true; + } -#endif // MPT_FILEREADER_STD_ISTREAM + // 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(TFileCursor &f) + { + static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); + typename mpt::make_le::type target; + if(Read(f, target)) + { + return 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(TFileCursor &f) + { + static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); + typename mpt::make_be::type target; + if(Read(f, target)) + { + return 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(TFileCursor &f, typename TFileCursor::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(!f.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(f, byte); + negative = std::numeric_limits::is_signed && ((byte & 0x80) != 0x00); + } else + { + // sign or zero extend + byte = negative ? 0xff : 0x00; + } + buf[i] = byte; + } + typename mpt::make_le::type target; + std::memcpy(&target, buf, sizeof(T)); + return 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(TFileCursor &f, typename TFileCursor::off_t size) + { + static_assert(std::numeric_limits::is_integer == true, "Target type is a not an integer"); + if(size == 0) + { + return 0; + } + if(!f.CanRead(size)) + { + return 0; + } + if(size < sizeof(T)) + { + return ReadTruncatedIntLE(f, size); + } + T retval = ReadIntLE(f); + f.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. + template + uint32 ReadUint32LE(TFileCursor &f) + { + return ReadIntLE(f); + } + + // Read unsigned 32-Bit integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + uint32 ReadUint32BE(TFileCursor &f) + { + return ReadIntBE(f); + } + + // Read signed 32-Bit integer in little-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + int32 ReadInt32LE(TFileCursor &f) + { + return ReadIntLE(f); + } + + // Read signed 32-Bit integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + int32 ReadInt32BE(TFileCursor &f) + { + return ReadIntBE(f); + } + + // Read unsigned 16-Bit integer in little-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + uint16 ReadUint16LE(TFileCursor &f) + { + return ReadIntLE(f); + } + + // Read unsigned 16-Bit integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + uint16 ReadUint16BE(TFileCursor &f) + { + return ReadIntBE(f); + } + + // Read signed 16-Bit integer in little-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + int16 ReadInt16LE(TFileCursor &f) + { + return ReadIntLE(f); + } + + // Read signed 16-Bit integer in big-endian format. + // If successful, the file cursor is advanced by the size of the integer. + template + int16 ReadInt16BE(TFileCursor &f) + { + return ReadIntBE(f); + } + + // Read a single 8bit character. + // If successful, the file cursor is advanced by the size of the integer. + template + char ReadChar(TFileCursor &f) + { + char target; + if(Read(f, target)) + { + return target; + } else + { + return 0; + } + } + + // Read unsigned 8-Bit integer. + // If successful, the file cursor is advanced by the size of the integer. + template + uint8 ReadUint8(TFileCursor &f) + { + uint8 target; + if(Read(f, target)) + { + return target; + } else + { + return 0; + } + } + + // Read signed 8-Bit integer. If successful, the file cursor is advanced by the size of the integer. + template + int8 ReadInt8(TFileCursor &f) + { + int8 target; + if(Read(f, 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. + template + float ReadFloatLE(TFileCursor &f) + { + IEEE754binary32LE target; + if(Read(f, 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. + template + float ReadFloatBE(TFileCursor &f) + { + IEEE754binary32BE target; + if(Read(f, 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. + template + double ReadDoubleLE(TFileCursor &f) + { + IEEE754binary64LE target; + if(Read(f, target)) + { + return target; + } else + { + return 0.0; + } + } + + // Read 64-Bit float in big-endian format. + // If successful, the file cursor is advanced by the size of the float. + template + double ReadDoubleBE(TFileCursor &f) + { + IEEE754binary64BE target; + if(Read(f, target)) + { + return target; + } else + { + return 0.0; + } + } + + // Read a struct. + // If successful, the file cursor is advanced by the size of the struct. Otherwise, the target is zeroed. + template + bool ReadStruct(TFileCursor &f, T &target) + { + static_assert(mpt::is_binary_safe::value); + if(Read(f, target)) + { + return true; + } else + { + Clear(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 + typename TFileCursor::off_t ReadStructPartial(TFileCursor &f, T &target, typename TFileCursor::off_t partialSize = sizeof(T)) + { + static_assert(mpt::is_binary_safe::value); + typename TFileCursor::off_t copyBytes = std::min(partialSize, sizeof(T)); + if(!f.CanRead(copyBytes)) + { + copyBytes = f.BytesLeft(); + } + f.GetRaw(mpt::as_raw_memory(target).data(), copyBytes); + std::memset(mpt::as_raw_memory(target).data() + copyBytes, 0, sizeof(target) - copyBytes); + f.Skip(partialSize); + return copyBytes; + } + + // 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(TFileCursor &f, char (&destBuffer)[destSize], const typename TFileCursor::off_t srcSize) + { + typename TFileCursor::PinnedRawDataView source = f.ReadPinnedRawDataView(srcSize); // Make sure the string is cached properly. + typename TFileCursor::off_t realSrcSize = source.size(); // In case fewer bytes are available + mpt::String::WriteAutoBuf(destBuffer) = mpt::String::ReadBuf(mode, 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(TFileCursor &f, std::string &dest, const typename TFileCursor::off_t srcSize) + { + dest.clear(); + typename TFileCursor::PinnedRawDataView source = f.ReadPinnedRawDataView(srcSize); // Make sure the string is cached properly. + typename TFileCursor::off_t realSrcSize = source.size(); // In case fewer bytes are available + dest = mpt::String::ReadBuf(mode, mpt::byte_cast(source.data()), realSrcSize); + return (realSrcSize > 0 || srcSize == 0); + } + + // Read a string of length srcSize into a mpt::charbuf 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(TFileCursor &f, mpt::charbuf &dest, const typename TFileCursor::off_t srcSize) + { + typename TFileCursor::PinnedRawDataView source = f.ReadPinnedRawDataView(srcSize); // Make sure the string is cached properly. + typename TFileCursor::off_t realSrcSize = source.size(); // In case fewer bytes are available + dest = mpt::String::ReadBuf(mode, 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(TFileCursor &f, mpt::ustring &dest, mpt::Charset charset, const typename TFileCursor::off_t srcSize) + { + dest.clear(); + typename TFileCursor::PinnedRawDataView source = f.ReadPinnedRawDataView(srcSize); // Make sure the string is cached properly. + typename TFileCursor::off_t realSrcSize = source.size(); // In case fewer bytes are available + dest = mpt::ToUnicode(charset, mpt::String::ReadBuf(mode, 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(TFileCursor &f, char (&destBuffer)[destSize], const typename TFileCursor::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(f, srcSize)) + return false; + return ReadString(f, destBuffer, std::min(static_cast(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(TFileCursor &f, std::string &dest, const typename TFileCursor::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(f, srcSize)) + return false; + return ReadString(f, dest, std::min(static_cast(srcSize), maxLength)); + } + + // Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a mpt::charbuf 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(TFileCursor &f, mpt::charbuf &dest, const typename TFileCursor::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(f, srcSize)) + return false; + return ReadString(f, dest, std::min(static_cast(srcSize), maxLength)); + } + + // Read a null-terminated string into a std::string + template + bool ReadNullString(TFileCursor &f, std::string &dest, const typename TFileCursor::off_t maxLength = std::numeric_limits::max()) + { + dest.clear(); + if(!f.CanRead(1)) + return false; + try + { + char buffer[64]; + typename TFileCursor::off_t avail = 0; + while((avail = std::min(f.GetRaw(buffer, std::size(buffer)), maxLength - dest.length())) != 0) + { + auto end = std::find(buffer, buffer + avail, '\0'); + dest.insert(dest.end(), buffer, end); + f.Skip(end - buffer); + if(end < buffer + avail) + { + // Found null char + f.Skip(1); + break; + } + } + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); + } + return dest.length() != 0; + } + + // Read a string up to the next line terminator into a std::string + template + bool ReadLine(TFileCursor &f, std::string &dest, const typename TFileCursor::off_t maxLength = std::numeric_limits::max()) + { + dest.clear(); + if(!f.CanRead(1)) + return false; + try + { + char buffer[64]; + char c = '\0'; + typename TFileCursor::off_t avail = 0; + while((avail = std::min(f.GetRaw(buffer, std::size(buffer)), maxLength - dest.length())) != 0) + { + auto end = std::find_if(buffer, buffer + avail, mpt::String::Traits::IsLineEnding); + dest.insert(dest.end(), buffer, end); + f.Skip(end - buffer); + if(end < buffer + avail) + { + // Found line ending + f.Skip(1); + // Handle CRLF line ending + if(*end == '\r') + { + if(Read(f, c) && c != '\n') + f.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(TFileCursor &f, T (&destArray)[destSize]) + { + static_assert(mpt::is_binary_safe::value); + if(f.CanRead(sizeof(destArray))) + { + f.ReadRaw(mpt::as_raw_memory(destArray)); + return true; + } else + { + Clear(destArray); + return false; + } + } + + // 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(TFileCursor &f, std::array &destArray) + { + static_assert(mpt::is_binary_safe::value); + if(f.CanRead(sizeof(destArray))) + { + f.ReadRaw(mpt::as_raw_memory(destArray)); + return true; + } else + { + destArray.fill(T()); + 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(TFileCursor &f, std::vector &destVector, size_t destSize) + { + static_assert(mpt::is_binary_safe::value); + destVector.resize(destSize); + if(f.CanRead(sizeof(T) * destSize)) + { + f.ReadRaw(mpt::as_raw_memory(destVector)); + return true; + } else + { + return false; + } + } + + template + std::array ReadArray(TFileCursor &f) + { + std::array destArray; + ReadArray(f, destArray); + return destArray; + } + + // 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(TFileCursor &f, const char *const magic, typename TFileCursor::off_t magicLength) + { + std::byte buffer[16] = { std::byte(0) }; + typename TFileCursor::off_t bytesRead = 0; + typename TFileCursor::off_t bytesRemain = magicLength; + while(bytesRemain) + { + typename TFileCursor::off_t numBytes = std::min(static_cast(sizeof(buffer)), bytesRemain); + if(f.GetRawWithOffset(bytesRead, buffer, numBytes) != numBytes) + return false; + if(memcmp(buffer, magic + bytesRead, numBytes)) + return false; + bytesRemain -= numBytes; + bytesRead += numBytes; + } + f.Skip(magicLength); + return true; + } + template + bool ReadMagic(TFileCursor &f, 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'); + } + return ReadMagic(f, static_cast(magic), static_cast(N - 1)); + } + + // 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(TFileCursor &f, T &target) + { + static_assert(std::numeric_limits::is_integer == true + && std::numeric_limits::is_signed == false, + "Target type is not an unsigned integer"); + + if(f.NoBytesLeft()) + { + target = 0; + return false; + } + + std::byte bytes[16]; // More than enough for any valid VarInt + typename TFileCursor::off_t avail = f.GetRaw(bytes, sizeof(bytes)); + typename TFileCursor::off_t readPos = 1; + + uint8 b = mpt::byte_cast(bytes[0]); + target = (b & 0x7F); + size_t writtenBits = static_cast(mpt::bit_width(target)); // Bits used in the most significant byte + + while(readPos < avail && (b & 0x80) != 0) + { + b = mpt::byte_cast(bytes[readPos++]); + target <<= 7; + target |= (b & 0x7F); + writtenBits += 7; + if(readPos == avail) + { + f.Skip(readPos); + avail = f.GetRaw(bytes, sizeof(bytes)); + readPos = 0; + } + } + f.Skip(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 FileReader +} // namespace mpt + +namespace FR = mpt::FileReader; namespace detail { @@ -101,16 +715,16 @@ class FileReader private: - typedef Ttraits traits_type; + using traits_type = Ttraits; public: - typedef typename traits_type::off_t off_t; + using off_t = typename traits_type::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; + using data_type = typename traits_type::data_type; + using ref_data_type = typename traits_type::ref_data_type; + using shared_data_type = typename traits_type::shared_data_type; + using value_data_type = typename traits_type::value_data_type; protected: @@ -315,8 +929,8 @@ public: { private: std::size_t size_; - const mpt::byte *pinnedData; - std::vector cache; + const std::byte *pinnedData; + std::vector cache; private: void Init(const FileReader &file, std::size_t size) { @@ -386,13 +1000,13 @@ public: } } 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(); } + void invalidate() { size_ = 0; pinnedData = nullptr; cache = std::vector(); } + const std::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(); } + mpt::const_byte_span::pointer begin() const { return span().data(); } + mpt::const_byte_span::pointer end() const { return span().data() + span().size(); } + mpt::const_byte_span::const_pointer cbegin() const { return span().data(); } + mpt::const_byte_span::const_pointer cend() const { return span().data() + span().size(); } }; // Returns a pinned view into the remaining raw data from cursor position. @@ -422,7 +1036,7 @@ public: // 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 + FILEREADER_DEPRECATED const std::byte *GetRawData() const { // deprecated because in case of an unseekable std::istream, this triggers caching of the whole file return DataContainer().GetRawData() + streamPos; @@ -437,670 +1051,300 @@ public: template std::size_t GetRawWithOffset(std::size_t offset, T *dst, std::size_t count) const { - return static_cast(DataContainer().Read(mpt::byte_cast(dst), streamPos + offset, count)); + return static_cast(DataContainer().Read(mpt::byte_cast(dst), streamPos + offset, count)); + } + std::size_t GetRawWithOffset(std::size_t offset, mpt::byte_span dst) const + { + return static_cast(DataContainer().Read(streamPos + offset, dst)); } template std::size_t GetRaw(T *dst, std::size_t count) const { - return static_cast(DataContainer().Read(mpt::byte_cast(dst), streamPos, count)); + return static_cast(DataContainer().Read(mpt::byte_cast(dst), streamPos, count)); + } + std::size_t GetRaw(mpt::byte_span dst) const + { + return static_cast(DataContainer().Read(streamPos, dst)); } 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)); + std::size_t result = static_cast(DataContainer().Read(mpt::byte_cast(dst), streamPos, count)); + streamPos += result; + return result; + } + std::size_t ReadRaw(mpt::byte_span dst) + { + std::size_t result = static_cast(DataContainer().Read(streamPos, dst)); streamPos += result; return result; } - std::vector GetRawDataAsByteVector() const + std::vector GetRawDataAsByteVector() const { PinnedRawDataView view = GetPinnedRawDataView(); - return std::vector(view.span().begin(), view.span().end()); + return mpt::make_vector(view.span()); } - std::vector ReadRawDataAsByteVector() + std::vector ReadRawDataAsByteVector() { PinnedRawDataView view = ReadPinnedRawDataView(); - return std::vector(view.span().begin(), view.span().end()); + return mpt::make_vector(view.span()); } - std::vector GetRawDataAsByteVector(std::size_t size) const + std::vector GetRawDataAsByteVector(std::size_t size) const { PinnedRawDataView view = GetPinnedRawDataView(size); - return std::vector(view.span().begin(), view.span().end()); + return mpt::make_vector(view.span()); } - std::vector ReadRawDataAsByteVector(std::size_t size) + std::vector ReadRawDataAsByteVector(std::size_t size) { PinnedRawDataView view = ReadPinnedRawDataView(size); - return std::vector(view.span().begin(), view.span().end()); + return mpt::make_vector(view.span()); } std::string GetRawDataAsString() const { PinnedRawDataView view = GetPinnedRawDataView(); - return std::string(mpt::byte_cast(view.span().begin()), mpt::byte_cast(view.span().end())); + mpt::span data = mpt::byte_cast>(view.span()); + return std::string(data.begin(), data.end()); } std::string ReadRawDataAsString() { PinnedRawDataView view = ReadPinnedRawDataView(); - return std::string(mpt::byte_cast(view.span().begin()), mpt::byte_cast(view.span().end())); + mpt::span data = mpt::byte_cast>(view.span()); + return std::string(data.begin(), data.end()); } std::string GetRawDataAsString(std::size_t size) const { PinnedRawDataView view = GetPinnedRawDataView(size); - return std::string(mpt::byte_cast(view.span().begin()), mpt::byte_cast(view.span().end())); + mpt::span data = mpt::byte_cast>(view.span()); + return std::string(data.begin(), data.end()); } std::string ReadRawDataAsString(std::size_t size) { PinnedRawDataView view = ReadPinnedRawDataView(size); - return std::string(mpt::byte_cast(view.span().begin()), mpt::byte_cast(view.span().end())); + mpt::span data = mpt::byte_cast>(view.span()); + return std::string(data.begin(), data.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) { - mpt::byte_span dst = mpt::as_raw_memory(target); - if(dst.size() != DataContainer().Read(streamPos, dst)) - { - return false; - } - streamPos += dst.size(); - return true; + return mpt::FileReader::Read(*this, target); } -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"); - typename mpt::make_le::type target; - if(Read(target)) - { - return target; - } else - { - return 0; - } + return mpt::FileReader::ReadIntLE(*this); } - // 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"); - typename mpt::make_be::type target; - if(Read(target)) - { - return target; - } else - { - return 0; - } + return mpt::FileReader::ReadIntLE(*this); } - // 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; - } - typename mpt::make_le::type target; - std::memcpy(&target, buf, sizeof(T)); - return target; + return mpt::FileReader::ReadTruncatedIntLE(*this, size); } - // 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; + return mpt::FileReader::ReadSizedIntLE(*this, size); } - // 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(); + return mpt::FileReader::ReadUint32LE(*this); } - // 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(); + return mpt::FileReader::ReadUint32BE(*this); } - // 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(); + return mpt::FileReader::ReadInt32LE(*this); } - // 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(); + return mpt::FileReader::ReadInt32BE(*this); } - // 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(); + return mpt::FileReader::ReadUint16LE(*this); } - // 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(); + return mpt::FileReader::ReadUint16BE(*this); } - // 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(); + return mpt::FileReader::ReadInt16LE(*this); } - // 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(); + return mpt::FileReader::ReadInt16BE(*this); } - // Read a single 8bit character. - // If successful, the file cursor is advanced by the size of the integer. char ReadChar() { - char target; - if(Read(target)) - { - return target; - } else - { - return 0; - } + return mpt::FileReader::ReadChar(*this); } - // 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; - } + return mpt::FileReader::ReadUint8(*this); } - // 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; - } + return mpt::FileReader::ReadInt8(*this); } - // 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; - } + return mpt::FileReader::ReadFloatLE(*this); } - // 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; - } + return mpt::FileReader::ReadFloatBE(*this); } - // 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.0; - } + return mpt::FileReader::ReadDoubleLE(*this); } - // 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.0; - } + return mpt::FileReader::ReadDoubleBE(*this); } - // 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 - { - Clear(target); - return false; - } + return mpt::FileReader::ReadStruct(*this, target); } - // 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)) + size_t ReadStructPartial(T &target, size_t partialSize = sizeof(T)) { - STATIC_ASSERT(mpt::is_binary_safe::value); - off_t copyBytes = std::min(partialSize, sizeof(T)); - if(!CanRead(copyBytes)) - { - copyBytes = BytesLeft(); - } - GetRaw(mpt::as_raw_memory(target).data(), copyBytes); - std::memset(mpt::as_raw_memory(target).data() + copyBytes, 0, sizeof(target) - copyBytes); - Skip(partialSize); - return true; + return mpt::FileReader::ReadStructPartial(*this, target, partialSize); } - // 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); + return mpt::FileReader::ReadString(*this, destBuffer, srcSize); } - // 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); + return mpt::FileReader::ReadString(*this, dest, srcSize); + } + + template + bool ReadString(mpt::charbuf &dest, const off_t srcSize) + { + return mpt::FileReader::ReadString(*this, dest, srcSize); } - // 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); + return mpt::FileReader::ReadString(*this, dest, charset, srcSize); } - // 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)); + return mpt::FileReader::ReadSizedString(*this, destBuffer, 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)); + return mpt::FileReader::ReadSizedString(*this, dest, maxLength); + } + + template + bool ReadSizedString(mpt::charbuf &dest, const off_t maxLength = std::numeric_limits::max()) + { + return mpt::FileReader::ReadSizedString(*this, dest, 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(GetRaw(buffer, mpt::size(buffer)), maxLength - dest.length())) != 0) - { - auto end = std::find(buffer, buffer + avail, '\0'); - dest.insert(dest.end(), buffer, end); - Skip(end - buffer); - if(end < buffer + avail) - { - // Found null char - Skip(1); - break; - } - } - } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) - { - MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); - } - return dest.length() != 0; + return mpt::FileReader::ReadNullString(*this, dest, maxLength); } - // 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]; - char c = '\0'; - off_t avail = 0; - while((avail = std::min(GetRaw(buffer, mpt::size(buffer)), maxLength - dest.length())) != 0) - { - auto end = std::find_if(buffer, buffer + avail, mpt::String::Traits::IsLineEnding); - dest.insert(dest.end(), buffer, end); - Skip(end - buffer); - if(end < buffer + avail) - { - // Found line ending - Skip(1); - // 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; + return mpt::FileReader::ReadLine(*this, dest, maxLength); } - // 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 - { - Clear(destArray); - return false; - } + return mpt::FileReader::ReadArray(*this, destArray); } + template bool ReadArray(std::array &destArray) { - STATIC_ASSERT(mpt::is_binary_safe::value); - if(CanRead(sizeof(destArray))) - { - for(auto &element : destArray) - { - Read(element); - } - return true; - } else - { - destArray.fill(T()); - return false; - } + return mpt::FileReader::ReadArray(*this, destArray); + } + + template + std::array ReadArray() + { + return mpt::FileReader::ReadArray(*this); } - // 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; - } + return mpt::FileReader::ReadVector(*this, destVector, destSize); } - // 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); - GetRaw(bytes, N - 1); - if(!std::memcmp(bytes, magic, N - 1)) - { - Skip(N - 1); - return true; - } - } - return false; + return mpt::FileReader::ReadMagic(*this, magic); } 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 = mpt::as_byte(0); - GetRawWithOffset(i, &c, 1); - if(c != mpt::byte_cast(magic[i])) - { - identical = false; - break; - } - } - if(identical) - { - Skip(magicLength); - return true; - } else - { - return false; - } - } else - { - return false; - } + return mpt::FileReader::ReadMagic(*this, magic, magicLength); } - // 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 = GetRaw(bytes, sizeof(bytes)); - off_t readPos = 1; - - size_t writtenBits = 0; - uint8 b = mpt::byte_cast(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 = mpt::byte_cast(bytes[readPos++]); - target <<= 7; - target |= (b & 0x7F); - writtenBits += 7; - if(readPos == avail) - { - Skip(readPos); - avail = GetRaw(bytes, sizeof(bytes)); - readPos = 0; - } - } - Skip(readPos); - - if(writtenBits > sizeof(target) * 8u) - { - // Overflow - target = Util::MaxValueOfType(target); - return false; - } else if((b & 0x80) != 0) - { - // Reached EOF - return false; - } - return true; + return mpt::FileReader::ReadVarInt(*this, target); } }; } // namespace detail -typedef detail::FileReader FileReader; +using FileReader = detail::FileReader; -typedef detail::FileReader MemoryFileReader; +using MemoryFileReader = detail::FileReader; // Initialize file reader object with pointer to data and data length. @@ -1109,8 +1353,6 @@ template static inline FileReader make_FileReader(mpt::span(bytedata), filename); } -#if defined(MPT_FILEREADER_STD_ISTREAM) - #if defined(MPT_FILEREADER_CALLBACK_STREAM) // Initialize file reader object with a CallbackStream. @@ -1138,29 +1380,23 @@ static inline FileReader make_FileReader(std::istream *s, const mpt::PathString ); } -#endif // MPT_FILEREADER_STD_ISTREAM - #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 make_FileReader(tmp.first, tmp.second); - #else - typename TInputFile::ContentsRef tmp = file.Get(); - return make_FileReader(mpt::as_span(tmp.first.data, tmp.first.size), tmp.second); - #endif + if(!file.IsValid()) + { + return FileReader(); + } + if(file.IsCached()) + { + return make_FileReader(file.GetCache(), &file.GetFilenameRef()); + } else + { + return make_FileReader(file.GetStream(), &file.GetFilenameRef()); + } } #endif // MPT_ENABLE_FILEIO diff --git a/Frameworks/OpenMPT/OpenMPT/common/FileReaderFwd.h b/Frameworks/OpenMPT/OpenMPT/common/FileReaderFwd.h index 6f73a69f7..2004bd625 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/FileReaderFwd.h +++ b/Frameworks/OpenMPT/OpenMPT/common/FileReaderFwd.h @@ -16,17 +16,9 @@ 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 +using FileReaderTraitsDefault = FileReaderTraitsStdStream; namespace detail { @@ -35,9 +27,9 @@ class FileReader; } // namespace detail -typedef detail::FileReader FileReader; +using FileReader = detail::FileReader; -typedef detail::FileReader MemoryFileReader; +using MemoryFileReader = detail::FileReader; OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/FlagSet.h b/Frameworks/OpenMPT/OpenMPT/common/FlagSet.h index e0aa76dc7..fa75bbdad 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/FlagSet.h +++ b/Frameworks/OpenMPT/OpenMPT/common/FlagSet.h @@ -24,7 +24,7 @@ OPENMPT_NAMESPACE_BEGIN template struct enum_traits { - typedef typename std::make_unsigned::type store_type; + using store_type = typename std::make_unsigned::type; }; @@ -35,9 +35,9 @@ 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; + using enum_type = enum_t; + using value_type = enum_value_type; + using store_type = typename enum_traits::store_type; private: store_type bits; public: @@ -94,10 +94,10 @@ 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; + using self_type = Enum; + using enum_type = enum_t; + using value_type = enum_value_type; + using store_type = typename value_type::store_type; private: enum_type value; public: @@ -150,10 +150,10 @@ template :: class FlagSet { public: - typedef FlagSet self_type; - typedef enum_t enum_type; - typedef enum_value_type value_type; - typedef store_t store_type; + using self_type = FlagSet; + using enum_type = enum_t; + using value_type = enum_value_type; + using store_type = store_t; private: @@ -195,8 +195,7 @@ public: { return load(); } - // The macro-based extended instrument fields writer in InstrumentExtensions.cpp currently needs this conversion. - /*MPT_DEPRECATED*/ MPT_CONSTEXPR11_FUN operator store_type () const noexcept + MPT_CONSTEXPR11_FUN explicit operator store_type () const noexcept { return load().as_bits(); } diff --git a/Frameworks/OpenMPT/OpenMPT/common/Logging.cpp b/Frameworks/OpenMPT/OpenMPT/common/Logging.cpp index 722556a84..6b4042c89 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/Logging.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/Logging.cpp @@ -110,8 +110,8 @@ void Logger::SendLogMessage(const mpt::source_location &loc, LogLevel level, con #endif // MODPLUG_TRACKER // remove eol if already present and add log level prefix const mpt::ustring message = LogLevelToString(level) + U_(": ") + mpt::String::RTrim(text, U_("\r\n")); - const mpt::ustring file = mpt::ToUnicode(mpt::CharsetASCII, loc.file_name() ? loc.file_name() : ""); - const mpt::ustring function = mpt::ToUnicode(mpt::CharsetASCII, loc.function_name() ? loc.function_name() : ""); + const mpt::ustring file = mpt::ToUnicode(mpt::CharsetSource, loc.file_name() ? loc.file_name() : ""); + const mpt::ustring function = mpt::ToUnicode(mpt::CharsetSource, loc.function_name() ? loc.function_name() : ""); const mpt::ustring line = mpt::ufmt::dec(loc.line()); #if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) #if MPT_OS_WINDOWS @@ -132,7 +132,7 @@ void Logger::SendLogMessage(const mpt::source_location &loc, LogLevel level, con } if(s_logfile) { - mpt::IO::WriteText(s_logfile, mpt::ToCharset(mpt::CharsetUTF8, mpt::format(U_("%1+%2 %3(%4): %5 [%6]\n")) + mpt::IO::WriteText(s_logfile, mpt::ToCharset(mpt::CharsetLogfile, mpt::format(U_("%1+%2 %3(%4): %5 [%6]\n")) ( mpt::Date::ANSI::ToUString(cur) , mpt::ufmt::right(6, mpt::ufmt::dec(diff)) , file @@ -168,42 +168,21 @@ void Logger::SendLogMessage(const mpt::source_location &loc, LogLevel level, con #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) << "]" + << mpt::ToCharset(mpt::CharsetStdIO, file) << "(" << mpt::ToCharset(mpt::CharsetStdIO, line) << ")" << ": " + << mpt::ToCharset(mpt::CharsetStdIO, message) + << " [" << mpt::ToCharset(mpt::CharsetStdIO, 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) << "]" + << mpt::ToCharset(mpt::CharsetStdIO, file) << "(" << mpt::ToCharset(mpt::CharsetStdIO, line) << ")" << ": " + << mpt::ToCharset(mpt::CharsetStdIO, message) + << " [" << mpt::ToCharset(mpt::CharsetStdIO, function) << "]" << std::endl; #endif // MODPLUG_TRACKER #endif // MPT_LOG_IS_DISABLED } -void LegacyLogger::operator () (const AnyStringLocale &text) -{ - SendLogMessage(loc, 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(loc, MPT_LEGACY_LOGLEVEL, "", mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, message)); -} - -void LegacyLogger::operator () (LogLevel level, const mpt::ustring &text) -{ - SendLogMessage(loc, level, "", text); -} @@ -219,7 +198,7 @@ namespace Trace { // Debugging functionality will use simple globals. -bool volatile g_Enabled = false; +std::atomic g_Enabled = ATOMIC_VAR_INIT(false); static bool g_Sealed = false; @@ -338,7 +317,7 @@ bool Dump(const mpt::PathString &filename) mpt::ofstream f(filename); - f << "Build: OpenMPT " << mpt::ToCharset(mpt::CharsetUTF8, Build::GetVersionStringExtended()) << std::endl; + f << "Build: OpenMPT " << mpt::ToCharset(mpt::CharsetLogfile, Build::GetVersionStringExtended()) << std::endl; bool qpcValid = false; @@ -350,7 +329,7 @@ bool Dump(const mpt::PathString &filename) qpcValid = true; } - f << "Dump: " << mpt::ToCharset(mpt::CharsetUTF8, mpt::Date::ANSI::ToUString(ftNow)) << std::endl; + f << "Dump: " << mpt::ToCharset(mpt::CharsetLogfile, mpt::Date::ANSI::ToUString(ftNow)) << std::endl; f << "Captured events: " << Entries.size() << std::endl; if(qpcValid && (Entries.size() > 0)) { @@ -367,7 +346,7 @@ bool Dump(const mpt::PathString &filename) std::string time; if(qpcValid) { - time = mpt::ToCharset(mpt::CharsetUTF8, mpt::Date::ANSI::ToUString( ftNow - static_cast( static_cast(qpcNow.QuadPart - entry.Timestamp) * (10000000.0 / static_cast(qpcFreq.QuadPart) ) ) ) ); + time = mpt::ToCharset(mpt::CharsetLogfile, mpt::Date::ANSI::ToUString( 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)); diff --git a/Frameworks/OpenMPT/OpenMPT/common/Logging.h b/Frameworks/OpenMPT/OpenMPT/common/Logging.h index 0d7c2e349..5a8bee674 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/Logging.h +++ b/Frameworks/OpenMPT/OpenMPT/common/Logging.h @@ -11,6 +11,9 @@ #include "BuildSettings.h" +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS +#include +#endif OPENMPT_NAMESPACE_BEGIN @@ -110,7 +113,7 @@ namespace log // All logging code gets compiled and immediately dead-code eliminated. #define MPT_LOG_IS_DISABLED #endif -static const int GlobalLogLevel = MPT_LOG_GLOBAL_LEVEL ; +static constexpr int GlobalLogLevel = MPT_LOG_GLOBAL_LEVEL ; #else extern int GlobalLogLevel; #endif @@ -155,35 +158,11 @@ public: /**/ -#define MPT_LEGACY_LOGLEVEL LogDebug - -class LegacyLogger : public Logger -{ -private: - const mpt::source_location loc; -public: - constexpr LegacyLogger(mpt::source_location loc) noexcept : loc(loc) {} - /* 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_SOURCE_LOCATION_CURRENT()) - - #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 @@ -199,7 +178,7 @@ namespace Trace { // 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; +extern std::atomic g_Enabled; static inline bool IsEnabled() { return g_Enabled; } enum class Direction : int8 diff --git a/Frameworks/OpenMPT/OpenMPT/common/Profiler.cpp b/Frameworks/OpenMPT/OpenMPT/common/Profiler.cpp index 037fd969e..fd3c7f6eb 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/Profiler.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/Profiler.cpp @@ -52,7 +52,7 @@ struct ProfileBlock class Statistics * stats; }; -static const std::size_t MAX_PROFILES = 1024; +static constexpr std::size_t MAX_PROFILES = 1024; static ProfileBlock Profiles[ MAX_PROFILES ]; diff --git a/Frameworks/OpenMPT/OpenMPT/common/Profiler.h b/Frameworks/OpenMPT/OpenMPT/common/Profiler.h index 41a4b9008..86f11dbf9 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/Profiler.h +++ b/Frameworks/OpenMPT/OpenMPT/common/Profiler.h @@ -98,7 +98,7 @@ public: /**/ -#define OPENMPT_PROFILE_FUNCTION(cat) OPENMPT_PROFILE_SCOPE(cat, __FUNCTION__) +#define OPENMPT_PROFILE_FUNCTION(cat) OPENMPT_PROFILE_SCOPE(cat, __func__) #else // !USE_PROFILER diff --git a/Frameworks/OpenMPT/OpenMPT/common/misc_util.cpp b/Frameworks/OpenMPT/OpenMPT/common/misc_util.cpp index 88319051a..1e43ebae8 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/misc_util.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/misc_util.cpp @@ -20,13 +20,13 @@ namespace Util { -static const MPT_UCHAR_TYPE EncodeNibble[16] = { +static constexpr mpt::uchar EncodeNibble[16] = { UC_('0'), UC_('1'), UC_('2'), UC_('3'), UC_('4'), UC_('5'), UC_('6'), UC_('7'), UC_('8'), UC_('9'), UC_('A'), UC_('B'), UC_('C'), UC_('D'), UC_('E'), UC_('F') }; -static inline bool DecodeByte(uint8 &byte, MPT_UCHAR_TYPE c1, MPT_UCHAR_TYPE c2) +static inline bool DecodeByte(uint8 &byte, mpt::uchar c1, mpt::uchar c2) { byte = 0; if(UC_('0') <= c1 && c1 <= UC_('9')) @@ -62,7 +62,7 @@ mpt::ustring BinToHex(mpt::const_byte_span src) { mpt::ustring result; result.reserve(src.size() * 2); - for(mpt::byte byte : src) + for(std::byte byte : src) { result.push_back(EncodeNibble[(mpt::byte_cast(byte) & 0xf0) >> 4]); result.push_back(EncodeNibble[mpt::byte_cast(byte) & 0x0f]); @@ -70,9 +70,9 @@ mpt::ustring BinToHex(mpt::const_byte_span src) return result; } -std::vector HexToBin(const mpt::ustring &src) +std::vector HexToBin(const mpt::ustring &src) { - std::vector result; + std::vector result; result.reserve(src.size() / 2); for(std::size_t i = 0; (i + 1) < src.size(); i += 2) { @@ -81,7 +81,7 @@ std::vector HexToBin(const mpt::ustring &src) { return result; } - result.push_back(mpt::byte_cast(byte)); + result.push_back(mpt::byte_cast(byte)); } return result; } diff --git a/Frameworks/OpenMPT/OpenMPT/common/misc_util.h b/Frameworks/OpenMPT/OpenMPT/common/misc_util.h index 3415a508d..720e08c14 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/misc_util.h +++ b/Frameworks/OpenMPT/OpenMPT/common/misc_util.h @@ -207,7 +207,7 @@ namespace Util namespace Util { -std::vector HexToBin(const mpt::ustring &src); +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)); } diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.cpp index faaa7d04a..760978fab 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.cpp @@ -12,18 +12,6 @@ #include "stdafx.h" #include "mptAlloc.h" -#include "mptBaseTypes.h" - -#include -#include - -#include -#include - -#if MPT_COMPILER_MSVC -#include -#endif - OPENMPT_NAMESPACE_BEGIN @@ -37,87 +25,6 @@ OPENMPT_NAMESPACE_BEGIN namespace mpt { - - -#if MPT_CXX_AT_LEAST(17) -#else -void* align(std::size_t alignment, std::size_t size, void* &ptr, std::size_t &space) noexcept -{ - std::size_t offset = static_cast(reinterpret_cast(ptr) & (alignment - 1)); - if(offset != 0) - { - offset = alignment - offset; - } - if((space < offset) || ((space - offset) < size)) - { - return nullptr; - } - ptr = static_cast(ptr) + offset; - space -= offset; - return ptr; -} -#endif - -aligned_raw_memory aligned_alloc_impl(std::size_t size, std::size_t count, std::size_t alignment) -{ - #if MPT_CXX_AT_LEAST(17) && !defined(MPT_COMPILER_QUIRK_NO_ALIGNEDALLOC) - std::size_t space = count * size; - void* mem = std::aligned_alloc(alignment, space); - if(!mem) - { - MPT_EXCEPTION_THROW_OUT_OF_MEMORY(); - } - return aligned_raw_memory{mem, mem}; - #elif MPT_COMPILER_MSVC || (defined(__clang__) && defined(_MSC_VER)) - std::size_t space = count * size; - void* mem = _aligned_malloc(space, alignment); - if(!mem) - { - MPT_EXCEPTION_THROW_OUT_OF_MEMORY(); - } - return aligned_raw_memory{mem, mem}; - #else - if(alignment > alignof(mpt::max_align_t)) - { - std::size_t space = count * size + (alignment - 1); - void* mem = std::malloc(space); - if(!mem) - { - MPT_EXCEPTION_THROW_OUT_OF_MEMORY(); - } - void* aligned_mem = mem; - void* aligned = mpt::align(alignment, size * count, aligned_mem, space); - if(!aligned) - { - MPT_EXCEPTION_THROW_OUT_OF_MEMORY(); - } - return aligned_raw_memory{aligned, mem}; - } else - { - std::size_t space = count * size; - void* mem = std::malloc(space); - if(!mem) - { - MPT_EXCEPTION_THROW_OUT_OF_MEMORY(); - } - return aligned_raw_memory{mem, mem}; - } - #endif -} - -void aligned_free(aligned_raw_memory raw) -{ - #if MPT_CXX_AT_LEAST(17) && !defined(MPT_COMPILER_QUIRK_NO_ALIGNEDALLOC) - std::free(raw.mem); - #elif MPT_COMPILER_MSVC || (defined(__clang__) && defined(_MSC_VER)) - _aligned_free(raw.mem); - #else - std::free(raw.mem); - #endif -} - - - } // namespace mpt diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h b/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h index 42e93a3f3..2c33e031d 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptAlloc.h @@ -18,8 +18,9 @@ #include "mptMemory.h" #include "mptSpan.h" +#include #include -#include +#include #include @@ -32,9 +33,9 @@ namespace mpt { -template inline span as_span(std::vector & cont) { return span(cont); } +template inline mpt::span as_span(std::vector & cont) { return mpt::span(cont); } -template inline span as_span(const std::vector & cont) { return span(cont); } +template inline mpt::span as_span(const std::vector & cont) { return mpt::span(cont); } @@ -53,13 +54,13 @@ struct GetRawBytesFunctor> { inline mpt::const_byte_span operator () (const std::vector & v) const { - STATIC_ASSERT(mpt::is_binary_safe::type>::value); - return mpt::as_span(reinterpret_cast(v.data()), v.size() * sizeof(T)); + static_assert(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(v.data()), v.size() * sizeof(T)); } inline mpt::byte_span operator () (std::vector & v) const { - STATIC_ASSERT(mpt::is_binary_safe::type>::value); - return mpt::as_span(reinterpret_cast(v.data()), v.size() * sizeof(T)); + static_assert(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(v.data()), v.size() * sizeof(T)); } }; @@ -68,8 +69,8 @@ struct GetRawBytesFunctor> { inline mpt::const_byte_span operator () (const std::vector & v) const { - STATIC_ASSERT(mpt::is_binary_safe::type>::value); - return mpt::as_span(reinterpret_cast(v.data()), v.size() * sizeof(T)); + static_assert(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(v.data()), v.size() * sizeof(T)); } }; @@ -79,22 +80,6 @@ struct GetRawBytesFunctor> -#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 defined(MPT_ENABLE_ALIGNED_ALLOC) @@ -104,7 +89,7 @@ namespace mpt -#if MPT_CXX_AT_LEAST(17) && !(MPT_COMPILER_CLANG && defined(__GLIBCXX__)) && !(MPT_COMPILER_CLANG && MPT_OS_MACOSX_OR_IOS) +#if !(MPT_COMPILER_CLANG && defined(__GLIBCXX__)) && !(MPT_COMPILER_CLANG && MPT_OS_MACOSX_OR_IOS) using std::launder; #else template @@ -115,147 +100,17 @@ MPT_NOINLINE T* launder(T* p) noexcept #endif -#if MPT_CXX_AT_LEAST(17) -using std::align; -#else -// pre-C++17, std::align does not support over-alignement -void* align(std::size_t alignment, std::size_t size, void* &ptr, std::size_t &space) noexcept; -#endif - - -struct aligned_raw_memory +template +struct alignas(static_cast(alignment)) aligned_array + : std::array { - void* aligned; - void* mem; + static_assert(static_cast(alignment) >= alignof(T)); + static_assert(((count * sizeof(T)) % static_cast(alignment)) == 0); + static_assert(sizeof(std::array) == (sizeof(T) * count)); }; -aligned_raw_memory aligned_alloc_impl(std::size_t size, std::size_t count, std::size_t alignment); - -template -inline aligned_raw_memory aligned_alloc(std::size_t size, std::size_t count) -{ - MPT_STATIC_ASSERT(alignment > 0); - MPT_CONSTEXPR14_ASSERT(mpt::weight(alignment) == 1); - return aligned_alloc_impl(size, count, alignment); -} - -void aligned_free(aligned_raw_memory raw); - -template -struct aligned_raw_buffer -{ - T* elements; - void* mem; -}; - -template -inline aligned_raw_buffer aligned_alloc(std::size_t count) -{ - MPT_STATIC_ASSERT(alignment >= alignof(T)); - aligned_raw_memory raw = aligned_alloc(sizeof(T), count); - return aligned_raw_buffer{mpt::launder(reinterpret_cast(raw.aligned)), raw.mem}; -} - -template -inline void aligned_free(aligned_raw_buffer buf) -{ - aligned_free(aligned_raw_memory{buf.elements, buf.mem}); -} - -template -struct aligned_raw_objects -{ - T* elements; - std::size_t count; - void* mem; -}; - -template -inline aligned_raw_objects aligned_new(std::size_t count, T init = T()) -{ - aligned_raw_buffer buf = aligned_alloc(count); - std::size_t constructed = 0; - try - { - for(std::size_t i = 0; i < count; ++i) - { - new(&(buf.elements[i])) T(init); - constructed++; - } - } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) - { - while(constructed--) - { - mpt::launder(&(buf.elements[constructed - 1]))->~T(); - } - aligned_free(buf); - MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); - } catch(...) - { - while(constructed--) - { - mpt::launder(&(buf.elements[constructed - 1]))->~T(); - } - aligned_free(buf); - throw; - } - return aligned_raw_objects{mpt::launder(buf.elements), count, buf.mem}; -} - -template -inline void aligned_delete(aligned_raw_objects objs) -{ - if(objs.elements) - { - std::size_t constructed = objs.count; - while(constructed--) - { - objs.elements[constructed - 1].~T(); - } - } - aligned_free(aligned_raw_buffer{objs.elements, objs.mem}); -} - -template -class aligned_buffer -{ -private: - aligned_raw_objects objs; -public: - explicit aligned_buffer(std::size_t count = 0) - : objs(aligned_new(count)) - { - } - aligned_buffer(const aligned_buffer&) = delete; - aligned_buffer& operator=(const aligned_buffer&) = delete; - ~aligned_buffer() - { - aligned_delete(objs); - } -public: - void destructive_resize(std::size_t count) - { - aligned_raw_objects tmpobjs = aligned_new(count); - { - using namespace std; - swap(objs, tmpobjs); - } - aligned_delete(tmpobjs); - } -public: - T* begin() noexcept { return objs.elements; } - const T* begin() const noexcept { return objs.elements; } - T* end() noexcept { return objs.elements + objs.count; } - const T* end() const noexcept { return objs.elements + objs.count; } - const T* cbegin() const noexcept { return objs.elements; } - const T* cend() const noexcept { return objs.elements + objs.count; } - T& operator[](std::size_t i) noexcept { return objs.elements[i]; } - const T& operator[](std::size_t i) const noexcept { return objs.elements[i]; } - T* data() noexcept { return objs.elements; } - const T* data() const noexcept { return objs.elements; } - std::size_t size() const noexcept { return objs.count; } -}; +static_assert(sizeof(mpt::aligned_array) == sizeof(std::array)); diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptAssert.h b/Frameworks/OpenMPT/OpenMPT/common/mptAssert.h index dca2b3dfd..7c2687adc 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptAssert.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptAssert.h @@ -56,7 +56,7 @@ OPENMPT_NAMESPACE_BEGIN -#if defined(_MFC_VER) && !defined(MPT_CPPCHECK_CUSTOM) +#if defined(MPT_WITH_MFC) && !defined(MPT_CPPCHECK_CUSTOM) #if !defined(ASSERT) #error "MFC is expected to #define ASSERT" @@ -72,7 +72,7 @@ OPENMPT_NAMESPACE_BEGIN // let MFC handle our asserts #define MPT_ASSERT_USE_FRAMEWORK 1 -#else // !_MFC_VER +#else // !MPT_WITH_MFC #if defined(ASSERT) #define MPT_FRAMEWORK_ASSERT_IS_DEFINED @@ -86,7 +86,7 @@ OPENMPT_NAMESPACE_BEGIN // handle assert in our own way without relying on some platform-/framework-specific assert implementation #define MPT_ASSERT_USE_FRAMEWORK 0 -#endif // _MFC_VER +#endif // MPT_WITH_MFC #if defined(MPT_FRAMEWORK_ASSERT_IS_DEFINED) && (MPT_ASSERT_USE_FRAMEWORK == 1) @@ -117,7 +117,7 @@ OPENMPT_NAMESPACE_BEGIN #else // !NO_ASSERTS -#define MPT_ASSERT_NOTREACHED() MPT_DO { MPT_CONSTANT_IF(!(0)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), "0"); } MPT_CHECKER_ASSUME(0); } MPT_WHILE_0 +#define MPT_ASSERT_NOTREACHED() MPT_DO { if constexpr(!(0)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), "0"); } MPT_CHECKER_ASSUME(0); } MPT_WHILE_0 #define MPT_ASSERT(expr) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 #define MPT_ASSERT_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 #define MPT_ASSERT_ALWAYS(expr) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0 @@ -136,17 +136,9 @@ MPT_NOINLINE void AssertHandler(const mpt::source_location &loc, const char *exp -#define MPT_CONSTEXPR11_ASSERT MPT_STATIC_ASSERT -#if MPT_CXX_AT_LEAST(14) && !MPT_MSVC_BEFORE(2017,5) -#define MPT_CONSTEXPR14_ASSERT MPT_STATIC_ASSERT -#else -#define MPT_CONSTEXPR14_ASSERT MPT_ASSERT -#endif -#if MPT_CXX_AT_LEAST(17) && !MPT_MSVC_BEFORE(2017,5) -#define MPT_CONSTEXPR17_ASSERT MPT_STATIC_ASSERT -#else -#define MPT_CONSTEXPR17_ASSERT MPT_ASSERT -#endif +#define MPT_CONSTEXPR11_ASSERT static_assert +#define MPT_CONSTEXPR14_ASSERT static_assert +#define MPT_CONSTEXPR17_ASSERT static_assert diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptBaseMacros.h b/Frameworks/OpenMPT/OpenMPT/common/mptBaseMacros.h index 4425abcf4..211fdaac9 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptBaseMacros.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptBaseMacros.h @@ -14,7 +14,9 @@ +#include #include +#include #include #include @@ -28,14 +30,44 @@ OPENMPT_NAMESPACE_BEGIN -// Compile time assert. -#if (MPT_CXX >= 17) -#define MPT_STATIC_ASSERT static_assert +#define MPT_PP_DEFER(m, ...) m(__VA_ARGS__) + +#define MPT_PP_STRINGIFY(x) #x + +#define MPT_PP_JOIN_HELPER(a, b) a ## b +#define MPT_PP_JOIN(a, b) MPT_PP_JOIN_HELPER(a, b) + +#define MPT_PP_UNIQUE_IDENTIFIER(prefix) MPT_PP_JOIN(prefix , __LINE__) + + + +#if MPT_COMPILER_MSVC + +#define MPT_WARNING(text) __pragma(message(__FILE__ "(" MPT_PP_DEFER(MPT_PP_STRINGIFY, __LINE__) "): Warning: " text)) +#define MPT_WARNING_STATEMENT(text) __pragma(message(__FILE__ "(" MPT_PP_DEFER(MPT_PP_STRINGIFY, __LINE__) "): Warning: " text)) + +#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG + +#define MPT_WARNING(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text)) +#define MPT_WARNING_STATEMENT(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text)) + #else -#define MPT_STATIC_ASSERT(expr) static_assert((expr), "compile time assertion failed: " #expr) + +// portable #pragma message or #warning replacement +#define MPT_WARNING(text) \ + static inline int MPT_PP_UNIQUE_IDENTIFIER(MPT_WARNING_NAME) () noexcept { \ + int warning [[deprecated("Warning: " text)]] = 0; \ + return warning; \ + } \ +/**/ +#define MPT_WARNING_STATEMENT(text) \ + int MPT_PP_UNIQUE_IDENTIFIER(MPT_WARNING_NAME) = [](){ \ + int warning [[deprecated("Warning: " text)]] = 0; \ + return warning; \ + }() \ +/**/ + #endif -// legacy -#define STATIC_ASSERT(x) MPT_STATIC_ASSERT(x) @@ -55,43 +87,76 @@ OPENMPT_NAMESPACE_BEGIN // constexpr #define MPT_CONSTEXPR11_FUN constexpr MPT_FORCEINLINE -#define MPT_CONSTEXPR11_VAR constexpr -#if MPT_CXX_AT_LEAST(14) && !MPT_MSVC_BEFORE(2017,5) #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 -#if MPT_CXX_AT_LEAST(17) && !MPT_MSVC_BEFORE(2017,5) #define MPT_CONSTEXPR17_FUN constexpr MPT_FORCEINLINE -#define MPT_CONSTEXPR17_VAR constexpr -#else -#define MPT_CONSTEXPR17_FUN MPT_FORCEINLINE -#define MPT_CONSTEXPR17_VAR const -#endif +#if MPT_CXX_AT_LEAST(20) +#define MPT_CONSTEXPR20_FUN constexpr MPT_FORCEINLINE +#define MPT_CONSTEXPR20_VAR constexpr +#else // !C++20 +#define MPT_CONSTEXPR20_FUN MPT_FORCEINLINE +#define MPT_CONSTEXPR20_VAR const +#endif // C++20 -// C++17 std::size -#if MPT_CXX_AT_LEAST(17) -namespace mpt { -using std::size; -} // namespace mpt -#else -namespace mpt { +namespace mpt +{ +template struct constant_value { static constexpr decltype(V) value() { return V; } }; +#define MPT_FORCE_CONSTEXPR(expr) (mpt::constant_value<( expr )>::value()) +} // namespace mpt + + + +#if MPT_CXX_AT_LEAST(20) +#define MPT_IS_CONSTANT_EVALUATED20() std::is_constant_evaluated() +#define MPT_IS_CONSTANT_EVALUATED() std::is_constant_evaluated() +#else // !C++20 +#define MPT_IS_CONSTANT_EVALUATED20() false +// this pessimizes the case for C++17 by always assuming constexpr context, which implies always running constexpr-friendly code +#define MPT_IS_CONSTANT_EVALUATED() true +#endif // C++20 + + + +namespace mpt +{ + template -MPT_CONSTEXPR11_FUN auto size(const T & v) -> decltype(v.size()) -{ - return v.size(); -} +struct stdarray_extent : std::integral_constant {}; + template -MPT_CONSTEXPR11_FUN std::size_t size(const T(&)[N]) noexcept +struct stdarray_extent> : std::integral_constant {}; + +template +struct is_stdarray : std::false_type {}; + +template +struct is_stdarray> : std::true_type {}; + +// mpt::extent is the same as std::extent, +// but also works for std::array, +// and asserts that the given type is actually an array type instead of returning 0. +// use as: +// mpt::extent() +// mpt::extent() +// mpt::extent() +// mpt::extent() +template +constexpr std::size_t extent() noexcept { - return N; + using Tarray = typename std::remove_cv::type>::type; + static_assert(std::is_array::value || mpt::is_stdarray::value); + if constexpr(mpt::is_stdarray::value) + { + return mpt::stdarray_extent(); + } else + { + return std::extent(); + } } + } // namespace mpt -#endif + // legacy #if MPT_COMPILER_MSVC OPENMPT_NAMESPACE_END @@ -115,41 +180,12 @@ OPENMPT_NAMESPACE_BEGIN -// 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 -#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 +#define MPT_ATTR_NODISCARD [[nodiscard]] +#define MPT_DISCARD(expr) static_cast(expr) -#if MPT_CXX_AT_LEAST(17) -#define MPT_CONSTANT_IF if constexpr -#endif - #if MPT_COMPILER_MSVC -#if !defined(MPT_CONSTANT_IF) -#define MPT_CONSTANT_IF(x) \ - __pragma(warning(push)) \ - __pragma(warning(disable:4127)) \ - if(x) \ - __pragma(warning(pop)) \ -/**/ -#endif #define MPT_MAYBE_CONSTANT_IF(x) \ __pragma(warning(push)) \ __pragma(warning(disable:4127)) \ @@ -178,11 +214,6 @@ OPENMPT_NAMESPACE_BEGIN /**/ #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) @@ -217,35 +248,6 @@ OPENMPT_NAMESPACE_BEGIN -// 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 - #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 - - - -#if MPT_COMPILER_GCC || MPT_COMPILER_CLANG -#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. diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptBaseTypes.h b/Frameworks/OpenMPT/OpenMPT/common/mptBaseTypes.h index cdef4e10e..1cc2932a5 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptBaseTypes.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptBaseTypes.h @@ -18,13 +18,13 @@ #include #include +#if MPT_CXX_AT_LEAST(20) +#include +#endif // C++20 #include #include -#if MPT_GCC_BEFORE(4,9,0) -#include -#endif #include @@ -33,14 +33,34 @@ 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; +namespace mpt +{ +template +struct select_type +{ +}; +template +struct select_type +{ + using type = Ta; +}; +template +struct select_type +{ + using type = Tb; +}; +} // namespace mpt + + + +using int8 = std::int8_t; +using int16 = std::int16_t; +using int32 = std::int32_t; +using int64 = std::int64_t; +using uint8 = std::uint8_t; +using uint16 = std::uint16_t; +using uint32 = std::uint32_t; +using uint64 = std::uint64_t; constexpr int8 int8_min = std::numeric_limits::min(); constexpr int16 int16_min = std::numeric_limits::min(); @@ -58,45 +78,124 @@ constexpr uint32 uint32_max = std::numeric_limits::max(); constexpr uint64 uint64_max = std::numeric_limits::max(); -typedef float float32; -MPT_STATIC_ASSERT(sizeof(float32) == 4); -typedef double float64; -MPT_STATIC_ASSERT(sizeof(float64) == 8); +// fp half +// n/a +// fp single +using single = float; +constexpr single operator"" _fs(long double lit) +{ + return static_cast(lit); +} -MPT_STATIC_ASSERT(sizeof(std::uintptr_t) == sizeof(void*)); +// fp double +constexpr double operator"" _fd(long double lit) +{ + return static_cast(lit); +} +// fp extended +constexpr long double operator"" _fe(long double lit) +{ + return static_cast(lit); +} -MPT_STATIC_ASSERT(std::numeric_limits::digits == 8); +// fp quad +// n/a -MPT_STATIC_ASSERT(sizeof(char) == 1); +using float32 = mpt::select_type::type + >::type + >::type; +constexpr float32 operator"" _f32(long double lit) +{ + return static_cast(lit); +} -#if MPT_CXX_AT_LEAST(17) -namespace mpt { -using byte = std::byte; -} // namespace mpt -#define MPT_BYTE_IS_STD_BYTE 1 +using float64 = mpt::select_type::type + >::type + >::type; +constexpr float64 operator"" _f64(long double lit) +{ + return static_cast(lit); +} + +namespace mpt +{ +template +struct float_traits +{ + static constexpr bool is_float = !std::numeric_limits::is_integer; + static constexpr bool is_hard = is_float && !MPT_COMPILER_QUIRK_FLOAT_EMULATED; + static constexpr bool is_soft = is_float && MPT_COMPILER_QUIRK_FLOAT_EMULATED; + static constexpr bool is_float32 = is_float && (sizeof(T) == 4); + static constexpr bool is_float64 = is_float && (sizeof(T) == 8); + static constexpr bool is_native_endian = is_float && !MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN; + static constexpr bool is_ieee754_binary = is_float && std::numeric_limits::is_iec559 && !MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754; + static constexpr bool is_ieee754_binary32 = is_float && is_ieee754_binary && is_float32; + static constexpr bool is_ieee754_binary64 = is_float && is_ieee754_binary && is_float64; + static constexpr bool is_ieee754_binary32ne = is_float && is_ieee754_binary && is_float32 && is_native_endian; + static constexpr bool is_ieee754_binary64ne = is_float && is_ieee754_binary && is_float64 && is_native_endian; +}; +} // namespace mpt + +#if MPT_COMPILER_QUIRK_FLOAT_PREFER32 +using nativefloat = float32; +#elif MPT_COMPILER_QUIRK_FLOAT_PREFER64 +using nativefloat = float64; #else -// In C++11 and C++14, a C++17 compatible definition of byte would not be required to be allowed to alias other types, -// thus just use a typedef for unsigned char which is guaranteed to be allowed to alias. -//enum class byte : unsigned char { }; -namespace mpt { -typedef unsigned char byte; -} // namespace mpt -#define MPT_BYTE_IS_STD_BYTE 0 +// prefer smaller floats, but try to use IEEE754 floats +using nativefloat = mpt::select_type::is_iec559, + float + , + mpt::select_type::is_iec559, + double + , + mpt::select_type::is_iec559, + long double + , + float + >::type + >::type + >::type; #endif -MPT_STATIC_ASSERT(sizeof(mpt::byte) == 1); -MPT_STATIC_ASSERT(alignof(mpt::byte) == 1); +constexpr nativefloat operator"" _nf(long double lit) +{ + return static_cast(lit); +} -namespace mpt { -#if MPT_GCC_BEFORE(4,9,0) -typedef ::max_align_t max_align_t; -#else -typedef std::max_align_t max_align_t; -#endif -} // namespace mpt + +static_assert(sizeof(std::uintptr_t) == sizeof(void*)); + + + +static_assert(std::numeric_limits::digits == 8); + +static_assert(sizeof(char) == 1); + +static_assert(sizeof(std::byte) == 1); +static_assert(alignof(std::byte) == 1); namespace mpt { @@ -104,7 +203,7 @@ constexpr int arch_bits = sizeof(void*) * 8; constexpr std::size_t pointer_size = sizeof(void*); } // namespace mpt -MPT_STATIC_ASSERT(mpt::arch_bits == static_cast(mpt::pointer_size) * 8); +static_assert(mpt::arch_bits == static_cast(mpt::pointer_size) * 8); @@ -124,6 +223,14 @@ struct limits namespace mpt { +#if MPT_CXX_AT_LEAST(20) + +using std::source_location; + +#define MPT_SOURCE_LOCATION_CURRENT() std::source_location::current() + +#else // !C++20 + // compatible with std::experimental::source_location from Library Fundamentals TS v2. struct source_location { @@ -172,7 +279,9 @@ public: } }; -#define MPT_SOURCE_LOCATION_CURRENT() mpt::source_location::current( __FILE__ , __FUNCTION__ , __LINE__ , 0 ) +#define MPT_SOURCE_LOCATION_CURRENT() mpt::source_location::current( __FILE__ , __func__ , __LINE__ , 0 ) + +#endif // C++20 } // namespace mpt diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptBaseUtils.h b/Frameworks/OpenMPT/OpenMPT/common/mptBaseUtils.h index 78127526f..a5f37a31b 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptBaseUtils.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptBaseUtils.h @@ -30,6 +30,10 @@ #include #include +#if MPT_COMPILER_MSVC +#include +#endif + OPENMPT_NAMESPACE_BEGIN @@ -49,46 +53,66 @@ OPENMPT_NAMESPACE_BEGIN +namespace mpt +{ +template +MPT_CONSTEXPR14_FUN std::array init_array(const Tx & x) +{ + std::array result{}; + for(std::size_t i = 0; i < N; ++i) + { + result[i] = x; + } + return result; +} +} // namespace mpt + + + +namespace mpt +{ + +// Work-around for the requirement of at least 1 non-throwing function argument combination in C++ (17,2a). + +template +MPT_CONSTEXPR14_FUN bool constexpr_throw_helper(Exception && e, bool really = true) +{ + //return !really ? really : throw std::forward(e); + if(really) + { + throw std::forward(e); + } + // cppcheck-suppress identicalConditionAfterEarlyExit + return really; +} +template +MPT_CONSTEXPR14_FUN bool constexpr_throw(Exception && e) +{ + return mpt::constexpr_throw_helper(std::forward(e)); +} + +template +constexpr T constexpr_throw_helper(Exception && e, bool really = true) +{ + //return !really ? really : throw std::forward(e); + if(really) + { + throw std::forward(e); + } + return T{}; +} +template +constexpr T constexpr_throw(Exception && e) +{ + return mpt::constexpr_throw_helper(std::forward(e)); +} + +} // namespace mpt + + namespace mpt { -// GCC 4.5 and up provides templated overloads of std::abs that convert -// integer type narrower than int to double. -// This is fixed as of GCC 7.1. -// 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). @@ -118,46 +142,46 @@ 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) + static_assert(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); + if constexpr(std::numeric_limits::is_signed && std::numeric_limits::is_signed) { - MPT_CONSTANT_IF(sizeof(Tdst) >= sizeof(Tsrc)) + if constexpr(sizeof(Tdst) >= sizeof(Tsrc)) { return static_cast(src); } else { - return static_cast(std::max(static_cast(std::numeric_limits::min()), std::min(src, static_cast(std::numeric_limits::max())))); + 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) + } else if constexpr(!std::numeric_limits::is_signed && !std::numeric_limits::is_signed) { - MPT_CONSTANT_IF(sizeof(Tdst) >= sizeof(Tsrc)) + if constexpr(sizeof(Tdst) >= sizeof(Tsrc)) { return static_cast(src); } else { - 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 MPT_CONSTANT_IF(std::numeric_limits::is_signed && !std::numeric_limits::is_signed) + } else if constexpr(std::numeric_limits::is_signed && !std::numeric_limits::is_signed) { - MPT_CONSTANT_IF(sizeof(Tdst) > sizeof(Tsrc)) + if constexpr(sizeof(Tdst) > sizeof(Tsrc)) { return static_cast(src); - } else MPT_CONSTANT_IF(sizeof(Tdst) == sizeof(Tsrc)) + } else if constexpr(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 { - 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)) + if constexpr(sizeof(Tdst) >= sizeof(Tsrc)) { - return static_cast(std::max(0, src)); + return static_cast(std::max(static_cast(0), src)); } else { - return static_cast(std::max(0, std::min(src, static_cast(std::numeric_limits::max())))); + return static_cast(std::max(static_cast(0), std::min(src, static_cast(std::numeric_limits::max())))); } } } @@ -165,11 +189,11 @@ inline Tdst saturate_cast(Tsrc src) template inline Tdst saturate_cast(double src) { - if(src >= std::numeric_limits::max()) + if(src >= static_cast(std::numeric_limits::max())) { return std::numeric_limits::max(); } - if(src <= std::numeric_limits::min()) + if(src <= static_cast(std::numeric_limits::min())) { return std::numeric_limits::min(); } @@ -179,11 +203,11 @@ inline Tdst saturate_cast(double src) template inline Tdst saturate_cast(float src) { - if(src >= std::numeric_limits::max()) + if(src >= static_cast(std::numeric_limits::max())) { return std::numeric_limits::max(); } - if(src <= std::numeric_limits::min()) + if(src <= static_cast(std::numeric_limits::min())) { return std::numeric_limits::min(); } @@ -191,51 +215,51 @@ inline Tdst saturate_cast(float src) } -template -MPT_CONSTEXPR14_FUN std::size_t weight(T val) noexcept -{ - MPT_STATIC_ASSERT(std::numeric_limits::is_integer); - typedef typename std::make_unsigned::type Tunsigned; - Tunsigned uval = static_cast(val); - std::size_t result = 0; - while(uval > 0) - { - if(uval & 0x1) - { - result++; - } - uval >>= 1; - } - return result; -} - #if MPT_CXX_AT_LEAST(20) -using std::ispow2; -using std::ceil2; -using std::floor2; -using std::log2p1; +using std::popcount; +using std::has_single_bit; +using std::bit_ceil; +using std::bit_floor; +using std::bit_width; +using std::rotl; +using std::rotr; #else // C++20 header. // Note that we do not use SFINAE here but instead rely on static_assert. -// Also note that for C++11 compilers, these functions are not constexpr. -// They could be implemented recursively to make them C++11 constexpr compatible if needed. template -MPT_CONSTEXPR14_FUN bool ispow2(T x) noexcept +MPT_CONSTEXPR14_FUN int popcount(T val) noexcept { - MPT_STATIC_ASSERT(std::numeric_limits::is_integer); - MPT_STATIC_ASSERT(std::is_unsigned::value); - return mpt::weight(x) == 1; + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); + int result = 0; + while(val > 0) + { + if(val & 0x1) + { + result++; + } + val >>= 1; + } + return result; } template -MPT_CONSTEXPR14_FUN T ceil2(T x) noexcept +MPT_CONSTEXPR14_FUN bool has_single_bit(T x) noexcept { - MPT_STATIC_ASSERT(std::numeric_limits::is_integer); - MPT_STATIC_ASSERT(std::is_unsigned::value); + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); + return mpt::popcount(x) == 1; +} + +template +MPT_CONSTEXPR14_FUN T bit_ceil(T x) noexcept +{ + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); T result = 1; while(result < x) { @@ -250,10 +274,10 @@ MPT_CONSTEXPR14_FUN T ceil2(T x) noexcept } template -MPT_CONSTEXPR14_FUN T floor2(T x) noexcept +MPT_CONSTEXPR14_FUN T bit_floor(T x) noexcept { - MPT_STATIC_ASSERT(std::numeric_limits::is_integer); - MPT_STATIC_ASSERT(std::is_unsigned::value); + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); if(x == 0) { return 0; @@ -272,10 +296,10 @@ MPT_CONSTEXPR14_FUN T floor2(T x) noexcept } template -MPT_CONSTEXPR14_FUN T log2p1(T x) noexcept +MPT_CONSTEXPR14_FUN T bit_width(T x) noexcept { - MPT_STATIC_ASSERT(std::numeric_limits::is_integer); - MPT_STATIC_ASSERT(std::is_unsigned::value); + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); T result = 0; while(x > 0) { @@ -285,58 +309,50 @@ MPT_CONSTEXPR14_FUN T log2p1(T x) noexcept return result; } +namespace detail +{ + +template +MPT_CONSTEXPR14_FUN T rotl(T x, int r) noexcept +{ + auto N = std::numeric_limits::digits; + return (x >> (N - r)) | (x << r); +} + +template +MPT_CONSTEXPR14_FUN T rotr(T x, int r) noexcept +{ + auto N = std::numeric_limits::digits; + return (x << (N - r)) | (x >> r); +} + +} // namespace detail + +template +MPT_CONSTEXPR14_FUN T rotl(T x, int s) noexcept +{ + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); + auto N = std::numeric_limits::digits; + auto r = s % N; + return (s < 0) ? detail::rotr(x, -s) : ((x >> (N - r)) | (x << r)); +} + +template +MPT_CONSTEXPR14_FUN T rotr(T x, int s) noexcept +{ + static_assert(std::numeric_limits::is_integer); + static_assert(std::is_unsigned::value); + auto N = std::numeric_limits::digits; + auto r = s % N; + return (s < 0) ? detail::rotl(x, -s) : ((x << (N - r)) | (x >> r)); +} + #endif } // 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 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); + 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); } }; @@ -389,8 +405,8 @@ inline T ExponentialGrow(const T &x, const Tlimit &limit) { return 2; } - T add = std::min(x >> 1, std::numeric_limits::max() - x); - return std::min(x + add, mpt::saturate_cast(limit)); + T add = std::min(x >> 1, std::numeric_limits::max() - x); + return std::min(x + add, mpt::saturate_cast(limit)); } template @@ -402,34 +418,6 @@ inline T ExponentialGrow(const T &x) } //namespace Util -namespace mpt -{ - -// C++17 clamp - -#if MPT_CXX_AT_LEAST(17) - -using std::clamp; - -#else - -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()); -} - -#endif - -} // 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. @@ -489,8 +477,8 @@ 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); + static_assert(std::numeric_limits::is_integer); + 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); @@ -505,8 +493,8 @@ MPT_FORCEINLINE auto rshift_signed_standard(T x, int y) -> decltype(x >> y) 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); + static_assert(std::numeric_limits::is_integer); + 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); @@ -523,16 +511,16 @@ MPT_FORCEINLINE auto lshift_signed_standard(T x, int y) -> decltype(x << y) 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); + static_assert(std::numeric_limits::is_integer); + 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); + static_assert(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_signed); return x << y; } @@ -564,9 +552,22 @@ MPT_FORCEINLINE auto lshift_signed(T x, int y) -> decltype(x << y) #endif -} // namespace mpt +template +struct array_size; +template +struct array_size> +{ + static constexpr std::size_t size = N; +}; +template +struct array_size +{ + static constexpr std::size_t size = N; +}; + +} // namespace mpt namespace Util { @@ -574,8 +575,7 @@ 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)();} -} - +} // namespace Util namespace mpt { @@ -677,58 +677,4 @@ namespace Util { -namespace mpt -{ - -#if MPT_CXX_AT_LEAST(17) - -using std::gcd; -using std::lcm; - -#else - - // 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; - } - -#endif - -} // namespace mpt - - - OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptBufferIO.h b/Frameworks/OpenMPT/OpenMPT/common/mptBufferIO.h deleted file mode 100644 index fb5cf369c..000000000 --- a/Frameworks/OpenMPT/OpenMPT/common/mptBufferIO.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 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 "BuildSettings.h" - -#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 -{ - -#ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - -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 index 5fffb670c..c56708faf 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptCPU.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptCPU.cpp @@ -29,7 +29,7 @@ uint8 ProcModel = 0; uint8 ProcStepping = 0; -#if MPT_COMPILER_MSVC && (defined(ENABLE_X86) || defined(ENABLE_X64)) +#if MPT_COMPILER_MSVC && (defined(ENABLE_X86) || defined(ENABLE_X64)) && defined(ENABLE_CPUID) #include @@ -97,42 +97,25 @@ static cpuid_result cpuid(uint32 function) } -#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 - - void InitProcSupport() { RealProcSupport = 0; ProcSupport = 0; - MemsetZero(ProcVendorID); - MemsetZero(ProcBrandID); + mpt::String::WriteAutoBuf(ProcVendorID) = ""; + mpt::String::WriteAutoBuf(ProcBrandID) = ""; ProcFamily = 0; ProcModel = 0; ProcStepping = 0; + ProcSupport |= PROCSUPPORT_ASM_INTRIN; + ProcSupport |= PROCSUPPORT_CPUID; + { cpuid_result VendorString = cpuid(0x00000000u); mpt::String::WriteAutoBuf(ProcVendorID) = VendorString.as_string(); - - // 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)) + if(VendorString.a >= 0x00000001u) { cpuid_result StandardFeatureFlags = cpuid(0x00000001u); uint32 Stepping = (StandardFeatureFlags.a >> 0) & 0x0f; @@ -140,40 +123,21 @@ void InitProcSupport() 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) { - 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); - } + 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); } ProcStepping = static_cast(Stepping); - if(StandardFeatureFlags.d & (1<<15)) ProcSupport |= PROCSUPPORT_CMOV; if(StandardFeatureFlags.d & (1<<23)) ProcSupport |= PROCSUPPORT_MMX; if(StandardFeatureFlags.d & (1<<25)) ProcSupport |= PROCSUPPORT_SSE; if(StandardFeatureFlags.d & (1<<26)) ProcSupport |= PROCSUPPORT_SSE2; @@ -181,115 +145,56 @@ void InitProcSupport() 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; + if(StandardFeatureFlags.c & (1<<28)) ProcSupport |= PROCSUPPORT_AVX; } - bool canExtended = false; - // 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() == "GenuineIntel") - { // Intel - - // 5.9.x : Quark - // 6.11.x: P3-S (Tualatin) - if((ProcFamily > 6) || ((ProcFamily == 6) && (ProcModel >= 11)) || ((ProcFamily == 5) && (ProcModel >= 9))) - { - canExtended = true; - } - - } else 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. - canExtended = true; - } - - } else if(VendorString.as_string() == "CentaurHauls") - { // Centaur (IDT WinChip or VIA C3) - - if(ProcFamily == 5) - { // IDT - - if(ProcModel >= 8) - { // >= WinChip 2 - canExtended = true; - } - - } else if(ProcFamily >= 6) - { // VIA - - if((ProcFamily >= 7) || ((ProcFamily == 6) && (ProcModel >= 7))) - { // >= C3 Samuel 2 - canExtended = true; - } - - } - - } 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 - canExtended = true; - } - - } else if(VendorString.as_string() == "Geode by NSC") - { // National Semiconductor - - if((ProcFamily > 5) || ((ProcFamily == 5) && (ProcModel >= 5))) - { // >= Geode GX2 - canExtended = true; - } - - } else - { // unknown, which nowadays most likely means some virtualized CPU - - // we assume extended flags present in this case - canExtended = true; - - } - - if(canExtended) + cpuid_result ExtendedVendorString = cpuid(0x80000000u); + if(ExtendedVendorString.a >= 0x80000001u) { - cpuid_result ExtendedVendorString = cpuid(0x80000000u); - if(ExtendedVendorString.a >= 0x80000001u) + cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u); + if(ExtendedFeatureFlags.d & (1<<29)) ProcSupport |= PROCSUPPORT_LM; + } + if(ExtendedVendorString.a >= 0x80000004u) + { + mpt::String::WriteAutoBuf(ProcBrandID) = cpuid(0x80000002u).as_string4() + cpuid(0x80000003u).as_string4() + cpuid(0x80000004u).as_string4(); + if(ExtendedVendorString.a >= 0x80000007u) { - cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u); - if(ExtendedFeatureFlags.d & (1<<29)) ProcSupport |= PROCSUPPORT_LM; - if((VendorString.as_string() == "AuthenticAMD") || (VendorString.as_string() == "AMDisbetter!")) - { - if(ExtendedFeatureFlags.d & (1<<15)) ProcSupport |= PROCSUPPORT_CMOV; - 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; - } - if(ExtendedVendorString.a >= 0x80000004u) - { - mpt::String::WriteAutoBuf(ProcBrandID) = cpuid(0x80000002u).as_string4() + cpuid(0x80000003u).as_string4() + cpuid(0x80000004u).as_string4(); + cpuid_result ExtendedFeatures = cpuid(0x80000007u); + if(ExtendedFeatures.b & (1<< 5)) ProcSupport |= PROCSUPPORT_AVX2; } } } - // We do not have to check if SSE got enabled by the OS because we only do - // support Windows >= XP. Windows will always enable SSE since Windows 98 SE. + RealProcSupport = ProcSupport; + +} + + +#elif MPT_COMPILER_MSVC && (defined(ENABLE_X86) || defined(ENABLE_X64)) + + +void InitProcSupport() +{ + + RealProcSupport = 0; + ProcSupport = 0; + mpt::String::WriteAutoBuf(ProcVendorID) = ""; + mpt::String::WriteAutoBuf(ProcBrandID) = ""; + ProcFamily = 0; + ProcModel = 0; + ProcStepping = 0; + + ProcSupport |= PROCSUPPORT_ASM_INTRIN; + + { + + if(IsProcessorFeaturePresent(PF_MMX_INSTRUCTIONS_AVAILABLE) != 0) ProcSupport |= PROCSUPPORT_MMX; + if(IsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE) != 0) ProcSupport |= PROCSUPPORT_SSE; + if(IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE) != 0) ProcSupport |= PROCSUPPORT_SSE2; + if(IsProcessorFeaturePresent(PF_SSE3_INSTRUCTIONS_AVAILABLE) != 0) ProcSupport |= PROCSUPPORT_SSE3; + + } RealProcSupport = ProcSupport; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptCPU.h b/Frameworks/OpenMPT/OpenMPT/common/mptCPU.h index e70dc1f17..5b3eb3a24 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptCPU.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptCPU.h @@ -16,26 +16,30 @@ OPENMPT_NAMESPACE_BEGIN -#ifdef ENABLE_ASM +#ifdef MODPLUG_TRACKER -#define PROCSUPPORT_LM 0x00001 // Processor supports long mode (amd64) -#define PROCSUPPORT_CMOV 0x00004 // Processor supports conditional move instructions (i686) +#define PROCSUPPORT_ASM_INTRIN 0x00001 // assembly and intrinsics are enabled at runtime +#define PROCSUPPORT_CPUID 0x00002 // Processor supports modern cpuid +#define PROCSUPPORT_LM 0x00004 // Processor supports long mode (amd64) #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 +#define PROCSUPPORT_AVX 0x10000 // Processor supports AVX instructions +#define PROCSUPPORT_AVX2 0x20000 // Processor supports AVX2 instructions -static const uint32 PROCSUPPORT_i586 = 0u ; -static const uint32 PROCSUPPORT_i686 = 0u | PROCSUPPORT_CMOV ; -static const uint32 PROCSUPPORT_x86_SSE = 0u | PROCSUPPORT_CMOV | PROCSUPPORT_SSE ; -static const uint32 PROCSUPPORT_x86_SSE2 = 0u | PROCSUPPORT_CMOV | PROCSUPPORT_SSE | PROCSUPPORT_SSE2 ; -static const uint32 PROCSUPPORT_AMD64 = 0u | PROCSUPPORT_CMOV | PROCSUPPORT_SSE | PROCSUPPORT_SSE2 | PROCSUPPORT_LM; +static constexpr uint32 PROCSUPPORT_i586 = 0u ; +static constexpr uint32 PROCSUPPORT_x86_SSE = 0u | PROCSUPPORT_SSE ; +static constexpr uint32 PROCSUPPORT_x86_SSE2 = 0u | PROCSUPPORT_SSE | PROCSUPPORT_SSE2 ; +static constexpr uint32 PROCSUPPORT_AMD64 = 0u | PROCSUPPORT_SSE | PROCSUPPORT_SSE2 | PROCSUPPORT_LM; + +#endif + + +#ifdef ENABLE_ASM extern uint32 RealProcSupport; extern uint32 ProcSupport; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptCRC.h b/Frameworks/OpenMPT/OpenMPT/common/mptCRC.h index 498015b14..de5a96f67 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptCRC.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptCRC.h @@ -113,7 +113,7 @@ public: inline void processByte(byte_type byte) { - MPT_CONSTANT_IF(reverseData) + if constexpr(reverseData) { value = (value >> 8) ^ read_table(static_cast((value & 0xff) ^ byte)); } else @@ -152,13 +152,11 @@ public: return *this; } -#if MPT_BYTE_IS_STD_BYTE - inline crc & process(mpt::byte c) + inline crc & process(std::byte c) { processByte(mpt::byte_cast(c)); return *this; } -#endif template crc & process(InputIt beg, InputIt end) @@ -196,13 +194,11 @@ public: return *this; } -#if MPT_BYTE_IS_STD_BYTE - inline crc & operator () (mpt::byte c) + inline crc & operator () (std::byte c) { processByte(mpt::byte_cast(c)); return *this; } -#endif template crc & operator () (InputIt beg, InputIt end) diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptException.h b/Frameworks/OpenMPT/OpenMPT/common/mptException.h index 59bf21139..1e2a44687 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptException.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptException.h @@ -17,14 +17,14 @@ #include "mptBaseMacros.h" #include -#if !defined(_MFC_VER) +#if !defined(MPT_WITH_MFC) #include -#endif // !_MFC_VER +#endif // !MPT_WITH_MFC -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) // cppcheck-suppress missingInclude #include -#endif // _MFC_VER +#endif // MPT_WITH_MFC @@ -35,21 +35,21 @@ OPENMPT_NAMESPACE_BEGIN // 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) +#if defined(MPT_WITH_MFC) #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(e) MPT_DO { MPT_UNUSED_VARIABLE(e); 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 +#else // !MPT_WITH_MFC #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(e) MPT_DO { MPT_UNUSED_VARIABLE(e); throw; } MPT_WHILE_0 #define MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e) MPT_DO { MPT_UNUSED_VARIABLE(e); } MPT_WHILE_0 -#endif // _MFC_VER +#endif // MPT_WITH_MFC diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptExceptionText.h b/Frameworks/OpenMPT/OpenMPT/common/mptExceptionText.h index 67fc443d9..571fac2b6 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptExceptionText.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptExceptionText.h @@ -53,19 +53,19 @@ template <> inline std::string get_exception_text(const std::except #if defined(MPT_ENABLE_CHARSET_LOCALE) template <> inline mpt::lstring get_exception_text(const std::exception & e) { - return mpt::ToLocale(mpt::CharsetLocaleOrUTF8, mpt::get_exception_text_impl(e)); + return mpt::ToLocale(mpt::CharsetException, mpt::get_exception_text_impl(e)); } #endif #if MPT_WSTRING_FORMAT template <> inline std::wstring get_exception_text(const std::exception & e) { - return mpt::ToWide(mpt::CharsetLocaleOrUTF8, mpt::get_exception_text_impl(e)); + return mpt::ToWide(mpt::CharsetException, mpt::get_exception_text_impl(e)); } #endif #if MPT_USTRING_MODE_UTF8 template <> inline mpt::ustring get_exception_text(const std::exception & e) { - return mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, mpt::get_exception_text_impl(e)); + return mpt::ToUnicode(mpt::CharsetException, mpt::get_exception_text_impl(e)); } #endif diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.cpp index eae979c7a..c5119f6fb 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.cpp @@ -99,12 +99,12 @@ mpt::tstring SafeOutputFile::convert_mode(std::ios_base::openmode mode, FlushMod fopen_mode = _T("r"); break; case std::ios_base::out: - MPT_FALLTHROUGH; + [[fallthrough]]; case std::ios_base::out | std::ios_base::trunc: fopen_mode = _T("w"); break; case std::ios_base::app: - MPT_FALLTHROUGH; + [[fallthrough]]; case std::ios_base::out | std::ios_base::app: fopen_mode = _T("a"); break; @@ -115,7 +115,7 @@ mpt::tstring SafeOutputFile::convert_mode(std::ios_base::openmode mode, FlushMod fopen_mode = _T("w+"); break; case std::ios_base::out | std::ios_base::in | std::ios_base::app: - MPT_FALLTHROUGH; + [[fallthrough]]; case std::ios_base::in | std::ios_base::app: fopen_mode = _T("a+"); break; @@ -171,12 +171,25 @@ FILE * SafeOutputFile::internal_fopen(const mpt::PathString &filename, std::ios_ // cppcheck-suppress exceptThrowInDestructor SafeOutputFile::~SafeOutputFile() noexcept(false) { + const bool mayThrow = (std::uncaught_exceptions() == 0); if(!stream()) { + #if MPT_COMPILER_MSVC + if(m_f) + { + fclose(m_f); + } + #endif // MPT_COMPILER_MSVC return; } if(!stream().rdbuf()) { + #if MPT_COMPILER_MSVC + if(m_f) + { + fclose(m_f); + } + #endif // MPT_COMPILER_MSVC return; } #if MPT_COMPILER_MSVC @@ -210,8 +223,12 @@ SafeOutputFile::~SafeOutputFile() noexcept(false) errorOnFlush = true; } #endif // MPT_COMPILER_MSVC - // ignore errorOnFlush here, and re-throw the earlier exception - throw; + if(mayThrow) + { + // ignore errorOnFlush here, and re-throw the earlier exception + // cppcheck-suppress exceptThrowInDestructor + throw; + } } } #if MPT_COMPILER_MSVC @@ -227,8 +244,9 @@ SafeOutputFile::~SafeOutputFile() noexcept(false) errorOnFlush = true; } #endif // MPT_COMPILER_MSVC - if(errorOnFlush && (stream().exceptions() & (std::ios::badbit | std::ios::failbit))) + if(mayThrow && errorOnFlush && (stream().exceptions() & (std::ios::badbit | std::ios::failbit))) { + // cppcheck-suppress exceptThrowInDestructor throw std::ios_base::failure(std::string("Error flushing file buffers.")); } } @@ -243,11 +261,11 @@ SafeOutputFile::~SafeOutputFile() noexcept(false) namespace mpt { -LazyFileRef & LazyFileRef::operator = (const std::vector &data) +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::WriteRaw(file, mpt::as_span(data)); mpt::IO::Flush(file); return *this; } @@ -256,7 +274,7 @@ 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::WriteRaw(file, mpt::as_span(data)); mpt::IO::Flush(file); return *this; } @@ -265,23 +283,23 @@ 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::WriteRaw(file, mpt::as_span(data)); mpt::IO::Flush(file); return *this; } -LazyFileRef::operator std::vector () const +LazyFileRef::operator std::vector () const { mpt::ifstream file(m_Filename, std::ios::binary); if(!mpt::IO::IsValid(file)) { - return std::vector(); + 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))); + std::vector buf(mpt::saturate_cast(mpt::IO::TellRead(file))); mpt::IO::SeekBegin(file); - mpt::IO::ReadRaw(file, buf.data(), buf.size()); + mpt::IO::ReadRaw(file, mpt::as_span(buf)); return buf; } @@ -296,7 +314,7 @@ LazyFileRef::operator std::vector () const 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()); + mpt::IO::ReadRaw(file, mpt::as_span(buf)); return buf; } @@ -311,7 +329,7 @@ LazyFileRef::operator std::string () const 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()); + mpt::IO::ReadRaw(file, mpt::as_span(buf)); return std::string(buf.begin(), buf.end()); } @@ -320,149 +338,22 @@ LazyFileRef::operator std::string () const #endif // MODPLUG_TRACKER - -#ifdef MODPLUG_TRACKER - -#if MPT_OS_WINDOWS - -CMappedFile::~CMappedFile() +bool InputFile::DefaultToLargeAddressSpaceUsage() { - Close(); + return false; } -bool CMappedFile::Open(const mpt::PathString &filename) -{ - m_hFile = CreateFile( - 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() + : m_IsCached(false) { return; } -InputFile::InputFile(const mpt::PathString &filename) - : m_Filename(filename) +InputFile::InputFile(const mpt::PathString &filename, bool allowWholeFileCaching) + : m_IsCached(false) { -#if defined(MPT_FILEREADER_STD_ISTREAM) - m_File.open(m_Filename, std::ios::binary | std::ios::in); -#else - m_File.Open(m_Filename); -#endif + Open(filename, allowWholeFileCaching); } InputFile::~InputFile() @@ -471,57 +362,82 @@ InputFile::~InputFile() } -bool InputFile::Open(const mpt::PathString &filename) +bool InputFile::Open(const mpt::PathString &filename, bool allowWholeFileCaching) { + m_IsCached = false; + m_Cache.resize(0); + m_Cache.shrink_to_fit(); m_Filename = filename; -#if defined(MPT_FILEREADER_STD_ISTREAM) m_File.open(m_Filename, std::ios::binary | std::ios::in); + if(allowWholeFileCaching) + { + if(mpt::IO::IsReadSeekable(m_File)) + { + if(!mpt::IO::SeekEnd(m_File)) + { + m_File.close(); + return false; + } + mpt::IO::Offset filesize = mpt::IO::TellRead(m_File); + if(!mpt::IO::SeekBegin(m_File)) + { + m_File.close(); + return false; + } + if(Util::TypeCanHoldValue(filesize)) + { + std::size_t buffersize = mpt::saturate_cast(filesize); + m_Cache.resize(buffersize); + if(mpt::IO::ReadRaw(m_File, mpt::as_span(m_Cache)) != filesize) + { + m_File.close(); + return false; + } + if(!mpt::IO::SeekBegin(m_File)) + { + m_File.close(); + return false; + } + m_IsCached = true; + return true; + } + } + } 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() +bool InputFile::IsCached() const { - InputFile::ContentsRef result; - result.first = &m_File; - result.second = m_File.good() ? &m_Filename : nullptr; - return result; + return m_IsCached; } -#else -InputFile::ContentsRef InputFile::Get() +const mpt::PathString& InputFile::GetFilenameRef() const { - 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(); - if(result.first.data) - result.first.size = m_File.GetLength(); - result.second = &m_Filename; - return result; + return m_Filename; +} + + +std::istream* InputFile::GetStream() +{ + MPT_ASSERT(!m_IsCached); + return &m_File; +} + + +mpt::const_byte_span InputFile::GetCache() +{ + MPT_ASSERT(m_IsCached); + return mpt::as_span(m_Cache); } -#endif #else // !MPT_ENABLE_FILEIO diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.h b/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.h index 53fec7e07..1c8ad5099 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptFileIO.h @@ -68,7 +68,7 @@ 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); + base.open(mpt::ToCharset(mpt::Charset::Locale, filename.AsNative()).c_str(), mode); #else base.open(filename.AsNativePrefixed().c_str(), mode); #endif @@ -181,7 +181,7 @@ class SafeOutputFile private: FlushMode m_FlushMode; #if MPT_COMPILER_MSVC - FILE *m_f; + FILE *m_f = nullptr; #endif // MPT_COMPILER_MSVC mpt::ofstream m_s; #if MPT_COMPILER_MSVC @@ -198,6 +198,8 @@ public: , m_s(filename, mode) #endif // MPT_COMPILER_MSVC { + if(!stream().is_open()) + stream().setstate(mpt::ofstream::failbit); } mpt::ofstream& stream() { @@ -245,10 +247,10 @@ public: return; } public: - LazyFileRef & operator = (const std::vector &data); + 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::vector () const; operator std::string () const; }; @@ -259,59 +261,25 @@ public: } // 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 + mpt::ifstream m_File; + bool m_IsCached; + std::vector m_Cache; +public: + static bool DefaultToLargeAddressSpaceUsage(); public: InputFile(); - InputFile(const mpt::PathString &filename); + InputFile(const mpt::PathString &filename, bool allowWholeFileCaching = DefaultToLargeAddressSpaceUsage()); ~InputFile(); - bool Open(const mpt::PathString &filename); + bool Open(const mpt::PathString &filename, bool allowWholeFileCaching = DefaultToLargeAddressSpaceUsage()); 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(); + bool IsCached() const; + const mpt::PathString& GetFilenameRef() const; + std::istream* GetStream(); + mpt::const_byte_span GetCache(); }; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp index e1c3e3de3..41d06903c 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptIO.cpp @@ -17,9 +17,6 @@ #include #include #include -#ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM -#include -#endif // MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM OPENMPT_NAMESPACE_BEGIN @@ -30,100 +27,84 @@ namespace mpt { namespace IO { -#ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - -// 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_QUIRK_MSVC_STRINGSTREAM - -//STATIC_ASSERT(sizeof(std::streamoff) == 8); // Assert 64bit file support. +//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(); } +bool IsReadSeekable(std::istream & f) +{ + f.clear(); + std::streampos oldpos = f.tellg(); + if(f.fail() || oldpos == std::streampos(-1)) + { + f.clear(); + return false; + } + f.seekg(0, std::ios::beg); + if(f.fail()) + { + f.clear(); + f.seekg(oldpos); + f.clear(); + return false; + } + f.seekg(0, std::ios::end); + if(f.fail()) + { + f.clear(); + f.seekg(oldpos); + f.clear(); + return false; + } + std::streampos length = f.tellg(); + if(f.fail() || length == std::streampos(-1)) + { + f.clear(); + f.seekg(oldpos); + f.clear(); + return false; + } + f.seekg(oldpos); + f.clear(); + return true; +} +bool IsWriteSeekable(std::ostream & f) +{ + f.clear(); + std::streampos oldpos = f.tellp(); + if(f.fail() || oldpos == std::streampos(-1)) + { + f.clear(); + return false; + } + f.seekp(0, std::ios::beg); + if(f.fail()) + { + f.clear(); + f.seekp(oldpos); + f.clear(); + return false; + } + f.seekp(0, std::ios::end); + if(f.fail()) + { + f.clear(); + f.seekp(oldpos); + f.clear(); + return false; + } + std::streampos length = f.tellp(); + if(f.fail() || length == std::streampos(-1)) + { + f.clear(); + f.seekp(oldpos); + f.clear(); + return false; + } + f.seekp(oldpos); + f.clear(); + return true; +} IO::Offset TellRead(std::istream & f) { return f.tellg(); @@ -134,150 +115,60 @@ IO::Offset TellWrite(std::ostream & f) } bool SeekBegin(std::ostream & f) { - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - 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) { - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - 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) { - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - 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) { - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - 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) { - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - 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) { - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - 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; } - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - 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; } - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - 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; } - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - 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; } - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - 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; } - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - 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; } - #ifdef MPT_COMPILER_QUIRK_MSVC_STRINGSTREAM - 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(); } +IO::Offset ReadRawImpl(std::istream & f, mpt::byte_span data) { f.read(mpt::byte_cast(data.data()), data.size()); return f.gcount(); } +bool WriteRawImpl(std::ostream & f, mpt::const_byte_span data) { f.write(mpt::byte_cast(data.data()), data.size()); return !f.fail(); } bool IsEof(std::istream & f) { return f.eof(); } bool Flush(std::ostream & f) { f.flush(); return !f.fail(); } @@ -289,15 +180,19 @@ bool Flush(std::ostream & f) { f.flush(); return !f.fail(); } -#if defined(MPT_FILEREADER_STD_ISTREAM) - - - -FileDataContainerSeekable::FileDataContainerSeekable(off_t streamLength) +FileDataContainerSeekable::FileDataContainerSeekable(off_t streamLength, bool buffered) : streamLength(streamLength) , cached(false) + , m_Buffered(buffered) + , m_Buffer(m_Buffered ? static_cast(BUFFER_SIZE) : 0) { - return; + if(m_Buffered) + { + for(std::size_t chunkIndex = 0; chunkIndex < NUM_CHUNKS; ++chunkIndex) + { + m_ChunkIndexLRU[chunkIndex] = chunkIndex; + } + } } void FileDataContainerSeekable::CacheStream() const @@ -306,11 +201,48 @@ void FileDataContainerSeekable::CacheStream() const { return; } + if(m_Buffered) + { + m_Buffered = false; + for (std::size_t chunkIndex = 0; chunkIndex < NUM_CHUNKS; ++chunkIndex) + { + m_ChunkInfo[chunkIndex].ChunkValid = false; + } + m_Buffer.resize(0); + m_Buffer.shrink_to_fit(); + } cache.resize(streamLength); InternalRead(cache.data(), 0, streamLength); cached = true; } +std::size_t FileDataContainerSeekable::InternalFillPageAndReturnIndex(off_t pos) const +{ + pos = Util::AlignDown(pos, static_cast(CHUNK_SIZE)); + for(std::size_t chunkLRUIndex = 0; chunkLRUIndex < NUM_CHUNKS; ++chunkLRUIndex) + { + std::size_t chunkIndex = m_ChunkIndexLRU[chunkLRUIndex]; + if(m_ChunkInfo[chunkIndex].ChunkValid && (m_ChunkInfo[chunkIndex].ChunkOffset == pos)) + { + std::size_t chunk = std::move(m_ChunkIndexLRU[chunkLRUIndex]); + std::move_backward(m_ChunkIndexLRU.begin(), m_ChunkIndexLRU.begin() + chunkLRUIndex, m_ChunkIndexLRU.begin() + (chunkLRUIndex + 1)); + m_ChunkIndexLRU[0] = std::move(chunk); + return chunkIndex; + } + } + { + std::size_t chunk = std::move(m_ChunkIndexLRU[NUM_CHUNKS - 1]); + std::move_backward(m_ChunkIndexLRU.begin(), m_ChunkIndexLRU.begin() + (NUM_CHUNKS - 1), m_ChunkIndexLRU.begin() + NUM_CHUNKS); + m_ChunkIndexLRU[0] = std::move(chunk); + } + std::size_t chunkIndex = m_ChunkIndexLRU[0]; + chunk_info& chunk = m_ChunkInfo[chunkIndex]; + chunk.ChunkOffset = pos; + chunk.ChunkLength = InternalRead(chunk_data(chunkIndex).data(), pos, CHUNK_SIZE); + chunk.ChunkValid = true; + return chunkIndex; +} + bool FileDataContainerSeekable::IsValid() const { return true; @@ -326,7 +258,7 @@ bool FileDataContainerSeekable::HasPinnedView() const return cached; } -const mpt::byte *FileDataContainerSeekable::GetRawData() const +const std::byte *FileDataContainerSeekable::GetRawData() const { CacheStream(); return cache.data(); @@ -337,57 +269,51 @@ 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 +IFileDataContainer::off_t FileDataContainerSeekable::Read(std::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); + 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 InternalReadBuffered(dst, pos, count); + } +} + +IFileDataContainer::off_t FileDataContainerSeekable::InternalReadBuffered(std::byte* dst, off_t pos, off_t count) const +{ + if(!m_Buffered) { return InternalRead(dst, pos, count); } + off_t totalRead = 0; + while (count > 0) + { + std::size_t chunkIndex = InternalFillPageAndReturnIndex(pos); + off_t pageSkip = pos - m_ChunkInfo[chunkIndex].ChunkOffset; + off_t chunkWanted = std::min(static_cast(CHUNK_SIZE) - pageSkip, count); + off_t chunkGot = (m_ChunkInfo[chunkIndex].ChunkLength > pageSkip) ? (m_ChunkInfo[chunkIndex].ChunkLength - pageSkip) : 0; + off_t chunk = std::min(chunkWanted, chunkGot); + std::copy(chunk_data(chunkIndex).data() + pageSkip, chunk_data(chunkIndex).data() + pageSkip + chunk, dst); + pos += chunk; + dst += chunk; + totalRead += chunk; + count -= chunk; + if (chunkWanted > chunk) + { + return totalRead; + } + } + return totalRead; } 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; + return mpt::IO::IsReadSeekable(*stream); } IFileDataContainer::off_t FileDataContainerStdStreamSeekable::GetLength(std::istream *stream) @@ -401,13 +327,13 @@ IFileDataContainer::off_t FileDataContainerStdStreamSeekable::GetLength(std::ist } FileDataContainerStdStreamSeekable::FileDataContainerStdStreamSeekable(std::istream *s) - : FileDataContainerSeekable(GetLength(s)) + : FileDataContainerSeekable(GetLength(s), true) , stream(s) { return; } -IFileDataContainer::off_t FileDataContainerStdStreamSeekable::InternalRead(mpt::byte *dst, off_t pos, off_t count) const +IFileDataContainer::off_t FileDataContainerStdStreamSeekable::InternalRead(std::byte *dst, off_t pos, off_t count) const { stream->clear(); // tellg needs eof and fail bits unset std::streampos currentpos = stream->tellg(); @@ -487,7 +413,7 @@ void FileDataContainerUnseekable::CacheStreamUpTo(off_t pos, off_t length) const streamFullyCached = true; } -void FileDataContainerUnseekable::ReadCached(mpt::byte *dst, IFileDataContainer::off_t pos, IFileDataContainer::off_t count) const +void FileDataContainerUnseekable::ReadCached(std::byte *dst, IFileDataContainer::off_t pos, IFileDataContainer::off_t count) const { std::copy(cache.begin() + pos, cache.begin() + pos + count, dst); } @@ -507,7 +433,7 @@ bool FileDataContainerUnseekable::HasPinnedView() const return true; // we have the cache which is required for seeking anyway } -const mpt::byte *FileDataContainerUnseekable::GetRawData() const +const std::byte *FileDataContainerUnseekable::GetRawData() const { CacheStream(); return cache.data(); @@ -519,14 +445,14 @@ IFileDataContainer::off_t FileDataContainerUnseekable::GetLength() const return cachesize; } -IFileDataContainer::off_t FileDataContainerUnseekable::Read(mpt::byte *dst, IFileDataContainer::off_t pos, IFileDataContainer::off_t count) const +IFileDataContainer::off_t FileDataContainerUnseekable::Read(std::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); + IFileDataContainer::off_t cache_avail = std::min(IFileDataContainer::off_t(cachesize) - pos, count); ReadCached(dst, pos, cache_avail); return cache_avail; } @@ -552,7 +478,7 @@ IFileDataContainer::off_t FileDataContainerUnseekable::GetReadableLength(IFileDa { return 0; } - return std::min(cachesize - pos, length); + return std::min(static_cast(cachesize) - pos, length); } @@ -574,7 +500,7 @@ bool FileDataContainerStdStream::InternalEof() const } } -IFileDataContainer::off_t FileDataContainerStdStream::InternalRead(mpt::byte *dst, off_t count) const +IFileDataContainer::off_t FileDataContainerStdStream::InternalRead(std::byte *dst, off_t count) const { stream->read(mpt::byte_cast(dst), count); return static_cast(stream->gcount()); @@ -664,13 +590,13 @@ IFileDataContainer::off_t FileDataContainerCallbackStreamSeekable::GetLength(Cal } FileDataContainerCallbackStreamSeekable::FileDataContainerCallbackStreamSeekable(CallbackStream s) - : FileDataContainerSeekable(GetLength(s)) + : FileDataContainerSeekable(GetLength(s), false) , stream(s) { return; } -IFileDataContainer::off_t FileDataContainerCallbackStreamSeekable::InternalRead(mpt::byte *dst, off_t pos, off_t count) const +IFileDataContainer::off_t FileDataContainerCallbackStreamSeekable::InternalRead(std::byte *dst, off_t pos, off_t count) const { if(!stream.read) { @@ -710,7 +636,7 @@ bool FileDataContainerCallbackStream::InternalEof() const return eof_reached; } -IFileDataContainer::off_t FileDataContainerCallbackStream::InternalRead(mpt::byte *dst, off_t count) const +IFileDataContainer::off_t FileDataContainerCallbackStream::InternalRead(std::byte *dst, off_t count) const { if(eof_reached) { @@ -741,8 +667,5 @@ IFileDataContainer::off_t FileDataContainerCallbackStream::InternalRead(mpt::byt #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 index 24825bd33..19a28c108 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptIO.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptIO.h @@ -50,6 +50,8 @@ inline bool OffsetFits(IO::Offset off) bool IsValid(std::ostream & f); bool IsValid(std::istream & f); bool IsValid(std::iostream & f); +bool IsReadSeekable(std::istream& f); +bool IsWriteSeekable(std::ostream& f); IO::Offset TellRead(std::istream & f); IO::Offset TellWrite(std::ostream & f); bool SeekBegin(std::ostream & f); @@ -64,8 +66,8 @@ 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); +IO::Offset ReadRawImpl(std::istream & f, mpt::byte_span data); +bool WriteRawImpl(std::ostream & f, mpt::const_byte_span data); bool IsEof(std::istream & f); bool Flush(std::ostream & f); @@ -74,14 +76,16 @@ bool Flush(std::ostream & f); template class WriteBuffer; template bool IsValid(WriteBuffer & f) { return IsValid(f.file()); } +template bool IsReadSeekable(WriteBuffer & f) { return IsReadSeekable(f.file()); } +template bool IsWriteSeekable(WriteBuffer & f) { return IsWriteSeekable(f.file()); } template IO::Offset TellRead(WriteBuffer & f) { f.FlushLocal(); return TellRead(f.file()); } template IO::Offset TellWrite(WriteBuffer & f) { return TellWrite(f.file()) + f.GetCurrentSize(); } template bool SeekBegin(WriteBuffer & f) { f.FlushLocal(); return SeekBegin(f.file()); } template bool SeekEnd(WriteBuffer & f) { f.FlushLocal(); return SeekEnd(f.file()); } -template bool SeekAbsolute(WriteBuffer & f, IO::Offset pos) { return f.FlushLocal(); SeekAbsolute(f.file(), pos); } -template bool SeekRelative(WriteBuffer & f, IO::Offset off) { return f.FlushLocal(); SeekRelative(f.file(), off); } -template IO::Offset ReadRawImpl(WriteBuffer & f, mpt::byte * data, std::size_t size) { f.FlushLocal(); return ReadRawImpl(f.file(), data, size); } -template bool WriteRawImpl(WriteBuffer & f, const mpt::byte * data, std::size_t size) { return f.Write(mpt::as_span(data, size)); } +template bool SeekAbsolute(WriteBuffer & f, IO::Offset pos) { f.FlushLocal(); return SeekAbsolute(f.file(), pos); } +template bool SeekRelative(WriteBuffer & f, IO::Offset off) { f.FlushLocal(); return SeekRelative(f.file(), off); } +template IO::Offset ReadRawImpl(WriteBuffer & f, mpt::byte_span data) { f.FlushLocal(); return ReadRawImpl(f.file(), data); } +template bool WriteRawImpl(WriteBuffer & f, mpt::const_byte_span data) { return f.Write(data); } template bool IsEof(WriteBuffer & f) { f.FlushLocal(); return IsEof(f.file()); } template bool Flush(WriteBuffer & f) { f.FlushLocal(); return Flush(f.file()); } @@ -125,7 +129,7 @@ template bool SeekRelative(std::pair, IO::Offs f.second += off; return true; } -template IO::Offset ReadRawImpl(std::pair, IO::Offset> & f, mpt::byte * data, std::size_t size) +template IO::Offset ReadRawImpl(std::pair, IO::Offset> & f, mpt::byte_span data) { if(f.second < 0) { @@ -135,27 +139,27 @@ template IO::Offset ReadRawImpl(std::pair, IO: { 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); + std::size_t num = mpt::saturate_cast(std::min(static_cast(f.first.size()) - f.second, static_cast(data.size()))); + std::copy(mpt::byte_cast(f.first.data() + f.second), mpt::byte_cast(f.first.data() + f.second + num), data.data()); f.second += num; return num; } -template bool WriteRawImpl(std::pair, IO::Offset> & f, const mpt::byte * data, std::size_t size) +template bool WriteRawImpl(std::pair, IO::Offset> & f, mpt::const_byte_span data) { if(f.second < 0) { return false; } - if(f.second >= static_cast(f.first.size())) + 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) + std::size_t num = mpt::saturate_cast(std::min(static_cast(f.first.size()) - f.second, static_cast(data.size()))); + if(num != data.size()) { return false; } - std::copy(data, data + num, mpt::byte_cast(f.first.data() + f.second)); + std::copy(data.data(), data.data() + num, mpt::byte_cast(f.first.data() + f.second)); f.second += num; return true; } @@ -165,7 +169,7 @@ template bool IsEof(std::pair, IO::Offset> & f } template bool Flush(std::pair, IO::Offset> & f) { - MPT_UNREFERENCED_PARAMTER(f); + MPT_UNREFERENCED_PARAMETER(f); return true; } @@ -174,25 +178,25 @@ template bool Flush(std::pair, IO::Offset> & f template inline IO::Offset ReadRaw(Tfile & f, Tbyte * data, std::size_t size) { - return IO::ReadRawImpl(f, mpt::byte_cast(data), size); + return IO::ReadRawImpl(f, mpt::as_span(mpt::byte_cast(data), size)); } template inline IO::Offset ReadRaw(Tfile & f, mpt::span data) { - return IO::ReadRawImpl(f, mpt::byte_cast(data.data()), data.size()); + return IO::ReadRawImpl(f, mpt::byte_cast(data)); } template inline bool WriteRaw(Tfile & f, const Tbyte * data, std::size_t size) { - return IO::WriteRawImpl(f, mpt::byte_cast(data), size); + return IO::WriteRawImpl(f, mpt::as_span(mpt::byte_cast(data), size)); } template inline bool WriteRaw(Tfile & f, mpt::span data) { - return IO::WriteRawImpl(f, mpt::byte_cast(data.data()), data.size()); + return IO::WriteRawImpl(f, mpt::byte_cast(data)); } template @@ -210,7 +214,7 @@ inline bool Write(Tfile & f, const Tbinary & v) template inline bool Write(Tfile & f, const std::vector & v) { - STATIC_ASSERT(mpt::is_binary_safe::value); + static_assert(mpt::is_binary_safe::value); return IO::WriteRaw(f, mpt::as_raw_memory(v)); } @@ -222,17 +226,17 @@ inline bool WritePartial(Tfile & f, const T & v, size_t size = sizeof(T)) } template -inline bool ReadByte(Tfile & f, mpt::byte & v) +inline bool ReadByte(Tfile & f, std::byte & v) { bool result = false; - mpt::byte byte = mpt::as_byte(0); - const IO::Offset readResult = IO::ReadRaw(f, &byte, sizeof(mpt::byte)); + std::byte byte = mpt::as_byte(0); + const IO::Offset readResult = IO::ReadRaw(f, &byte, sizeof(std::byte)); if(readResult < 0) { result = false; } else { - result = (static_cast(readResult) == sizeof(mpt::byte)); + result = (static_cast(readResult) == sizeof(std::byte)); } v = byte; return result; @@ -242,7 +246,7 @@ template inline bool ReadBinaryTruncatedLE(Tfile & f, T & v, std::size_t size) { bool result = false; - MPT_STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); uint8 bytes[sizeof(T)]; std::memset(bytes, 0, sizeof(T)); const IO::Offset readResult = IO::ReadRaw(f, bytes, std::min(size, sizeof(T))); @@ -263,7 +267,7 @@ template inline bool ReadIntLE(Tfile & f, T & v) { bool result = false; - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); uint8 bytes[sizeof(T)]; std::memset(bytes, 0, sizeof(T)); const IO::Offset readResult = IO::ReadRaw(f, bytes, sizeof(T)); @@ -284,7 +288,7 @@ template inline bool ReadIntBE(Tfile & f, T & v) { bool result = false; - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); uint8 bytes[sizeof(T)]; std::memset(bytes, 0, sizeof(T)); const IO::Offset readResult = IO::ReadRaw(f, bytes, sizeof(T)); @@ -364,7 +368,7 @@ inline bool ReadAdaptiveInt64LE(Tfile & f, uint64 & v) template inline bool ReadSizedStringLE(Tfile & f, std::string & str, Tsize maxSize = std::numeric_limits::max()) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); str.clear(); Tsize size = 0; if(!mpt::IO::ReadIntLE(f, size)) @@ -391,14 +395,14 @@ inline bool ReadSizedStringLE(Tfile & f, std::string & str, Tsize maxSize = std: template inline bool WriteIntLE(Tfile & f, const T v) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return IO::Write(f, mpt::as_le(v)); } template inline bool WriteIntBE(Tfile & f, const T v) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return IO::Write(f, mpt::as_be(v)); } @@ -448,10 +452,10 @@ inline bool WriteAdaptiveInt32LE(Tfile & f, const uint32 v, std::size_t fixedSiz } else if(v < 0x400000 && minSize <= 3 && 3 <= maxSize) { 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); + std::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) { @@ -498,19 +502,19 @@ inline bool WriteAdaptiveInt64LE(Tfile & f, const uint64 v, std::size_t fixedSiz 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]; + static_assert(std::numeric_limits::is_integer); + static_assert(!std::numeric_limits::is_signed); + std::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 >> (n * 7u)) & 0x7F) | 0x80); } } - out[numBytes++] = static_cast(v & 0x7F); - MPT_ASSERT(numBytes <= mpt::size(out)); + out[numBytes++] = static_cast(v & 0x7F); + MPT_ASSERT(numBytes <= std::size(out)); if(bytesWritten != nullptr) *bytesWritten = numBytes; return mpt::IO::WriteRaw(f, out, numBytes); } @@ -518,7 +522,7 @@ bool WriteVarInt(Tfile & f, const T v, size_t *bytesWritten = nullptr) template inline bool WriteSizedStringLE(Tfile & f, const std::string & str) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); if(str.size() > std::numeric_limits::max()) { return false; @@ -675,8 +679,6 @@ public: -#if defined(MPT_FILEREADER_STD_ISTREAM) - class IFileDataContainer { public: typedef std::size_t off_t; @@ -690,9 +692,9 @@ public: virtual bool IsValid() const = 0; virtual bool HasFastGetLength() const = 0; virtual bool HasPinnedView() const = 0; - virtual const mpt::byte *GetRawData() const = 0; + virtual const std::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 off_t Read(std::byte *dst, off_t pos, off_t count) const = 0; virtual off_t Read(off_t pos, mpt::byte_span dst) const { @@ -720,7 +722,7 @@ public: { return 0; } - return std::min(length, dataLength - pos); + return std::min(length, dataLength - pos); } }; @@ -744,7 +746,7 @@ public: return true; } - const mpt::byte *GetRawData() const override + const std::byte *GetRawData() const override { return nullptr; } @@ -753,7 +755,7 @@ public: { return 0; } - off_t Read(mpt::byte * /*dst*/, off_t /*pos*/, off_t /*count*/) const override + off_t Read(std::byte * /*dst*/, off_t /*pos*/, off_t /*count*/) const override { return 0; } @@ -781,7 +783,7 @@ public: { return data->HasPinnedView(); } - const mpt::byte *GetRawData() const override + const std::byte *GetRawData() const override { return data->GetRawData() + dataOffset; } @@ -789,7 +791,7 @@ public: { return dataLength; } - off_t Read(mpt::byte *dst, off_t pos, off_t count) const override + off_t Read(std::byte *dst, off_t pos, off_t count) const override { if(pos >= dataLength) { @@ -827,11 +829,36 @@ private: off_t streamLength; mutable bool cached; - mutable std::vector cache; + mutable std::vector cache; + +private: + + mutable bool m_Buffered; + enum : std::size_t { + CHUNK_SIZE = mpt::IO::BUFFERSIZE_SMALL, + BUFFER_SIZE = mpt::IO::BUFFERSIZE_NORMAL + }; + enum : std::size_t { + NUM_CHUNKS = BUFFER_SIZE / CHUNK_SIZE + }; + struct chunk_info { + off_t ChunkOffset = 0; + off_t ChunkLength = 0; + bool ChunkValid = false; + }; + mutable std::vector m_Buffer; + mpt::byte_span chunk_data(std::size_t chunkIndex) const + { + return mpt::byte_span(m_Buffer.data() + (chunkIndex * CHUNK_SIZE), CHUNK_SIZE); + } + mutable std::array m_ChunkInfo; + mutable std::array m_ChunkIndexLRU; + + std::size_t InternalFillPageAndReturnIndex(off_t pos) const; protected: - FileDataContainerSeekable(off_t length); + FileDataContainerSeekable(off_t length, bool buffered); private: @@ -842,13 +869,15 @@ public: bool IsValid() const override; bool HasFastGetLength() const override; bool HasPinnedView() const override; - const mpt::byte *GetRawData() const override; + const std::byte *GetRawData() const override; off_t GetLength() const override; - off_t Read(mpt::byte *dst, off_t pos, off_t count) const override; + off_t Read(std::byte *dst, off_t pos, off_t count) const override; private: - virtual off_t InternalRead(mpt::byte *dst, off_t pos, off_t count) const = 0; + off_t InternalReadBuffered(std::byte* dst, off_t pos, off_t count) const; + + virtual off_t InternalRead(std::byte *dst, off_t pos, off_t count) const = 0; }; @@ -868,7 +897,7 @@ public: private: - off_t InternalRead(mpt::byte *dst, off_t pos, off_t count) const override; + off_t InternalRead(std::byte *dst, off_t pos, off_t count) const override; }; @@ -877,7 +906,7 @@ class FileDataContainerUnseekable : public IFileDataContainer { private: - mutable std::vector cache; + mutable std::vector cache; mutable std::size_t cachesize; mutable bool streamFullyCached; @@ -898,23 +927,23 @@ private: private: - void ReadCached(mpt::byte *dst, off_t pos, off_t count) const; + void ReadCached(std::byte *dst, off_t pos, off_t count) const; public: bool IsValid() const override; bool HasFastGetLength() const override; bool HasPinnedView() const override; - const mpt::byte *GetRawData() const override; + const std::byte *GetRawData() const override; off_t GetLength() const override; - off_t Read(mpt::byte *dst, off_t pos, off_t count) const override; + off_t Read(std::byte *dst, off_t pos, off_t count) const override; bool CanRead(off_t pos, off_t length) const override; off_t GetReadableLength(off_t pos, off_t length) const override; private: virtual bool InternalEof() const = 0; - virtual off_t InternalRead(mpt::byte *dst, off_t count) const = 0; + virtual off_t InternalRead(std::byte *dst, off_t count) const = 0; }; @@ -932,7 +961,7 @@ public: private: bool InternalEof() const override; - off_t InternalRead(mpt::byte *dst, off_t count) const override; + off_t InternalRead(std::byte *dst, off_t count) const override; }; @@ -963,7 +992,7 @@ public: static off_t GetLength(CallbackStream stream); FileDataContainerCallbackStreamSeekable(CallbackStream s); private: - off_t InternalRead(mpt::byte *dst, off_t pos, off_t count) const override; + off_t InternalRead(std::byte *dst, off_t pos, off_t count) const override; }; @@ -976,36 +1005,20 @@ public: FileDataContainerCallbackStream(CallbackStream s); private: bool InternalEof() const override; - off_t InternalRead(mpt::byte *dst, off_t count) const override; + off_t InternalRead(std::byte *dst, off_t count) const override; }; #endif // MPT_FILEREADER_CALLBACK_STREAM -#endif - - class FileDataContainerMemory -#if defined(MPT_FILEREADER_STD_ISTREAM) : public IFileDataContainer -#endif { -#if defined(MPT_FILEREADER_STD_ISTREAM) -#define MPT_FILEDATACONTAINERMEMORY_OVERRIDE override -#else -#define MPT_FILEDATACONTAINERMEMORY_OVERRIDE -#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 + const std::byte *streamData; // Pointer to memory-mapped file off_t streamLength; // Size of memory-mapped file in bytes public: @@ -1014,48 +1027,48 @@ public: public: - bool IsValid() const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + bool IsValid() const override { return streamData != nullptr; } - bool HasFastGetLength() const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + bool HasFastGetLength() const override { return true; } - bool HasPinnedView() const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + bool HasPinnedView() const override { return true; } - const mpt::byte *GetRawData() const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + const std::byte *GetRawData() const override { return streamData; } - off_t GetLength() const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + off_t GetLength() const override { return streamLength; } - off_t Read(mpt::byte *dst, off_t pos, off_t count) const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + off_t Read(std::byte *dst, off_t pos, off_t count) const override { if(pos >= streamLength) { return 0; } - off_t avail = std::min(streamLength - pos, count); + off_t avail = std::min(streamLength - pos, count); std::copy(streamData + pos, streamData + pos + avail, dst); return avail; } - off_t Read(off_t pos, mpt::byte_span dst) const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + off_t Read(off_t pos, mpt::byte_span dst) const override { return Read(dst.data(), pos, dst.size()); } - bool CanRead(off_t pos, off_t length) const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + bool CanRead(off_t pos, off_t length) const override { if((pos == streamLength) && (length == 0)) { @@ -1068,13 +1081,13 @@ public: return (length <= streamLength - pos); } - off_t GetReadableLength(off_t pos, off_t length) const MPT_FILEDATACONTAINERMEMORY_OVERRIDE + off_t GetReadableLength(off_t pos, off_t length) const override { if(pos >= streamLength) { return 0; } - return std::min(length, streamLength - pos); + return std::min(length, streamLength - pos); } }; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.cpp index 41b422782..6073a8666 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.cpp @@ -100,7 +100,7 @@ public: if(WindowsVersion.IsAtLeast(mpt::Windows::Version::Win8)) { hasKB2533623 = true; - } else if(WindowsVersion.IsAtLeast(mpt::Windows::Version::WinVista)) + } else { HMODULE hKernel32DLL = LoadLibraryW(L"kernel32.dll"); if(hKernel32DLL) @@ -133,7 +133,7 @@ public: // Just rely on the default search path here. case mpt::LibrarySearchPathApplication: { - const mpt::PathString dllPath = mpt::GetAppPath(); + const mpt::PathString dllPath = mpt::GetExecutablePath(); if(!dllPath.empty() && mpt::PathIsAbsolute(dllPath) && dllPath.IsDirectory()) { hModule = LoadLibrary((dllPath + path.GetFileName()).AsNative().c_str()); @@ -165,7 +165,7 @@ public: break; case mpt::LibrarySearchPathApplication: { - const mpt::PathString dllPath = mpt::GetAppPath(); + const mpt::PathString dllPath = mpt::GetExecutablePath(); if(!dllPath.empty() && mpt::PathIsAbsolute(dllPath) && dllPath.IsDirectory()) { hModule = LoadLibrary((dllPath + path.GetFileName()).AsNative().c_str()); @@ -194,6 +194,10 @@ public: } + LibraryHandle(const LibraryHandle &) = delete; + + LibraryHandle & operator=(const LibraryHandle &) = delete; + ~LibraryHandle() { if(IsValid()) @@ -237,6 +241,10 @@ public: return; } + LibraryHandle(const LibraryHandle &) = delete; + + LibraryHandle & operator=(const LibraryHandle &) = delete; + ~LibraryHandle() { return; @@ -287,6 +295,10 @@ public: handle = lt_dlopenext(path.GetFileName().AsNative().c_str()); } + LibraryHandle(const LibraryHandle &) = delete; + + LibraryHandle & operator=(const LibraryHandle &) = delete; + ~LibraryHandle() { if(IsValid()) @@ -338,6 +350,10 @@ public: handle = dlopen(path.GetFileName().AsNative().c_str(), RTLD_NOW); } + LibraryHandle(const LibraryHandle &) = delete; + + LibraryHandle & operator=(const LibraryHandle &) = delete; + ~LibraryHandle() { if(IsValid()) @@ -381,6 +397,10 @@ public: return; } + LibraryHandle(const LibraryHandle &) = delete; + + LibraryHandle & operator=(const LibraryHandle &) = delete; + ~LibraryHandle() { return; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.h b/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.h index d182b910b..b865f3e54 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptLibrary.h @@ -104,7 +104,7 @@ public: #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); + static_assert(std::is_function::value); #endif const FuncPtr addr = GetProcAddress(symbol); f = reinterpret_cast(addr); diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptMemory.h b/Frameworks/OpenMPT/OpenMPT/common/mptMemory.h index 539c58dec..e788dc118 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptMemory.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptMemory.h @@ -21,6 +21,7 @@ #include #endif #include +#include #include @@ -36,8 +37,8 @@ namespace mpt { -typedef mpt::span byte_span; -typedef mpt::span const_byte_span; +typedef mpt::span byte_span; +typedef mpt::span const_byte_span; @@ -49,19 +50,15 @@ typedef mpt::span const_byte_span; 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 { }; -#if MPT_BYTE_IS_STD_BYTE -template <> struct is_byte_castable : public std::true_type { }; -#endif +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 { }; -#if MPT_BYTE_IS_STD_BYTE -template <> struct is_byte_castable : public std::true_type { }; -#endif +template <> struct is_byte_castable : public std::true_type { }; template struct is_byte : public std::false_type { }; -template <> struct is_byte : public std::true_type { }; -template <> struct is_byte : public std::true_type { }; +template <> struct is_byte : public std::true_type { }; +template <> struct is_byte : public std::true_type { }; // Tell which types are safe to binary write into files. @@ -74,9 +71,7 @@ template struct is_binary_safe : public std::false_type { }; 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 { }; -#if MPT_BYTE_IS_STD_BYTE -template <> struct is_binary_safe : public std::true_type { }; -#endif +template <> struct is_binary_safe : public std::true_type { }; // Generic Specialization for arrays. template struct is_binary_safe : public is_binary_safe { }; @@ -88,9 +83,9 @@ template struct is_binary_safe::value); \ + static_assert(sizeof( type ) == (size) ); \ + static_assert(alignof( type ) == 1); \ + static_assert(std::is_standard_layout< type >::value); \ namespace mpt { \ template <> struct is_binary_safe< type > : public std::true_type { }; \ } \ @@ -122,7 +117,7 @@ struct value_initializer template inline void Clear(T & x) { - MPT_STATIC_ASSERT(!std::is_pointer::value); + static_assert(!std::is_pointer::value); value_initializer()(x); } @@ -132,13 +127,8 @@ 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_COMPILER_CLANG && defined(__GLIBCXX__)) - MPT_STATIC_ASSERT(std::is_standard_layout::value); - MPT_STATIC_ASSERT(std::is_trivial::value || mpt::is_binary_safe::value); // approximation -#else // default - MPT_STATIC_ASSERT(std::is_standard_layout::value); - MPT_STATIC_ASSERT((std::is_trivially_default_constructible::value && std::is_trivially_copyable::value) || mpt::is_binary_safe::value); // C++11, but not supported on most compilers we care about -#endif + static_assert(std::is_standard_layout::value); + static_assert((std::is_trivially_default_constructible::value && std::is_trivially_copyable::value) || mpt::is_binary_safe::value); std::memset(&a, 0, sizeof(T)); } @@ -152,39 +142,13 @@ namespace mpt { using std::bit_cast; #else // C++2a compatible bit_cast. -// See . -// Not implementing constexpr because this is not easily possible pre C++2a. +// Not implementing constexpr because this is not easily possible pre C++20. template -MPT_FORCEINLINE Tdst bit_cast(const Tsrc & src) noexcept +MPT_FORCEINLINE typename std::enable_if<(sizeof(Tdst) == sizeof(Tsrc)) && std::is_trivially_copyable::value && std::is_trivially_copyable::value, Tdst>::type bit_cast(const Tsrc & src) noexcept { - MPT_STATIC_ASSERT(sizeof(Tdst) == sizeof(Tsrc)); -#if MPT_GCC_BEFORE(5,1,0) || (MPT_COMPILER_CLANG && defined(__GLIBCXX__)) - MPT_STATIC_ASSERT(std::is_trivial::value); // approximation - MPT_STATIC_ASSERT(std::is_trivial::value); // approximation -#else // default - MPT_STATIC_ASSERT(std::is_trivially_copyable::value); - MPT_STATIC_ASSERT(std::is_trivially_copyable::value); -#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. - union { - Tsrc src; - Tdst dst; - } conv; - conv.src = src; - return conv.dst; - #else // MPT_COMPILER - // 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 ). - Tdst dst{}; - std::memcpy(&dst, &src, sizeof(Tdst)); - return dst; - #endif // MPT_COMPILER + Tdst dst{}; + std::memcpy(&dst, &src, sizeof(Tdst)); + return dst; } #endif @@ -195,12 +159,12 @@ 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(sizeof(Tsrc) == sizeof(std::byte)); + static_assert(sizeof(Tdst) == sizeof(std::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 || mpt::is_byte::value); - STATIC_ASSERT(std::is_integral::value || mpt::is_byte::value); + static_assert(std::is_integral::value || mpt::is_byte::value); + static_assert(std::is_integral::value || mpt::is_byte::value); return static_cast(src); } }; @@ -209,13 +173,13 @@ 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 || mpt::is_byte::value); - STATIC_ASSERT(std::is_integral::value || mpt::is_byte::value); - return mpt::as_span(mpt::byte_cast_impl()(src.begin()), mpt::byte_cast_impl()(src.end())); + static_assert(sizeof(Tsrc) == sizeof(std::byte)); + static_assert(sizeof(Tdst) == sizeof(std::byte)); + static_assert(mpt::is_byte_castable::value); + static_assert(mpt::is_byte_castable::value); + static_assert(std::is_integral::value || mpt::is_byte::value); + static_assert(std::is_integral::value || mpt::is_byte::value); + return mpt::as_span(mpt::byte_cast_impl()(src.data()), mpt::byte_cast_impl()(src.data() + src.size())); } }; template @@ -223,12 +187,12 @@ 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 || mpt::is_byte::value); - STATIC_ASSERT(std::is_integral::value || mpt::is_byte::value); + static_assert(sizeof(Tsrc) == sizeof(std::byte)); + static_assert(sizeof(Tdst) == sizeof(std::byte)); + static_assert(mpt::is_byte_castable::value); + static_assert(mpt::is_byte_castable::value); + static_assert(std::is_integral::value || mpt::is_byte::value); + static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; @@ -241,9 +205,9 @@ 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 || mpt::is_byte::value); + static_assert(sizeof(Tdst) == sizeof(std::byte)); + static_assert(mpt::is_byte_castable::value); + static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; @@ -252,9 +216,9 @@ 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 || mpt::is_byte::value); + static_assert(sizeof(Tdst) == sizeof(std::byte)); + static_assert(mpt::is_byte_castable::value); + static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; @@ -263,9 +227,9 @@ 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 || mpt::is_byte::value); + static_assert(sizeof(Tsrc) == sizeof(std::byte)); + static_assert(mpt::is_byte_castable::value); + static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; @@ -274,9 +238,9 @@ 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 || mpt::is_byte::value); + static_assert(sizeof(Tsrc) == sizeof(std::byte)); + static_assert(mpt::is_byte_castable::value); + static_assert(std::is_integral::value || mpt::is_byte::value); return reinterpret_cast(src); } }; @@ -298,10 +262,10 @@ inline Tdst void_cast(Tsrc src) template -MPT_CONSTEXPR14_FUN mpt::byte as_byte(T src) noexcept +MPT_CONSTEXPR14_FUN std::byte as_byte(T src) noexcept { - MPT_STATIC_ASSERT(std::is_integral::value); - return static_cast(static_cast(src)); + static_assert(std::is_integral::value); + return static_cast(static_cast(src)); } @@ -311,13 +275,13 @@ struct GetRawBytesFunctor { inline mpt::const_byte_span operator () (const T & v) const { - STATIC_ASSERT(mpt::is_binary_safe::type>::value); - return mpt::as_span(reinterpret_cast(&v), sizeof(T)); + static_assert(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(&v), sizeof(T)); } inline mpt::byte_span operator () (T & v) const { - STATIC_ASSERT(mpt::is_binary_safe::type>::value); - return mpt::as_span(reinterpret_cast(&v), sizeof(T)); + static_assert(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(&v), sizeof(T)); } }; @@ -326,13 +290,13 @@ struct GetRawBytesFunctor { inline mpt::const_byte_span operator () (const T (&v)[N]) const { - STATIC_ASSERT(mpt::is_binary_safe::type>::value); - return mpt::as_span(reinterpret_cast(v), N * sizeof(T)); + static_assert(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(v), N * sizeof(T)); } inline mpt::byte_span operator () (T (&v)[N]) const { - STATIC_ASSERT(mpt::is_binary_safe::type>::value); - return mpt::as_span(reinterpret_cast(v), N * sizeof(T)); + static_assert(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(v), N * sizeof(T)); } }; @@ -341,15 +305,15 @@ struct GetRawBytesFunctor { inline mpt::const_byte_span operator () (const T (&v)[N]) const { - STATIC_ASSERT(mpt::is_binary_safe::type>::value); - return mpt::as_span(reinterpret_cast(v), N * sizeof(T)); + static_assert(mpt::is_binary_safe::type>::value); + return mpt::as_span(reinterpret_cast(v), N * sizeof(T)); } }; // 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 *), +// Using a wrapper (by default just around a cast to const std::byte *), // allows for implementing raw memory access // via on-demand generating a cached serialized representation. template inline mpt::const_byte_span as_raw_memory(const T & v) diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptMutex.h b/Frameworks/OpenMPT/OpenMPT/common/mptMutex.h index 669a0c350..8b021aa6f 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptMutex.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptMutex.h @@ -174,7 +174,7 @@ public: #if MPT_MUTEX_STD -#define MPT_LOCK_GUARD std::lock_guard +template using lock_guard = std::lock_guard; #else // !MPT_MUTEX_STD @@ -188,8 +188,6 @@ public: ~lock_guard() { mutex.unlock(); } }; -#define MPT_LOCK_GUARD mpt::lock_guard - #endif // MPT_MUTEX_STD #ifdef MODPLUG_TRACKER diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp index 9d75ab755..f4d244998 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptOS.cpp @@ -15,10 +15,71 @@ #include #endif +#if defined(MODPLUG_TRACKER) +#if !MPT_OS_WINDOWS +#include +#endif // !MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + OPENMPT_NAMESPACE_BEGIN +#if defined(MODPLUG_TRACKER) + +namespace mpt +{ +namespace OS +{ + +mpt::OS::Class GetClassFromSysname(mpt::ustring sysname) +{ + mpt::OS::Class result = mpt::OS::Class::Unknown; + if(sysname == U_("")) + { + result = mpt::OS::Class::Unknown; + } else if(sysname == U_("Windows") || sysname == U_("WindowsNT") || sysname == U_("Windows_NT")) + { + result = mpt::OS::Class::Windows; + } else if(sysname == U_("Linux")) + { + result = mpt::OS::Class::Linux; + } else if(sysname == U_("Darwin")) + { + result = mpt::OS::Class::Darwin; + } else if(sysname == U_("FreeBSD") || sysname == U_("DragonFly") || sysname == U_("NetBSD") || sysname == U_("OpenBSD") || sysname == U_("MidnightBSD")) + { + result = mpt::OS::Class::BSD; + } else if(sysname == U_("Haiku")) + { + result = mpt::OS::Class::Haiku; + } else if(sysname == U_("MS-DOS")) + { + result = mpt::OS::Class::DOS; + } + return result; +} + +mpt::OS::Class GetClass() +{ + #if MPT_OS_WINDOWS + return mpt::OS::Class::Windows; + #else // !MPT_OS_WINDOWS + utsname uname_result; + if(uname(&uname_result) != 0) + { + return mpt::OS::Class::Unknown; + } + return mpt::OS::GetClassFromSysname(mpt::ToUnicode(mpt::Charset::ASCII, mpt::String::ReadAutoBuf(uname_result.sysname))); + #endif // MPT_OS_WINDOWS +} + +} // namespace OS +} // namespace mpt + +#endif // MODPLUG_TRACKER + + namespace mpt { namespace Windows @@ -52,7 +113,7 @@ static mpt::Windows::Version VersionFromNTDDI_VERSION() noexcept mpt::Windows::Version::WinNT4 #endif ; - return mpt::Windows::Version(System, mpt::Windows::Version::ServicePack(((NTDDI_VERSION & 0xffffu) >> 8) & 0xffu, ((NTDDI_VERSION & 0xffffu) >> 0) & 0xffu), 0); + return mpt::Windows::Version(System, mpt::Windows::Version::ServicePack(((NTDDI_VERSION & 0xffffu) >> 8) & 0xffu, ((NTDDI_VERSION & 0xffffu) >> 0) & 0xffu), 0, 0); } @@ -77,6 +138,7 @@ static mpt::Windows::Version GatherWindowsVersion() noexcept #if MPT_COMPILER_MSVC #pragma warning(push) #pragma warning(disable:4996) // 'GetVersionExW': was declared deprecated +#pragma warning(disable:28159) // Consider using 'IsWindows*' instead of 'GetVersionExW'. Reason: Deprecated. Use VerifyVersionInfo* or IsWindows* macros from VersionHelpers. #endif // MPT_COMPILER_MSVC #if MPT_COMPILER_CLANG #pragma clang diagnostic push @@ -96,10 +158,17 @@ static mpt::Windows::Version GatherWindowsVersion() noexcept { return VersionFromNTDDI_VERSION(); } + DWORD dwProductType = 0; + dwProductType = PRODUCT_UNDEFINED; + if(GetProductInfo(versioninfoex.dwMajorVersion, versioninfoex.dwMinorVersion, versioninfoex.wServicePackMajor, versioninfoex.wServicePackMinor, &dwProductType) == FALSE) + { + dwProductType = PRODUCT_UNDEFINED; + } return mpt::Windows::Version( mpt::Windows::Version::System(versioninfoex.dwMajorVersion, versioninfoex.dwMinorVersion), mpt::Windows::Version::ServicePack(versioninfoex.wServicePackMajor, versioninfoex.wServicePackMinor), - versioninfoex.dwBuildNumber + versioninfoex.dwBuildNumber, + dwProductType ); #endif // MPT_OS_WINDOWS_WINRT } @@ -135,6 +204,7 @@ Version::Version() noexcept , m_System() , m_ServicePack() , m_Build() + , m_Type() { } @@ -145,11 +215,12 @@ Version Version::NoWindows() noexcept } -Version::Version(mpt::Windows::Version::System system, mpt::Windows::Version::ServicePack servicePack, mpt::Windows::Version::Build build) noexcept +Version::Version(mpt::Windows::Version::System system, mpt::Windows::Version::ServicePack servicePack, mpt::Windows::Version::Build build, mpt::Windows::Version::TypeId type) noexcept : m_SystemIsWindows(true) , m_System(system) , m_ServicePack(servicePack) , m_Build(build) + , m_Type(type) { } @@ -284,7 +355,13 @@ mpt::Windows::Version::Build Version::GetBuild() const noexcept } -static MPT_CONSTEXPR11_VAR struct { Version::System version; const MPT_UCHAR_TYPE * name; bool showDetails; } versionMap[] = +mpt::Windows::Version::TypeId Version::GetTypeId() const noexcept +{ + return m_Type; +} + + +static constexpr struct { Version::System version; const mpt::uchar * name; bool showDetails; } versionMap[] = { { mpt::Windows::Version::WinNewer, UL_("Windows 10 (or newer)"), false }, { mpt::Windows::Version::Win10, UL_("Windows 10"), true }, @@ -369,7 +446,7 @@ mpt::ustring Version::GetName() const } else { result = mpt::format(U_("Wine (unknown version: '%1') (%2)"))( - mpt::ToUnicode(mpt::CharsetUTF8, v.RawVersion()) + mpt::ToUnicode(mpt::Charset::UTF8, v.RawVersion()) , name ); } @@ -410,11 +487,7 @@ mpt::Windows::Version::System Version::GetMinimumKernelLevel() noexcept { uint64 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 + minimumKernelVersion = std::max(minimumKernelVersion, static_cast(mpt::Windows::Version::WinVista)); #endif return mpt::Windows::Version::System(minimumKernelVersion); } @@ -502,7 +575,7 @@ struct ArchitectureInfo { Architecture Arch; int Bitness; - const MPT_UCHAR_TYPE * Name; + const mpt::uchar * Name; }; static constexpr ArchitectureInfo architectureInfo [] = { { Architecture::x86 , 32, UL_("x86") }, @@ -836,8 +909,7 @@ mpt::Wine::Version GetMinimumWineVersion() VersionContext::VersionContext() : m_IsWine(false) - , m_HostIsLinux(false) - , m_HostIsBSD(false) + , m_HostClass(mpt::OS::Class::Unknown) { #if MPT_OS_WINDOWS m_IsWine = mpt::Windows::IsWine(); @@ -869,9 +941,8 @@ VersionContext::VersionContext() 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"); + m_Version = mpt::Wine::Version(mpt::ToUnicode(mpt::Charset::UTF8, m_RawVersion)); + m_HostClass = mpt::OS::GetClassFromSysname(mpt::ToUnicode(mpt::Charset::UTF8, m_RawHostSysName)); #endif // MPT_OS_WINDOWS } diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptOS.h b/Frameworks/OpenMPT/OpenMPT/common/mptOS.h index 851ca9699..5888f71f8 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptOS.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptOS.h @@ -19,6 +19,34 @@ OPENMPT_NAMESPACE_BEGIN +#if defined(MODPLUG_TRACKER) + +namespace mpt +{ +namespace OS +{ + +enum class Class +{ + Unknown, + Windows, + Linux, + Darwin, + BSD, + Haiku, + DOS, +}; + +mpt::OS::Class GetClassFromSysname(mpt::ustring sysname); + +mpt::OS::Class GetClass(); + +} // namespace OS +} // namespace mpt + +#endif // MODPLUG_TRACKER + + namespace mpt { namespace Windows @@ -92,6 +120,8 @@ public: typedef uint32 Build; + typedef uint32 TypeId; + static mpt::ustring VersionToString(mpt::Windows::Version::System version); private: @@ -101,6 +131,7 @@ private: System m_System; ServicePack m_ServicePack; Build m_Build; + TypeId m_Type; private: @@ -110,7 +141,7 @@ public: static Version NoWindows() noexcept; - Version(mpt::Windows::Version::System system, mpt::Windows::Version::ServicePack servicePack, mpt::Windows::Version::Build build) noexcept; + Version(mpt::Windows::Version::System system, mpt::Windows::Version::ServicePack servicePack, mpt::Windows::Version::Build build, mpt::Windows::Version::TypeId type) noexcept; public: @@ -131,6 +162,7 @@ public: mpt::Windows::Version::System GetSystem() const noexcept; mpt::Windows::Version::ServicePack GetServicePack() const noexcept; mpt::Windows::Version::Build GetBuild() const noexcept; + mpt::Windows::Version::TypeId GetTypeId() const noexcept; mpt::ustring GetName() const; #ifdef MODPLUG_TRACKER @@ -248,8 +280,7 @@ protected: std::string m_RawHostSysName; std::string m_RawHostRelease; mpt::Wine::Version m_Version; - bool m_HostIsLinux; - bool m_HostIsBSD; + mpt::OS::Class m_HostClass; public: VersionContext(); public: @@ -260,8 +291,7 @@ public: 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; } + mpt::OS::Class HostClass() const { return m_HostClass; } }; } // namespace Wine diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptOSError.h b/Frameworks/OpenMPT/OpenMPT/common/mptOSError.h new file mode 100644 index 000000000..99f999be1 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptOSError.h @@ -0,0 +1,99 @@ +/* + * mptOSError.h + * ------------ + * Purpose: OS-specific error message 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 "BuildSettings.h" + + + +#include "mptBaseMacros.h" +#include "mptException.h" +#include "mptString.h" +#include "mptStringFormat.h" + +#if defined(MODPLUG_TRACKER) +#if MPT_OS_WINDOWS +#include +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + +#if defined(MODPLUG_TRACKER) +#if MPT_OS_WINDOWS +#include +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MODPLUG_TRACKER) +#if MPT_OS_WINDOWS + +namespace Windows +{ + + +inline mpt::ustring GetErrorMessage(DWORD errorCode, HANDLE hModule = NULL) +{ + mpt::ustring message; + void *lpMsgBuf = nullptr; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | (hModule ? FORMAT_MESSAGE_FROM_HMODULE : 0) | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + hModule, + errorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&lpMsgBuf, + 0, + NULL); + if(!lpMsgBuf) + { + DWORD e = GetLastError(); + if((e == ERROR_NOT_ENOUGH_MEMORY) || (e == ERROR_OUTOFMEMORY)) + { + MPT_EXCEPTION_THROW_OUT_OF_MEMORY(); + } + return {}; + } + try + { + message = mpt::ToUnicode(mpt::winstring((LPTSTR)lpMsgBuf)); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + LocalFree(lpMsgBuf); + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); + } + LocalFree(lpMsgBuf); + return message; +} + + +class Error + : public std::runtime_error +{ +public: + Error(DWORD errorCode, HANDLE hModule = NULL) + : std::runtime_error(mpt::ToCharset(mpt::CharsetException, mpt::format(U_("Windows Error: 0x%1: %2"))(mpt::ufmt::hex0<8>(errorCode), GetErrorMessage(errorCode, hModule)))) + { + return; + } +}; + + +} // namespace Windows + +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptOSException.h b/Frameworks/OpenMPT/OpenMPT/common/mptOSException.h new file mode 100644 index 000000000..6e5eed117 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/common/mptOSException.h @@ -0,0 +1,188 @@ +/* + * mptOSException.h + * ---------------- + * Purpose: platform-specific exception/signal 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 "BuildSettings.h" + +#include "mptBaseMacros.h" +#include "mptBaseTypes.h" + +#if defined(MODPLUG_TRACKER) +#if MPT_OS_WINDOWS +#include +#endif // MPT_OS_WINDOWS +#endif // MODPLUG_TRACKER + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MODPLUG_TRACKER) + + +#if MPT_OS_WINDOWS + + +namespace Windows +{ + + +namespace SEH +{ + + +struct Code +{ +private: + DWORD m_Code; +public: + constexpr Code(DWORD code) noexcept + : m_Code(code) + { + return; + } +public: + constexpr DWORD code() const noexcept + { + return m_Code; + } +}; + + +template +auto TryFilterHandleThrow(const Tfn &fn, const Tfilter &filter, const Thandler &handler) -> decltype(fn()) +{ + static_assert(std::is_trivially_copy_assignable::value); + static_assert(std::is_trivially_copy_constructible::value); + static_assert(std::is_trivially_move_assignable::value); + static_assert(std::is_trivially_move_constructible::value); + DWORD code = 0; + __try + { + return fn(); + } __except(filter(GetExceptionCode(), GetExceptionInformation())) + { + code = GetExceptionCode(); + } + throw Windows::SEH::Code(code); +} + + +template +void TryFilterHandleVoid(const Tfn &fn, const Tfilter &filter, const Thandler &handler) +{ + static_assert(std::is_same::value || std::is_trivially_copy_assignable::value); + static_assert(std::is_same::value || std::is_trivially_copy_constructible::value); + static_assert(std::is_same::value || std::is_trivially_move_assignable::value); + static_assert(std::is_same::value || std::is_trivially_move_constructible::value); + __try + { + fn(); + return; + } __except(filter(GetExceptionCode(), GetExceptionInformation())) + { + DWORD code = GetExceptionCode(); + handler(code); + } + return; +} + + +template +auto TryFilterHandleDefault(const Tfn &fn, const Tfilter &filter, const Thandler &handler, decltype(fn()) def = decltype(fn()){}) -> decltype(fn()) +{ + static_assert(std::is_trivially_copy_assignable::value); + static_assert(std::is_trivially_copy_constructible::value); + static_assert(std::is_trivially_move_assignable::value); + static_assert(std::is_trivially_move_constructible::value); + auto result = def; + __try + { + result = fn(); + } __except(filter(GetExceptionCode(), GetExceptionInformation())) + { + DWORD code = GetExceptionCode(); + result = handler(code); + } + return result; +} + + +template +auto TryReturnOrThrow(const Tfn &fn) -> decltype(fn()) +{ + return TryFilterHandleThrow( + fn, + [](auto code, auto eptr) + { + MPT_UNREFERENCED_PARAMETER(code); + MPT_UNREFERENCED_PARAMETER(eptr); + return EXCEPTION_EXECUTE_HANDLER; + }, + [](auto code) + { + throw Windows::SEH::Code(code); + }); +} + + +template +DWORD TryOrError(const Tfn &fn) +{ + DWORD result = DWORD{0}; + TryFilterHandleVoid( + fn, + [](auto code, auto eptr) + { + MPT_UNREFERENCED_PARAMETER(code); + MPT_UNREFERENCED_PARAMETER(eptr); + return EXCEPTION_EXECUTE_HANDLER; + }, + [&result](auto code) + { + result = code; + }); + return result; +} + + +template +auto TryReturnOrDefault(const Tfn &fn, decltype(fn()) def = decltype(fn()){}) -> decltype(fn()) +{ + return TryFilterHandleDefault( + fn, + [](auto code, auto eptr) + { + MPT_UNREFERENCED_PARAMETER(code); + MPT_UNREFERENCED_PARAMETER(eptr); + return EXCEPTION_EXECUTE_HANDLER; + }, + [def](auto code) + { + MPT_UNREFERENCED_PARAMETER(code); + return def; + }); +} + + +} // namspace SEH + + +} // namespace Windows + + +#endif // MPT_OS_WINDOWS + + +#endif // MODPLUG_TRACKER + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptPathString.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptPathString.cpp index 09876dd48..fabc7bca5 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptPathString.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptPathString.cpp @@ -524,7 +524,7 @@ bool DeleteWholeDirectoryTree(mpt::PathString path) #if defined(MPT_ENABLE_DYNBIND) || defined(MPT_ENABLE_TEMPFILE) -mpt::PathString GetAppPath() +mpt::PathString GetExecutablePath() { std::vector exeFileName(MAX_PATH); while(GetModuleFileName(0, exeFileName.data(), mpt::saturate_cast(exeFileName.size())) >= exeFileName.size()) @@ -578,8 +578,8 @@ mpt::PathString GetTempDirectory() return mpt::PathString::FromNative(tempPath.data()); } } - // use app directory as fallback - return mpt::GetAppPath(); + // use exe directory as fallback + return mpt::GetExecutablePath(); } mpt::PathString CreateTempFileName(const mpt::PathString &fileNamePrefix, const mpt::PathString &fileNameExtension) @@ -735,7 +735,7 @@ void SanitizeFilename(mpt::u8string &str) } #endif // MPT_USTRING_MODE_UTF8 -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) void SanitizeFilename(CString &str) { for(int i = 0; i < str.GetLength(); i++) @@ -743,7 +743,7 @@ void SanitizeFilename(CString &str) str.SetAt(i, SanitizeFilenameChar(str.GetAt(i))); } } -#endif // MFC +#endif // MPT_WITH_MFC #endif // MODPLUG_TRACKER diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptPathString.h b/Frameworks/OpenMPT/OpenMPT/common/mptPathString.h index 8a5a6ec20..169dc5e0f 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptPathString.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptPathString.h @@ -21,7 +21,7 @@ OPENMPT_NAMESPACE_BEGIN #define MPT_DEPRECATED_PATH -//#define MPT_DEPRECATED_PATH MPT_DEPRECATED +//#define MPT_DEPRECATED_PATH [[deprecated]] @@ -120,9 +120,9 @@ public: 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 GetFileName() const; // File name without extension, e.g. "OpenMPT" PathString GetFileExt() const; // Extension including dot, e.g. ".exe" - PathString GetFullFileName() const; // File name + extension, e.g. "mptrack.exe" + PathString GetFullFileName() const; // File name + extension, e.g. "OpenMPT.exe" // Verify if this path represents a valid directory on the file system. bool IsDirectory() const; @@ -204,27 +204,27 @@ public: #endif // conversions #if defined(MPT_ENABLE_CHARSET_LOCALE) - MPT_DEPRECATED_PATH std::string ToLocale() const { return mpt::ToCharset(mpt::CharsetLocale, path); } + MPT_DEPRECATED_PATH std::string ToLocale() const { return mpt::ToCharset(mpt::Charset::Locale, path); } #endif - std::string ToUTF8() const { return mpt::ToCharset(mpt::CharsetUTF8, path); } + std::string ToUTF8() const { return mpt::ToCharset(mpt::Charset::UTF8, path); } std::wstring ToWide() const { return mpt::ToWide(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::ToWin(mpt::CharsetLocale, path)); } - static PathString FromLocaleSilent(const std::string &path) { return PathString(mpt::ToWin(mpt::CharsetLocale, path)); } + MPT_DEPRECATED_PATH static PathString FromLocale(const std::string &path) { return PathString(mpt::ToWin(mpt::Charset::Locale, path)); } + static PathString FromLocaleSilent(const std::string &path) { return PathString(mpt::ToWin(mpt::Charset::Locale, path)); } #endif - static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToWin(mpt::CharsetUTF8, path)); } + static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToWin(mpt::Charset::UTF8, path)); } static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToWin(path)); } static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToWin(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) +#if defined(MPT_WITH_MFC) // CString TCHAR, so this is CHAR or WCHAR, depending on UNICODE CString ToCString() const { return mpt::ToCString(path); } static PathString FromCString(const CString &path) { return PathString(mpt::ToWin(path)); } -#endif +#endif // MPT_WITH_MFC // Convert a path to its simplified form, i.e. remove ".\" and "..\" entries mpt::PathString Simplify() const; @@ -234,39 +234,39 @@ public: // 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); } + std::string ToUTF8() const { return mpt::ToCharset(mpt::Charset::UTF8, mpt::Charset::Locale, path); } #if MPT_WSTRING_CONVERT - std::wstring ToWide() const { return mpt::ToWide(mpt::CharsetLocale, path); } + std::wstring ToWide() const { return mpt::ToWide(mpt::Charset::Locale, path); } #endif - mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::CharsetLocale, path); } + mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::Charset::Locale, 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)); } + static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToCharset(mpt::Charset::Locale, mpt::Charset::UTF8, path)); } #if MPT_WSTRING_CONVERT - static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::CharsetLocale, path)); } + static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::Charset::Locale, path)); } #endif - static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::CharsetLocale, path)); } + static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::Charset::Locale, 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); } + std::wstring ToWide() const { return mpt::ToWide(mpt::Charset::UTF8, path); } #endif - mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::CharsetUTF8, path); } + mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::Charset::UTF8, path); } static PathString FromUTF8(const std::string &path) { return PathString(path); } #if MPT_WSTRING_CONVERT - static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::CharsetUTF8, path)); } + static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::Charset::UTF8, path)); } #endif - static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::CharsetUTF8, path)); } + static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::Charset::UTF8, 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 PathString(path); } + [[deprecated]] mpt::PathString Simplify() const { return PathString(path); } #endif // MPT_OS_WINDOWS @@ -277,12 +277,12 @@ public: #if defined(MPT_ENABLE_CHARSET_LOCALE) #if MPT_OS_WINDOWS #ifdef UNICODE -MPT_DEPRECATED static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::CharsetLocale, x.ToUnicode()); } +[[deprecated]] static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUnicode()); } #else -MPT_DEPRECATED_PATH static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::CharsetLocale, x.AsNative()); } +MPT_DEPRECATED_PATH static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.AsNative()); } #endif #else -MPT_DEPRECATED_PATH static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::CharsetLocale, x.ToUnicode()); } +MPT_DEPRECATED_PATH static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUnicode()); } #endif #endif static inline mpt::ustring ToUString(const mpt::PathString & x) { return x.ToUnicode(); } @@ -339,8 +339,8 @@ bool DeleteWholeDirectoryTree(mpt::PathString path); #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(); +// Returns the application executable path or an empty string (if unknown), e.g. "C:\mptrack\" +mpt::PathString GetExecutablePath(); #endif // MPT_ENABLE_DYNBIND || MPT_ENABLE_TEMPFILE @@ -418,20 +418,20 @@ void SanitizeFilename(mpt::u8string &str); template void SanitizeFilename(char (&buffer)[size]) { - STATIC_ASSERT(size > 0); + static_assert(size > 0); SanitizeFilename(buffer, buffer + size); } template void SanitizeFilename(wchar_t (&buffer)[size]) { - STATIC_ASSERT(size > 0); + static_assert(size > 0); SanitizeFilename(buffer, buffer + size); } -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) void SanitizeFilename(CString &str); -#endif +#endif // MPT_WITH_MFC #endif // MODPLUG_TRACKER diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp index e4ba915e2..84e0db941 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.cpp @@ -34,16 +34,16 @@ static T log2(T x) } -static MPT_CONSTEXPR11_FUN int lower_bound_entropy_bits(unsigned int x) +static MPT_CONSTEXPR14_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 MPT_CONSTEXPR14_FUN bool is_mask(T x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + 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; @@ -86,7 +86,7 @@ static T generate_timeseed() { uint64be time; time = std::chrono::duration_cast(std::chrono::system_clock().now().time_since_epoch()).count(); - mpt::byte bytes[sizeof(time)]; + std::byte bytes[sizeof(time)]; std::memcpy(bytes, &time, sizeof(time)); hash(std::begin(bytes), std::end(bytes)); } @@ -94,7 +94,7 @@ static T generate_timeseed() { uint64be time; time = std::chrono::duration_cast(std::chrono::high_resolution_clock().now().time_since_epoch()).count(); - mpt::byte bytes[sizeof(time)]; + std::byte bytes[sizeof(time)]; std::memcpy(bytes, &time, sizeof(time)); hash(std::begin(bytes), std::end(bytes)); } @@ -126,8 +126,19 @@ crand::result_type crand::operator()() #endif // MODPLUG_TRACKER sane_random_device::sane_random_device() - : rd_reliable(rd.entropy() > 0.0) + : rd_reliable(false) { + try + { + prd = std::make_unique(); + rd_reliable = ((*prd).entropy() > 0.0); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); + } catch(const std::exception &) + { + rd_reliable = false; + } if(!rd_reliable) { init_fallback(); @@ -136,9 +147,19 @@ sane_random_device::sane_random_device() sane_random_device::sane_random_device(const std::string & token_) : token(token_) - , rd(token) - , rd_reliable(rd.entropy() > 0.0) + , rd_reliable(false) { + try + { + prd = std::make_unique(token); + rd_reliable = ((*prd).entropy() > 0.0); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); + } catch(const std::exception &) + { + rd_reliable = false; + } if(!rd_reliable) { init_fallback(); @@ -160,7 +181,7 @@ void sane_random_device::init_fallback() seeds.push_back(static_cast(static_cast(token[i]))); } std::seed_seq seed(seeds.begin(), seeds.end()); - rd_fallback = mpt::make_unique(seed); + rd_fallback = std::make_unique(seed); } else { uint64 seed_val = mpt::generate_timeseed(); @@ -168,53 +189,59 @@ void sane_random_device::init_fallback() 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); + rd_fallback = std::make_unique(seed); } } } sane_random_device::result_type sane_random_device::operator()() { - MPT_LOCK_GUARD l(m); + mpt::lock_guard l(m); result_type result = 0; - try + if(prd) { - 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)) + try + { + if constexpr(std::random_device::min() != 0 || !mpt::is_mask(std::random_device::max())) + { // insane std::random_device + // This implementation is not exactly uniformly distributed but good enough + // for OpenMPT. + double rd_min = static_cast(std::random_device::min()); + double rd_max = static_cast(std::random_device::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) { - result = (result << rd_bits) | static_cast(rd()); - } else + tmp = (tmp * rd_size) + (static_cast((*prd)()) - 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(std::random_device::max()); + for(std::size_t entropy = 0; entropy < (sizeof(result_type) * 8); entropy += rd_bits) { - result = result | static_cast(rd()); + if(rd_bits < (sizeof(result_type) * 8)) + { + result = (result << rd_bits) | static_cast((*prd)()); + } else + { + result = result | static_cast((*prd)()); + } } } + } catch(const std::exception &) + { + rd_reliable = false; + init_fallback(); } - } catch(const std::exception &) + } else { rd_reliable = false; - init_fallback(); } if(!rd_reliable) { // std::random_device is unreliable diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h index 1442e73ef..84a7c029b 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptRandom.h @@ -44,50 +44,113 @@ namespace mpt #ifdef MPT_BUILD_FUZZER -static const uint32 FUZZER_RNG_SEED = 3141592653u; // pi +static constexpr uint32 FUZZER_RNG_SEED = 3141592653u; // pi #endif // MPT_BUILD_FUZZER namespace detail { -MPT_CONSTEXPR11_FUN int lower_bound_entropy_bits(unsigned int x) +MPT_CONSTEXPR14_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; + if(x >= 0xffffffffu) + { + return 32; + } else if(x >= 0x7fffffffu) + { + return 31; + } else if(x >= 0x3fffffffu) + { + return 30; + } else if(x >= 0x1fffffffu) + { + return 29; + } else if(x >= 0x0fffffffu) + { + return 28; + } else if(x >= 0x07ffffffu) + { + return 27; + } else if(x >= 0x03ffffffu) + { + return 26; + } else if(x >= 0x01ffffffu) + { + return 25; + } else if(x >= 0x00ffffffu) + { + return 24; + } else if(x >= 0x007fffffu) + { + return 23; + } else if(x >= 0x003fffffu) + { + return 22; + } else if(x >= 0x001fffffu) + { + return 21; + } else if(x >= 0x000fffffu) + { + return 20; + } else if(x >= 0x0007ffffu) + { + return 19; + } else if(x >= 0x0003ffffu) + { + return 18; + } else if(x >= 0x0001ffffu) + { + return 17; + } else if(x >= 0x0000ffffu) + { + return 16; + } else if(x >= 0x00007fffu) + { + return 15; + } else if(x >= 0x00003fffu) + { + return 14; + } else if(x >= 0x00001fffu) + { + return 13; + } else if(x >= 0x00000fffu) + { + return 12; + } else if(x >= 0x000007ffu) + { + return 11; + } else if(x >= 0x000003ffu) + { + return 10; + } else if(x >= 0x000001ffu) + { + return 9; + } else if(x >= 0x000000ffu) + { + return 8; + } else if(x >= 0x0000007fu) + { + return 7; + } else if(x >= 0x0000003fu) + { + return 6; + } else if(x >= 0x0000001fu) + { + return 5; + } else if(x >= 0x0000000fu) + { + return 4; + } else if(x >= 0x00000007u) + { + return 3; + } else if(x >= 0x00000003u) + { + return 2; + } else if(x >= 0x00000001u) + { + return 1; + } + return 0; } } @@ -111,15 +174,15 @@ template struct engine_traits template inline T random(Trng & rng) { - STATIC_ASSERT(std::numeric_limits::is_integer); + 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)) + if constexpr(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) + constexpr 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 { @@ -132,22 +195,22 @@ inline T random(Trng & rng) template inline T random(Trng & rng) { - STATIC_ASSERT(std::numeric_limits::is_integer); + 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) + 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)) + if constexpr(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) + constexpr 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)) + if constexpr(required_entropy_bits >= (sizeof(T) * 8)) { return static_cast(result); } else @@ -159,15 +222,15 @@ inline T random(Trng & rng) template inline T random(Trng & rng, std::size_t required_entropy_bits) { - STATIC_ASSERT(std::numeric_limits::is_integer); + 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) + 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)) + if constexpr(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) + constexpr 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 { @@ -183,20 +246,6 @@ inline T random(Trng & rng, std::size_t required_entropy_bits) } } -template struct float_traits { }; -template <> struct float_traits { - typedef uint32 mantissa_uint_type; - enum : int { mantissa_bits = 24 }; -}; -template <> struct float_traits { - typedef uint64 mantissa_uint_type; - enum : int { mantissa_bits = 53 }; -}; -template <> struct float_traits { - typedef uint64 mantissa_uint_type; - enum : int { mantissa_bits = 63 }; -}; - template struct uniform_real_distribution { @@ -213,9 +262,8 @@ public: 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; + const int mantissa_bits = std::numeric_limits::digits; + return ((b - a) * static_cast(mpt::random(rng)) / static_cast((static_cast(1u) << mantissa_bits))) + a; } }; @@ -223,7 +271,7 @@ public: template inline T random(Trng & rng, T min, T max) { - STATIC_ASSERT(!std::numeric_limits::is_integer); + static_assert(!std::numeric_limits::is_integer); typedef mpt::uniform_real_distribution dis_type; dis_type dis(min, max); return static_cast(dis(rng)); @@ -265,12 +313,12 @@ public: } static MPT_CONSTEXPR11_FUN result_type max() { - STATIC_ASSERT(((result_mask >> result_shift) << result_shift) == result_mask); + 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)); + static_assert(((static_cast(1) << result_bits_) - 1) == (result_mask >> result_shift)); return result_bits_; } inline result_type operator()() @@ -292,6 +340,59 @@ typedef lcg lcg_msvc typedef lcg lcg_c99; typedef lcg lcg_musl; +template +class modplug +{ +public: + typedef Tstate state_type; + typedef Tvalue result_type; +private: + state_type state1; + state_type state2; +public: + template + explicit inline modplug(Trng &rd) + : state1(mpt::random(rd)) + , state2(mpt::random(rd)) + { + } + explicit inline modplug(state_type seed1, state_type seed2) + : state1(seed1) + , state2(seed2) + { + } +public: + static MPT_CONSTEXPR11_FUN result_type min() + { + return static_cast(0); + } + static MPT_CONSTEXPR11_FUN result_type max() + { + return std::numeric_limits::max(); + } + static MPT_CONSTEXPR11_FUN int result_bits() + { + static_assert(std::is_integral::value); + static_assert(std::is_unsigned::value); + return std::numeric_limits::digits; + } + inline result_type operator()() + { + state_type a = state1; + state_type b = state2; + a = mpt::rotl(a, rol1); + a ^= x1; + a += x2 + (b * x3); + b += mpt::rotl(a, rol2) * x4; + state1 = a; + state2 = b; + result_type result = static_cast(b); + return result; + } +}; + +typedef modplug modplug_dither; + } // namespace rng @@ -325,7 +426,7 @@ public: { return RAND_MAX; } - static MPT_CONSTEXPR11_FUN int result_bits() + static MPT_CONSTEXPR14_FUN int result_bits() { return detail::lower_bound_entropy_bits(RAND_MAX); } @@ -348,7 +449,7 @@ class sane_random_device private: mpt::mutex m; std::string token; - std::random_device rd; + std::unique_ptr prd; bool rd_reliable; std::unique_ptr rd_fallback; public: @@ -410,7 +511,7 @@ template <> struct engine_traits { static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; } template static inline rng_type make(Trd & rd) { - std::unique_ptr> values = mpt::make_unique>(rd); + std::unique_ptr> values = std::make_unique>(rd); std::seed_seq seed(values->begin(), values->end()); return rng_type(seed); } @@ -423,7 +524,7 @@ template <> struct engine_traits { static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; } template static inline rng_type make(Trd & rd) { - std::unique_ptr> values = mpt::make_unique>(rd); + std::unique_ptr> values = std::make_unique>(rd); std::seed_seq seed(values->begin(), values->end()); return rng_type(seed); } @@ -534,7 +635,7 @@ public: } result_type operator()() { - MPT_LOCK_GUARD l(m); + mpt::lock_guard l(m); return mpt::random(rng); } }; @@ -609,7 +710,7 @@ public: public: typename engine_traits::result_type operator()() { - MPT_LOCK_GUARD l(m); + mpt::lock_guard l(m); return Trng::operator()(); } }; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptSpan.h b/Frameworks/OpenMPT/OpenMPT/common/mptSpan.h index 064c198e2..02ec0b313 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptSpan.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptSpan.h @@ -1,7 +1,7 @@ /* * mptSpan.h * --------- - * Purpose: Various useful utility functions. + * Purpose: C++20 span. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. @@ -15,8 +15,14 @@ #include "mptBaseTypes.h" +#if MPT_CXX_AT_LEAST(20) +#include +#include +#else // !C++20 #include #include +#include +#endif // C++20 @@ -29,6 +35,12 @@ namespace mpt { +#if MPT_CXX_AT_LEAST(20) + +using std::span; + +#else // !C++20 + // 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. @@ -40,18 +52,18 @@ class span public: - typedef std::size_t size_type; + using element_type = T; + using value_type = typename std::remove_cv::type; + using index_type = std::size_t; + using pointer = T *; + using const_pointer = const T *; + using reference = T &; + using const_reference = const T &; - typedef T value_type; - typedef T & reference; - typedef T * pointer; - typedef const T * const_pointer; - typedef const T & const_reference; + using iterator = pointer; + using const_iterator = const_pointer; - typedef pointer iterator; - typedef const_pointer const_iterator; - - typedef typename std::iterator_traits::difference_type difference_type; + using difference_type = typename std::iterator_traits::difference_type; private: @@ -60,22 +72,26 @@ private: public: - span() : m_beg(nullptr), m_end(nullptr) { } + span() noexcept : 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) { } + span(pointer data, index_type size) : m_beg(data), m_end(data + size) { } - template span(U (&arr)[N]) : m_beg(arr), m_end(arr + N) { } + template span(element_type (&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()) { } + template span(std::array &arr) : m_beg(arr.data()), m_end(arr.data() + arr.size()) { } + + template span(const std::array &arr) : m_beg(arr.data()), m_end(arr.data() + arr.size()) { } + + template span(Cont &cont) : m_beg(std::data(cont)), m_end(std::data(cont) + std::size(cont)) { } 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; } - + span & operator = (const span & other) noexcept = default; + iterator begin() const { return iterator(m_beg); } iterator end() const { return iterator(m_end); } @@ -84,24 +100,26 @@ public: operator bool () const noexcept { return m_beg != nullptr; } - reference operator[](size_type index) { return at(index); } - const_reference operator[](size_type index) const { return at(index); } + reference operator[](index_type index) { return at(index); } + const_reference operator[](index_type index) const { return at(index); } bool operator==(span const & other) const noexcept { return size() == other.size() && (m_beg == other.m_beg || std::equal(begin(), end(), other.begin())); } bool operator!=(span const & other) const noexcept { return !(*this == other); } - reference at(size_type index) { return m_beg[index]; } - const_reference at(size_type index) const { return m_beg[index]; } + reference at(index_type index) { return m_beg[index]; } + const_reference at(index_type index) const { return m_beg[index]; } pointer data() const noexcept { return m_beg; } bool empty() const noexcept { return size() == 0; } - size_type size() const noexcept { return static_cast(std::distance(m_beg, m_end)); } - size_type length() const noexcept { return size(); } + index_type size() const noexcept { return static_cast(std::distance(m_beg, m_end)); } + index_type length() const noexcept { return size(); } }; // class span +#endif // C++20 + 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); } diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptString.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptString.cpp index 823b6785b..b8a8563f3 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptString.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptString.cpp @@ -12,17 +12,13 @@ #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 +#include + #if defined(MODPLUG_TRACKER) #include #endif // MODPLUG_TRACKER @@ -35,11 +31,6 @@ #include #endif -#if defined(MPT_CHARSET_ICONV) -#include -#include -#endif - OPENMPT_NAMESPACE_BEGIN @@ -119,7 +110,7 @@ List of string types runtime conversion. Only use for string literals containing non-ascii characters (use MPT_USTRING otherwise). - * MPT_ULITERAL / MPT_UCHAR / MPT_UCHAR_TYPE (OpenMPT, libopenmpt) + * MPT_ULITERAL / MPT_UCHAR / mpt::uchar (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 @@ -207,7 +198,7 @@ if in libopenmpt else if performance critical inner loop if needs unicode support - T = MPT_UCHAR_TYPE* / MPT_ULITERAL + T = mpt::uchar* / MPT_ULITERAL else T = char*, document the encoding if not clear from context fi @@ -227,7 +218,7 @@ else T = CString else if constexpr context or global data - T = MPT_UCHAR_TYPE* / MPT_ULITERAL + T = mpt::uchar* / MPT_ULITERAL else T = mpt::ustring fi @@ -312,7 +303,7 @@ namespace mpt { namespace String { /* default 1:1 mapping -static const uint32 CharsetTableISO8859_1[256] = { +static constexpr char32_t 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, @@ -332,9 +323,7 @@ static const uint32 CharsetTableISO8859_1[256] = { }; */ -#if defined(MPT_CHARSET_CODECVTUTF8) || defined(MPT_CHARSET_INTERNAL) || defined(MPT_CHARSET_WIN32) - -static const uint32 CharsetTableISO8859_15[256] = { +static constexpr char32_t 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, @@ -353,7 +342,7 @@ static const uint32 CharsetTableISO8859_15[256] = { 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff }; -static const uint32 CharsetTableWindows1252[256] = { +static constexpr char32_t 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, @@ -372,7 +361,7 @@ static const uint32 CharsetTableWindows1252[256] = { 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff }; -static const uint32 CharsetTableCP437[256] = { +static constexpr char32_t 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, @@ -391,14 +380,12 @@ static const uint32 CharsetTableCP437[256] = { 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] = { +static constexpr char32_t 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, @@ -418,7 +405,7 @@ static const uint32 CharsetTableCP437AMS[256] = { }; // 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] = { +static constexpr char32_t 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, @@ -441,26 +428,106 @@ static const uint32 CharsetTableCP437AMS2[256] = { #if defined(MPT_COMPILER_QUIRK_NO_WCHAR) -typedef char32_t widechar; -typedef std::u32string widestring; +using widechar = char32_t; +using widestring = std::u32string; static constexpr widechar wide_default_replacement = 0xFFFD; #else // !MPT_COMPILER_QUIRK_NO_WCHAR -typedef wchar_t widechar; -typedef std::wstring widestring; +using widechar = wchar_t; +using widestring = std::wstring; static constexpr widechar wide_default_replacement = L'\uFFFD'; #endif // !MPT_COMPILER_QUIRK_NO_WCHAR -static widestring From8bit(const std::string &str, const uint32 (&table)[256], widechar replacement = wide_default_replacement) +#if MPT_OS_WINDOWS + +static bool TestCodePage(UINT cp) +{ + return IsValidCodePage(cp) ? true : false; +} + +static bool HasCharset(Charset charset) +{ + bool result = false; + switch(charset) + { +#if defined(MPT_ENABLE_CHARSET_LOCALE) + case Charset::Locale: result = true; break; +#endif + case Charset::UTF8: result = TestCodePage(CP_UTF8); break; + case Charset::ASCII: result = TestCodePage(20127); break; + case Charset::ISO8859_1: result = TestCodePage(28591); break; + case Charset::ISO8859_15: result = TestCodePage(28605); break; + case Charset::CP437: result = TestCodePage(437); break; + case Charset::Windows1252: result = TestCodePage(1252); break; + case Charset::CP437AMS: result = false; break; + case Charset::CP437AMS2: result = false; break; + } + return result; +} + +static UINT CharsetToCodepage(Charset charset) +{ + switch(charset) + { +#if defined(MPT_ENABLE_CHARSET_LOCALE) + case Charset::Locale: return CP_ACP; break; +#endif + case Charset::UTF8: return CP_UTF8; break; + case Charset::ASCII: return 20127; break; + case Charset::ISO8859_1: return 28591; break; + case Charset::ISO8859_15: return 28605; break; + case Charset::CP437: return 437; break; + case Charset::CP437AMS: return 437; break; // fallback, should not happen + case Charset::CP437AMS2: return 437; break; // fallback, should not happen + case Charset::Windows1252: return 1252; break; + } + return 0; +} + +template +static Tdststring EncodeCodepage(UINT codepage, const widestring &src) +{ + static_assert(sizeof(typename Tdststring::value_type) == sizeof(char)); + static_assert((std::is_same::value)); + Tdststring encoded_string; + int required_size = WideCharToMultiByte(codepage, 0, src.data(), mpt::saturate_cast(src.size()), nullptr, 0, nullptr, nullptr); + if(required_size > 0) + { + encoded_string.resize(required_size); + WideCharToMultiByte(codepage, 0, src.data(), mpt::saturate_cast(src.size()), reinterpret_cast(encoded_string.data()), required_size, nullptr, nullptr); + } + return encoded_string; +} + +template +static widestring DecodeCodepage(UINT codepage, const Tsrcstring &src) +{ + static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char)); + static_assert((std::is_same::value)); + widestring decoded_string; + int required_size = MultiByteToWideChar(codepage, 0, reinterpret_cast(src.data()), mpt::saturate_cast(src.size()), nullptr, 0); + if(required_size > 0) + { + decoded_string.resize(required_size); + MultiByteToWideChar(codepage, 0, reinterpret_cast(src.data()), mpt::saturate_cast(src.size()), decoded_string.data(), required_size); + } + return decoded_string; +} + +#endif // MPT_OS_WINDOWS + + +template +static widestring From8bit(const Tsrcstring &str, const char32_t (&table)[256], widechar replacement = wide_default_replacement) { widestring 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)) + std::size_t c = static_cast(static_cast(str[i])); + if(c < std::size(table)) { - res.push_back(static_cast(static_cast(table[c]))); + res.push_back(static_cast(table[c])); } else { res.push_back(replacement); @@ -469,22 +536,23 @@ static widestring From8bit(const std::string &str, const uint32 (&table)[256], w return res; } -static std::string To8bit(const widestring &str, const uint32 (&table)[256], char replacement = '?') +template +static Tdststring To8bit(const widestring &str, const char32_t (&table)[256], char replacement = '?') { - std::string res; + Tdststring res; res.reserve(str.length()); for(std::size_t i = 0; i < str.length(); ++i) { - uint32 c = str[i]; + char32_t c = static_cast(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) + for(std::size_t x = 0x20; x < std::size(table); ++x) { if(c == table[x]) { - res.push_back(static_cast(static_cast(x))); + res.push_back(static_cast(static_cast(x))); found = true; break; } @@ -492,11 +560,11 @@ static std::string To8bit(const widestring &str, const uint32 (&table)[256], cha if(!found) { // try control characters - for(std::size_t x = 0x00; x < mpt::size(table) && x < 0x20; ++x) + for(std::size_t x = 0x00; x < std::size(table) && x < 0x20; ++x) { if(c == table[x]) { - res.push_back(static_cast(static_cast(x))); + res.push_back(static_cast(static_cast(x))); found = true; break; } @@ -510,9 +578,8 @@ static std::string To8bit(const widestring &str, const uint32 (&table)[256], cha return res; } -#if defined(MPT_CHARSET_CODECVTUTF8) || defined(MPT_CHARSET_INTERNAL) || defined(MPT_CHARSET_WIN32) - -static widestring FromAscii(const std::string &str, widechar replacement = wide_default_replacement) +template +static widestring FromAscii(const Tsrcstring &str, widechar replacement = wide_default_replacement) { widestring res; res.reserve(str.length()); @@ -530,16 +597,17 @@ static widestring FromAscii(const std::string &str, widechar replacement = wide_ return res; } -static std::string ToAscii(const widestring &str, char replacement = '?') +template +static Tdststring ToAscii(const widestring &str, char replacement = '?') { - std::string res; + Tdststring res; res.reserve(str.length()); for(std::size_t i = 0; i < str.length(); ++i) { - uint32 c = str[i]; + char32_t c = static_cast(str[i]); if(c <= 0x7f) { - res.push_back(static_cast(static_cast(c))); + res.push_back(static_cast(static_cast(c))); } else { res.push_back(replacement); @@ -548,7 +616,8 @@ static std::string ToAscii(const widestring &str, char replacement = '?') return res; } -static widestring FromISO_8859_1(const std::string &str, widechar replacement = wide_default_replacement) +template +static widestring FromISO_8859_1(const Tsrcstring &str, widechar replacement = wide_default_replacement) { MPT_UNREFERENCED_PARAMETER(replacement); widestring res; @@ -561,16 +630,17 @@ static widestring FromISO_8859_1(const std::string &str, widechar replacement = return res; } -static std::string ToISO_8859_1(const widestring &str, char replacement = '?') +template +static Tdststring ToISO_8859_1(const widestring &str, char replacement = '?') { - std::string res; + Tdststring res; res.reserve(str.length()); for(std::size_t i = 0; i < str.length(); ++i) { - uint32 c = str[i]; + char32_t c = static_cast(str[i]); if(c <= 0xff) { - res.push_back(static_cast(static_cast(c))); + res.push_back(static_cast(static_cast(c))); } else { res.push_back(replacement); @@ -603,7 +673,7 @@ static std::wstring LocaleDecode(const std::string &str, const std::locale & loc return std::wstring(); } std::vector out; - typedef std::codecvt codecvt_type; + using codecvt_type = std::codecvt; std::mbstate_t state = std::mbstate_t(); const codecvt_type & facet = std::use_facet(locale); codecvt_type::result result = codecvt_type::partial; @@ -688,7 +758,7 @@ static std::string LocaleEncode(const std::wstring &str, const std::locale & loc return std::string(); } std::vector out; - typedef std::codecvt codecvt_type; + using codecvt_type = std::codecvt; std::mbstate_t state = std::mbstate_t(); const codecvt_type & facet = std::use_facet(locale); codecvt_type::result result = codecvt_type::partial; @@ -766,12 +836,15 @@ static std::string LocaleEncode(const std::wstring &str, const std::locale & loc return std::string(&(out[0]), out_next); } -static std::wstring FromLocale(const std::string &str, wchar_t replacement = L'\uFFFD') +static std::wstring FromLocaleCpp(const std::string &str, wchar_t replacement) { try { std::locale locale(""); // user locale return String::LocaleDecode(str, locale, replacement); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); } catch(...) { // nothing @@ -780,6 +853,9 @@ static std::wstring FromLocale(const std::string &str, wchar_t replacement = L'\ { std::locale locale; // current c++ locale return String::LocaleDecode(str, locale, replacement); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); } catch(...) { // nothing @@ -788,20 +864,26 @@ static std::wstring FromLocale(const std::string &str, wchar_t replacement = L'\ { std::locale locale = std::locale::classic(); // "C" locale return String::LocaleDecode(str, locale, replacement); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); } catch(...) { // nothing } MPT_ASSERT_NOTREACHED(); - return String::FromAscii(str, replacement); // fallback + return String::FromAscii(str, replacement); // fallback } -static std::string ToLocale(const std::wstring &str, char replacement = '?') +static std::string ToLocaleCpp(const std::wstring &str, char replacement) { try { std::locale locale(""); // user locale return String::LocaleEncode(str, locale, replacement); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); } catch(...) { // nothing @@ -810,6 +892,9 @@ static std::string ToLocale(const std::wstring &str, char replacement = '?') { std::locale locale; // current c++ locale return String::LocaleEncode(str, locale, replacement); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); } catch(...) { // nothing @@ -818,47 +903,55 @@ static std::string ToLocale(const std::wstring &str, char replacement = '?') { std::locale locale = std::locale::classic(); // "C" locale return String::LocaleEncode(str, locale, replacement); + } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) + { + MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e); } catch(...) { // nothing } MPT_ASSERT_NOTREACHED(); - return String::ToAscii(str, replacement); // fallback + return String::ToAscii(str, replacement); // fallback } + +template +static std::wstring FromLocale(const Tsrcstring &str, wchar_t replacement = L'\uFFFD') +{ + std::string tmp(str.begin(), str.end()); + return FromLocaleCpp(tmp, replacement); +} +template <> +std::wstring FromLocale(const std::string &str, wchar_t replacement) +{ + return FromLocaleCpp(str, replacement); +} + +template +static Tdststring ToLocale(const std::wstring &str, char replacement = '?') +{ + std::string tmp = ToLocaleCpp(str, replacement); + return Tdststring(tmp.begin(), tmp.end()); +} +template <> +std::string ToLocale(const std::wstring &str, char replacement) +{ + return ToLocaleCpp(str, replacement); +} + + #endif // MPT_ENABLE_CHARSET_LOCALE && !MPT_LOCALE_ASSUME_CHARSET -#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') +template +static widestring FromUTF8(const Tsrcstring &str, widechar replacement = wide_default_replacement) { - 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 widestring FromUTF8(const std::string &str, widechar replacement = wide_default_replacement) -{ - const std::string &in = str; + const Tsrcstring &in = str; widestring out; // state: std::size_t charsleft = 0; - uint32 ucs4 = 0; + char32_t ucs4 = 0; for ( uint8 c : in ) { @@ -893,16 +986,16 @@ static widestring FromUTF8(const std::string &str, widechar replacement = wide_d charsleft--; if ( charsleft == 0 ) { - MPT_CONSTANT_IF ( sizeof( widechar ) == 2 ) { + if constexpr ( sizeof( widechar ) == 2 ) { if ( ucs4 > 0x1fffff ) { out.push_back( replacement ); ucs4 = 0; charsleft = 0; } if ( ucs4 <= 0xffff ) { - out.push_back( (uint16)ucs4 ); + out.push_back( static_cast(ucs4) ); } else { - uint32 surrogate = ucs4 - 0x10000; + uint32 surrogate = static_cast(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 ); @@ -928,18 +1021,19 @@ static widestring FromUTF8(const std::string &str, widechar replacement = wide_d } -static std::string ToUTF8(const widestring &str, char replacement = '?') +template +static Tdststring ToUTF8(const widestring &str, char replacement = '?') { const widestring &in = str; - std::string out; + Tdststring out; for ( std::size_t i=0; i( wc ); if ( i + 1 < in.length() ) { // check for surrogate pair @@ -953,14 +1047,14 @@ static std::string ToUTF8(const widestring &str, char replacement = '?') ucs4 = ( static_cast(hi_sur) << 10 ) | ( static_cast(lo_sur) << 0 ); } else { // no surrogate pair - ucs4 = static_cast( c ); + ucs4 = static_cast( c ); } } else { // no surrogate possible - ucs4 = static_cast( c ); + ucs4 = static_cast( c ); } } else { - ucs4 = static_cast( wc ); + ucs4 = static_cast( wc ); } if ( ucs4 > 0x1fffff ) { @@ -995,6 +1089,8 @@ static std::string ToUTF8(const widestring &str, char replacement = '?') if ( charsleft == numchars ) { out.push_back( utf8[ charsleft - 1 ] | ( ((1< -static Tdststring EncodeImplFallback(Charset charset, const widestring &src); -#endif // !MPT_CHARSET_ICONV // templated on 8bit strings because of type-safe variants template static Tdststring EncodeImpl(Charset charset, const widestring &src) { - MPT_STATIC_ASSERT(sizeof(typename Tdststring::value_type) == sizeof(char)); - MPT_STATIC_ASSERT((std::is_same::value)); - 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(); - } - #if MPT_CXX_AT_LEAST(17) - Tdststring encoded_string(required_size, char()); - WideCharToMultiByte(codepage, 0, src.c_str(), -1, encoded_string.data(), required_size, nullptr, nullptr); - encoded_string.resize(encoded_string.size() - 1); // remove \0 - return encoded_string; - #else - 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()); + static_assert(sizeof(typename Tdststring::value_type) == sizeof(char)); + static_assert((std::is_same::value)); + #if defined(MPT_ENABLE_CHARSET_LOCALE) + #if defined(MPT_LOCALE_ASSUME_CHARSET) + if(charset == Charset::Locale) + { + charset = MPT_LOCALE_ASSUME_CHARSET; + } #endif - #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(widechar); - 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(widechar); - inbytesleft -= sizeof(widechar); - 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 -static Tdststring EncodeImplFallback(Charset charset, const widestring &src) -{ - std::string out; + #if MPT_OS_WINDOWS + if(HasCharset(charset)) + { + return EncodeCodepage(CharsetToCodepage(charset), src); + } + #endif + switch(charset) + { #if defined(MPT_ENABLE_CHARSET_LOCALE) #if defined(MPT_LOCALE_ASSUME_CHARSET) - if(charset == CharsetLocale) - { - charset = MPT_LOCALE_ASSUME_CHARSET; - } + case Charset::Locale: MPT_ASSERT_NOTREACHED(); break; + #else + case Charset::Locale: return String::ToLocale(src); break; #endif #endif - switch(charset) - { -#if defined(MPT_ENABLE_CHARSET_LOCALE) -#if defined(MPT_LOCALE_ASSUME_CHARSET) - case CharsetLocale: MPT_ASSERT_NOTREACHED(); break; -#else - case CharsetLocale: out = String::ToLocale(src); break; -#endif -#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()); + case Charset::UTF8: return String::ToUTF8(src); break; + case Charset::ASCII: return String::ToAscii(src); break; + case Charset::ISO8859_1: return String::ToISO_8859_1(src); break; + case Charset::ISO8859_15: return String::To8bit(src, CharsetTableISO8859_15); break; + case Charset::CP437: return String::To8bit(src, CharsetTableCP437); break; + case Charset::CP437AMS: return String::To8bit(src, CharsetTableCP437AMS); break; + case Charset::CP437AMS2: return String::To8bit(src, CharsetTableCP437AMS2); break; + case Charset::Windows1252: return String::To8bit(src, CharsetTableWindows1252); break; + } + return Tdststring(); } -#endif // !MPT_CHARSET_ICONV -#if !defined(MPT_CHARSET_ICONV) -template -static widestring DecodeImplFallback(Charset charset, const Tsrcstring &src); -#endif // !MPT_CHARSET_ICONV - // templated on 8bit strings because of type-safe variants template static widestring DecodeImpl(Charset charset, const Tsrcstring &src) { - MPT_STATIC_ASSERT(sizeof(typename Tsrcstring::value_type) == sizeof(char)); - MPT_STATIC_ASSERT((std::is_same::value)); - if(charset == CharsetCP437AMS || charset == CharsetCP437AMS2) - { - std::string in(src.begin(), src.end()); - widestring 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 widestring(); - } - #if MPT_CXX_AT_LEAST(17) - widestring decoded_string(required_size, widechar()); - MultiByteToWideChar(codepage, 0, reinterpret_cast(src.c_str()), -1, decoded_string.data(), required_size); - decoded_string.resize(decoded_string.size() - 1); // remove \0 - return decoded_string; - #else - std::vector decoded_string(required_size); - MultiByteToWideChar(codepage, 0, reinterpret_cast(src.c_str()), -1, decoded_string.data(), required_size); - return decoded_string.data(); - #endif - #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(widechar); - while(iconv(conv, &inbuf, &inbytesleft, &outbuf, &outbytesleft) == static_cast(-1)) - { - if(errno == EILSEQ || errno == EILSEQ) + static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char)); + static_assert((std::is_same::value)); + #if defined(MPT_ENABLE_CHARSET_LOCALE) + #if defined(MPT_LOCALE_ASSUME_CHARSET) + if(charset == Charset::Locale) { - inbuf++; - inbytesleft--; - for(std::size_t i = 0; i < sizeof(widechar); ++i) - { - outbuf[i] = 0; - } - widechar tmp = 0xfffd; - std::memcpy(outbuf, &tmp, sizeof(widechar)); - outbuf += sizeof(widechar); - outbytesleft -= sizeof(widechar); - iconv(conv, NULL, NULL, NULL, NULL); // reset state - } else - { - iconv_close(conv); - conv = iconv_t(); - return widestring(); + charset = MPT_LOCALE_ASSUME_CHARSET; } - } - iconv_close(conv); - conv = iconv_t(); - return wide_string.data(); - #else - return DecodeImplFallback(charset, src); + #endif #endif -} - -#if !defined(MPT_CHARSET_ICONV) -template -static widestring DecodeImplFallback(Charset charset, const Tsrcstring &src) -{ - std::string in(src.begin(), src.end()); - widestring out; + #if MPT_OS_WINDOWS + if(HasCharset(charset)) + { + return DecodeCodepage(CharsetToCodepage(charset), src); + } + #endif + switch(charset) + { #if defined(MPT_ENABLE_CHARSET_LOCALE) #if defined(MPT_LOCALE_ASSUME_CHARSET) - if(charset == CharsetLocale) - { - charset = MPT_LOCALE_ASSUME_CHARSET; - } + case Charset::Locale: MPT_ASSERT_NOTREACHED(); break; + #else + case Charset::Locale: return String::FromLocale(src); break; #endif #endif - switch(charset) - { -#if defined(MPT_ENABLE_CHARSET_LOCALE) -#if defined(MPT_LOCALE_ASSUME_CHARSET) - case CharsetLocale: MPT_ASSERT_NOTREACHED(); break; -#else - case CharsetLocale: out = String::FromLocale(in); break; -#endif -#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; + case Charset::UTF8: return String::FromUTF8(src); break; + case Charset::ASCII: return String::FromAscii(src); break; + case Charset::ISO8859_1: return String::FromISO_8859_1(src); break; + case Charset::ISO8859_15: return String::From8bit(src, CharsetTableISO8859_15); break; + case Charset::CP437: return String::From8bit(src, CharsetTableCP437); break; + case Charset::CP437AMS: return String::From8bit(src, CharsetTableCP437AMS); break; + case Charset::CP437AMS2: return String::From8bit(src, CharsetTableCP437AMS2); break; + case Charset::Windows1252: return String::From8bit(src, CharsetTableWindows1252); break; + } + return widestring(); } -#endif // !MPT_CHARSET_ICONV // templated on 8bit strings because of type-safe variants template static 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)); + 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 + return EncodeImpl(to, DecodeImpl(from, src)); } @@ -1457,7 +1209,7 @@ static Tdststring ConvertImpl(Charset to, Charset from, const Tsrcstring &src) bool IsUTF8(const std::string &str) { - return (str == String::EncodeImpl(mpt::CharsetUTF8, String::DecodeImpl(mpt::CharsetUTF8, str))); + return (str == String::EncodeImpl(mpt::Charset::UTF8, String::DecodeImpl(mpt::Charset::UTF8, str))); } @@ -1469,7 +1221,7 @@ std::wstring ToWide(Charset from, const std::string &str) #if defined(MPT_ENABLE_CHARSET_LOCALE) std::wstring ToWide(const mpt::lstring &str) { - return String::DecodeImpl(CharsetLocale, str); + return String::DecodeImpl(Charset::Locale, str); } #endif // MPT_ENABLE_CHARSET_LOCALE #endif @@ -1487,7 +1239,7 @@ std::string ToCharset(Charset to, Charset from, const std::string &str) #if defined(MPT_ENABLE_CHARSET_LOCALE) std::string ToCharset(Charset to, const mpt::lstring &str) { - return String::ConvertImpl(to, CharsetLocale, str); + return String::ConvertImpl(to, Charset::Locale, str); } #endif // MPT_ENABLE_CHARSET_LOCALE @@ -1495,12 +1247,12 @@ std::string ToCharset(Charset to, const mpt::lstring &str) #if MPT_WSTRING_CONVERT mpt::lstring ToLocale(const std::wstring &str) { - return String::EncodeImpl(CharsetLocale, str); + return String::EncodeImpl(Charset::Locale, str); } #endif mpt::lstring ToLocale(Charset from, const std::string &str) { - return String::ConvertImpl(CharsetLocale, from, str); + return String::ConvertImpl(Charset::Locale, from, str); } #endif // MPT_ENABLE_CHARSET_LOCALE @@ -1536,14 +1288,14 @@ mpt::winstring ToWin(const mpt::lstring &str) #endif // MPT_OS_WINDOWS -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) CString ToCString(const std::wstring &str) { #ifdef UNICODE return str.c_str(); #else - return ToCharset(CharsetLocale, str).c_str(); + return ToCharset(Charset::Locale, str).c_str(); #endif } CString ToCString(Charset from, const std::string &str) @@ -1551,7 +1303,7 @@ CString ToCString(Charset from, const std::string &str) #ifdef UNICODE return ToWide(from, str).c_str(); #else - return ToCharset(CharsetLocale, from, str).c_str(); + return ToCharset(Charset::Locale, from, str).c_str(); #endif } std::wstring ToWide(const CString &str) @@ -1559,7 +1311,7 @@ std::wstring ToWide(const CString &str) #ifdef UNICODE return str.GetString(); #else - return ToWide(CharsetLocale, str.GetString()); + return ToWide(Charset::Locale, str.GetString()); #endif } std::string ToCharset(Charset to, const CString &str) @@ -1567,7 +1319,7 @@ std::string ToCharset(Charset to, const CString &str) #ifdef UNICODE return ToCharset(to, str.GetString()); #else - return ToCharset(to, CharsetLocale, str.GetString()); + return ToCharset(to, Charset::Locale, str.GetString()); #endif } #if defined(MPT_ENABLE_CHARSET_LOCALE) @@ -1582,7 +1334,7 @@ CString ToCString(const mpt::lstring &str) mpt::lstring ToLocale(const CString &str) { #ifdef UNICODE - return String::EncodeImpl(CharsetLocale, str.GetString()); + return String::EncodeImpl(Charset::Locale, str.GetString()); #else return str.GetString(); #endif @@ -1595,7 +1347,7 @@ mpt::winstring ToWin(const CString &str) } #endif // MPT_OS_WINDOWS -#endif // MFC +#endif // MPT_WITH_MFC #if MPT_USTRING_MODE_WIDE @@ -1604,29 +1356,29 @@ mpt::winstring ToWin(const CString &str) #if MPT_WSTRING_CONVERT mpt::ustring ToUnicode(const std::wstring &str) { - return String::EncodeImpl(mpt::CharsetUTF8, str); + return String::EncodeImpl(mpt::Charset::UTF8, str); } #endif mpt::ustring ToUnicode(Charset from, const std::string &str) { - return String::ConvertImpl(mpt::CharsetUTF8, from, str); + return String::ConvertImpl(mpt::Charset::UTF8, from, str); } #if defined(MPT_ENABLE_CHARSET_LOCALE) mpt::ustring ToUnicode(const mpt::lstring &str) { - return String::ConvertImpl(mpt::CharsetUTF8, mpt::CharsetLocale, str); + return String::ConvertImpl(mpt::Charset::UTF8, mpt::Charset::Locale, str); } #endif // MPT_ENABLE_CHARSET_LOCALE -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) mpt::ustring ToUnicode(const CString &str) { #ifdef UNICODE - return String::EncodeImpl(mpt::CharsetUTF8, str.GetString()); + return String::EncodeImpl(mpt::Charset::UTF8, str.GetString()); #else // !UNICODE - return String::ConvertImpl(mpt::CharsetUTF8, mpt::CharsetLocale, str.GetString()); + return String::ConvertImpl(mpt::Charset::UTF8, mpt::Charset::Locale, str.GetString()); #endif // UNICODE } -#endif // MFC +#endif // MPT_WITH_MFC #endif // MPT_USTRING_MODE_WIDE #if MPT_USTRING_MODE_WIDE @@ -1635,39 +1387,39 @@ mpt::ustring ToUnicode(const CString &str) #if MPT_WSTRING_CONVERT std::wstring ToWide(const mpt::ustring &str) { - return String::DecodeImpl(mpt::CharsetUTF8, str); + return String::DecodeImpl(mpt::Charset::UTF8, str); } #endif std::string ToCharset(Charset to, const mpt::ustring &str) { - return String::ConvertImpl(to, mpt::CharsetUTF8, str); + return String::ConvertImpl(to, mpt::Charset::UTF8, str); } #if defined(MPT_ENABLE_CHARSET_LOCALE) mpt::lstring ToLocale(const mpt::ustring &str) { - return String::ConvertImpl(mpt::CharsetLocale, mpt::CharsetUTF8, str); + return String::ConvertImpl(mpt::Charset::Locale, mpt::Charset::UTF8, str); } #endif // MPT_ENABLE_CHARSET_LOCALE #if MPT_OS_WINDOWS mpt::winstring ToWin(const mpt::ustring &str) { #ifdef UNICODE - return String::DecodeImpl(mpt::CharsetUTF8, str); + return String::DecodeImpl(mpt::Charset::UTF8, str); #else - return String::ConvertImpl(mpt::CharsetLocale, mpt::CharsetUTF8, str); + return String::ConvertImpl(mpt::Charset::Locale, mpt::Charset::UTF8, str); #endif } #endif // MPT_OS_WINDOWS -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) CString ToCString(const mpt::ustring &str) { #ifdef UNICODE - return String::DecodeImpl(mpt::CharsetUTF8, str).c_str(); + return String::DecodeImpl(mpt::Charset::UTF8, str).c_str(); #else // !UNICODE - return String::ConvertImpl(mpt::CharsetLocale, mpt::CharsetUTF8, str).c_str(); + return String::ConvertImpl(mpt::Charset::Locale, mpt::Charset::UTF8, str).c_str(); #endif // UNICODE } -#endif // MFC +#endif // MPT_WITH_MFC #endif // MPT_USTRING_MODE_WIDE @@ -1680,27 +1432,27 @@ static mpt::Charset CharsetFromCodePage(uint16 codepage, mpt::Charset fallback, switch(codepage) { case 65001: - result = mpt::CharsetUTF8; + result = mpt::Charset::UTF8; if(isFallback) *isFallback = false; break; case 20127: - result = mpt::CharsetASCII; + result = mpt::Charset::ASCII; if(isFallback) *isFallback = false; break; case 28591: - result = mpt::CharsetISO8859_1; + result = mpt::Charset::ISO8859_1; if(isFallback) *isFallback = false; break; case 28605: - result = mpt::CharsetISO8859_15; + result = mpt::Charset::ISO8859_15; if(isFallback) *isFallback = false; break; case 437: - result = mpt::CharsetCP437; + result = mpt::Charset::CP437; if(isFallback) *isFallback = false; break; case 1252: - result = mpt::CharsetWindows1252; + result = mpt::Charset::Windows1252; if(isFallback) *isFallback = false; break; default: @@ -1711,46 +1463,18 @@ static mpt::Charset CharsetFromCodePage(uint16 codepage, mpt::Charset fallback, return result; } -#if MPT_OS_WINDOWS - -static bool TestCodePage(uint16 codepage) -{ - return IsValidCodePage(codepage) ? true : false; -} - -static mpt::ustring FromCodePageDirect(uint16 codepage, const std::string & src) -{ - int required_size = MultiByteToWideChar(codepage, 0, src.c_str(), -1, nullptr, 0); - if(required_size <= 0) - { - return mpt::ustring(); - } - #if MPT_CXX_AT_LEAST(17) - std::wstring decoded_string(required_size, wchar_t()); - MultiByteToWideChar(codepage, 0, src.c_str(), -1, decoded_string.data(), required_size); - decoded_string.resize(decoded_string.size() - 1); // remove \0 - return mpt::ToUnicode(decoded_string); - #else - std::vector decoded_string(required_size); - MultiByteToWideChar(codepage, 0, src.c_str(), -1, decoded_string.data(), required_size); - return mpt::ToUnicode(std::wstring(decoded_string.data())); - #endif -} - -#endif // MPT_OS_WINDOWS - mpt::ustring ToUnicode(uint16 codepage, mpt::Charset fallback, const std::string &str) { #if MPT_OS_WINDOWS mpt::ustring result; bool noCharsetMatch = true; - mpt::Charset fileCharset = mpt::CharsetFromCodePage(codepage, fallback, &noCharsetMatch); - if(noCharsetMatch && TestCodePage(codepage)) + mpt::Charset charset = mpt::CharsetFromCodePage(codepage, fallback, &noCharsetMatch); + if(noCharsetMatch && mpt::String::TestCodePage(codepage)) { - result = mpt::FromCodePageDirect(codepage, str); + result = mpt::ToUnicode(mpt::String::DecodeCodepage(codepage, str)); } else { - result = mpt::ToUnicode(fileCharset, str); + result = mpt::ToUnicode(charset, str); } return result; #else // !MPT_OS_WINDOWS @@ -1811,7 +1535,7 @@ int CompareNoCaseAscii(const char *a, const char *b, std::size_t n) return 0; } -int CompareNoCaseAscii(const std::string &a, const std::string &b) +int CompareNoCaseAscii(std::string_view a, std::string_view b) { for(std::size_t i = 0; i < std::min(a.length(), b.length()); ++i) { @@ -1832,11 +1556,17 @@ int CompareNoCaseAscii(const std::string &a, const std::string &b) return a.length() < b.length() ? -1 : 1; } +int CompareNoCaseAscii(const std::string &a, const std::string &b) +{ + return CompareNoCaseAscii(std::string_view(a), std::string_view(b)); +} + + #if defined(MODPLUG_TRACKER) mpt::ustring ToLowerCase(const mpt::ustring &s) { - #if defined(_MFC_VER) + #if defined(MPT_WITH_MFC) #if defined(UNICODE) CString tmp = mpt::ToCString(s); tmp.MakeLower(); @@ -1846,16 +1576,16 @@ mpt::ustring ToLowerCase(const mpt::ustring &s) tmp.MakeLower(); return mpt::ToUnicode(tmp.GetString()); #endif // UNICODE - #else // !_MFC_VER + #else // !MPT_WITH_MFC std::wstring ws = mpt::ToWide(s); std::transform(ws.begin(), ws.end(), ws.begin(), &std::towlower); return mpt::ToUnicode(ws); - #endif // _MFC_VER + #endif // MPT_WITH_MFC } mpt::ustring ToUpperCase(const mpt::ustring &s) { - #if defined(_MFC_VER) + #if defined(MPT_WITH_MFC) #if defined(UNICODE) CString tmp = mpt::ToCString(s); tmp.MakeUpper(); @@ -1865,11 +1595,11 @@ mpt::ustring ToUpperCase(const mpt::ustring &s) tmp.MakeUpper(); return mpt::ToUnicode(tmp.GetString()); #endif // UNICODE - #else // !_MFC_VER + #else // !MPT_WITH_MFC std::wstring ws = mpt::ToWide(s); std::transform(ws.begin(), ws.end(), ws.begin(), &std::towlower); return mpt::ToUnicode(ws); - #endif // _MFC_VER + #endif // MPT_WITH_MFC } #endif // MODPLUG_TRACKER diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptString.h b/Frameworks/OpenMPT/OpenMPT/common/mptString.h index 1e09e722e..e4a0a73db 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptString.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptString.h @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -47,9 +48,9 @@ template struct string_traits { - typedef Tstring string_type; - typedef typename string_type::size_type size_type; - typedef typename string_type::value_type char_type; + using string_type = Tstring; + using size_type = typename string_type::size_type; + using char_type = typename string_type::value_type; static inline std::size_t length(const string_type &str) { return str.length(); } @@ -68,14 +69,14 @@ struct string_traits }; -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) template <> struct string_traits { - typedef CString string_type; - typedef int size_type; - typedef typename CString::XCHAR char_type; + using string_type = CString; + using size_type = int; + using char_type = typename CString::XCHAR; static inline size_type length(const string_type &str) { return str.GetLength(); } @@ -94,7 +95,7 @@ struct string_traits } }; -#endif +#endif // MPT_WITH_MFC @@ -178,54 +179,64 @@ inline Tstring Replace(Tstring str, const Tstring2 &oldStr_, const Tstring3 &new } // namespace String -static inline std::size_t strnlen(const char *str, std::size_t n) +static inline std::string truncate(std::string str, std::size_t maxLen) { -#if MPT_COMPILER_MSVC - return ::strnlen(str, n); -#else - if(n >= std::numeric_limits::max()) + if(str.length() > maxLen) { - return std::strlen(str); + str.resize(maxLen); } - for(std::size_t i = 0; i < n; ++i) - { - if(str[i] == '\0') - { - return i; - } - } - return n; -#endif + return str; } -enum Charset { +enum class Charset { - CharsetUTF8, + UTF8, - CharsetASCII, // strictly 7-bit ASCII + ASCII, // strictly 7-bit ASCII - CharsetISO8859_1, - CharsetISO8859_15, + ISO8859_1, + ISO8859_15, - CharsetCP437, - CharsetCP437AMS, - CharsetCP437AMS2, + CP437, + CP437AMS, + CP437AMS2, - CharsetWindows1252, + Windows1252, #if defined(MPT_ENABLE_CHARSET_LOCALE) - CharsetLocale, // CP_ACP on windows, current C locale otherwise + Locale, // CP_ACP on windows, current C locale otherwise #endif // MPT_ENABLE_CHARSET_LOCALE }; + +// source code / preprocessor (i.e. # token) +inline constexpr Charset CharsetSource = Charset::ASCII; + +// debug log files +inline constexpr Charset CharsetLogfile = Charset::UTF8; + +// std::clog / std::cout / std::cerr +#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS && defined(MPT_ENABLE_CHARSET_LOCALE) +inline constexpr Charset CharsetStdIO = Charset::Locale; +#else +inline constexpr Charset CharsetStdIO = Charset::UTF8; +#endif + +// std::exception::what() +#if defined(MPT_ENABLE_CHARSET_LOCALE) +inline constexpr Charset CharsetException = Charset::Locale; +#else +inline constexpr Charset CharsetException = Charset::UTF8; +#endif + // Locale in tracker builds, UTF8 in non-locale-aware libopenmpt builds. #if defined(MPT_ENABLE_CHARSET_LOCALE) -const Charset CharsetLocaleOrUTF8 = CharsetLocale; +inline constexpr Charset CharsetLocaleOrUTF8 = Charset::Locale; #else -const Charset CharsetLocaleOrUTF8 = CharsetUTF8; +inline constexpr Charset CharsetLocaleOrUTF8 = Charset::UTF8; #endif @@ -267,30 +278,41 @@ struct charset_char_traits : std::char_traits { #if defined(MPT_ENABLE_CHARSET_LOCALE) -typedef MPT_ENCODED_STRING_TYPE(mpt::CharsetLocale) lstring; +using lstring = MPT_ENCODED_STRING_TYPE(mpt::Charset::Locale); #endif // MPT_ENABLE_CHARSET_LOCALE #if MPT_OS_WINDOWS template struct windows_char_traits { }; -template <> struct windows_char_traits { typedef mpt::lstring string_type; }; -template <> struct windows_char_traits { typedef std::wstring string_type; }; +template <> struct windows_char_traits { using string_type = mpt::lstring; }; +template <> struct windows_char_traits { using string_type = std::wstring; }; #ifdef UNICODE -typedef windows_char_traits::string_type tstring; +using tstring = windows_char_traits::string_type; #else -typedef windows_char_traits::string_type tstring; +using tstring = windows_char_traits::string_type; #endif -typedef mpt::tstring winstring; +using winstring = mpt::tstring; #endif // MPT_OS_WINDOWS #if MPT_ENABLE_U8STRING -typedef MPT_ENCODED_STRING_TYPE(mpt::CharsetUTF8) u8string; +#if MPT_CXX_AT_LEAST(20) + +using u8string = std::u8string; + +#define MPT_U8CHAR_TYPE char8_t +#define MPT_U8CHAR(x) u8 ## x +#define MPT_U8LITERAL(x) u8 ## x +#define MPT_U8STRING(x) std::u8string( u8 ## x ) + +#else // !C++20 + +using u8string = MPT_ENCODED_STRING_TYPE(mpt::Charset::UTF8); #define MPT_U8CHAR_TYPE char #define MPT_U8CHAR(x) x @@ -311,6 +333,8 @@ typedef MPT_ENCODED_STRING_TYPE(mpt::CharsetUTF8) u8string; // mpt::u8string is meant as an alternative implementaion to std::wstring // for implementing the unicode string type mpt::ustring. +#endif // C++20 + #endif // MPT_ENABLE_U8STRING @@ -367,7 +391,7 @@ mpt::winstring ToWin(const mpt::lstring &str); #endif // MPT_OS_WINDOWS -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) #if !(MPT_WSTRING_CONVERT) #error "MFC depends on MPT_WSTRING_CONVERT)" #endif @@ -394,7 +418,7 @@ mpt::winstring ToWin(const CString &str); std::wstring ToWide(const CString &str); std::string ToCharset(Charset to, const CString &str); -#endif // MFC +#endif // MPT_WITH_MFC // mpt::ustring @@ -423,8 +447,8 @@ std::string ToCharset(Charset to, const CString &str); #error "MPT_USTRING_MODE_WIDE and MPT_USTRING_MODE_UTF8 are mutually exclusive." #endif -typedef std::wstring ustring; -#define MPT_UCHAR_TYPE wchar_t +using ustring = std::wstring; +using uchar = wchar_t; #define MPT_UCHAR(x) L ## x #define MPT_ULITERAL(x) L ## x #define MPT_USTRING(x) std::wstring( L ## x ) @@ -436,11 +460,11 @@ typedef std::wstring ustring; #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 ) +using ustring = mpt::u8string; +using uchar = MPT_U8CHAR_TYPE; +#define MPT_UCHAR(x) MPT_U8CHAR( x ) +#define MPT_ULITERAL(x) MPT_U8LITERAL( x ) +#define MPT_USTRING(x) MPT_U8STRING( x ) #endif // MPT_USTRING_MODE_UTF8 @@ -459,7 +483,7 @@ static inline mpt::ustring ToUnicode(Charset from, const char * str) { return To #if defined(MPT_ENABLE_CHARSET_LOCALE) static inline mpt::ustring ToUnicode(const mpt::lstring &str) { return ToWide(str); } #endif // MPT_ENABLE_CHARSET_LOCALE -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) static inline mpt::ustring ToUnicode(const CString &str) { return ToWide(str); } #endif // MFC #else // !MPT_USTRING_MODE_WIDE @@ -473,9 +497,9 @@ static inline mpt::ustring ToUnicode(Charset from, const char * str) { return To #if defined(MPT_ENABLE_CHARSET_LOCALE) mpt::ustring ToUnicode(const mpt::lstring &str); #endif // MPT_ENABLE_CHARSET_LOCALE -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) mpt::ustring ToUnicode(const CString &str); -#endif // MFC +#endif // MPT_WITH_MFC #endif // MPT_USTRING_MODE_WIDE #if MPT_USTRING_MODE_WIDE @@ -494,9 +518,9 @@ mpt::lstring ToLocale(const mpt::ustring &str); #if MPT_OS_WINDOWS mpt::winstring ToWin(const mpt::ustring &str); #endif // MPT_OS_WINDOWS -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) CString ToCString(const mpt::ustring &str); -#endif // MFC +#endif // MPT_WITH_MFC #endif // MPT_USTRING_MODE_WIDE // The MPT_UTF8 allows specifying UTF8 char arrays. @@ -504,7 +528,7 @@ CString ToCString(const mpt::ustring &str); // 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 ) +#define MPT_UTF8(x) mpt::ToUnicode(mpt::Charset::UTF8, x ) @@ -522,8 +546,10 @@ 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(std::string_view a, std::string_view b); int CompareNoCaseAscii(const std::string &a, const std::string &b); + #if defined(MODPLUG_TRACKER) mpt::ustring ToLowerCase(const mpt::ustring &s); @@ -548,7 +574,7 @@ mpt::ustring ToUpperCase(const mpt::ustring &s); // 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 +template class BasicAnyString : public mpt::ustring { @@ -556,17 +582,17 @@ private: static mpt::ustring From8bit(const std::string &str) { - MPT_CONSTANT_IF(charset == mpt::CharsetUTF8) + if constexpr(charset == mpt::Charset::UTF8) { - return mpt::ToUnicode(mpt::CharsetUTF8, str); + return mpt::ToUnicode(mpt::Charset::UTF8, str); } else { // auto utf8 detection - MPT_CONSTANT_IF(tryUTF8) + if constexpr(tryUTF8) { if(mpt::IsUTF8(str)) { - return mpt::ToUnicode(mpt::CharsetUTF8, str); + return mpt::ToUnicode(mpt::Charset::UTF8, str); } else { return mpt::ToUnicode(charset, str); @@ -600,9 +626,9 @@ public: #endif // mfc -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) BasicAnyString(const CString &str) : mpt::ustring(mpt::ToUnicode(str)) { } -#endif +#endif // MPT_WITH_MFC // fallback for custom string types template BasicAnyString(const Tstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } @@ -632,9 +658,9 @@ public: #endif // mfc -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) AnyUnicodeString(const CString &str) : mpt::ustring(mpt::ToUnicode(str)) { } -#endif +#endif // MPT_WITH_MFC // fallback for custom string types template AnyUnicodeString(const Tstring &str) : mpt::ustring(mpt::ToUnicode(str)) { } @@ -645,32 +671,32 @@ public: // AnyString // Try to do the smartest auto-magic we can do. #if defined(MPT_ENABLE_CHARSET_LOCALE) -typedef BasicAnyString AnyString; +using AnyString = BasicAnyString; #elif MPT_OS_WINDOWS -typedef BasicAnyString AnyString; +using AnyString = BasicAnyString; #else -typedef BasicAnyString AnyString; +using AnyString = BasicAnyString; #endif // AnyStringLocale // char-based strings are assumed to be in locale encoding. #if defined(MPT_ENABLE_CHARSET_LOCALE) -typedef BasicAnyString AnyStringLocale; +using AnyStringLocale = BasicAnyString; #else -typedef BasicAnyString AnyStringLocale; +using AnyStringLocale = BasicAnyString; #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; +using AnyStringUTF8orLocale = BasicAnyString; #else -typedef BasicAnyString AnyStringUTF8orLocale; +using AnyStringUTF8orLocale = BasicAnyString; #endif // AnyStringUTF8 // char-based strings are assumed to be in UTF8. -typedef BasicAnyString AnyStringUTF8; +using AnyStringUTF8 = BasicAnyString; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptStringBuffer.h b/Frameworks/OpenMPT/OpenMPT/common/mptStringBuffer.h index f0b7e59f8..4c8681ac9 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptStringBuffer.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptStringBuffer.h @@ -13,6 +13,7 @@ #include "BuildSettings.h" +#include "mptMemory.h" #include "mptString.h" #include @@ -33,19 +34,19 @@ namespace String { - enum ReadWriteMode + enum ReadWriteMode : uint8 { // Reading / Writing: Standard null-terminated string handling. - nullTerminated, + nullTerminated = 1, // 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, + maybeNullTerminated = 2, // Reading: String may contain null characters anywhere. They should be treated as spaces. // Writing: A space-padded string is written. - spacePadded, + spacePadded = 3, // 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 + spacePaddedNull = 4, }; namespace detail @@ -75,7 +76,7 @@ public: : buf(buf) , size(size) { - MPT_STATIC_ASSERT(sizeof(Tchar) == sizeof(typename Tstring::value_type)); + static_assert(sizeof(Tchar) == sizeof(typename Tstring::value_type)); MPT_ASSERT(size > 0); } StringBufRefImpl(const StringBufRefImpl &) = delete; @@ -87,6 +88,10 @@ public: std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0 return Tstring(buf, buf + len); } + bool empty() const + { + return buf[0] == Tchar('\0'); + } StringBufRefImpl & operator = (const Tstring & str) { std::fill(buf, buf + size, Tchar('\0')); @@ -109,7 +114,7 @@ public: : buf(buf) , size(size) { - MPT_STATIC_ASSERT(sizeof(Tchar) == sizeof(typename Tstring::value_type)); + static_assert(sizeof(Tchar) == sizeof(typename Tstring::value_type)); MPT_ASSERT(size > 0); } StringBufRefImpl(const StringBufRefImpl &) = delete; @@ -121,6 +126,10 @@ public: std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0 return Tstring(buf, buf + len); } + bool empty() const + { + return buf[0] == Tchar('\0'); + } }; namespace String { @@ -169,6 +178,49 @@ inline StringBufRefImpl(0)> struct charbuf; + +template +struct charbuf(0)> +{ +public: + typedef char Tchar; + using char_type = Tchar; + using string_type = std::basic_string; + constexpr std::size_t static_length() const { return len; } +public: + Tchar buf[len]; +public: + charbuf() + { + Clear(buf); + } + charbuf(const charbuf &) = default; + charbuf(charbuf &&) = default; + charbuf & operator = (const charbuf &) = default; + charbuf & operator = (charbuf &&) = default; + const Tchar & operator[](std::size_t i) const { return buf[i]; } + std::string str() const { return static_cast(*this); } + operator string_type () const + { + return mpt::String::ReadAutoBuf(buf); + } + bool empty() const + { + return mpt::String::ReadAutoBuf(buf).empty(); + } + charbuf & operator = (const string_type & str) + { + mpt::String::WriteAutoBuf(buf) = str; + return *this; + } +public: + friend bool operator!=(const std::string & a, const charbuf & b) { return a != b.str(); } + friend bool operator!=(const charbuf & a, const std::string & b) { return a.str() != b; } + friend bool operator==(const std::string & a, const charbuf & b) { return a == b.str(); } + friend bool operator==(const charbuf & a, const std::string & b) { return a.str() == b; } +}; + template class StringModeBufRefImpl { @@ -184,7 +236,7 @@ public: , size(size) , mode(mode) { - MPT_STATIC_ASSERT(sizeof(Tchar) == 1); + static_assert(sizeof(Tchar) == 1); } StringModeBufRefImpl(const StringModeBufRefImpl &) = delete; StringModeBufRefImpl(StringModeBufRefImpl &&) = default; @@ -194,6 +246,10 @@ public: { return String::detail::ReadStringBuffer(mode, buf, size); } + bool empty() const + { + return String::detail::ReadStringBuffer(mode, buf, size).empty(); + } StringModeBufRefImpl & operator = (const std::string & str) { String::detail::WriteStringBuffer(mode, buf, size, str.data(), str.size()); @@ -216,7 +272,7 @@ public: , size(size) , mode(mode) { - MPT_STATIC_ASSERT(sizeof(Tchar) == 1); + static_assert(sizeof(Tchar) == 1); } StringModeBufRefImpl(const StringModeBufRefImpl &) = delete; StringModeBufRefImpl(StringModeBufRefImpl &&) = default; @@ -226,6 +282,10 @@ public: { return String::detail::ReadStringBuffer(mode, buf, size); } + bool empty() const + { + return String::detail::ReadStringBuffer(mode, buf, size).empty(); + } }; namespace String { @@ -251,6 +311,46 @@ inline StringModeBufRefImpl WriteBuf(String::ReadWriteMode mode, Tchar * } } // namespace String +template +struct charbuf +{ +public: + typedef char Tchar; + using char_type = Tchar; + using string_type = std::basic_string; +public: + Tchar buf[len]; +public: + charbuf() = default; + charbuf(const charbuf &) = default; + charbuf(charbuf &&) = default; + charbuf & operator = (const charbuf &) = default; + charbuf & operator = (charbuf &&) = default; + operator string_type () const + { + return mpt::String::ReadBuf(mode, buf); + } + bool empty() const + { + return mpt::String::ReadBuf(mode, buf).empty(); + } + charbuf & operator = (const string_type & str) + { + mpt::String::WriteBuf(mode, buf) = str; + return *this; + } +}; + + +// see MPT_BINARY_STRUCT +template +struct is_binary_safe> : public std::true_type { }; +template +struct is_binary_safe(0)>> : public std::false_type { }; +static_assert(sizeof(mpt::charbuf<7>) == 7); +static_assert(alignof(mpt::charbuf<7>) == 1); +static_assert(std::is_standard_layout>::value); + #ifdef MODPLUG_TRACKER @@ -279,7 +379,7 @@ inline StringBufRefImpl class CStringBufRefImpl @@ -363,7 +463,7 @@ inline CStringBufRefImpl WriteCStringBuf(Tchar * buf, std::size_t size) } } // namespace String -#endif // _MFC_VER +#endif // MPT_WITH_MFC #endif // MPT_OS_WINDOWS @@ -388,7 +488,7 @@ namespace String template void SetNullTerminator(char (&buffer)[size]) { - STATIC_ASSERT(size > 0); + static_assert(size > 0); buffer[size - 1] = 0; } @@ -403,7 +503,7 @@ namespace String template void SetNullTerminator(wchar_t (&buffer)[size]) { - STATIC_ASSERT(size > 0); + static_assert(size > 0); buffer[size - 1] = 0; } @@ -420,7 +520,7 @@ namespace String template void FixNullString(char (&buffer)[size]) { - STATIC_ASSERT(size > 0); + static_assert(size > 0); SetNullTerminator(buffer); size_t pos = 0; // Find the first null char. @@ -449,244 +549,6 @@ namespace String } - // 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(); - - try - { - dest = mpt::String::detail::ReadStringBuffer(mode, src, srcSize); - } 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 and counts the length that contains actual characters. - std::size_t lengthWithoutNullOrSpace = 0; - for(std::size_t pos = 0; pos < srcSize; ++pos) - { - char c = srcBuffer[pos]; - if(c != '\0' && c != ' ') - { - lengthWithoutNullOrSpace = pos + 1; - } - if(c == '\0') - { - c = ' '; - } - if(pos < destSize - 1) - { - destBuffer[pos] = c; - } - } - - std::size_t destLength = std::min(lengthWithoutNullOrSpace, destSize - 1); - - dst += destLength; - - } - - // 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); - - mpt::String::detail::WriteStringBuffer(mode, destBuffer, destSize, srcBuffer, srcSize); - - } - - // 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()); - } - - -#if MPT_GCC_AT_LEAST(8,1,0) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstringop-truncation" -#endif - - // 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_GCC_AT_LEAST(8,1,0) -#pragma GCC diagnostic pop -#endif - #if MPT_COMPILER_MSVC #pragma warning(pop) diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp index a8f92ae6d..76c6868ad 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.cpp @@ -10,7 +10,7 @@ #include "stdafx.h" #include "mptStringFormat.h" -#if MPT_CXX_AT_LEAST(17) && MPT_COMPILER_MSVC +#if MPT_COMPILER_MSVC #define MPT_FORMAT_CXX17_INT 1 #else #define MPT_FORMAT_CXX17_INT 0 @@ -59,7 +59,7 @@ template inline void SaneInsert(Tstream & s, const unsigned ch #if MPT_FORMAT_CXX17_INT #if MPT_WSTRING_FORMAT -std::wstring ToWideSimple(const std::string &nstr) +static std::wstring ToWideSimple(const std::string &nstr) { std::wstring wstr(nstr.size(), L'\0'); for(std::size_t i = 0; i < nstr.size(); ++i) @@ -148,17 +148,6 @@ static inline std::wstring ToWStringHelperFloat(const T & x) } #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 ToStringHelperInt(static_cast(x)); } std::string ToString(const signed char & x) { return ToStringHelperInt(x); } std::string ToString(const unsigned char & x) { return ToStringHelperInt(x); } @@ -174,19 +163,15 @@ std::string ToString(const float & x) { return ToStringHelperFloat(x); } std::string ToString(const double & x) { return ToStringHelperFloat(x); } std::string ToString(const long double & x) { return ToStringHelperFloat(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) +#if defined(MPT_WITH_MFC) mpt::ustring ToUString(const CString & x) { return mpt::ToUnicode(x); } -#endif +#endif // MPT_WITH_MFC #if MPT_USTRING_MODE_WIDE mpt::ustring ToUString(const bool & x) { return ToWStringHelperInt(static_cast(x)); } mpt::ustring ToUString(const signed char & x) { return ToWStringHelperInt(x); } @@ -204,32 +189,29 @@ mpt::ustring ToUString(const double & x) { return ToWStringHelperFloat(x); } mpt::ustring ToUString(const long double & x) { return ToWStringHelperFloat(x); } #endif #if MPT_USTRING_MODE_UTF8 -mpt::ustring ToUString(const bool & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(static_cast(x))); } -mpt::ustring ToUString(const signed char & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } -mpt::ustring ToUString(const unsigned char & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } -mpt::ustring ToUString(const signed short & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } -mpt::ustring ToUString(const unsigned short & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } -mpt::ustring ToUString(const signed int & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } -mpt::ustring ToUString(const unsigned int & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } -mpt::ustring ToUString(const signed long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } -mpt::ustring ToUString(const unsigned long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } -mpt::ustring ToUString(const signed long long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } -mpt::ustring ToUString(const unsigned long long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperInt(x)); } -mpt::ustring ToUString(const float & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperFloat(x)); } -mpt::ustring ToUString(const double & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperFloat(x)); } -mpt::ustring ToUString(const long double & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelperFloat(x)); } +mpt::ustring ToUString(const bool & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(static_cast(x))); } +mpt::ustring ToUString(const signed char & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned char & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const signed short & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned short & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const signed int & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned int & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const signed long & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned long & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const signed long long & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const unsigned long long & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); } +mpt::ustring ToUString(const float & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperFloat(x)); } +mpt::ustring ToUString(const double & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperFloat(x)); } +mpt::ustring ToUString(const long double & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperFloat(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) +#if defined(MPT_WITH_MFC) std::wstring ToWString(const CString & x) { return mpt::ToWide(x); } -#endif +#endif // MPT_WITH_MFC std::wstring ToWString(const bool & x) { return ToWStringHelperInt(static_cast(x)); } std::wstring ToWString(const signed char & x) { return ToWStringHelperInt(x); } std::wstring ToWString(const unsigned char & x) { return ToWStringHelperInt(x); } @@ -468,10 +450,6 @@ static inline std::wstring FormatValWHelperFloat(const T & x, const FormatSpec & #endif -std::string FormatVal(const char & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } -#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) -std::string FormatVal(const wchar_t & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } -#endif // !MPT_COMPILER_QUIRK_NO_WCHAR std::string FormatVal(const bool & x, const FormatSpec & f) { return FormatValHelperInt(static_cast(x), f); } std::string FormatVal(const signed char & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } std::string FormatVal(const unsigned char & x, const FormatSpec & f) { return FormatValHelperInt(x, f); } @@ -488,8 +466,6 @@ std::string FormatVal(const double & x, const FormatSpec & f) { return FormatVal std::string FormatVal(const long double & x, const FormatSpec & f) { return FormatValHelperFloat(x, f); } #if MPT_USTRING_MODE_WIDE -mpt::ustring FormatValU(const char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } -mpt::ustring FormatValU(const wchar_t & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } mpt::ustring FormatValU(const bool & x, const FormatSpec & f) { return FormatValWHelperInt(static_cast(x), f); } mpt::ustring FormatValU(const signed char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } @@ -506,31 +482,23 @@ mpt::ustring FormatValU(const double & x, const FormatSpec & f) { return FormatV mpt::ustring FormatValU(const long double & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); } #endif #if MPT_USTRING_MODE_UTF8 -mpt::ustring FormatValU(const char & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } -#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) -mpt::ustring FormatValU(const wchar_t & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } -#endif // !MPT_COMPILER_QUIRK_NO_WCHAR -mpt::ustring FormatValU(const bool & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(static_cast(x), f)); } -mpt::ustring FormatValU(const signed char & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } -mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } -mpt::ustring FormatValU(const signed short & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } -mpt::ustring FormatValU(const unsigned short & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } -mpt::ustring FormatValU(const signed int & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } -mpt::ustring FormatValU(const unsigned int & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } -mpt::ustring FormatValU(const signed long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } -mpt::ustring FormatValU(const unsigned long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } -mpt::ustring FormatValU(const signed long long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } -mpt::ustring FormatValU(const unsigned long long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperInt(x, f)); } -mpt::ustring FormatValU(const float & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperFloat(x, f)); } -mpt::ustring FormatValU(const double & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperFloat(x, f)); } -mpt::ustring FormatValU(const long double & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatValHelperFloat(x, f)); } +mpt::ustring FormatValU(const bool & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(static_cast(x), f)); } +mpt::ustring FormatValU(const signed char & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const signed short & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned short & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const signed int & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned int & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const signed long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const signed long long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const unsigned long long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); } +mpt::ustring FormatValU(const float & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperFloat(x, f)); } +mpt::ustring FormatValU(const double & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperFloat(x, f)); } +mpt::ustring FormatValU(const long double & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperFloat(x, f)); } #endif #if MPT_WSTRING_FORMAT -std::wstring FormatValW(const char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } -#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) -std::wstring FormatValW(const wchar_t & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } -#endif // !MPT_COMPILER_QUIRK_NO_WCHAR std::wstring FormatValW(const bool & x, const FormatSpec & f) { return FormatValWHelperInt(static_cast(x), f); } std::wstring FormatValW(const signed char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } std::wstring FormatValW(const unsigned char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); } @@ -548,145 +516,6 @@ std::wstring FormatValW(const long double & x, const FormatSpec & f) { return Fo #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 - ) -{ - typedef typename mpt::string_traits traits; - Tstring result; - const typename traits::size_type len = traits::length(format); - traits::reserve(result, len); - for(typename traits::size_type pos = 0; pos != len; ++pos) - { - typename traits::char_type c = format[pos]; - if(pos + 1 != len && c == typename traits::char_type('%')) - { - pos++; - c = format[pos]; - if(typename traits::char_type('1') <= c && c <= typename traits::char_type('9')) - { - const std::size_t n = c - typename traits::char_type('0'); - switch(n) - { - case 1: traits::append(result, x1); break; - case 2: traits::append(result, x2); break; - case 3: traits::append(result, x3); break; - case 4: traits::append(result, x4); break; - case 5: traits::append(result, x5); break; - case 6: traits::append(result, x6); break; - case 7: traits::append(result, x7); break; - case 8: traits::append(result, x8); break; - } - continue; - } else if(c != typename traits::char_type('%')) - { - traits::append(result, 1, typename traits::char_type('%')); - } - } - traits::append(result, 1, c); - } - return result; -} - -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(MPT_ENABLE_CHARSET_LOCALE) -mpt::lstring PrintImpl(const mpt::lstring & format - , const mpt::lstring & x1 - , const mpt::lstring & x2 - , const mpt::lstring & x3 - , const mpt::lstring & x4 - , const mpt::lstring & x5 - , const mpt::lstring & x6 - , const mpt::lstring & x7 - , const mpt::lstring & x8 - ) -{ - return PrintImplTemplate(format, x1,x2,x3,x4,x5,x6,x7,x8); -} -#endif // MPT_ENABLE_CHARSET_LOCALE - -#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 diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.h b/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.h index 2ddad8e71..2eb6a7c37 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptStringFormat.h @@ -71,25 +71,25 @@ namespace mpt // fallback to member function ToUString() #if MPT_USTRING_MODE_UTF8 -template auto ToString(const T & x) -> decltype(mpt::ToCharset(mpt::CharsetUTF8, x.ToUString())) { return mpt::ToCharset(mpt::CharsetUTF8, x.ToUString()); } +template auto ToString(const T & x) -> decltype(mpt::ToCharset(mpt::Charset::UTF8, x.ToUString())) { return mpt::ToCharset(mpt::Charset::UTF8, x.ToUString()); } #else template auto ToString(const T & x) -> decltype(mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x.ToUString())) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x.ToUString()); } #endif 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 +std::string ToString(const char &x) = delete; // 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 +std::string ToString(const std::wstring & x) = delete; // Unknown encoding. +std::string ToString(const wchar_t * const & x) = delete; // Unknown encoding. +std::string ToString(const wchar_t &x ) = delete; // 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 CString & x); +std::string ToString(const mpt::ustring & x) = delete; // Unknown encoding. #endif +#if defined(MPT_WITH_MFC) +std::string ToString(const CString & x) = delete; // unknown encoding +#endif // MPT_WITH_MFC std::string ToString(const bool & x); std::string ToString(const signed char & x); std::string ToString(const unsigned char & x); @@ -109,19 +109,19 @@ std::string ToString(const long double & x); 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 +mpt::ustring ToUString(const std::string & x) = delete; // Unknown encoding. +mpt::ustring ToUString(const char * const & x) = delete; // 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::ustring ToUString(const char & x) = delete; // 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 +mpt::ustring ToUString(const wchar_t & x) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead #endif -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) mpt::ustring ToUString(const CString & x); -#endif +#endif // MPT_WITH_MFC mpt::ustring ToUString(const bool & x); mpt::ustring ToUString(const signed char & x); mpt::ustring ToUString(const unsigned char & x); @@ -138,18 +138,18 @@ 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 +std::wstring ToWString(const std::string & x) = delete; // Unknown encoding. +std::wstring ToWString(const char * const & x) = delete; // 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. +std::wstring ToWString(const char & x) = delete; // 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 +std::wstring ToWString(const wchar_t & x) = delete; // 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) +#if defined(MPT_WITH_MFC) std::wstring ToWString(const CString & x); -#endif +#endif // MPT_WITH_MFC std::wstring ToWString(const bool & x); std::wstring ToWString(const signed char & x); std::wstring ToWString(const unsigned char & x); @@ -173,10 +173,10 @@ template struct ToLocaleHelper { mpt::lstring operator () (const T template <> struct ToLocaleHelper { mpt::lstring operator () (const mpt::lstring & v) { return v; } }; #endif // MPT_ENABLE_CHARSET_LOCALE -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) template struct ToCStringHelper { CString operator () (const T & v) { return mpt::ToCString(ToUString(v)); } }; template <> struct ToCStringHelper { CString operator () (const CString & v) { return v; } }; -#endif +#endif // MPT_WITH_MFC template struct ToStringTFunctor {}; template <> struct ToStringTFunctor { template inline std::string operator() (const T & x) { return ToString(x); } }; @@ -187,9 +187,9 @@ template <> struct ToStringTFunctor { template inline #if defined(MPT_ENABLE_CHARSET_LOCALE) template <> struct ToStringTFunctor { template inline mpt::lstring operator() (const T & x) { return mpt::ToLocaleHelper()(x); } }; #endif // MPT_ENABLE_CHARSET_LOCALE -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) template <> struct ToStringTFunctor { template inline CString operator() (const T & x) { return mpt::ToCStringHelper()(x); } }; -#endif +#endif // MPT_WITH_MFC template inline Tstring ToStringT(const T & x) { return ToStringTFunctor()(x); } @@ -215,13 +215,13 @@ enum FormatFlagsEnum typedef unsigned int FormatFlags; -STATIC_ASSERT(sizeof(FormatFlags) >= sizeof(fmt_base::FormatFlagsEnum)); +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 +std::string FormatVal(const char & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) -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 wchar_t & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead #endif // !MPT_COMPILER_QUIRK_NO_WCHAR std::string FormatVal(const bool & x, const FormatSpec & f); std::string FormatVal(const signed char & x, const FormatSpec & f); @@ -238,9 +238,9 @@ 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); -MPT_DEPRECATED mpt::ustring FormatValU(const char & x, const FormatSpec & f); // deprecated to catch potential API mis-use, use std::string(1, x) instead +mpt::ustring FormatValU(const char & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) -MPT_DEPRECATED mpt::ustring FormatValU(const wchar_t & x, const FormatSpec & f); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead +mpt::ustring FormatValU(const wchar_t & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead #endif // !MPT_COMPILER_QUIRK_NO_WCHAR mpt::ustring FormatValU(const bool & x, const FormatSpec & f); mpt::ustring FormatValU(const signed char & x, const FormatSpec & f); @@ -258,9 +258,9 @@ mpt::ustring FormatValU(const double & x, const FormatSpec & f); mpt::ustring FormatValU(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 +std::wstring FormatValW(const char & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead #if !defined(MPT_COMPILER_QUIRK_NO_WCHAR) -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 wchar_t & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead #endif // !MPT_COMPILER_QUIRK_NO_WCHAR std::wstring FormatValW(const bool & x, const FormatSpec & f); std::wstring FormatValW(const signed char & x, const FormatSpec & f); @@ -285,15 +285,15 @@ template <> struct FormatValTFunctor { template inlin template <> struct FormatValTFunctor { template inline std::wstring operator() (const T & x, const FormatSpec & f) { return FormatValW(x, f); } }; #endif #if defined(MPT_ENABLE_CHARSET_LOCALE) -template <> struct FormatValTFunctor { template inline mpt::lstring operator() (const T & x, const FormatSpec & f) { return mpt::ToLocale(mpt::CharsetLocale, FormatVal(x, f)); } }; +template <> struct FormatValTFunctor { template inline mpt::lstring operator() (const T & x, const FormatSpec & f) { return mpt::ToLocale(mpt::Charset::Locale, FormatVal(x, f)); } }; #endif // MPT_ENABLE_CHARSET_LOCALE -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC ) #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 +#else // !UNICODE +template <> struct FormatValTFunctor { template inline CString operator() (const T & x, const FormatSpec & f) { return mpt::ToCString(mpt::Charset::Locale, FormatVal(x, f)); } }; +#endif // UNICODE +#endif // MPT_WITH_MFC class FormatSpec @@ -353,6 +353,28 @@ public: MPT_CONSTEXPR14_FUN FormatSpec & Precision(int p) noexcept { return Prec(p); } }; +template +struct pointer_cast_helper +{ + Tdst operator()(const Tsrc & src) const { return src; } +}; +template +struct pointer_cast_helper +{ + Tdst operator()(const Tptr * const & src) const { return reinterpret_cast(src); } +}; +template +struct pointer_cast_helper +{ + Tdst operator()(const Tptr * const & src) const { return reinterpret_cast(src); } +}; + + +template +Tdst pointer_cast(const Tsrc & src) +{ + return pointer_cast_helper()(src); +} template struct fmtT : fmt_base @@ -373,98 +395,111 @@ static inline Tstring fmt(const T& x, const FormatSpec& f) template static inline Tstring dec(const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseDec().FillOff()); } template static inline Tstring dec0(const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseDec().FillNul().Width(width)); } template static inline Tstring dec(unsigned int g, char s, const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseDec().FillOff().Group(g).GroupSep(s)); } template static inline Tstring dec0(unsigned int g, char s, const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseDec().FillNul().Width(width).Group(g).GroupSep(s)); } template static inline Tstring hex(const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + 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); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillOff()); } template static inline Tstring hex0(const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + 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); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillNul().Width(width)); } template static inline Tstring hex(unsigned int g, char s, const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillOff().Group(g).GroupSep(s)); } template static inline Tstring HEX(unsigned int g, char s, const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillOff().Group(g).GroupSep(s)); } template static inline Tstring hex0(unsigned int g, char s, const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseLow().FillNul().Width(width).Group(g).GroupSep(s)); } template static inline Tstring HEX0(unsigned int g, char s, const T& x) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); return FormatValTFunctor()(x, FormatSpec().BaseHex().CaseUpp().FillNul().Width(width).Group(g).GroupSep(s)); } template static inline Tstring flt(const T& x, int precision = -1) { - STATIC_ASSERT(std::is_floating_point::value); + static_assert(std::is_floating_point::value); return FormatValTFunctor()(x, FormatSpec().NotaNrm().FillOff().Precision(precision)); } template static inline Tstring fix(const T& x, int precision = -1) { - STATIC_ASSERT(std::is_floating_point::value); + static_assert(std::is_floating_point::value); return FormatValTFunctor()(x, FormatSpec().NotaFix().FillOff().Precision(precision)); } template static inline Tstring sci(const T& x, int precision = -1) { - STATIC_ASSERT(std::is_floating_point::value); + static_assert(std::is_floating_point::value); return FormatValTFunctor()(x, FormatSpec().NotaSci().FillOff().Precision(precision)); } +template +static inline Tstring ptr(const T& x) +{ + static_assert(std::is_pointer::value || std::is_same::value || std::is_same::value, ""); + return hex0(pointer_cast(x)); +} +template +static inline Tstring PTR(const T& x) +{ + static_assert(std::is_pointer::value || std::is_same::value || std::is_same::value, ""); + return HEX0(pointer_cast(x)); +} + static inline Tstring pad_left(std::size_t width_, const Tstring &str) { typedef mpt::string_traits traits; @@ -514,9 +549,9 @@ typedef fmtT lfmt; #if MPT_OS_WINDOWS typedef fmtT tfmt; #endif -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) typedef fmtT cfmt; -#endif +#endif // MPT_WITH_MFC } // namespace mpt @@ -546,305 +581,117 @@ template <> struct to_string_type { typedef mpt::ustring type; #if defined(MPT_ENABLE_CHARSET_LOCALE) template <> struct to_string_type { typedef mpt::lstring type; }; #endif // MPT_ENABLE_CHARSET_LOCALE -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) template <> struct to_string_type { typedef CString type; }; -#endif +#endif // MPT_WITH_MFC 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(MPT_ENABLE_CHARSET_LOCALE) -mpt::lstring PrintImpl(const mpt::lstring & format - , const mpt::lstring & x1 = mpt::lstring() - , const mpt::lstring & x2 = mpt::lstring() - , const mpt::lstring & x3 = mpt::lstring() - , const mpt::lstring & x4 = mpt::lstring() - , const mpt::lstring & x5 = mpt::lstring() - , const mpt::lstring & x6 = mpt::lstring() - , const mpt::lstring & x7 = mpt::lstring() - , const mpt::lstring & x8 = mpt::lstring() - ); -#endif // MPT_ENABLE_CHARSET_LOCALE - -#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 +class 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 - ); -} +public: -template< - typename T1 -> -Tstring operator() ( - const T1& x1 - ) const { - return mpt::String::detail::PrintImpl(format - , ToStringTFunctor()(x1) - ); -} + typedef typename mpt::String::detail::to_string_type::type Tstring; -template< - typename T1 - , typename T2 -> -Tstring operator() ( - const T1& x1 - , const T2& x2 - ) const { - return mpt::String::detail::PrintImpl(format - , ToStringTFunctor()(x1) - , ToStringTFunctor()(x2) - ); -} +private: -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) - ); -} + Tstring format; -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) - ); -} +private: -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) - ); -} + MPT_NOINLINE Tstring do_format(mpt::span vals) const + { + typedef typename mpt::string_traits traits; + Tstring result; + const typename traits::size_type len = traits::length(format); + traits::reserve(result, len); + for(typename traits::size_type pos = 0; pos != len; ++pos) + { + typename traits::char_type c = format[pos]; + if(pos + 1 != len && c == typename traits::char_type('%')) + { + pos++; + c = format[pos]; + if(typename traits::char_type('1') <= c && c <= typename traits::char_type('9')) + { + const std::size_t n = c - typename traits::char_type('0') - 1; + if(n < std::size(vals)) + { + traits::append(result, vals[n]); + } + continue; + } else if(c != typename traits::char_type('%')) + { + traits::append(result, 1, typename traits::char_type('%')); + } + } + traits::append(result, 1, c); + } + return result; + } -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) - ); -} +public: -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) - ); -} + message_formatter(Tstring format_) + : format(std::move(format_)) + { + } -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) - ); -} +public: + + template + Tstring operator() (const Ts&... xs) const + { + const std::array vals{{ToStringTFunctor()(xs)...}}; + return do_format(mpt::as_span(vals)); + } }; // struct message_formatter template -message_formatter::type> format(const Tformat &format) +message_formatter::type> format(Tformat format) { typedef typename mpt::String::detail::to_string_type::type Tstring; - return message_formatter(Tstring(format)); + return message_formatter(Tstring(std::move(format))); } #if MPT_WSTRING_FORMAT -static inline message_formatter wformat(const std::wstring &format) +static inline message_formatter wformat(std::wstring format) { - return message_formatter(format); + return message_formatter(std::move(format)); } #endif -static inline message_formatter uformat(const mpt::ustring &format) +static inline message_formatter uformat(mpt::ustring format) { - return message_formatter(format); + return message_formatter(std::move(format)); } #if defined(MPT_ENABLE_CHARSET_LOCALE) -static inline message_formatter lformat(const mpt::lstring &format) +static inline message_formatter lformat(mpt::lstring format) { - return message_formatter(format); + return message_formatter(std::move(format)); } #endif // MPT_ENABLE_CHARSET_LOCALE #if MPT_OS_WINDOWS -static inline message_formatter tformat(const mpt::tstring &format) +static inline message_formatter tformat(mpt::tstring format) { - return message_formatter(format); + return message_formatter(std::move(format)); } #endif -#if defined(_MFC_VER) -static inline message_formatter cformat(const CString &format) +#if defined(MPT_WITH_MFC) +static inline message_formatter cformat(CString format) { - return message_formatter(format); + return message_formatter(std::move(format)); } -#endif +#endif // MPT_WITH_MFC } // namespace mpt diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptStringParse.h b/Frameworks/OpenMPT/OpenMPT/common/mptStringParse.h index 269a44f2f..488df8939 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptStringParse.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptStringParse.h @@ -80,19 +80,19 @@ template<> inline double ConvertStrTo(const std::wstring &str) { return ConvertS template<> inline long double ConvertStrTo(const std::wstring &str) { return ConvertStrToLongDouble(str); } #endif -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) 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)); + return ConvertStrTo(mpt::ToCharset(mpt::Charset::UTF8, str)); #else // !UNICODE - return ConvertStrTo(mpt::ToCharset(mpt::CharsetLocale, str)); + return ConvertStrTo(mpt::ToCharset(mpt::Charset::Locale, str)); #endif // UNICODE } -#endif // _MFC_VER +#endif // MPT_WITH_MFC template inline T ConvertStrTo(const char *str) @@ -123,7 +123,7 @@ inline T ConvertStrTo(const wchar_t *str) template inline T ConvertStrTo(const mpt::ustring &str) { - return ConvertStrTo(mpt::ToCharset(mpt::CharsetUTF8, str)); + return ConvertStrTo(mpt::ToCharset(mpt::Charset::UTF8, str)); } template<> inline mpt::ustring ConvertStrTo(const mpt::ustring &str) { return str; } #if MPT_WSTRING_CONVERT @@ -135,7 +135,7 @@ template<> inline std::wstring ConvertStrTo(const mpt::ustring &str) { return mp template inline T ConvertStrTo(const mpt::lstring &str) { - return ConvertStrTo(mpt::ToCharset(mpt::CharsetLocale, str)); + return ConvertStrTo(mpt::ToCharset(mpt::Charset::Locale, str)); } template<> inline mpt::lstring ConvertStrTo(const mpt::lstring &str) { return str; } #endif @@ -176,7 +176,7 @@ inline T Hex(const char *str) template inline T Hex(const std::wstring &str) { - return Hex(mpt::ToCharset(mpt::CharsetUTF8, str)); + return Hex(mpt::ToCharset(mpt::Charset::UTF8, str)); } template @@ -195,7 +195,7 @@ inline T Hex(const wchar_t *str) template inline T Hex(const mpt::ustring &str) { - return Hex(mpt::ToCharset(mpt::CharsetUTF8, str)); + return Hex(mpt::ToCharset(mpt::Charset::UTF8, str)); } #endif diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptThread.h b/Frameworks/OpenMPT/OpenMPT/common/mptThread.h index 86a479464..8b55c9d4d 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptThread.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptThread.h @@ -104,6 +104,18 @@ public: DWORD dummy = 0; // For Win9x threadHandle = CreateThread(NULL, 0, function, userData, 0, &dummy); } + + UnmanagedThread(UnmanagedThread &&) = default; + UnmanagedThread & operator=(UnmanagedThread &&) = default; + + UnmanagedThread(const UnmanagedThread &) = delete; + UnmanagedThread & operator=(const UnmanagedThread &) = delete; + + // unmanaged, user has to free resources + ~UnmanagedThread() + { + } + }; // Thread that operates on a member function diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp index 704d1f957..3b1440117 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptTime.cpp @@ -47,7 +47,7 @@ uint64 Now() mpt::ustring ToUString(uint64 time100ns) { - static const std::size_t bufsize = 256; + constexpr std::size_t bufsize = 256; mpt::ustring result; @@ -233,7 +233,7 @@ void MultimediaClock::SetPeriod(uint32 ms) { return; } - ms = mpt::clamp(ms, caps.wPeriodMin, caps.wPeriodMax); + ms = std::clamp(mpt::saturate_cast(ms), caps.wPeriodMin, caps.wPeriodMax); if(timeBeginPeriod(ms) != MMSYSERR_NOERROR) { return; diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp index 1dbbc3507..6a975db2a 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptUUID.cpp @@ -20,9 +20,9 @@ #if MPT_OS_WINDOWS #include #include -#if defined(MODPLUG_TRACKER) || !defined(NO_DMO) || MPT_OS_WINDOWS_WINRT +#if defined(MODPLUG_TRACKER) || defined(MPT_WITH_DMO) || MPT_OS_WINDOWS_WINRT #include -#endif // MODPLUG_TRACKER || !NO_DMO || MPT_OS_WINDOWS_WINRT +#endif // MODPLUG_TRACKER || MPT_WITH_DMO || MPT_OS_WINDOWS_WINRT #endif // MPT_OS_WINDOWS @@ -36,7 +36,7 @@ namespace Util { -#if defined(MODPLUG_TRACKER) || !defined(NO_DMO) +#if defined(MODPLUG_TRACKER) || defined(MPT_WITH_DMO) mpt::winstring CLSIDToString(CLSID clsid) @@ -298,7 +298,7 @@ bool IsValid(UUID uuid) } -#endif // MODPLUG_TRACKER || !NO_DMO +#endif // MODPLUG_TRACKER || MPT_WITH_DMO } // namespace Util @@ -348,7 +348,7 @@ mpt::UUID UUIDFromWin32(::UUID uuid) return result; } -#if defined(MODPLUG_TRACKER) || !defined(NO_DMO) +#if defined(MODPLUG_TRACKER) || defined(MPT_WITH_DMO) UUID::UUID(::UUID uuid) { @@ -360,7 +360,7 @@ UUID::operator ::UUID () const return UUIDToWin32(*this); } -#endif // MODPLUG_TRACKER || !NO_DMO +#endif // MODPLUG_TRACKER || MPT_WITH_DMO #endif // MPT_OS_WINDOWS @@ -535,6 +535,7 @@ UUID::UUID(GUIDms guid) UUID::operator UUIDbin() const { UUIDbin result; + Clear(result); result.Data1 = GetData1(); result.Data2 = GetData2(); result.Data3 = GetData3(); @@ -545,6 +546,7 @@ UUID::operator UUIDbin() const UUID::operator GUIDms() const { GUIDms result; + Clear(result); result.Data1 = GetData1(); result.Data2 = GetData2(); result.Data3 = GetData3(); diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptUUID.h b/Frameworks/OpenMPT/OpenMPT/common/mptUUID.h index 677fafce3..6892e9201 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptUUID.h +++ b/Frameworks/OpenMPT/OpenMPT/common/mptUUID.h @@ -15,11 +15,13 @@ #include "Endianness.h" +#include + #if MPT_OS_WINDOWS -#if defined(MODPLUG_TRACKER) || !defined(NO_DMO) +#if defined(MODPLUG_TRACKER) || defined(MPT_WITH_DMO) #include #include -#endif // MODPLUG_TRACKER || !NO_DMO +#endif // MODPLUG_TRACKER || MPT_WITH_DMO #endif // MPT_OS_WINDOWS @@ -30,7 +32,7 @@ OPENMPT_NAMESPACE_BEGIN namespace Util { -#if defined(MODPLUG_TRACKER) || !defined(NO_DMO) +#if defined(MODPLUG_TRACKER) || defined(MPT_WITH_DMO) // COM CLSID<->string conversion // A CLSID string is not necessarily a standard UUID string, @@ -56,7 +58,7 @@ GUID CreateGUID(); // Checks the UUID against the NULL UUID. Returns false if it is NULL, true otherwise. bool IsValid(UUID uuid); -#endif // MODPLUG_TRACKER || !NO_DMO +#endif // MODPLUG_TRACKER || MPT_WITH_DMO } // namespace Util @@ -96,6 +98,9 @@ public: MPT_CONSTEXPR11_FUN uint16 GetData2() const noexcept { return Data2; } MPT_CONSTEXPR11_FUN uint16 GetData3() const noexcept { return Data3; } MPT_CONSTEXPR11_FUN uint64 GetData4() const noexcept { return Data4; } +public: + MPT_CONSTEXPR11_FUN uint64 GetData64_1() const noexcept { return (static_cast(Data1) << 32) | (static_cast(Data2) << 16) | (static_cast(Data3) << 0); } + MPT_CONSTEXPR11_FUN uint64 GetData64_2() const noexcept { return Data4; } public: // xxxxxxxx-xxxx-Mmxx-Nnxx-xxxxxxxxxxxx // <--32-->-<16>-<16>-<-------64------> @@ -109,10 +114,10 @@ private: MPT_CONSTEXPR11_FUN uint8 Nn() const noexcept { return static_cast((Data4 >> 56) & 0xffu); } void MakeRFC4122(uint8 version) noexcept; public: -#if MPT_OS_WINDOWS && (defined(MODPLUG_TRACKER) || !defined(NO_DMO)) +#if MPT_OS_WINDOWS && (defined(MODPLUG_TRACKER) || defined(MPT_WITH_DMO)) explicit UUID(::UUID uuid); operator ::UUID () const; -#endif // MPT_OS_WINDOWS && (MODPLUG_TRACKER || !NO_DMO) +#endif // MPT_OS_WINDOWS && (MODPLUG_TRACKER || MPT_WITH_DMO) private: static MPT_CONSTEXPR11_FUN uint8 NibbleFromChar(char x) { @@ -120,7 +125,7 @@ private: ('0' <= x && x <= '9') ? static_cast(x - '0' + 0) : ('a' <= x && x <= 'z') ? static_cast(x - 'a' + 10) : ('A' <= x && x <= 'Z') ? static_cast(x - 'A' + 10) : - throw std::domain_error(""); + mpt::constexpr_throw(std::domain_error("")); } static MPT_CONSTEXPR11_FUN uint8 ByteFromHex(char x, char y) { @@ -159,7 +164,7 @@ public: | (static_cast(ParseHex16(str + 24)) << 32) | (static_cast(ParseHex32(str + 28)) << 0) ) - : throw std::domain_error(""); + : mpt::constexpr_throw(std::domain_error("")); } public: MPT_CONSTEXPR11_FUN UUID() noexcept : Data1(0), Data2(0), Data3(0), Data4(0) { } diff --git a/Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp b/Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp index adf1b23c6..12f62963a 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/mptWine.cpp @@ -247,11 +247,12 @@ ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet " + EscapePosixShell(dirPosix) + "exit" + "\n"; + wrapperscript += scriptFilenamePosixEscape + "\n"; + wrapperscript += "MPT_RESULT=$?" "\n"; + wrapperscript += "echo ${MPT_RESULT} > " + dirPosixEscape + "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"; + wrapperscript += "(" + scriptFilenamePosixEscape + "; echo $? >&4) 4>" + dirPosixEscape + "exit 1>" + dirPosixEscape + "out 2>" + dirPosixEscape + "err" "\n"; } else { - wrapperscript += std::string() + "(" + EscapePosixShell(scriptFilenamePosix) + "; echo $? >&4) 2>&1 4>" + EscapePosixShell(dirPosix) + "exit | tee " + EscapePosixShell(dirPosix) + "out" + "\n"; + wrapperscript += "(" + scriptFilenamePosixEscape + "; echo $? >&4) 2>&1 4>" + dirPosixEscape + "exit | tee " + dirPosixEscape + "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"; + wrapperscript += "echo done > " + dirPosixEscape + "done" "\n"; + cleanupscript += "rm " + dirPosixEscape + "done" "\n"; + cleanupscript += "rm " + dirPosixEscape + "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"; + cleanupscript += "rm " + dirPosixEscape + "out" "\n"; + cleanupscript += "rm " + dirPosixEscape + "err" "\n"; } else { - cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "out" + "\n"; + cleanupscript += "rm " + dirPosixEscape + "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"; + cleanupscript += "rm -r " + dirPosixEscape + "filetree" "\n"; + cleanupscript += "rm " + dirPosixEscape + "script.sh" "\n"; + cleanupscript += "rm " + dirPosixEscape + "wrapper.sh" "\n"; + cleanupscript += "rm " + dirPosixEscape + "wrapperstarter.sh" "\n"; + cleanupscript += "rm " + dirPosixEscape + "terminal.sh" "\n"; if(flags[ExecFlagAsync]) { wrapperscript += cleanupscript; @@ -346,10 +348,10 @@ ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet path = mpt::String::Split(mpt::ToUnicode(mpt::CharsetUTF8, file.first), U_("/")); + std::vector path = mpt::String::Split(mpt::ToUnicode(mpt::Charset::UTF8, file.first), U_("/")); mpt::PathString combinedPath = dirWindows + P_("filetree") + P_("\\"); if(path.size() > 1) { @@ -362,7 +364,7 @@ ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet/dev/null 1>/dev/null ; then" "\n"; - terminalscript += " chmod u+x " + EscapePosixShell(dirPosix) + "wrapperstarter.sh" "\n"; - terminalscript += " exec `command -v " + terminal + "` -e \"" + EscapePosixShell(dirPosix) + "wrapperstarter.sh\"" "\n"; + terminalscript += " chmod u+x " + dirPosixEscape + "wrapperstarter.sh" "\n"; + terminalscript += " exec `command -v " + terminal + "` -e \"" + dirPosixEscape + "wrapperstarter.sh\"" "\n"; terminalscript += "fi" "\n"; } @@ -430,21 +432,19 @@ ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet 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); + std::wstring unixcommandW = mpt::ToWide(mpt::Charset::UTF8, unixcommand); + std::wstring titleW = mpt::ToWide(mpt::Charset::UTF8, title); STARTUPINFOW startupInfo; MemsetZero(startupInfo); - startupInfo.lpTitle = &titleWv[0]; + startupInfo.lpTitle = titleW.data(); startupInfo.cb = sizeof(startupInfo); PROCESS_INFORMATION processInformation; MemsetZero(processInformation); @@ -454,15 +454,15 @@ ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet 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); + std::wstring unixcommandW = mpt::ToWide(mpt::Charset::UTF8, unixcommand); + std::wstring titleW = mpt::ToWide(mpt::Charset::UTF8, title); STARTUPINFOW startupInfo; MemsetZero(startupInfo); - startupInfo.lpTitle = &titleWv[0]; + startupInfo.lpTitle = titleW.data(); startupInfo.cb = sizeof(startupInfo); PROCESS_INFORMATION processInformation; MemsetZero(processInformation); @@ -527,21 +524,20 @@ ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet >() diff --git a/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.cpp b/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.cpp index 5ac868411..0c82bf4e1 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.cpp @@ -14,9 +14,9 @@ #include #include +#include #include "misc_util.h" -#include "mptBufferIO.h" OPENMPT_NAMESPACE_BEGIN @@ -26,11 +26,13 @@ namespace srlztn { -//#define SSB_LOGGING +#ifdef MPT_ALL_LOGGING +#define SSB_LOGGING +#endif #ifdef SSB_LOGGING -#define SSB_LOG(x) Log(x) +#define SSB_LOG(x) MPT_LOG(LogDebug, "serialization", x) #else #define SSB_LOG(x) MPT_DO { } MPT_WHILE_0 #endif @@ -73,7 +75,7 @@ static void WriteAdaptive12String(std::ostream& oStrm, const std::string& str) void WriteItemString(std::ostream& oStrm, const std::string &str) { - uint32 id = static_cast(std::min(str.size(), (uint32_max >> 4))) << 4; + uint32 id = static_cast(std::min(str.size(), static_cast((uint32_max >> 4)))) << 4; id |= 12; // 12 == 1100b Binarywrite(oStrm, id); id >>= 4; @@ -91,7 +93,7 @@ void ReadItemString(std::istream& iStrm, std::string& str, const DataSize) const uint8 nSizeBytes = (id & 12) >> 2; // 12 == 1100b if (nSizeBytes > 0) { - uint8 bytes = std::min(3, nSizeBytes); + uint8 bytes = std::min(uint8(3), nSizeBytes); uint8 v2 = 0; uint8 v3 = 0; uint8 v4 = 0; @@ -102,7 +104,7 @@ void ReadItemString(std::istream& iStrm, std::string& str, const DataSize) id |= (v2 << 8) | (v3 << 16) | (v4 << 24); } // Limit to 1 MB. - str.resize(std::min(id >> 4, 1000000)); + str.resize(std::min(id >> 4, uint32(1000000))); for(size_t i = 0; i < str.size(); i++) iStrm.read(&str[i], 1); @@ -116,7 +118,7 @@ mpt::ustring ID::AsString() const { if(IsPrintable()) { - return mpt::ToUnicode(mpt::CharsetISO8859_1, m_ID); + return mpt::ToUnicode(mpt::Charset::ISO8859_1, m_ID); } if(m_ID.length() > 8) { @@ -133,22 +135,22 @@ const char Ssb::s_EntryID[3] = {'2','2','8'}; #ifdef SSB_LOGGING -static const MPT_UCHAR_TYPE tstrWriteHeader[] = UL_("Write header with ID = %1\n"); -static const MPT_UCHAR_TYPE tstrWriteProgress[] = UL_("Wrote entry: {num, id, rpos, size} = {%1, %2, %3, %4}\n"); -static const MPT_UCHAR_TYPE tstrWritingMap[] = UL_("Writing map to rpos: %1\n"); -static const MPT_UCHAR_TYPE tstrMapEntryWrite[] = UL_("Writing map entry: id=%1, rpos=%2, size=%3\n"); -static const MPT_UCHAR_TYPE strWriteNote[] = UL_("Write note: "); -static const MPT_UCHAR_TYPE tstrEndOfStream[] = UL_("End of stream(rpos): %1\n"); +static const mpt::uchar tstrWriteHeader[] = UL_("Write header with ID = %1\n"); +static const mpt::uchar tstrWriteProgress[] = UL_("Wrote entry: {num, id, rpos, size} = {%1, %2, %3, %4}\n"); +static const mpt::uchar tstrWritingMap[] = UL_("Writing map to rpos: %1\n"); +static const mpt::uchar tstrMapEntryWrite[] = UL_("Writing map entry: id=%1, rpos=%2, size=%3\n"); +static const mpt::uchar strWriteNote[] = UL_("Write note: "); +static const mpt::uchar tstrEndOfStream[] = UL_("End of stream(rpos): %1\n"); -static const MPT_UCHAR_TYPE tstrReadingHeader[] = UL_("Read header with expected ID = %1\n"); -static const MPT_UCHAR_TYPE strNoMapInFile[] = UL_("No map in the file.\n"); -static const MPT_UCHAR_TYPE strIdMismatch[] = UL_("ID mismatch, terminating read.\n"); -static const MPT_UCHAR_TYPE strIdMatch[] = UL_("ID match, continuing reading.\n"); -static const MPT_UCHAR_TYPE tstrReadingMap[] = UL_("Reading map from rpos: %1\n"); -static const MPT_UCHAR_TYPE tstrEndOfMap[] = UL_("End of map(rpos): %1\n"); -static const MPT_UCHAR_TYPE tstrReadProgress[] = UL_("Read entry: {num, id, rpos, size, desc} = {%1, %2, %3, %4, %5}\n"); -static const MPT_UCHAR_TYPE tstrNoEntryFound[] = UL_("No entry with id %1 found.\n"); -static const MPT_UCHAR_TYPE strReadNote[] = UL_("Read note: "); +static const mpt::uchar tstrReadingHeader[] = UL_("Read header with expected ID = %1\n"); +static const mpt::uchar strNoMapInFile[] = UL_("No map in the file.\n"); +static const mpt::uchar strIdMismatch[] = UL_("ID mismatch, terminating read.\n"); +static const mpt::uchar strIdMatch[] = UL_("ID match, continuing reading.\n"); +static const mpt::uchar tstrReadingMap[] = UL_("Reading map from rpos: %1\n"); +static const mpt::uchar tstrEndOfMap[] = UL_("End of map(rpos): %1\n"); +static const mpt::uchar tstrReadProgress[] = UL_("Read entry: {num, id, rpos, size, desc} = {%1, %2, %3, %4, %5}\n"); +static const mpt::uchar tstrNoEntryFound[] = UL_("No entry with id %1 found.\n"); +static const mpt::uchar strReadNote[] = UL_("Read note: "); #endif @@ -246,7 +248,7 @@ void SsbWrite::WriteMapItem(const ID &id, rposDataStart, nDatasize)); - mpt::ostringstream mapStream; + std::ostringstream mapStream; if(m_nIdbytes > 0) { @@ -386,8 +388,11 @@ void SsbWrite::OnWroteItem(const ID &id, const Postype& posBeforeWrite) { const Offtype nRawEntrySize = oStrm.tellp() - posBeforeWrite; - if (nRawEntrySize < 0 || static_cast(nRawEntrySize) > std::numeric_limits::max()) - { AddWriteNote(SNW_INSUFFICIENT_DATASIZETYPE); return; } + MPT_MAYBE_CONSTANT_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; } @@ -555,8 +560,11 @@ void SsbRead::BeginRead(const ID &id, const uint64& nVersion) const Offtype rawEndOfHdrData = iStrm.tellg() - m_posStart; - if (rawEndOfHdrData < 0 || static_cast(rawEndOfHdrData) > std::numeric_limits::max()) - { AddReadNote(SNR_INSUFFICIENT_RPOSTYPE); return; } + MPT_MAYBE_CONSTANT_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; @@ -718,7 +726,7 @@ void SsbWrite::FinishWrite() } // Seek to end. - oStrm.seekp(std::max(posMapEnd, posDataEnd)); + oStrm.seekp(std::max(posMapEnd, posDataEnd)); SSB_LOG(mpt::format(mpt::ustring(tstrEndOfStream))(oStrm.tellp() - m_posStart)); } diff --git a/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.h b/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.h index 51984f946..ee8f58b51 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.h +++ b/Frameworks/OpenMPT/OpenMPT/common/serialization_utils.h @@ -180,7 +180,7 @@ template <> inline void Binaryread(std::istream& iStrm, float& data, const Offtype bytecount) { typedef IEEE754binary32LE T; - mpt::byte bytes[sizeof(T)]; + std::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, @@ -192,7 +192,7 @@ template <> inline void Binaryread(std::istream& iStrm, double& data, const Offtype bytecount) { typedef IEEE754binary64LE T; - mpt::byte bytes[sizeof(T)]; + std::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, @@ -234,7 +234,7 @@ public: template static ID FromInt(const T &val) { - STATIC_ASSERT(std::numeric_limits::is_integer); + static_assert(std::numeric_limits::is_integer); typename mpt::make_le::type valle; valle = val; return ID(std::string(mpt::byte_cast(mpt::as_raw_memory(valle).data()), mpt::byte_cast(mpt::as_raw_memory(valle).data() + sizeof(valle)))); diff --git a/Frameworks/OpenMPT/OpenMPT/common/stdafx.h b/Frameworks/OpenMPT/OpenMPT/common/stdafx.h index a6a36a56c..e1210fcef 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/stdafx.h +++ b/Frameworks/OpenMPT/OpenMPT/common/stdafx.h @@ -18,9 +18,7 @@ #if defined(MODPLUG_TRACKER) -#if MPT_OS_WINDOWS - -#if !defined(MPT_BUILD_WINESUPPORT) +#if defined(MPT_WITH_MFC) // cppcheck-suppress missingInclude #include // MFC core @@ -41,7 +39,9 @@ // cppcheck-suppress missingInclude #include -#endif // !MPT_BUILD_WINESUPPORT +#endif // MPT_WITH_MFC + +#if MPT_OS_WINDOWS #include #include @@ -61,7 +61,9 @@ // this will be available everywhere #include "../common/mptBaseMacros.h" +// // +// // // @@ -97,23 +99,34 @@ // "mptBaseTypes.h" // "mptSpan.h" // +// // #include "../common/mptAlloc.h" // "mptBaseMacros.h" // "mptMemory.h" // "mptSpan.h" +// // -// +// // #include "../common/mptString.h" // // // +// // // +#include "../common/mptStringBuffer.h" + +#include "../common/mptOSError.h" +// "mptException.h" +// "mptString.h" +// +// + #include "../common/mptExceptionText.h" // "mptException.h" // "mptString.h" @@ -124,6 +137,7 @@ #include "../common/mptPathString.h" #include "../common/Logging.h" +// #include "../common/misc_util.h" diff --git a/Frameworks/OpenMPT/OpenMPT/common/version.cpp b/Frameworks/OpenMPT/OpenMPT/common/version.cpp index 60ad87c54..40efdaf40 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/version.cpp +++ b/Frameworks/OpenMPT/OpenMPT/common/version.cpp @@ -23,6 +23,13 @@ OPENMPT_NAMESPACE_BEGIN +#define MPT_MAKE_VERSION_NUMERIC_HELPER(prefix,v0,v1,v2,v3) Version( prefix ## v0 , prefix ## v1 , prefix ## v2 , prefix ## v3 ) +#define MPT_MAKE_VERSION_NUMERIC(v0,v1,v2,v3) MPT_MAKE_VERSION_NUMERIC_HELPER(0x, v0, v1, v2, v3) + +#define MPT_VERSION_CURRENT MPT_MAKE_VERSION_NUMERIC(VER_MAJORMAJOR,VER_MAJOR,VER_MINOR,VER_MINORMINOR) + + + static_assert((MPT_VERSION_CURRENT.GetRawVersion() & 0xffffu) != 0x0000u, "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)."); @@ -80,10 +87,10 @@ bool Version::IsTestVersion() const noexcept { return ( // Legacy - (*this > MAKE_VERSION_NUMERIC(1,17,02,54) && *this < MAKE_VERSION_NUMERIC(1,18,02,00) && *this != MAKE_VERSION_NUMERIC(1,18,00,00)) + (*this > MPT_V("1.17.02.54") && *this < MPT_V("1.18.02.00") && *this != MPT_V("1.18.00.00")) || // Test builds have non-zero VER_MINORMINOR - (*this > MAKE_VERSION_NUMERIC(1,18,02,00) && ((m_Version & 0xFFFFFF00u) != m_Version)) + (*this > MPT_V("1.18.02.00") && ((m_Version & 0xFFFFFF00u) != m_Version)) ); } @@ -94,7 +101,7 @@ namespace Source { static mpt::ustring GetUrl() { #ifdef OPENMPT_VERSION_URL - return mpt::ToUnicode(mpt::CharsetASCII, OPENMPT_VERSION_URL); + return mpt::ToUnicode(mpt::Charset::ASCII, OPENMPT_VERSION_URL); #else return mpt::ustring(); #endif @@ -132,15 +139,7 @@ static int GetRevision() } 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 - #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 + MPT_WARNING_STATEMENT("SVN revision unknown. Please check your build system."); return 0; #endif } @@ -209,7 +208,7 @@ static bool IsPackage() static mpt::ustring GetSourceDate() { #if defined(OPENMPT_VERSION_DATE) - return mpt::ToUnicode(mpt::CharsetASCII, OPENMPT_VERSION_DATE); + return mpt::ToUnicode(mpt::Charset::ASCII, OPENMPT_VERSION_DATE); #else return mpt::ustring(); #endif @@ -286,9 +285,9 @@ mpt::ustring GetBuildDateString() mpt::ustring result; #ifdef MODPLUG_TRACKER #if defined(OPENMPT_BUILD_DATE) - result = mpt::ToUnicode(mpt::CharsetASCII, OPENMPT_BUILD_DATE ); + result = mpt::ToUnicode(mpt::Charset::ASCII, OPENMPT_BUILD_DATE ); #else - result = mpt::ToUnicode(mpt::CharsetASCII, __DATE__ " " __TIME__ ); + result = mpt::ToUnicode(mpt::Charset::ASCII, __DATE__ " " __TIME__ ); #endif #else // !MODPLUG_TRACKER result = SourceInfo::Current().Date(); @@ -317,18 +316,6 @@ mpt::ustring GetBuildFeaturesString() mpt::ustring retval; #ifdef LIBOPENMPT_BUILD retval = UL_("") - #if defined(MPT_CHARSET_WIN32) - UL_(" +WINAPI") - #endif - #if defined(MPT_CHARSET_ICONV) - UL_(" +ICONV") - #endif - #if defined(MPT_CHARSET_CODECVTUTF8) - UL_(" +CODECVTUTF8") - #endif - #if defined(MPT_CHARSET_INTERNAL) - UL_(" +INTERNALCHARSETS") - #endif #if defined(MPT_WITH_ZLIB) UL_(" +ZLIB") #endif @@ -364,13 +351,13 @@ mpt::ustring GetBuildFeaturesString() #else UL_(" -PLUGINS") #endif - #if !defined(NO_DMO) + #if defined(MPT_WITH_DMO) UL_(" +DMO") #endif ; #endif #ifdef MODPLUG_TRACKER - MPT_CONSTANT_IF(mpt::arch_bits == 64) + if constexpr(mpt::arch_bits == 64) { if (true && (mpt::Windows::Version::GetMinimumKernelLevel() <= mpt::Windows::Version::WinXP64) @@ -378,7 +365,7 @@ mpt::ustring GetBuildFeaturesString() ) { retval += UL_(" WIN64OLD"); } - } else MPT_CONSTANT_IF(mpt::arch_bits == 32) + } else if constexpr(mpt::arch_bits == 32) { if (true && (mpt::Windows::Version::GetMinimumKernelLevel() <= mpt::Windows::Version::WinXP) @@ -396,7 +383,7 @@ mpt::ustring GetBuildFeaturesString() #ifdef NO_VST UL_(" NO_VST") #endif - #ifdef NO_DMO + #ifndef MPT_WITH_DMO UL_(" NO_DMO") #endif #ifdef NO_PLUGINS @@ -588,19 +575,19 @@ mpt::ustring GetURL(Build::Url key) mpt::ustring GetFullCreditsString() { - return mpt::ToUnicode(mpt::CharsetUTF8, + return mpt::ToUnicode(mpt::Charset::UTF8, #ifdef MODPLUG_TRACKER "OpenMPT / ModPlug Tracker\n" #else "libopenmpt (based on OpenMPT / ModPlug Tracker)\n" #endif "\n" - "Copyright \xC2\xA9 2004-2019 Contributors\n" + "Copyright \xC2\xA9 2004-2020 Contributors\n" "Copyright \xC2\xA9 1997-2003 Olivier Lapicque\n" "\n" "Contributors:\n" - "Johannes Schultz (2008-2019)\n" - "J\xC3\xB6rn Heusipp (2012-2019)\n" + "Johannes Schultz (2008-2020)\n" + "J\xC3\xB6rn Heusipp (2012-2020)\n" "Ahti Lepp\xC3\xA4nen (2005-2011)\n" "Robin Fernandes (2004-2007)\n" "Sergiy Pylypenko (2007)\n" @@ -610,6 +597,7 @@ mpt::ustring GetFullCreditsString() "\n" "Additional patch submitters:\n" "coda (https://coda.s3m.us/)\n" + "Jo\xC3\xA3o Baptista de Paula e Silva (https://joaobapt.com/)\n" "kode54 (https://kode54.net/)\n" "Revenant (https://revenant1.net/)\n" "xaimus (http://xaimus.com/)\n" @@ -657,6 +645,9 @@ mpt::ustring GetFullCreditsString() "Shayde / Reality Productions for Opal OPL3 emulator\n" "https://www.3eality.com/\n" "\n" + "Ryuhei Mori for TinyFFT\n" + "https://github.com/ryuhei-mori/tinyfft\n" + "\n" #ifdef MPT_WITH_ZLIB "Jean-loup Gailly and Mark Adler for zlib\n" "https://zlib.net/\n" @@ -736,7 +727,8 @@ mpt::ustring GetFullCreditsString() #endif #if defined(MPT_WITH_LAME) "The LAME project for LAME\n" - "http://lame.sourceforge.net/\n" + "https://lame.sourceforge.io/\n" + "\n" #endif #if defined(MPT_WITH_NLOHMANNJSON) "Niels Lohmann et al. for nlohmann-json\n" @@ -785,7 +777,7 @@ mpt::ustring GetFullCreditsString() mpt::ustring GetLicenseString() { return MPT_UTF8( - "Copyright (c) 2004-2019, OpenMPT contributors" "\n" + "Copyright (c) 2004-2020, OpenMPT contributors" "\n" "Copyright (c) 1997-2003, Olivier Lapicque" "\n" "All rights reserved." "\n" "" "\n" diff --git a/Frameworks/OpenMPT/OpenMPT/common/version.h b/Frameworks/OpenMPT/OpenMPT/common/version.h index 5a8d33274..95e8c5b8a 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/version.h +++ b/Frameworks/OpenMPT/OpenMPT/common/version.h @@ -12,8 +12,11 @@ #include "BuildSettings.h" +#include "mptString.h" #include "FlagSet.h" +#include + OPENMPT_NAMESPACE_BEGIN @@ -106,6 +109,74 @@ public: // Returns true if a given version number is from a test build, false if it's a release build. bool IsTestVersion() const noexcept; +public: + + struct LiteralParser + { + + public: + + // Work-around for GCC 5 which complains about instanciating non-literal type inside a constexpr function when using mpt::constexpr_throw(std::runtime_error("")). + struct ParseException {}; + + private: + + static MPT_CONSTEXPR11_FUN uint8 NibbleFromChar(char x) + { + return + ('0' <= x && x <= '9') ? static_cast(x - '0' + 0) : + ('a' <= x && x <= 'z') ? static_cast(x - 'a' + 10) : + ('A' <= x && x <= 'Z') ? static_cast(x - 'A' + 10) : + mpt::constexpr_throw(std::domain_error("")); + } + + public: + + static MPT_CONSTEXPR14_FUN Version Parse(const char * str, std::size_t len) + { + // 0123456789 + // 1.23.45.67 + uint8 v[4] = {0, 0, 0, 0}; + std::size_t field = 0; + std::size_t fieldlen = 0; + for(std::size_t i = 0; i < len; ++i) + { + char c = str[i]; + if(c == '.') + { + if(field >= 3) + { + mpt::constexpr_throw(ParseException()); + } + if(fieldlen == 0) + { + mpt::constexpr_throw(ParseException()); + } + field++; + fieldlen = 0; + } else if(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) + { + fieldlen++; + if(fieldlen > 2) + { + mpt::constexpr_throw(ParseException()); + } + v[field] <<= 4; + v[field] |= NibbleFromChar(c); + } else + { + mpt::constexpr_throw(ParseException()); + } + } + if(fieldlen == 0) + { + mpt::constexpr_throw(ParseException()); + } + return Version(v[0], v[1], v[2], v[3]); + } + + }; + }; MPT_CONSTEXPR11_FUN bool operator == (const Version &a, const Version &b) noexcept @@ -134,11 +205,15 @@ MPT_CONSTEXPR11_FUN bool operator > (const Version &a, const Version &b) noexcep } -//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 MPT_MAKE_VERSION_NUMERIC_HELPER(prefix,v0,v1,v2,v3) Version( prefix ## v0 , prefix ## v1 , prefix ## v2 , prefix ## v3 ) -#define MAKE_VERSION_NUMERIC(v0,v1,v2,v3) MPT_MAKE_VERSION_NUMERIC_HELPER(0x, v0, v1, v2, v3) +MPT_CONSTEXPR14_FUN Version operator "" _LiteralVersionImpl (const char * str, std::size_t len) +{ + return Version::LiteralParser::Parse(str, len); +} + +// Create Version object from version string and check syntax, all at compile time. +// cppcheck false-positive +// cppcheck-suppress preprocessorErrorDirective +#define MPT_V(strver) Version{MPT_FORCE_CONSTEXPR(( strver ## _LiteralVersionImpl ).GetRawVersion())} diff --git a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h index 35bb47f03..9e0e119c1 100644 --- a/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h +++ b/Frameworks/OpenMPT/OpenMPT/common/versionNumber.h @@ -1,7 +1,7 @@ /* * versionNumber.h * --------------- - * Purpose: OpenMPT version handling. + * Purpose: OpenMPT version number. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. @@ -14,16 +14,10 @@ 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. +// Version definitions. The only thing that needs to be changed when changing version number. #define VER_MAJORMAJOR 1 -#define VER_MAJOR 28 -#define VER_MINOR 08 +#define VER_MAJOR 29 +#define VER_MINOR 05 #define VER_MINORMINOR 00 -//Numerical value of the version. -#define MPT_VERSION_CURRENT MAKE_VERSION_NUMERIC(VER_MAJORMAJOR,VER_MAJOR,VER_MINOR,VER_MINORMINOR) - OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-master.sh b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-master.sh index 0820ff7a8..227d9695e 100755 --- a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-master.sh +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/fuzz-master.sh @@ -7,9 +7,6 @@ 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 +cp -d ../../bin/* $FUZZING_TEMPDIR/bin/ 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/get-afl.sh b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/get-afl.sh index 43d0f4bb4..5cd7ea690 100755 --- a/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/get-afl.sh +++ b/Frameworks/OpenMPT/OpenMPT/contrib/fuzzing/get-afl.sh @@ -1,21 +1,16 @@ #!/usr/bin/env bash cd "${0%/*}" -AFL_VERSION="$(wget --quiet -O - "https://api.github.com/repos/google/AFL/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")')" +AFL_VERSION="$(wget --quiet -O - "https://api.github.com/repos/AFLplusplus/AFLplusplus/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")')" AFL_FILENAME="$AFL_VERSION.tar.gz" -AFL_URL="https://github.com/google/AFL/archive/$AFL_FILENAME" +AFL_URL="https://github.com/AFLplusplus/AFLplusplus/archive/$AFL_FILENAME" rm $AFL_FILENAME wget $AFL_URL || exit tar -xzvf $AFL_FILENAME rm $AFL_FILENAME -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 ../.. +cd AFLplusplus-* +make source-only || exit +cd .. rm -rf afl -mv AFL-* afl \ No newline at end of file +mv AFLplusplus-* afl \ No newline at end of file diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/LICENSE b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/LICENSE new file mode 100644 index 000000000..e81fc3ff8 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2004-2020, 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. + diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/Makefile.am b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/Makefile.am new file mode 100644 index 000000000..9f4f96516 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/Makefile.am @@ -0,0 +1,46 @@ + +ACLOCAL_AMFLAGS = -I m4 --install +EXTRA_DIST = +EXTRA_DIST += LICENSE +EXTRA_DIST += libopenmpt_modplug.pc.in +EXTRA_DIST += libmodplug.pc.in +EXTRA_DIST += test.sh +MOSTLYCLEANFILES = + +dist_doc_DATA = +dist_doc_DATA += LICENSE +nobase_dist_doc_DATA = + +bin_PROGRAMS = +check_PROGRAMS = +lib_LTLIBRARIES = + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = +nobase_include_HEADERS = + +if ENABLE_LIBOPENMPT_MODPLUG +lib_LTLIBRARIES += libopenmpt_modplug.la +libopenmpt_modplug_la_LDFLAGS = -version-info 1:0:0 -no-undefined +libopenmpt_modplug_la_CPPFLAGS = -I$(srcdir)/ $(LIBOPENMPT_CPPFLAGS) +libopenmpt_modplug_la_CXXFLAGS = $(LIBOPENMPT_CFLAGS) +libopenmpt_modplug_la_CFLAGS = $(LIBOPENMPT_CFLAGS) +libopenmpt_modplug_la_LIBADD = $(LIBOPENMPT_LIBS) +libopenmpt_modplug_la_SOURCES = +libopenmpt_modplug_la_SOURCES += libopenmpt_modplug.c +libopenmpt_modplug_la_SOURCES += libopenmpt_modplug_cpp.cpp +endif + +if ENABLE_LIBMODPLUG +pkgconfig_DATA += libmodplug.pc +lib_LTLIBRARIES += libmodplug.la +libmodplug_la_LDFLAGS = -version-info 1:0:0 -no-undefined +nobase_include_HEADERS += libmodplug/modplug.h libmodplug/sndfile.h libmodplug/stdafx.h +libmodplug_la_CPPFLAGS = -I$(srcdir)/ $(LIBOPENMPT_CPPFLAGS) +libmodplug_la_CXXFLAGS = $(LIBOPENMPT_CFLAGS) +libmodplug_la_CFLAGS = $(LIBOPENMPT_CFLAGS) +libmodplug_la_LIBADD = $(LIBOPENMPT_LIBS) +libmodplug_la_SOURCES = +libmodplug_la_SOURCES += libopenmpt_modplug.c +libmodplug_la_SOURCES += libopenmpt_modplug_cpp.cpp +endif diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/autogen.sh b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/autogen.sh new file mode 100755 index 000000000..05ff21447 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/autogen.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +autoreconf -i diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/clean.sh b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/clean.sh new file mode 100755 index 000000000..fac1804fb --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/clean.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +./autogen.sh +./configure +make distclean diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/config.h.in b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/config.h.in new file mode 100644 index 000000000..b72c3f925 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/config.h.in @@ -0,0 +1,81 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* define if the compiler supports basic C++11 syntax */ +#undef HAVE_CXX11 + +/* define if the compiler supports basic C++14 syntax */ +#undef HAVE_CXX14 + +/* define if the compiler supports basic C++17 syntax */ +#undef HAVE_CXX17 + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/configure.ac b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/configure.ac new file mode 100644 index 000000000..fd543eca3 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/configure.ac @@ -0,0 +1,66 @@ +AC_INIT([libopenmpt-modplug], [0.8.8.5-openmpt1], [https://bugs.openmpt.org/], [libopenmpt-modplug], [https://lib.openmpt.org/]) +AC_PREREQ([2.68]) + +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_FILES([Makefile libopenmpt_modplug.pc libmodplug.pc]) + +AM_INIT_AUTOMAKE([1.11 -Wall -Werror foreign subdir-objects]) + +AM_PROG_AR + +LT_INIT + +AC_SYS_LARGEFILE + +PKG_PROG_PKG_CONFIG([0.24]) +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_CXX +AC_PROG_INSTALL + +LIBOPENMPT_REQUIRES_PRIVATE= +LIBOPENMPT_LIBS_PRIVATE= + +# We want a modern C compiler +AC_PROG_CC_STDC +#AC_PROG_CC_C99 + +# We need C++11 support +AX_CXX_COMPILE_STDCXX(17, [noext], [optional]) +AS_IF([test "x$HAVE_CXX17" != "x1"], + [ + AX_CXX_COMPILE_STDCXX(14, [noext], [optional]) + AS_IF([test "x$HAVE_CXX14" != "x1"], + [ + AX_CXX_COMPILE_STDCXX(11, [noext], [mandatory]) + ],[] + ) + ],[] +) + +AC_LANG_PUSH([C]) +AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CFLAGS="$CFLAGS -fvisibility=hidden"]) +AX_CFLAGS_WARN_ALL +AC_LANG_POP([C]) + +AC_LANG_PUSH([C++]) +AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CXXFLAGS="$CXXFLAGS -fvisibility=hidden"]) +AX_CXXFLAGS_WARN_ALL +AC_LANG_POP([C++]) + +PKG_CHECK_MODULES([LIBOPENMPT], [libopenmpt]) + +# libmodplug emulation +AC_ARG_ENABLE([libopenmpt_modplug], AS_HELP_STRING([--disable-libopenmpt_modplug], [Disable the libopenmpt_modplug emulation library of the libmodplug interface.])) +AM_CONDITIONAL([ENABLE_LIBOPENMPT_MODPLUG], [test "x$enable_libopenmpt_modplug" != "xno"]) + +# libmodplug replacement +AC_ARG_ENABLE([libmodplug], AS_HELP_STRING([--enable-libmodplug], [Enable libmodplug replacement library based on libopenmpt. +WARNING: This will replace your current libmodplug installation. +CAUTION: The emulation of the libmodplug interface is not complete as libmodplug exposes lots of internal implementation details. If any of those is used by an application, the emulation via libopenmpt will fail and/or crash. +])) +AM_CONDITIONAL([ENABLE_LIBMODPLUG], [test "x$enable_libmodplug" = "xyes"]) + +AC_OUTPUT diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/dist.sh b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/dist.sh new file mode 100755 index 000000000..e96826f07 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/dist.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -e + +./test.sh + +./autogen.sh +./configure +make dist diff --git a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/libmodplug.pc.in b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug.pc.in similarity index 91% rename from Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/libmodplug.pc.in rename to Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug.pc.in index dc35085b2..e8c796d1d 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/libmodplug.pc.in +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug.pc.in @@ -5,7 +5,7 @@ includedir=${prefix}/include Name: libmodplug Description: The ModPlug mod file playing library (emulated via libopenmpt). -Version: 0.8.8.5 +Version: @PACKAGE_VERSION@ Requires.private: libopenmpt Libs: -L${libdir} -lmodplug Libs.private: diff --git a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug.h b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/modplug.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/modplug.h rename to Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/modplug.h diff --git a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/sndfile.h b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/sndfile.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/sndfile.h rename to Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/sndfile.h diff --git a/Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/stdafx.h b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/stdafx.h similarity index 100% rename from Frameworks/OpenMPT/OpenMPT/include/modplug/include/libmodplug/stdafx.h rename to Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libmodplug/stdafx.h diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug.c b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.c similarity index 99% rename from Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug.c rename to Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.c index da13edc3f..51dd3d79f 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug.c +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.c @@ -19,7 +19,7 @@ #endif #endif /* _MSC_VER */ -#include "libopenmpt.h" +#include #include #include diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.pc.in b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.pc.in new file mode 100644 index 000000000..89c1fee89 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=${prefix}/include + +Name: libopenmpt_modplug +Description: The ModPlug mod file playing library (emulated via libopenmpt). +Version: @PACKAGE_VERSION@ +Requires.private: libopenmpt +Libs: -L${libdir} -lopenmpt_modplug +Libs.private: +Cflags: -I${includedir} diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug_cpp.cpp b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug_cpp.cpp similarity index 99% rename from Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug_cpp.cpp rename to Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug_cpp.cpp index f487339f2..fd5375d3b 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_modplug_cpp.cpp +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/libopenmpt_modplug_cpp.cpp @@ -36,7 +36,7 @@ Metadata and other state is not provided or updated. #endif #endif /* _MSC_VER */ -#include "libopenmpt.hpp" +#include #include #include diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/m4/ax_cxx_compile_stdcxx.m4 b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/m4/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 000000000..0b6cb3a7d --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1,972 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the specified +# version of the C++ standard. If necessary, add switches to CXX and +# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) +# or '14' (for the C++14 standard). +# +# The second argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The third argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline support for the specified C++ standard is +# required and that the macro should error out if no mode with that +# support is found. If specified 'optional', then configuration proceeds +# regardless, after defining HAVE_CXX${VERSION} if and only if a +# supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016, 2018 Krzesimir Nowak +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 9 + +dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro +dnl (serial version number 13). + +AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl + m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], + [$1], [14], [ax_cxx_compile_alternatives="14 1y"], + [$1], [17], [ax_cxx_compile_alternatives="17 1z"], + [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$2], [], [], + [$2], [ext], [], + [$2], [noext], [], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], + [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], + [$3], [optional], [ax_cxx_compile_cxx$1_required=false], + [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + + m4_if([$2], [noext], [], [dnl + if test x$ac_success = xno; then + for alternative in ${ax_cxx_compile_alternatives}; do + switch="-std=gnu++${alternative}" + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + + m4_if([$2], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for alternative in ${ax_cxx_compile_alternatives}; do + for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + if test x$ac_success = xyes; then + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx$1_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) + fi + fi + if test x$ac_success = xno; then + HAVE_CXX$1=0 + AC_MSG_NOTICE([No compiler with C++$1 support was found]) + else + HAVE_CXX$1=1 + AC_DEFINE(HAVE_CXX$1,1, + [define if the compiler supports basic C++$1 syntax]) + fi + AC_SUBST(HAVE_CXX$1) +]) + + +dnl Test body for checking C++11 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 +) + + +dnl Test body for checking C++14 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 +) + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 +) + +dnl Tests for new features in C++11 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual void f() {} + }; + + struct Derived : public Base + { + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check single_type; + typedef check> double_type; + typedef check>> triple_type; + typedef check>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template + struct sum; + + template + struct sum + { + static constexpr auto value = N0 + sum::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { func(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + +]]) + + +dnl Tests for new features in C++14 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_separators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + +]]) + + +dnl Tests for new features in C++17 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ + +// If the compiler admits that it is not ready for C++17, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus <= 201402L + +#error "This is not a C++17 compiler" + +#else + +#if defined(__clang__) + #define REALLY_CLANG +#else + #if defined(__GNUC__) + #define REALLY_GCC + #endif +#endif + +#include +#include +#include + +namespace cxx17 +{ + +#if !defined(REALLY_CLANG) + namespace test_constexpr_lambdas + { + + // TODO: test it with clang++ from git + + constexpr int foo = [](){return 42;}(); + + } +#endif // !defined(REALLY_CLANG) + + namespace test::nested_namespace::definitions + { + + } + + namespace test_fold_expression + { + + template + int multiply(Args... args) + { + return (args * ... * 1); + } + + template + bool all(Args... args) + { + return (args && ...); + } + + } + + namespace test_extended_static_assert + { + + static_assert (true); + + } + + namespace test_auto_brace_init_list + { + + auto foo = {5}; + auto bar {5}; + + static_assert(std::is_same, decltype(foo)>::value); + static_assert(std::is_same::value); + } + + namespace test_typename_in_template_template_parameter + { + + template typename X> struct D; + + } + + namespace test_fallthrough_nodiscard_maybe_unused_attributes + { + + int f1() + { + return 42; + } + + [[nodiscard]] int f2() + { + [[maybe_unused]] auto unused = f1(); + + switch (f1()) + { + case 17: + f1(); + [[fallthrough]]; + case 42: + f1(); + } + return f1(); + } + + } + + namespace test_extended_aggregate_initialization + { + + struct base1 + { + int b1, b2 = 42; + }; + + struct base2 + { + base2() { + b3 = 42; + } + int b3; + }; + + struct derived : base1, base2 + { + int d; + }; + + derived d1 {{1, 2}, {}, 4}; // full initialization + derived d2 {{}, {}, 4}; // value-initialized bases + + } + + namespace test_general_range_based_for_loop + { + + struct iter + { + int i; + + int& operator* () + { + return i; + } + + const int& operator* () const + { + return i; + } + + iter& operator++() + { + ++i; + return *this; + } + }; + + struct sentinel + { + int i; + }; + + bool operator== (const iter& i, const sentinel& s) + { + return i.i == s.i; + } + + bool operator!= (const iter& i, const sentinel& s) + { + return !(i == s); + } + + struct range + { + iter begin() const + { + return {0}; + } + + sentinel end() const + { + return {5}; + } + }; + + void f() + { + range r {}; + + for (auto i : r) + { + [[maybe_unused]] auto v = i; + } + } + + } + + namespace test_lambda_capture_asterisk_this_by_value + { + + struct t + { + int i; + int foo() + { + return [*this]() + { + return i; + }(); + } + }; + + } + + namespace test_enum_class_construction + { + + enum class byte : unsigned char + {}; + + byte foo {42}; + + } + + namespace test_constexpr_if + { + + template + int f () + { + if constexpr(cond) + { + return 13; + } + else + { + return 42; + } + } + + } + + namespace test_selection_statement_with_initializer + { + + int f() + { + return 13; + } + + int f2() + { + if (auto i = f(); i > 0) + { + return 3; + } + + switch (auto i = f(); i + 4) + { + case 17: + return 2; + + default: + return 1; + } + } + + } + +#if !defined(REALLY_CLANG) + namespace test_template_argument_deduction_for_class_templates + { + + // TODO: test it with clang++ from git + + template + struct pair + { + pair (T1 p1, T2 p2) + : m1 {p1}, + m2 {p2} + {} + + T1 m1; + T2 m2; + }; + + void f() + { + [[maybe_unused]] auto p = pair{13, 42u}; + } + + } +#endif // !defined(REALLY_CLANG) + + namespace test_non_type_auto_template_parameters + { + + template + struct B + {}; + + B<5> b1; + B<'a'> b2; + + } + +#if !defined(REALLY_CLANG) + namespace test_structured_bindings + { + + // TODO: test it with clang++ from git + + int arr[2] = { 1, 2 }; + std::pair pr = { 1, 2 }; + + auto f1() -> int(&)[2] + { + return arr; + } + + auto f2() -> std::pair& + { + return pr; + } + + struct S + { + int x1 : 2; + volatile double y1; + }; + + S f3() + { + return {}; + } + + auto [ x1, y1 ] = f1(); + auto& [ xr1, yr1 ] = f1(); + auto [ x2, y2 ] = f2(); + auto& [ xr2, yr2 ] = f2(); + const auto [ x3, y3 ] = f3(); + + } +#endif // !defined(REALLY_CLANG) + +#if !defined(REALLY_CLANG) + namespace test_exception_spec_type_system + { + + // TODO: test it with clang++ from git + + struct Good {}; + struct Bad {}; + + void g1() noexcept; + void g2(); + + template + Bad + f(T*, T*); + + template + Good + f(T1*, T2*); + + static_assert (std::is_same_v); + + } +#endif // !defined(REALLY_CLANG) + + namespace test_inline_variables + { + + template void f(T) + {} + + template inline T g(T) + { + return T{}; + } + + template<> inline void f<>(int) + {} + + template<> int g<>(int) + { + return 5; + } + + } + +} // namespace cxx17 + +#endif // __cplusplus <= 201402L + +]]) diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/test.sh b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/test.sh new file mode 100755 index 000000000..7df7ed3fb --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.8.5/test.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -e + +./autogen.sh + +./configure +make +make distcheck +make distclean diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/LICENSE b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/LICENSE new file mode 100644 index 000000000..e81fc3ff8 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2004-2020, 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. + diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/Makefile.am b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/Makefile.am new file mode 100644 index 000000000..9f4f96516 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/Makefile.am @@ -0,0 +1,46 @@ + +ACLOCAL_AMFLAGS = -I m4 --install +EXTRA_DIST = +EXTRA_DIST += LICENSE +EXTRA_DIST += libopenmpt_modplug.pc.in +EXTRA_DIST += libmodplug.pc.in +EXTRA_DIST += test.sh +MOSTLYCLEANFILES = + +dist_doc_DATA = +dist_doc_DATA += LICENSE +nobase_dist_doc_DATA = + +bin_PROGRAMS = +check_PROGRAMS = +lib_LTLIBRARIES = + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = +nobase_include_HEADERS = + +if ENABLE_LIBOPENMPT_MODPLUG +lib_LTLIBRARIES += libopenmpt_modplug.la +libopenmpt_modplug_la_LDFLAGS = -version-info 1:0:0 -no-undefined +libopenmpt_modplug_la_CPPFLAGS = -I$(srcdir)/ $(LIBOPENMPT_CPPFLAGS) +libopenmpt_modplug_la_CXXFLAGS = $(LIBOPENMPT_CFLAGS) +libopenmpt_modplug_la_CFLAGS = $(LIBOPENMPT_CFLAGS) +libopenmpt_modplug_la_LIBADD = $(LIBOPENMPT_LIBS) +libopenmpt_modplug_la_SOURCES = +libopenmpt_modplug_la_SOURCES += libopenmpt_modplug.c +libopenmpt_modplug_la_SOURCES += libopenmpt_modplug_cpp.cpp +endif + +if ENABLE_LIBMODPLUG +pkgconfig_DATA += libmodplug.pc +lib_LTLIBRARIES += libmodplug.la +libmodplug_la_LDFLAGS = -version-info 1:0:0 -no-undefined +nobase_include_HEADERS += libmodplug/modplug.h libmodplug/sndfile.h libmodplug/stdafx.h +libmodplug_la_CPPFLAGS = -I$(srcdir)/ $(LIBOPENMPT_CPPFLAGS) +libmodplug_la_CXXFLAGS = $(LIBOPENMPT_CFLAGS) +libmodplug_la_CFLAGS = $(LIBOPENMPT_CFLAGS) +libmodplug_la_LIBADD = $(LIBOPENMPT_LIBS) +libmodplug_la_SOURCES = +libmodplug_la_SOURCES += libopenmpt_modplug.c +libmodplug_la_SOURCES += libopenmpt_modplug_cpp.cpp +endif diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/autogen.sh b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/autogen.sh new file mode 100755 index 000000000..05ff21447 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/autogen.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +autoreconf -i diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/clean.sh b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/clean.sh new file mode 100755 index 000000000..fac1804fb --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/clean.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +./autogen.sh +./configure +make distclean diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/config.h.in b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/config.h.in new file mode 100644 index 000000000..b72c3f925 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/config.h.in @@ -0,0 +1,81 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* define if the compiler supports basic C++11 syntax */ +#undef HAVE_CXX11 + +/* define if the compiler supports basic C++14 syntax */ +#undef HAVE_CXX14 + +/* define if the compiler supports basic C++17 syntax */ +#undef HAVE_CXX17 + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/configure.ac b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/configure.ac new file mode 100644 index 000000000..341700686 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/configure.ac @@ -0,0 +1,66 @@ +AC_INIT([libopenmpt-modplug], [0.8.9.0-openmpt1], [https://bugs.openmpt.org/], [libopenmpt-modplug], [https://lib.openmpt.org/]) +AC_PREREQ([2.68]) + +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_FILES([Makefile libopenmpt_modplug.pc libmodplug.pc]) + +AM_INIT_AUTOMAKE([1.11 -Wall -Werror foreign subdir-objects]) + +AM_PROG_AR + +LT_INIT + +AC_SYS_LARGEFILE + +PKG_PROG_PKG_CONFIG([0.24]) +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_CXX +AC_PROG_INSTALL + +LIBOPENMPT_REQUIRES_PRIVATE= +LIBOPENMPT_LIBS_PRIVATE= + +# We want a modern C compiler +AC_PROG_CC_STDC +#AC_PROG_CC_C99 + +# We need C++11 support +AX_CXX_COMPILE_STDCXX(17, [noext], [optional]) +AS_IF([test "x$HAVE_CXX17" != "x1"], + [ + AX_CXX_COMPILE_STDCXX(14, [noext], [optional]) + AS_IF([test "x$HAVE_CXX14" != "x1"], + [ + AX_CXX_COMPILE_STDCXX(11, [noext], [mandatory]) + ],[] + ) + ],[] +) + +AC_LANG_PUSH([C]) +AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CFLAGS="$CFLAGS -fvisibility=hidden -DSYM_VISIBILITY"]) +AX_CFLAGS_WARN_ALL +AC_LANG_POP([C]) + +AC_LANG_PUSH([C++]) +AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CXXFLAGS="$CXXFLAGS -fvisibility=hidden -DSYM_VISIBILITY"]) +AX_CXXFLAGS_WARN_ALL +AC_LANG_POP([C++]) + +PKG_CHECK_MODULES([LIBOPENMPT], [libopenmpt]) + +# libmodplug emulation +AC_ARG_ENABLE([libopenmpt_modplug], AS_HELP_STRING([--disable-libopenmpt_modplug], [Disable the libopenmpt_modplug emulation library of the libmodplug interface.])) +AM_CONDITIONAL([ENABLE_LIBOPENMPT_MODPLUG], [test "x$enable_libopenmpt_modplug" != "xno"]) + +# libmodplug replacement +AC_ARG_ENABLE([libmodplug], AS_HELP_STRING([--enable-libmodplug], [Enable libmodplug replacement library based on libopenmpt. +WARNING: This will replace your current libmodplug installation. +CAUTION: The emulation of the libmodplug interface is not complete as libmodplug exposes lots of internal implementation details. If any of those is used by an application, the emulation via libopenmpt will fail and/or crash. +])) +AM_CONDITIONAL([ENABLE_LIBMODPLUG], [test "x$enable_libmodplug" = "xyes"]) + +AC_OUTPUT diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/dist.sh b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/dist.sh new file mode 100755 index 000000000..e96826f07 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/dist.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -e + +./test.sh + +./autogen.sh +./configure +make dist diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug.pc.in b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug.pc.in new file mode 100644 index 000000000..e8c796d1d --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/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: @PACKAGE_VERSION@ +Requires.private: libopenmpt +Libs: -L${libdir} -lmodplug +Libs.private: +Cflags: -I${includedir} + diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/modplug.h b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/modplug.h new file mode 100644 index 000000000..62dd1f42c --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/modplug.h @@ -0,0 +1,185 @@ +/* + * This source code is public domain. + * + * Authors: Kenton Varda (C interface wrapper) + */ + +#ifndef MODPLUG_MODPLUG_H +#define MODPLUG_MODPLUG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) +# if defined(MODPLUG_BUILD) && defined(DLL_EXPORT) /* building libmodplug as a dll for windows */ +# define MODPLUG_EXPORT __declspec(dllexport) +# elif defined(MODPLUG_BUILD) || defined(MODPLUG_STATIC) /* building or using static libmodplug for windows */ +# define MODPLUG_EXPORT +# else +# define MODPLUG_EXPORT __declspec(dllimport) /* using libmodplug dll for windows */ +# endif +#elif defined(MODPLUG_BUILD) && defined(SYM_VISIBILITY) +# define MODPLUG_EXPORT __attribute__((visibility("default"))) +#else +#define MODPLUG_EXPORT +#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. */ +MODPLUG_EXPORT ModPlugFile* ModPlug_Load(const void* data, int size); +/* Unload a mod file. */ +MODPLUG_EXPORT 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. */ +MODPLUG_EXPORT 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. */ +MODPLUG_EXPORT 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. */ +MODPLUG_EXPORT 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. */ +MODPLUG_EXPORT 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. */ +MODPLUG_EXPORT void ModPlug_GetSettings(ModPlug_Settings* settings); +MODPLUG_EXPORT void ModPlug_SetSettings(const ModPlug_Settings* settings); + +/* New ModPlug API Functions */ +/* NOTE: Master Volume (1-512) */ +MODPLUG_EXPORT unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) ; +MODPLUG_EXPORT void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) ; + +MODPLUG_EXPORT int ModPlug_GetCurrentSpeed(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetCurrentTempo(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetCurrentOrder(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetCurrentPattern(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetCurrentRow(ModPlugFile* file); +MODPLUG_EXPORT int ModPlug_GetPlayingChannels(ModPlugFile* file); + +MODPLUG_EXPORT void ModPlug_SeekOrder(ModPlugFile* file,int order); +MODPLUG_EXPORT int ModPlug_GetModuleType(ModPlugFile* file); +MODPLUG_EXPORT char* ModPlug_GetMessage(ModPlugFile* file); + +#define MODPLUG_NO_FILESAVE /* experimental yet. must match stdafx.h. */ +#ifndef MODPLUG_NO_FILESAVE +/* + * EXPERIMENTAL Export Functions + */ +/*Export to a Scream Tracker 3 S3M module. EXPERIMENTAL (only works on Little-Endian platforms)*/ +MODPLUG_EXPORT char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath); + +/*Export to a Extended Module (XM). EXPERIMENTAL (only works on Little-Endian platforms)*/ +MODPLUG_EXPORT char ModPlug_ExportXM(ModPlugFile* file, const char* filepath); + +/*Export to a Amiga MOD file. EXPERIMENTAL.*/ +MODPLUG_EXPORT char ModPlug_ExportMOD(ModPlugFile* file, const char* filepath); + +/*Export to a Impulse Tracker IT file. Should work OK in Little-Endian & Big-Endian platforms :-) */ +MODPLUG_EXPORT char ModPlug_ExportIT(ModPlugFile* file, const char* filepath); +#endif /* MODPLUG_NO_FILESAVE */ + +MODPLUG_EXPORT unsigned int ModPlug_NumInstruments(ModPlugFile* file); +MODPLUG_EXPORT unsigned int ModPlug_NumSamples(ModPlugFile* file); +MODPLUG_EXPORT unsigned int ModPlug_NumPatterns(ModPlugFile* file); +MODPLUG_EXPORT unsigned int ModPlug_NumChannels(ModPlugFile* file); +MODPLUG_EXPORT unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff); +MODPLUG_EXPORT unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff); + +/* + * Retrieve pattern note-data + */ +MODPLUG_EXPORT 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) + */ +MODPLUG_EXPORT void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) ; +MODPLUG_EXPORT void ModPlug_UnloadMixerCallback(ModPlugFile* file) ; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/sndfile.h b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/sndfile.h new file mode 100644 index 000000000..f390e4144 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/sndfile.h @@ -0,0 +1,1028 @@ +/* + * 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 MODPLUG_SNDFILE_H +#define MODPLUG_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 MODPLUG_EXPORT 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 MODPLUG_EXPORT CSoundFile +//============== +{ +public: // Static Members + static UINT m_nXBassDepth; + static UINT m_nXBassRange; + static UINT m_nReverbDepth; + static UINT m_nReverbDelay; + static UINT gnReverbType; + static UINT m_nProLogicDepth; + static UINT m_nProLogicDelay; + static UINT m_nStereoSeparation; + static UINT m_nMaxMixChannels; + static LONG m_nStreamVolume; + static DWORD gdwSysInfo; + static DWORD gdwSoundSetup; + static DWORD gdwMixingFreq; + static DWORD gnBitsPerSample; + static DWORD gnChannels; + static UINT gnAGC; + static UINT gnVolumeRampSamples; + static UINT gnVUMeter; + static UINT 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, m_nNextStartRow; + 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/contrib/libmodplug-0.8.9.0/libmodplug/stdafx.h b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/stdafx.h new file mode 100644 index 000000000..5daea9182 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libmodplug/stdafx.h @@ -0,0 +1,141 @@ +/* + * This source code is public domain. + * + * Authors: Rani Assaf , + * Olivier Lapicque , + * Adam Goode (endian and char fixes for PPC) + */ + +#ifndef MODPLUG_STDAFX_H +#define MODPLUG_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 + +/* disable AGC and FILESAVE for all targets for uniformity. */ +#define NO_AGC +#define MODPLUG_NO_FILESAVE + +#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) {} + +#undef strcasecmp +#undef strncasecmp +#define strcasecmp(a,b) _stricmp(a,b) +#define strncasecmp(a,b,c) _strnicmp(a,b,c) + +#define HAVE_SINF 1 + +#ifndef isblank +#define isblank(c) ((c) == ' ' || (c) == '\t') +#endif + +#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 LPCTSTR LPCSTR +#define lstrcpyn strncpy +#define lstrcpy strcpy +#define lstrcmp strcmp +#define wsprintf sprintf + +#define WAVE_FORMAT_PCM 1 + +#define GHND 0 +#define GlobalFreePtr(p) free((void *)(p)) +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 */ ) {} + +#ifndef FALSE +#define FALSE false +#endif + +#ifndef TRUE +#define TRUE true +#endif + +#endif /* _WIN32 */ + +#if defined(_WIN32) || defined(__CYGWIN__) +# if defined(MODPLUG_BUILD) && defined(DLL_EXPORT) /* building libmodplug as a dll for windows */ +# define MODPLUG_EXPORT __declspec(dllexport) +# elif defined(MODPLUG_BUILD) || defined(MODPLUG_STATIC) /* building or using static libmodplug for windows */ +# define MODPLUG_EXPORT +# else +# define MODPLUG_EXPORT __declspec(dllimport) /* using libmodplug dll for windows */ +# endif +#elif defined(MODPLUG_BUILD) && defined(SYM_VISIBILITY) +# define MODPLUG_EXPORT __attribute__((visibility("default"))) +#else +#define MODPLUG_EXPORT +#endif + +#endif diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.c b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.c new file mode 100644 index 000000000..51dd3d79f --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.c @@ -0,0 +1,604 @@ +/* + * libopenmpt_modplug.c + * -------------------- + * Purpose: libopenmpt emulation of the libmodplug interface + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#ifndef NO_LIBMODPLUG + +#ifdef LIBOPENMPT_BUILD_DLL +#undef LIBOPENMPT_BUILD_DLL +#endif + +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#endif /* _MSC_VER */ + +#include + +#include +#include +#include +#include +#include +#include + +#define MODPLUG_BUILD +#ifdef _MSC_VER +#ifdef MPT_BUILD_MSVC_SHARED +#define DLL_EXPORT +#endif /* MPT_BUILD_MSVC_SHARED */ +#ifdef MPT_BUILD_MSVC_STATIC +#define MODPLUG_STATIC +#endif /* MPT_BUILD_MSVC_STATIC */ +#endif /* _MSC_VER */ +#ifdef _MSC_VER +#define LIBOPENMPT_MODPLUG_API +#else /* !_MSC_VER */ +#define LIBOPENMPT_MODPLUG_API LIBOPENMPT_API_HELPER_EXPORT +#endif /* _MSC_VER */ +#include "libmodplug/modplug.h" + +/* from libmodplug/sndfile.h */ +/* header is not c clean */ +#define MIXING_ATTENUATION 4 +#define MOD_TYPE_NONE 0x0 +#define MOD_TYPE_MOD 0x1 +#define MOD_TYPE_S3M 0x2 +#define MOD_TYPE_XM 0x4 +#define MOD_TYPE_MED 0x8 +#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 BUFFER_COUNT 1024 + +struct _ModPlugFile { + openmpt_module* mod; + signed short* buf; + signed int* mixerbuf; + char* name; + char* message; + ModPlug_Settings settings; + ModPlugMixerProc mixerproc; + ModPlugNote** patterns; +}; + +static ModPlug_Settings globalsettings = { + MODPLUG_ENABLE_OVERSAMPLING|MODPLUG_ENABLE_NOISE_REDUCTION, + 2, + 16, + 44100, + MODPLUG_RESAMPLE_LINEAR, + 128, + 256, + 0, + 0, + 0, + 0, + 0, + 0, + 0 +}; + +static int32_t modplugresamplingmode_to_filterlength(int mode) +{ + if(mode<0){ + return 1; + } + switch(mode){ + case MODPLUG_RESAMPLE_NEAREST: return 1; break; + case MODPLUG_RESAMPLE_LINEAR: return 2; break; + case MODPLUG_RESAMPLE_SPLINE: return 4; break; + case MODPLUG_RESAMPLE_FIR: return 8; break; + } + return 8; +} + +LIBOPENMPT_MODPLUG_API ModPlugFile* ModPlug_Load(const void* data, int size) +{ + ModPlugFile* file = malloc(sizeof(ModPlugFile)); + const char* name = NULL; + const char* message = NULL; + if(!file) return NULL; + memset(file,0,sizeof(ModPlugFile)); + memcpy(&file->settings,&globalsettings,sizeof(ModPlug_Settings)); + file->mod = openmpt_module_create_from_memory2(data,size,NULL,NULL,NULL,NULL,NULL,NULL,NULL); + if(!file->mod){ + free(file); + return NULL; + } + file->buf = malloc(BUFFER_COUNT*sizeof(signed short)*4); + if(!file->buf){ + openmpt_module_destroy(file->mod); + free(file); + return NULL; + } + openmpt_module_set_repeat_count(file->mod,file->settings.mLoopCount); + name = openmpt_module_get_metadata(file->mod,"title"); + if(name){ + file->name = malloc(strlen(name)+1); + if(file->name){ + strcpy(file->name,name); + } + openmpt_free_string(name); + name = NULL; + }else{ + file->name = malloc(strlen("")+1); + if(file->name){ + strcpy(file->name,""); + } + } + message = openmpt_module_get_metadata(file->mod,"message"); + if(message){ + file->message = malloc(strlen(message)+1); + if(file->message){ + strcpy(file->message,message); + } + openmpt_free_string(message); + message = NULL; + }else{ + file->message = malloc(strlen("")+1); + if(file->message){ + strcpy(file->message,""); + } + } + openmpt_module_set_render_param(file->mod,OPENMPT_MODULE_RENDER_STEREOSEPARATION_PERCENT,file->settings.mStereoSeparation*100/128); + openmpt_module_set_render_param(file->mod,OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH,modplugresamplingmode_to_filterlength(file->settings.mResamplingMode)); + return file; +} + +LIBOPENMPT_MODPLUG_API void ModPlug_Unload(ModPlugFile* file) +{ + int p; + if(!file) return; + if(file->patterns){ + for(p=0;pmod);p++){ + if(file->patterns[p]){ + free(file->patterns[p]); + file->patterns[p] = NULL; + } + } + free(file->patterns); + file->patterns = NULL; + } + if(file->mixerbuf){ + free(file->mixerbuf); + file->mixerbuf = NULL; + } + openmpt_module_destroy(file->mod); + file->mod = NULL; + free(file->name); + file->name = NULL; + free(file->message); + file->message = NULL; + free(file->buf); + file->buf = NULL; + free(file); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_Read(ModPlugFile* file, void* buffer, int size) +{ + int framesize; + int framecount; + int frames; + int rendered; + int frame; + int channel; + int totalrendered; + signed short* in; + signed int* mixbuf; + unsigned char* buf8; + signed short* buf16; + signed int* buf32; + if(!file) return 0; + framesize = file->settings.mBits/8*file->settings.mChannels; + framecount = size/framesize; + buf8 = buffer; + buf16 = buffer; + buf32 = buffer; + totalrendered = 0; + while(framecount>0){ + frames = framecount; + if(frames>BUFFER_COUNT){ + frames = BUFFER_COUNT; + } + if(file->settings.mChannels==1){ + rendered = (int)openmpt_module_read_mono(file->mod,file->settings.mFrequency,frames,&file->buf[frames*0]); + }else if(file->settings.mChannels==2){ + rendered = (int)openmpt_module_read_stereo(file->mod,file->settings.mFrequency,frames,&file->buf[frames*0],&file->buf[frames*1]); + }else if(file->settings.mChannels==4){ + rendered = (int)openmpt_module_read_quad(file->mod,file->settings.mFrequency,frames,&file->buf[frames*0],&file->buf[frames*1],&file->buf[frames*2],&file->buf[frames*3]); + }else{ + return 0; + } + in = file->buf; + if(file->mixerproc&&file->mixerbuf){ + mixbuf=file->mixerbuf; + for(frame=0;framesettings.mChannels;channel++){ + *mixbuf = in[frames*channel+frame]<<(32-16-1-MIXING_ATTENUATION); + mixbuf++; + } + } + file->mixerproc(file->mixerbuf,file->settings.mChannels*frames,file->settings.mChannels); + mixbuf=file->mixerbuf; + for(frame=0;framesettings.mChannels;channel++){ + in[frames*channel+frame] = *mixbuf>>(32-16-1-MIXING_ATTENUATION); + mixbuf++; + } + } + } + if(file->settings.mBits==8){ + for(frame=0;framesettings.mChannels;channel++){ + *buf8 = in[frames*channel+frame]/256+0x80; + buf8++; + } + } + }else if(file->settings.mBits==16){ + for(frame=0;framesettings.mChannels;channel++){ + *buf16 = in[frames*channel+frame]; + buf16++; + } + } + }else if(file->settings.mBits==32){ + for(frame=0;framesettings.mChannels;channel++){ + *buf32 = in[frames*channel+frame] << (32-16-1-MIXING_ATTENUATION); + buf32++; + } + } + }else{ + return 0; + } + totalrendered += rendered; + framecount -= frames; + if(!rendered) break; + } + memset(((char*)buffer)+totalrendered*framesize,0,size-totalrendered*framesize); + return totalrendered*framesize; +} + +LIBOPENMPT_MODPLUG_API const char* ModPlug_GetName(ModPlugFile* file) +{ + if(!file) return NULL; + return file->name; +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetLength(ModPlugFile* file) +{ + if(!file) return 0; + return (int)(openmpt_module_get_duration_seconds(file->mod)*1000.0); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_Seek(ModPlugFile* file, int millisecond) +{ + if(!file) return; + openmpt_module_set_position_seconds(file->mod,(double)millisecond*0.001); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_GetSettings(ModPlug_Settings* settings) +{ + if(!settings) return; + memcpy(settings,&globalsettings,sizeof(ModPlug_Settings)); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_SetSettings(const ModPlug_Settings* settings) +{ + if(!settings) return; + memcpy(&globalsettings,settings,sizeof(ModPlug_Settings)); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) +{ + int32_t val; + if(!file) return 0; + val = 0; + if(!openmpt_module_get_render_param(file->mod,OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL,&val)) return 128; + return (unsigned int)(128.0*pow(10.0,val*0.0005)); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) +{ + if(!file) return; + openmpt_module_set_render_param(file->mod,OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL,(int32_t)(2000.0*log10(cvol/128.0))); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentSpeed(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_speed(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentTempo(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_tempo(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentOrder(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_order(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentPattern(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_pattern(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentRow(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_row(file->mod); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetPlayingChannels(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_current_playing_channels(file->mod); +} + +LIBOPENMPT_MODPLUG_API void ModPlug_SeekOrder(ModPlugFile* file,int order) +{ + if(!file) return; + openmpt_module_set_position_order_row(file->mod,order,0); +} + +LIBOPENMPT_MODPLUG_API int ModPlug_GetModuleType(ModPlugFile* file) +{ + const char* type; + int retval; + if(!file) return 0; + type = openmpt_module_get_metadata(file->mod,"type"); + retval = MOD_TYPE_NONE; + if(!type){ + return retval; + } + if(!strcmp(type,"mod")){ + retval = MOD_TYPE_MOD; + }else if(!strcmp(type,"s3m")){ + retval = MOD_TYPE_S3M; + }else if(!strcmp(type,"xm")){ + retval = MOD_TYPE_XM; + }else if(!strcmp(type,"med")){ + retval = MOD_TYPE_MED; + }else if(!strcmp(type,"mtm")){ + retval = MOD_TYPE_MTM; + }else if(!strcmp(type,"it")){ + retval = MOD_TYPE_IT; + }else if(!strcmp(type,"669")){ + retval = MOD_TYPE_669; + }else if(!strcmp(type,"ult")){ + retval = MOD_TYPE_ULT; + }else if(!strcmp(type,"stm")){ + retval = MOD_TYPE_STM; + }else if(!strcmp(type,"far")){ + retval = MOD_TYPE_FAR; + }else if(!strcmp(type,"s3m")){ + retval = MOD_TYPE_WAV; + }else if(!strcmp(type,"amf")){ + retval = MOD_TYPE_AMF; + }else if(!strcmp(type,"ams")){ + retval = MOD_TYPE_AMS; + }else if(!strcmp(type,"dsm")){ + retval = MOD_TYPE_DSM; + }else if(!strcmp(type,"mdl")){ + retval = MOD_TYPE_MDL; + }else if(!strcmp(type,"okt")){ + retval = MOD_TYPE_OKT; + }else if(!strcmp(type,"mid")){ + retval = MOD_TYPE_MID; + }else if(!strcmp(type,"dmf")){ + retval = MOD_TYPE_DMF; + }else if(!strcmp(type,"ptm")){ + retval = MOD_TYPE_PTM; + }else if(!strcmp(type,"dbm")){ + retval = MOD_TYPE_DBM; + }else if(!strcmp(type,"mt2")){ + retval = MOD_TYPE_MT2; + }else if(!strcmp(type,"amf0")){ + retval = MOD_TYPE_AMF0; + }else if(!strcmp(type,"psm")){ + retval = MOD_TYPE_PSM; + }else if(!strcmp(type,"j2b")){ + retval = MOD_TYPE_J2B; + }else if(!strcmp(type,"abc")){ + retval = MOD_TYPE_ABC; + }else if(!strcmp(type,"pat")){ + retval = MOD_TYPE_PAT; + }else if(!strcmp(type,"umx")){ + retval = MOD_TYPE_UMX; + }else{ + retval = MOD_TYPE_IT; /* fallback, most complex type */ + } + openmpt_free_string(type); + return retval; +} + +LIBOPENMPT_MODPLUG_API char* ModPlug_GetMessage(ModPlugFile* file) +{ + if(!file) return NULL; + return file->message; +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumInstruments(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_num_instruments(file->mod); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumSamples(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_num_samples(file->mod); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumPatterns(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_num_patterns(file->mod); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumChannels(ModPlugFile* file) +{ + if(!file) return 0; + return openmpt_module_get_num_channels(file->mod); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff) +{ + const char* str; + char buf[32]; + if(!file) return 0; + str = openmpt_module_get_sample_name(file->mod,qual-1); + memset(buf,0,32); + if(str){ + strncpy(buf,str,31); + openmpt_free_string(str); + } + if(buff){ + strncpy(buff,buf,32); + } + return (unsigned int)strlen(buf); +} + +LIBOPENMPT_MODPLUG_API unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff) +{ + const char* str; + char buf[32]; + if(!file) return 0; + str = openmpt_module_get_instrument_name(file->mod,qual-1); + memset(buf,0,32); + if(str){ + strncpy(buf,str,31); + openmpt_free_string(str); + } + if(buff){ + strncpy(buff,buf,32); + } + return (unsigned int)strlen(buf); +} + +LIBOPENMPT_MODPLUG_API ModPlugNote* ModPlug_GetPattern(ModPlugFile* file, int pattern, unsigned int* numrows) +{ + int c; + int r; + int numr; + int numc; + ModPlugNote note; + if(!file) return NULL; + if(numrows){ + *numrows = openmpt_module_get_pattern_num_rows(file->mod,pattern); + } + if(pattern<0||pattern>=openmpt_module_get_num_patterns(file->mod)){ + return NULL; + } + if(!file->patterns){ + file->patterns = malloc(sizeof(ModPlugNote*)*openmpt_module_get_pattern_num_rows(file->mod,pattern)); + if(!file->patterns) return NULL; + memset(file->patterns,0,sizeof(ModPlugNote*)*openmpt_module_get_pattern_num_rows(file->mod,pattern)); + } + if(!file->patterns[pattern]){ + file->patterns[pattern] = malloc(sizeof(ModPlugNote)*openmpt_module_get_pattern_num_rows(file->mod,pattern)*openmpt_module_get_num_channels(file->mod)); + if(!file->patterns[pattern]) return NULL; + memset(file->patterns[pattern],0,sizeof(ModPlugNote)*openmpt_module_get_pattern_num_rows(file->mod,pattern)*openmpt_module_get_num_channels(file->mod)); + } + numr = openmpt_module_get_pattern_num_rows(file->mod,pattern); + numc = openmpt_module_get_num_channels(file->mod); + for(r=0;rmod,pattern,r,c,OPENMPT_MODULE_COMMAND_NOTE); + note.Instrument = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_INSTRUMENT); + note.VolumeEffect = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_VOLUMEEFFECT); + note.Effect = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_EFFECT); + note.Volume = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_VOLUME); + note.Parameter = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_PARAMETER); + memcpy(&file->patterns[pattern][r*numc+c],¬e,sizeof(ModPlugNote)); + } + } + return file->patterns[pattern]; +} + +LIBOPENMPT_MODPLUG_API void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) +{ + if(!file) return; + if(!file->mixerbuf){ + file->mixerbuf = malloc(BUFFER_COUNT*sizeof(signed int)*4); + } + file->mixerproc = proc; +} + +LIBOPENMPT_MODPLUG_API void ModPlug_UnloadMixerCallback(ModPlugFile* file) +{ + if(!file) return; + file->mixerproc = NULL; + if(file->mixerbuf){ + free(file->mixerbuf); + file->mixerbuf = NULL; + } +} + +LIBOPENMPT_MODPLUG_API char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath) +{ + (void)file; + /* not implemented */ + fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportS3M(%s) not implemented.\n",filepath); + return 0; +} + +LIBOPENMPT_MODPLUG_API char ModPlug_ExportXM(ModPlugFile* file, const char* filepath) +{ + (void)file; + /* not implemented */ + fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportXM(%s) not implemented.\n",filepath); + return 0; +} + +LIBOPENMPT_MODPLUG_API char ModPlug_ExportMOD(ModPlugFile* file, const char* filepath) +{ + (void)file; + /* not implemented */ + fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportMOD(%s) not implemented.\n",filepath); + return 0; +} + +LIBOPENMPT_MODPLUG_API char ModPlug_ExportIT(ModPlugFile* file, const char* filepath) +{ + (void)file; + /* not implemented */ + fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportIT(%s) not implemented.\n",filepath); + return 0; +} + +#endif /* NO_LIBMODPLUG */ diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.pc.in b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.pc.in new file mode 100644 index 000000000..89c1fee89 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=${prefix}/include + +Name: libopenmpt_modplug +Description: The ModPlug mod file playing library (emulated via libopenmpt). +Version: @PACKAGE_VERSION@ +Requires.private: libopenmpt +Libs: -L${libdir} -lopenmpt_modplug +Libs.private: +Cflags: -I${includedir} diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug_cpp.cpp b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug_cpp.cpp new file mode 100644 index 000000000..fd5375d3b --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/libopenmpt_modplug_cpp.cpp @@ -0,0 +1,887 @@ +/* + * libopenmpt_modplug_cpp.cpp + * -------------------------- + * Purpose: libopenmpt emulation of the libmodplug c++ interface + * Notes : WARNING! THIS IS A HACK! + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#ifndef NO_LIBMODPLUG + +/* + +*********************************************************************** +WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING +*********************************************************************** + +This is a dirty hack to emulate just so much of the libmodplug c++ +interface so that the current known users (mainly xmms-modplug itself, +gstreamer modplug, audacious, and stuff based on those) work. This is +neither a complete nor a correct implementation. +Metadata and other state is not provided or updated. + +*/ + +#ifdef UNICODE +#undef UNICODE +#endif +#ifdef _UNICODE +#undef _UNICODE +#endif + +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#endif /* _MSC_VER */ + +#include + +#include +#include + +#include +#include +#include +#include + +#define MODPLUG_BUILD +#ifdef _MSC_VER +/* libmodplug C++ header is broken for MSVC DLL builds */ +#define MODPLUG_STATIC +#endif /* _MSC_VER */ +#ifdef _MSC_VER +#define LIBOPENMPT_MODPLUG_API +#else /* !_MSC_VER */ +#define LIBOPENMPT_MODPLUG_API LIBOPENMPT_API_HELPER_EXPORT +#endif /* _MSC_VER */ +class LIBOPENMPT_MODPLUG_API CSoundFile; +#include "libmodplug/stdafx.h" +#include "libmodplug/sndfile.h" + +namespace { +template +void Clear( T & x ) +{ + std::memset( &x, 0, sizeof(T) ); +} +} + +//#define mpcpplog() fprintf(stderr, "%s %i\n", __func__, __LINE__) +#define mpcpplog() do{}while(0) + +#define UNUSED(x) (void)((x)) + +union self_t { + CHAR CompressionTable[16]; + openmpt::module * self_; +}; + +static void set_self( CSoundFile * that, openmpt::module * self_ ) { + self_t self_union; + Clear(self_union); + self_union.self_ = self_; + std::memcpy( that->CompressionTable, self_union.CompressionTable, sizeof( self_union.CompressionTable ) ); +} + +static openmpt::module * get_self( const CSoundFile * that ) { + self_t self_union; + Clear(self_union); + std::memcpy( self_union.CompressionTable, that->CompressionTable, sizeof( self_union.CompressionTable ) ); + return self_union.self_; +} + +#define mod ( get_self( this ) ) + +#define update_state() \ + if ( mod ) m_nCurrentPattern = mod->get_current_order(); \ + if ( mod ) m_nPattern = mod->get_current_pattern(); \ + if ( mod ) m_nMusicSpeed = mod->get_current_speed(); \ + if ( mod ) m_nMusicTempo = mod->get_current_tempo(); \ +/**/ + +UINT CSoundFile::m_nXBassDepth = 0; +UINT CSoundFile::m_nXBassRange = 0; +UINT CSoundFile::m_nReverbDepth = 0; +UINT CSoundFile::m_nReverbDelay = 0; +UINT CSoundFile::gnReverbType = 0; +UINT CSoundFile::m_nProLogicDepth = 0; +UINT CSoundFile::m_nProLogicDelay = 0; +UINT CSoundFile::m_nStereoSeparation = 128; +UINT CSoundFile::m_nMaxMixChannels = 256; +LONG CSoundFile::m_nStreamVolume = 0x8000; +DWORD CSoundFile::gdwSysInfo = 0; +DWORD CSoundFile::gdwSoundSetup = 0; +DWORD CSoundFile::gdwMixingFreq = 44100; +DWORD CSoundFile::gnBitsPerSample = 16; +DWORD CSoundFile::gnChannels = 2; +UINT CSoundFile::gnAGC = 0; +UINT CSoundFile::gnVolumeRampSamples = 0; +UINT CSoundFile::gnVUMeter = 0; +UINT CSoundFile::gnCPUUsage = 0; +LPSNDMIXHOOKPROC CSoundFile::gpSndMixHook = 0; +PMIXPLUGINCREATEPROC CSoundFile::gpMixPluginCreateProc = 0; + +CSoundFile::CSoundFile() { + mpcpplog(); + Clear(Chn); + Clear(ChnMix); + Clear(Ins); + Clear(Headers); + Clear(ChnSettings); + Clear(Patterns); + Clear(PatternSize); + Clear(Order); + Clear(m_MidiCfg); + Clear(m_MixPlugins); + Clear(m_nDefaultSpeed); + Clear(m_nDefaultTempo); + Clear(m_nDefaultGlobalVolume); + Clear(m_dwSongFlags); + Clear(m_nChannels); + Clear(m_nMixChannels); + Clear(m_nMixStat); + Clear(m_nBufferCount); + Clear(m_nType); + Clear(m_nSamples); + Clear(m_nInstruments); + Clear(m_nTickCount); + Clear(m_nTotalCount); + Clear(m_nPatternDelay); + Clear(m_nFrameDelay); + Clear(m_nMusicSpeed); + Clear(m_nMusicTempo); + Clear(m_nNextRow); + Clear(m_nRow); + Clear(m_nPattern); + Clear(m_nCurrentPattern); + Clear(m_nNextPattern); + Clear(m_nRestartPos); + Clear(m_nMasterVolume); + Clear(m_nGlobalVolume); + Clear(m_nSongPreAmp); + Clear(m_nFreqFactor); + Clear(m_nTempoFactor); + Clear(m_nOldGlbVolSlide); + Clear(m_nMinPeriod); + Clear(m_nMaxPeriod); + Clear(m_nRepeatCount); + Clear(m_nInitialRepeatCount); + Clear(m_nGlobalFadeSamples); + Clear(m_nGlobalFadeMaxSamples); + Clear(m_nMaxOrderPosition); + Clear(m_nPatternNames); + Clear(m_lpszSongComments); + Clear(m_lpszPatternNames); + Clear(m_szNames); + Clear(CompressionTable); +} + +CSoundFile::~CSoundFile() { + mpcpplog(); + Destroy(); +} + +BOOL CSoundFile::Create( LPCBYTE lpStream, DWORD dwMemLength ) { + mpcpplog(); + try { + openmpt::module * m = new openmpt::module( lpStream, dwMemLength ); + set_self( this, m ); + std::strncpy( m_szNames[0], mod->get_metadata("title").c_str(), sizeof( m_szNames[0] ) - 1 ); + m_szNames[0][ sizeof( m_szNames[0] ) - 1 ] = '\0'; + std::string type = mod->get_metadata("type"); + m_nType = MOD_TYPE_NONE; + if ( type == "mod" ) { + m_nType = MOD_TYPE_MOD; + } else if ( type == "s3m" ) { + m_nType = MOD_TYPE_S3M; + } else if ( type == "xm" ) { + m_nType = MOD_TYPE_XM; + } else if ( type == "med" ) { + m_nType = MOD_TYPE_MED; + } else if ( type == "mtm" ) { + m_nType = MOD_TYPE_MTM; + } else if ( type == "it" ) { + m_nType = MOD_TYPE_IT; + } else if ( type == "669" ) { + m_nType = MOD_TYPE_669; + } else if ( type == "ult" ) { + m_nType = MOD_TYPE_ULT; + } else if ( type == "stm" ) { + m_nType = MOD_TYPE_STM; + } else if ( type == "far" ) { + m_nType = MOD_TYPE_FAR; + } else if ( type == "s3m" ) { + m_nType = MOD_TYPE_WAV; + } else if ( type == "amf" ) { + m_nType = MOD_TYPE_AMF; + } else if ( type == "ams" ) { + m_nType = MOD_TYPE_AMS; + } else if ( type == "dsm" ) { + m_nType = MOD_TYPE_DSM; + } else if ( type == "mdl" ) { + m_nType = MOD_TYPE_MDL; + } else if ( type == "okt" ) { + m_nType = MOD_TYPE_OKT; + } else if ( type == "mid" ) { + m_nType = MOD_TYPE_MID; + } else if ( type == "dmf" ) { + m_nType = MOD_TYPE_DMF; + } else if ( type == "ptm" ) { + m_nType = MOD_TYPE_PTM; + } else if ( type == "dbm" ) { + m_nType = MOD_TYPE_DBM; + } else if ( type == "mt2" ) { + m_nType = MOD_TYPE_MT2; + } else if ( type == "amf0" ) { + m_nType = MOD_TYPE_AMF0; + } else if ( type == "psm" ) { + m_nType = MOD_TYPE_PSM; + } else if ( type == "j2b" ) { + m_nType = MOD_TYPE_J2B; + } else if ( type == "abc" ) { + m_nType = MOD_TYPE_ABC; + } else if ( type == "pat" ) { + m_nType = MOD_TYPE_PAT; + } else if ( type == "umx" ) { + m_nType = MOD_TYPE_UMX; + } else { + m_nType = MOD_TYPE_IT; // fallback, most complex type + } + m_nChannels = mod->get_num_channels(); + m_nMasterVolume = 128; + m_nSamples = mod->get_num_samples(); + update_state(); + return TRUE; + } catch ( ... ) { + Destroy(); + return FALSE; + } +} + +BOOL CSoundFile::Destroy() { + mpcpplog(); + if ( mod ) { + delete mod; + set_self( this, 0 ); + } + return TRUE; +} + +UINT CSoundFile::GetNumChannels() const { + mpcpplog(); + return mod->get_num_channels(); +} + +static std::int32_t vol128_To_millibel( unsigned int vol ) { + return static_cast( 2000.0 * std::log10( static_cast( vol ) / 128.0 ) ); +} + +BOOL CSoundFile::SetMasterVolume( UINT vol, BOOL bAdjustAGC ) { + UNUSED(bAdjustAGC); + mpcpplog(); + m_nMasterVolume = vol; + mod->set_render_param( openmpt::module::RENDER_MASTERGAIN_MILLIBEL, vol128_To_millibel( m_nMasterVolume ) ); + return TRUE; +} + +UINT CSoundFile::GetNumPatterns() const { + mpcpplog(); + return mod->get_num_patterns(); +} + +UINT CSoundFile::GetNumInstruments() const { + mpcpplog(); + return mod->get_num_instruments(); +} + +void CSoundFile::SetCurrentOrder( UINT nOrder ) { + mpcpplog(); + mod->set_position_order_row( nOrder, 0 ); + update_state(); +} + +UINT CSoundFile::GetSampleName( UINT nSample, LPSTR s ) const { + mpcpplog(); + char buf[32]; + std::memset( buf, 0, 32 ); + if ( mod ) { + std::vector names = mod->get_sample_names(); + if ( 1 <= nSample && nSample <= names.size() ) { + std::strncpy( buf, names[ nSample - 1 ].c_str(), 31 ); + } + } + if ( s ) { + std::strncpy( s, buf, 32 ); + } + return static_cast( std::strlen( buf ) ); +} + +UINT CSoundFile::GetInstrumentName( UINT nInstr, LPSTR s ) const { + mpcpplog(); + char buf[32]; + std::memset( buf, 0, 32 ); + if ( mod ) { + std::vector names = mod->get_instrument_names(); + if ( 1 <= nInstr && nInstr <= names.size() ) { + std::strncpy( buf, names[ nInstr - 1 ].c_str(), 31 ); + } + } + if ( s ) { + std::strncpy( s, buf, 32 ); + } + return static_cast( std::strlen( buf ) ); +} + +void CSoundFile::LoopPattern( int nPat, int nRow ) { + UNUSED(nPat); + UNUSED(nRow); + mpcpplog(); + // todo +} + +void CSoundFile::CheckCPUUsage( UINT nCPU ) { + UNUSED(nCPU); + mpcpplog(); +} + +BOOL CSoundFile::SetPatternName( UINT nPat, LPCSTR lpszName ) { + UNUSED(nPat); + mpcpplog(); + if ( !lpszName ) { + return FALSE; + } + // todo + return TRUE; +} + +BOOL CSoundFile::GetPatternName( UINT nPat, LPSTR lpszName, UINT cbSize ) const { + UNUSED(nPat); + mpcpplog(); + if ( !lpszName || cbSize <= 0 ) { + return FALSE; + } + std::memset( lpszName, 0, cbSize ); + // todo + return TRUE; +} + +BOOL CSoundFile::ReadXM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadS3M(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMod(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMed(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMTM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadSTM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadIT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::Read669(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadUlt(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadWav(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadDSM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadFAR(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMDL(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadOKT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadDMF(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadPTM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadDBM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadAMF(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMT2(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadPSM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadJ2B(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadUMX(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadABC(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::TestABC(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadMID(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::TestMID(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::ReadPAT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } +BOOL CSoundFile::TestPAT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; } + +#ifndef MODPLUG_NO_FILESAVE + +UINT CSoundFile::WriteSample( FILE * f, MODINSTRUMENT * pins, UINT nFlags, UINT nMaxLen ) { + UNUSED(f); + UNUSED(pins); + UNUSED(nFlags); + UNUSED(nMaxLen); + mpcpplog(); + return 0; +} + +BOOL CSoundFile::SaveXM( LPCSTR lpszFileName, UINT nPacking ) { + UNUSED(lpszFileName); + UNUSED(nPacking); + mpcpplog(); + return FALSE; +} + +BOOL CSoundFile::SaveS3M( LPCSTR lpszFileName, UINT nPacking ) { + UNUSED(lpszFileName); + UNUSED(nPacking); + mpcpplog(); + return FALSE; +} + +BOOL CSoundFile::SaveMod( LPCSTR lpszFileName, UINT nPacking ) { + UNUSED(lpszFileName); + UNUSED(nPacking); + mpcpplog(); + return FALSE; +} + +BOOL CSoundFile::SaveIT( LPCSTR lpszFileName, UINT nPacking ) { + UNUSED(lpszFileName); + UNUSED(nPacking); + mpcpplog(); + return FALSE; +} + +#endif + +UINT CSoundFile::GetBestSaveFormat() const { + mpcpplog(); + return MOD_TYPE_IT; +} + +UINT CSoundFile::GetSaveFormats() const { + mpcpplog(); + return MOD_TYPE_IT; +} + +void CSoundFile::ConvertModCommand( MODCOMMAND * ) const { + mpcpplog(); +} + +void CSoundFile::S3MConvert( MODCOMMAND * m, BOOL bIT ) const { + UNUSED(m); + UNUSED(bIT); + mpcpplog(); +} + +void CSoundFile::S3MSaveConvert( UINT * pcmd, UINT * pprm, BOOL bIT ) const { + UNUSED(pcmd); + UNUSED(pprm); + UNUSED(bIT); + mpcpplog(); +} + +WORD CSoundFile::ModSaveCommand( const MODCOMMAND * m, BOOL bXM ) const { + UNUSED(m); + UNUSED(bXM); + mpcpplog(); + return 0; +} + +VOID CSoundFile::ResetChannels() { + mpcpplog(); +} + +UINT CSoundFile::CreateStereoMix( int count ) { + UNUSED(count); + mpcpplog(); + return 0; +} + +BOOL CSoundFile::FadeSong( UINT msec ) { + UNUSED(msec); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::GlobalFadeSong( UINT msec ) { + UNUSED(msec); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::InitPlayer( BOOL bReset ) { + UNUSED(bReset); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::SetMixConfig( UINT nStereoSeparation, UINT nMaxMixChannels ) { + UNUSED(nMaxMixChannels); + mpcpplog(); + m_nStereoSeparation = nStereoSeparation; + return TRUE; +} + +DWORD CSoundFile::InitSysInfo() { + mpcpplog(); + return 0; +} + +void CSoundFile::SetAGC( BOOL b ) { + UNUSED(b); + mpcpplog(); +} + +void CSoundFile::ResetAGC() { + mpcpplog(); +} + +void CSoundFile::ProcessAGC( int count ) { + UNUSED(count); + mpcpplog(); +} + +BOOL CSoundFile::SetWaveConfig( UINT nRate, UINT nBits, UINT nChannels, BOOL bMMX ) { + UNUSED(bMMX); + mpcpplog(); + gdwMixingFreq = nRate; + gnBitsPerSample = nBits; + gnChannels = nChannels; + return TRUE; +} + +BOOL CSoundFile::SetWaveConfigEx( BOOL bSurround, BOOL bNoOverSampling, BOOL bReverb, BOOL hqido, BOOL bMegaBass, BOOL bNR, BOOL bEQ ) { + UNUSED(bSurround); + UNUSED(bReverb); + UNUSED(hqido); + UNUSED(bMegaBass); + UNUSED(bEQ); + mpcpplog(); + DWORD d = gdwSoundSetup & ~(SNDMIX_NORESAMPLING|SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); + if ( bNoOverSampling ) { + d |= SNDMIX_NORESAMPLING; + } else if ( !hqido ) { + d |= 0; + } else if ( !bNR ) { + d |= SNDMIX_HQRESAMPLER; + } else { + d |= (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); + } + gdwSoundSetup = d; + return TRUE; +} + +BOOL CSoundFile::SetResamplingMode( UINT nMode ) { + mpcpplog(); + DWORD d = gdwSoundSetup & ~(SNDMIX_NORESAMPLING|SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); + switch ( nMode ) { + case SRCMODE_NEAREST: + d |= SNDMIX_NORESAMPLING; + break; + case SRCMODE_LINEAR: + break; + case SRCMODE_SPLINE: + d |= SNDMIX_HQRESAMPLER; + break; + case SRCMODE_POLYPHASE: + d |= (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); + break; + default: + return FALSE; + break; + } + gdwSoundSetup = d; + return TRUE; +} + +BOOL CSoundFile::SetReverbParameters( UINT nDepth, UINT nDelay ) { + UNUSED(nDepth); + UNUSED(nDelay); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::SetXBassParameters( UINT nDepth, UINT nRange ) { + UNUSED(nDepth); + UNUSED(nRange); + mpcpplog(); + return TRUE; +} + +BOOL CSoundFile::SetSurroundParameters( UINT nDepth, UINT nDelay ) { + UNUSED(nDepth); + UNUSED(nDelay); + mpcpplog(); + return TRUE; +} + +UINT CSoundFile::GetMaxPosition() const { + mpcpplog(); + // rows in original, just use seconds here + if ( mod ) return static_cast( mod->get_duration_seconds() + 0.5 ); + return 0; +} + +DWORD CSoundFile::GetLength( BOOL bAdjust, BOOL bTotal ) { + UNUSED(bAdjust); + UNUSED(bTotal); + mpcpplog(); + if ( mod ) return static_cast( mod->get_duration_seconds() + 0.5 ); + return 0; +} + +UINT CSoundFile::GetSongComments( LPSTR s, UINT cbsize, UINT linesize ) { + UNUSED(linesize); + mpcpplog(); + if ( !s ) { + return 0; + } + if ( cbsize <= 0 ) { + return 0; + } + if ( !mod ) { + s[0] = '\0'; + return 1; + } + std::strncpy( s, mod->get_metadata("message").c_str(), cbsize ); + s[ cbsize - 1 ] = '\0'; + return static_cast( std::strlen( s ) + 1 ); +} + +UINT CSoundFile::GetRawSongComments( LPSTR s, UINT cbsize, UINT linesize ) { + UNUSED(linesize); + mpcpplog(); + if ( !s ) { + return 0; + } + if ( cbsize <= 0 ) { + return 0; + } + if ( !mod ) { + s[0] = '\0'; + return 1; + } + std::strncpy( s, mod->get_metadata("message_raw").c_str(), cbsize ); + s[ cbsize - 1 ] = '\0'; + return static_cast( std::strlen( s ) + 1 ); +} + +void CSoundFile::SetCurrentPos( UINT nPos ) { + mpcpplog(); + if ( mod ) mod->set_position_seconds( nPos ); + update_state(); +} + +UINT CSoundFile::GetCurrentPos() const { + mpcpplog(); + if ( mod ) return static_cast( mod->get_position_seconds() + 0.5 ); + return 0; +} + +static int get_stereo_separation() { + mpcpplog(); + return CSoundFile::m_nStereoSeparation * 100 / 128; +} + +static int get_filter_length() { + mpcpplog(); + if ( ( CSoundFile::gdwSoundSetup & (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE) ) == (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE) ) { + return 8; + } else if ( ( CSoundFile::gdwSoundSetup & SNDMIX_HQRESAMPLER ) == SNDMIX_HQRESAMPLER ) { + return 4; + } else if ( ( CSoundFile::gdwSoundSetup & SNDMIX_NORESAMPLING ) == SNDMIX_NORESAMPLING ) { + return 1; + } else { + return 2; + } +} + +static std::size_t get_sample_size() { + return (CSoundFile::gnBitsPerSample/8); +} + +static std::size_t get_num_channels() { + return CSoundFile::gnChannels; +} + +static std::size_t get_frame_size() { + return get_sample_size() * get_num_channels(); +} + +static int get_samplerate() { + return CSoundFile::gdwMixingFreq; +} + +UINT CSoundFile::Read( LPVOID lpBuffer, UINT cbBuffer ) { + mpcpplog(); + if ( !mod ) { + return 0; + } + mpcpplog(); + if ( !lpBuffer ) { + return 0; + } + mpcpplog(); + if ( cbBuffer <= 0 ) { + return 0; + } + mpcpplog(); + if ( get_samplerate() <= 0 ) { + return 0; + } + mpcpplog(); + if ( get_sample_size() != 1 && get_sample_size() != 2 && get_sample_size() != 4 ) { + return 0; + } + mpcpplog(); + if ( get_num_channels() != 1 && get_num_channels() != 2 && get_num_channels() != 4 ) { + return 0; + } + mpcpplog(); + std::memset( lpBuffer, 0, cbBuffer ); + const std::size_t frames_torender = cbBuffer / get_frame_size(); + short * out = reinterpret_cast( lpBuffer ); + std::vector tmpbuf; + if ( get_sample_size() == 1 || get_sample_size() == 4 ) { + tmpbuf.resize( frames_torender * get_num_channels() ); + out = &tmpbuf[0]; + } + + mod->set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, get_stereo_separation() ); + mod->set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, get_filter_length() ); + std::size_t frames_rendered = 0; + if ( get_num_channels() == 1 ) { + frames_rendered = mod->read( get_samplerate(), frames_torender, out ); + } else if ( get_num_channels() == 4 ) { + frames_rendered = mod->read_interleaved_quad( get_samplerate(), frames_torender, out ); + } else { + frames_rendered = mod->read_interleaved_stereo( get_samplerate(), frames_torender, out ); + } + + if ( get_sample_size() == 1 ) { + unsigned char * dst = reinterpret_cast( lpBuffer ); + for ( std::size_t sample = 0; sample < frames_rendered * get_num_channels(); ++sample ) { + dst[sample] = ( tmpbuf[sample] / 0x100 ) + 0x80; + } + } else if ( get_sample_size() == 4 ) { + int * dst = reinterpret_cast( lpBuffer ); + for ( std::size_t sample = 0; sample < frames_rendered * get_num_channels(); ++sample ) { + dst[sample] = tmpbuf[sample] << (32-16-1-MIXING_ATTENUATION); + } + } + update_state(); + return static_cast( frames_rendered ); +} + + +/* + +gstreamer modplug calls: + +mSoundFile->Create +mSoundFile->Destroy + +mSoundFile->SetWaveConfig +mSoundFile->SetWaveConfigEx +mSoundFile->SetResamplingMode +mSoundFile->SetSurroundParameters +mSoundFile->SetXBassParameters +mSoundFile->SetReverbParameters + +mSoundFile->GetMaxPosition (inline, -> GetLength) +mSoundFile->GetSongTime + +mSoundFile->GetTitle (inline) +mSoundFile->GetSongComments + +mSoundFile->SetCurrentPos +mSoundFile->Read + +mSoundFile->GetCurrentPos +mSoundFile->GetMusicTempo (inline) + +*/ + + +// really very internal symbols, probably nothing calls these directly + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wunused-parameter" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4100) +#endif + +BOOL CSoundFile::ReadNote() { mpcpplog(); return 0; } +BOOL CSoundFile::ProcessRow() { mpcpplog(); return 0; } +BOOL CSoundFile::ProcessEffects() { mpcpplog(); return 0; } +UINT CSoundFile::GetNNAChannel(UINT nChn) const { mpcpplog(); return 0; } +void CSoundFile::CheckNNA(UINT nChn, UINT instr, int note, BOOL bForceCut) { mpcpplog(); } +void CSoundFile::NoteChange(UINT nChn, int note, BOOL bPorta, BOOL bResetEnv) { mpcpplog(); } +void CSoundFile::InstrumentChange(MODCHANNEL *pChn, UINT instr, BOOL bPorta,BOOL bUpdVol,BOOL bResetEnv) { mpcpplog(); } +void CSoundFile::PortamentoUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::PortamentoDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FinePortamentoUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FinePortamentoDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::ExtraFinePortamentoUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::ExtraFinePortamentoDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::TonePortamento(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::Vibrato(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FineVibrato(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::VolumeSlide(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::PanningSlide(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::ChannelVolSlide(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FineVolumeUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::FineVolumeDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::Tremolo(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::Panbrello(MODCHANNEL *pChn, UINT param) { mpcpplog(); } +void CSoundFile::RetrigNote(UINT nChn, UINT param) { mpcpplog(); } +void CSoundFile::NoteCut(UINT nChn, UINT nTick) { mpcpplog(); } +void CSoundFile::KeyOff(UINT nChn) { mpcpplog(); } +int CSoundFile::PatternLoop(MODCHANNEL *, UINT param) { mpcpplog(); return 0; } +void CSoundFile::ExtendedMODCommands(UINT nChn, UINT param) { mpcpplog(); } +void CSoundFile::ExtendedS3MCommands(UINT nChn, UINT param) { mpcpplog(); } +void CSoundFile::ExtendedChannelEffect(MODCHANNEL *, UINT param) { mpcpplog(); } +void CSoundFile::ProcessMidiMacro(UINT nChn, LPCSTR pszMidiMacro, UINT param) { mpcpplog(); } +void CSoundFile::SetupChannelFilter(MODCHANNEL *pChn, BOOL bReset, int flt_modifier) const { mpcpplog(); } +void CSoundFile::DoFreqSlide(MODCHANNEL *pChn, LONG nFreqSlide) { mpcpplog(); } +void CSoundFile::SetTempo(UINT param) { mpcpplog(); } +void CSoundFile::SetSpeed(UINT param) { mpcpplog(); } +void CSoundFile::GlobalVolSlide(UINT param) { mpcpplog(); } +DWORD CSoundFile::IsSongFinished(UINT nOrder, UINT nRow) const { mpcpplog(); return 0; } +BOOL CSoundFile::IsValidBackwardJump(UINT nStartOrder, UINT nStartRow, UINT nJumpOrder, UINT nJumpRow) const { mpcpplog(); return 0; } +UINT CSoundFile::PackSample(int &sample, int next) { mpcpplog(); return 0; } +BOOL CSoundFile::CanPackSample(LPSTR pSample, UINT nLen, UINT nPacking, BYTE *result) { mpcpplog(); return 0; } +UINT CSoundFile::ReadSample(MODINSTRUMENT *pIns, UINT nFlags, LPCSTR pMemFile, DWORD dwMemLength) { mpcpplog(); return 0; } +BOOL CSoundFile::DestroySample(UINT nSample) { mpcpplog(); return 0; } +BOOL CSoundFile::DestroyInstrument(UINT nInstr) { mpcpplog(); return 0; } +BOOL CSoundFile::IsSampleUsed(UINT nSample) { mpcpplog(); return 0; } +BOOL CSoundFile::IsInstrumentUsed(UINT nInstr) { mpcpplog(); return 0; } +BOOL CSoundFile::RemoveInstrumentSamples(UINT nInstr) { mpcpplog(); return 0; } +UINT CSoundFile::DetectUnusedSamples(BOOL *) { mpcpplog(); return 0; } +BOOL CSoundFile::RemoveSelectedSamples(BOOL *) { mpcpplog(); return 0; } +void CSoundFile::AdjustSampleLoop(MODINSTRUMENT *pIns) { mpcpplog(); } +BOOL CSoundFile::ReadInstrumentFromSong(UINT nInstr, CSoundFile *, UINT nSrcInstrument) { mpcpplog(); return 0; } +BOOL CSoundFile::ReadSampleFromSong(UINT nSample, CSoundFile *, UINT nSrcSample) { mpcpplog(); return 0; } +UINT CSoundFile::GetNoteFromPeriod(UINT period) const { mpcpplog(); return 0; } +UINT CSoundFile::GetPeriodFromNote(UINT note, int nFineTune, UINT nC4Speed) const { mpcpplog(); return 0; } +UINT CSoundFile::GetFreqFromPeriod(UINT period, UINT nC4Speed, int nPeriodFrac) const { mpcpplog(); return 0; } +void CSoundFile::ResetMidiCfg() { mpcpplog(); } +UINT CSoundFile::MapMidiInstrument(DWORD dwProgram, UINT nChannel, UINT nNote) { mpcpplog(); return 0; } +BOOL CSoundFile::ITInstrToMPT(const void *p, INSTRUMENTHEADER *penv, UINT trkvers) { mpcpplog(); return 0; } +UINT CSoundFile::SaveMixPlugins(FILE *f, BOOL bUpdate) { mpcpplog(); return 0; } +UINT CSoundFile::LoadMixPlugins(const void *pData, UINT nLen) { mpcpplog(); return 0; } +#ifndef NO_FILTER +DWORD CSoundFile::CutOffToFrequency(UINT nCutOff, int flt_modifier) const { mpcpplog(); return 0; } +#endif +DWORD CSoundFile::TransposeToFrequency(int transp, int ftune) { mpcpplog(); return 0; } +int CSoundFile::FrequencyToTranspose(DWORD freq) { mpcpplog(); return 0; } +void CSoundFile::FrequencyToTranspose(MODINSTRUMENT *psmp) { mpcpplog(); } +MODCOMMAND *CSoundFile::AllocatePattern(UINT rows, UINT nchns) { mpcpplog(); return 0; } +signed char* CSoundFile::AllocateSample(UINT nbytes) { mpcpplog(); return 0; } +void CSoundFile::FreePattern(LPVOID pat) { mpcpplog(); } +void CSoundFile::FreeSample(LPVOID p) { mpcpplog(); } +UINT CSoundFile::Normalize24BitBuffer(LPBYTE pbuffer, UINT cbsizebytes, DWORD lmax24, DWORD dwByteInc) { mpcpplog(); return 0; } + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + + +#endif // NO_LIBMODPLUG diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/m4/ax_cxx_compile_stdcxx.m4 b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/m4/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 000000000..0b6cb3a7d --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1,972 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the specified +# version of the C++ standard. If necessary, add switches to CXX and +# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) +# or '14' (for the C++14 standard). +# +# The second argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The third argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline support for the specified C++ standard is +# required and that the macro should error out if no mode with that +# support is found. If specified 'optional', then configuration proceeds +# regardless, after defining HAVE_CXX${VERSION} if and only if a +# supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016, 2018 Krzesimir Nowak +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 9 + +dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro +dnl (serial version number 13). + +AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl + m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], + [$1], [14], [ax_cxx_compile_alternatives="14 1y"], + [$1], [17], [ax_cxx_compile_alternatives="17 1z"], + [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$2], [], [], + [$2], [ext], [], + [$2], [noext], [], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], + [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], + [$3], [optional], [ax_cxx_compile_cxx$1_required=false], + [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + + m4_if([$2], [noext], [], [dnl + if test x$ac_success = xno; then + for alternative in ${ax_cxx_compile_alternatives}; do + switch="-std=gnu++${alternative}" + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + + m4_if([$2], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for alternative in ${ax_cxx_compile_alternatives}; do + for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + if test x$ac_success = xyes; then + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx$1_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) + fi + fi + if test x$ac_success = xno; then + HAVE_CXX$1=0 + AC_MSG_NOTICE([No compiler with C++$1 support was found]) + else + HAVE_CXX$1=1 + AC_DEFINE(HAVE_CXX$1,1, + [define if the compiler supports basic C++$1 syntax]) + fi + AC_SUBST(HAVE_CXX$1) +]) + + +dnl Test body for checking C++11 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 +) + + +dnl Test body for checking C++14 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 +) + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 +) + +dnl Tests for new features in C++11 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual void f() {} + }; + + struct Derived : public Base + { + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check single_type; + typedef check> double_type; + typedef check>> triple_type; + typedef check>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template + struct sum; + + template + struct sum + { + static constexpr auto value = N0 + sum::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { func(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + +]]) + + +dnl Tests for new features in C++14 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_separators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + +]]) + + +dnl Tests for new features in C++17 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ + +// If the compiler admits that it is not ready for C++17, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus <= 201402L + +#error "This is not a C++17 compiler" + +#else + +#if defined(__clang__) + #define REALLY_CLANG +#else + #if defined(__GNUC__) + #define REALLY_GCC + #endif +#endif + +#include +#include +#include + +namespace cxx17 +{ + +#if !defined(REALLY_CLANG) + namespace test_constexpr_lambdas + { + + // TODO: test it with clang++ from git + + constexpr int foo = [](){return 42;}(); + + } +#endif // !defined(REALLY_CLANG) + + namespace test::nested_namespace::definitions + { + + } + + namespace test_fold_expression + { + + template + int multiply(Args... args) + { + return (args * ... * 1); + } + + template + bool all(Args... args) + { + return (args && ...); + } + + } + + namespace test_extended_static_assert + { + + static_assert (true); + + } + + namespace test_auto_brace_init_list + { + + auto foo = {5}; + auto bar {5}; + + static_assert(std::is_same, decltype(foo)>::value); + static_assert(std::is_same::value); + } + + namespace test_typename_in_template_template_parameter + { + + template typename X> struct D; + + } + + namespace test_fallthrough_nodiscard_maybe_unused_attributes + { + + int f1() + { + return 42; + } + + [[nodiscard]] int f2() + { + [[maybe_unused]] auto unused = f1(); + + switch (f1()) + { + case 17: + f1(); + [[fallthrough]]; + case 42: + f1(); + } + return f1(); + } + + } + + namespace test_extended_aggregate_initialization + { + + struct base1 + { + int b1, b2 = 42; + }; + + struct base2 + { + base2() { + b3 = 42; + } + int b3; + }; + + struct derived : base1, base2 + { + int d; + }; + + derived d1 {{1, 2}, {}, 4}; // full initialization + derived d2 {{}, {}, 4}; // value-initialized bases + + } + + namespace test_general_range_based_for_loop + { + + struct iter + { + int i; + + int& operator* () + { + return i; + } + + const int& operator* () const + { + return i; + } + + iter& operator++() + { + ++i; + return *this; + } + }; + + struct sentinel + { + int i; + }; + + bool operator== (const iter& i, const sentinel& s) + { + return i.i == s.i; + } + + bool operator!= (const iter& i, const sentinel& s) + { + return !(i == s); + } + + struct range + { + iter begin() const + { + return {0}; + } + + sentinel end() const + { + return {5}; + } + }; + + void f() + { + range r {}; + + for (auto i : r) + { + [[maybe_unused]] auto v = i; + } + } + + } + + namespace test_lambda_capture_asterisk_this_by_value + { + + struct t + { + int i; + int foo() + { + return [*this]() + { + return i; + }(); + } + }; + + } + + namespace test_enum_class_construction + { + + enum class byte : unsigned char + {}; + + byte foo {42}; + + } + + namespace test_constexpr_if + { + + template + int f () + { + if constexpr(cond) + { + return 13; + } + else + { + return 42; + } + } + + } + + namespace test_selection_statement_with_initializer + { + + int f() + { + return 13; + } + + int f2() + { + if (auto i = f(); i > 0) + { + return 3; + } + + switch (auto i = f(); i + 4) + { + case 17: + return 2; + + default: + return 1; + } + } + + } + +#if !defined(REALLY_CLANG) + namespace test_template_argument_deduction_for_class_templates + { + + // TODO: test it with clang++ from git + + template + struct pair + { + pair (T1 p1, T2 p2) + : m1 {p1}, + m2 {p2} + {} + + T1 m1; + T2 m2; + }; + + void f() + { + [[maybe_unused]] auto p = pair{13, 42u}; + } + + } +#endif // !defined(REALLY_CLANG) + + namespace test_non_type_auto_template_parameters + { + + template + struct B + {}; + + B<5> b1; + B<'a'> b2; + + } + +#if !defined(REALLY_CLANG) + namespace test_structured_bindings + { + + // TODO: test it with clang++ from git + + int arr[2] = { 1, 2 }; + std::pair pr = { 1, 2 }; + + auto f1() -> int(&)[2] + { + return arr; + } + + auto f2() -> std::pair& + { + return pr; + } + + struct S + { + int x1 : 2; + volatile double y1; + }; + + S f3() + { + return {}; + } + + auto [ x1, y1 ] = f1(); + auto& [ xr1, yr1 ] = f1(); + auto [ x2, y2 ] = f2(); + auto& [ xr2, yr2 ] = f2(); + const auto [ x3, y3 ] = f3(); + + } +#endif // !defined(REALLY_CLANG) + +#if !defined(REALLY_CLANG) + namespace test_exception_spec_type_system + { + + // TODO: test it with clang++ from git + + struct Good {}; + struct Bad {}; + + void g1() noexcept; + void g2(); + + template + Bad + f(T*, T*); + + template + Good + f(T1*, T2*); + + static_assert (std::is_same_v); + + } +#endif // !defined(REALLY_CLANG) + + namespace test_inline_variables + { + + template void f(T) + {} + + template inline T g(T) + { + return T{}; + } + + template<> inline void f<>(int) + {} + + template<> int g<>(int) + { + return 5; + } + + } + +} // namespace cxx17 + +#endif // __cplusplus <= 201402L + +]]) diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/test.sh b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/test.sh new file mode 100755 index 000000000..7df7ed3fb --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/libmodplug-0.8.9.0/test.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -e + +./autogen.sh + +./configure +make +make distcheck +make distclean diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/toolchain-djgpp/build.sh b/Frameworks/OpenMPT/OpenMPT/contrib/toolchain-djgpp/build.sh new file mode 100755 index 000000000..7d2d1a2c2 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/toolchain-djgpp/build.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +# apt install \ +# autoconf \ +# autoconf-archive \ +# autotools \ +# bison \ +# bzip2 \ +# curl \ +# dos2unix \ +# flex \ +# g++ \ +# gcc \ +# gzip \ +# make \ +# make \ +# pkg-config \ +# tar \ +# texinfo \ +# unzip \ +# wget \ +# xz-utils \ +# zlib1g-dev \ +# + +set -e + +MY_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +cd "${MY_DIR}" + +rm -rf build +rm -rf prefix + +mkdir build +mkdir prefix + +cd build +git clone https://github.com/jwt27/build-gcc build-gcc +cd build-gcc +git checkout 5d5747cc6da983e493a4258ff2009c338b2d6f60 # 2020-05-09 + +CFLAGS_FOR_TARGET="-O2" CXXFLAGS_FOR_TARGET="-O2" ./build-djgpp.sh --batch --prefix="${MY_DIR}/prefix" all diff --git a/Frameworks/OpenMPT/OpenMPT/contrib/toolchain-djgpp/setenv.sh b/Frameworks/OpenMPT/OpenMPT/contrib/toolchain-djgpp/setenv.sh new file mode 100755 index 000000000..5acdd5026 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/contrib/toolchain-djgpp/setenv.sh @@ -0,0 +1 @@ +export PATH="`pwd`/contrib/toolchain-djgpp/prefix/bin":${PATH} diff --git a/Frameworks/OpenMPT/OpenMPT/doc/contributing.md b/Frameworks/OpenMPT/OpenMPT/doc/contributing.md index ee1894088..e3eaa7331 100644 --- a/Frameworks/OpenMPT/OpenMPT/doc/contributing.md +++ b/Frameworks/OpenMPT/OpenMPT/doc/contributing.md @@ -2,8 +2,8 @@ Contributing ============ -OpenMPT, libopenmpt, openmpt123, xmp-openmpt, in_openmpt and foo_openmpt are -developed in the Subversion repository at +OpenMPT, libopenmpt, openmpt123, xmp-openmpt and in_openmpt are developed in +the Subversion repository at [https://source.openmpt.org/browse/openmpt/trunk/OpenMPT/](https://source.openmpt.org/browse/openmpt/trunk/OpenMPT/). Patches can be provided either against this Subversion repository or against our GitHub mirror at diff --git a/Frameworks/OpenMPT/OpenMPT/doc/module_formats.md b/Frameworks/OpenMPT/OpenMPT/doc/module_formats.md index f93c6dc8d..eb1e0b2b6 100644 --- a/Frameworks/OpenMPT/OpenMPT/doc/module_formats.md +++ b/Frameworks/OpenMPT/OpenMPT/doc/module_formats.md @@ -29,15 +29,11 @@ General hints determined beforehand. * Strings can be safely handled using: * `FileReader::ReadString` and friends for reading them directly from a file - * `mpt::String::Read` for reading them from a struct or char array, - * `mpt::String::Copy` for copying between char arrays or `std::string`. + * `mpt::String::ReadBuf` for reading them from a struct or char array - "Read" functions take care of string padding (zero / space padding), so those - should be used when extracting strings from files. "Copy" should only be used - on strings that have previously been read using the "Read" functions. - If the target is a char array rather than a `std::string`, these will take - care of properly null-terminating the target char array, and prevent reading - past the end of a (supposedly null-terminated) source char array. + These functions take care of string padding (zero / space padding) and will + avoid reading past the end of the string if there is no terminating null + character. * Do not use non-const static variables in your loader. Loaders need to be thread-safe for libopenmpt. * `FileReader` instances may be used to treat a portion of another file as its @@ -52,7 +48,8 @@ General hints `MODTYPE`. * Do not rely on hard-coded magic numbers. For example, when comparing if an index is valid for a given array, do not hard-code the array size but rather - use `mpt::size` or, for ensuring that char arrays are null-terminated, + use `std::size` (or `mpt::array_size` in contexts where `std::size` is not + usable) or, for ensuring that char arrays are null-terminated, `mpt::String::SetNullTerminator`. Similarly, do not assume any specific quantities for OpenMPT's constants like MAX_SAMPLES, MAX_PATTERN_ROWS, etc. These may change at any time. diff --git a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c.c b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c.c index 550f2d030..2dcd13d33 100644 --- a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c.c +++ b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c.c @@ -28,6 +28,8 @@ static int16_t left[BUFFERSIZE]; static int16_t right[BUFFERSIZE]; static int16_t * const buffers[2] = { left, right }; +static int16_t interleaved_buffer[BUFFERSIZE * 2]; +static int is_interleaved = 0; static void libopenmpt_example_logfunc( const char * message, void * userdata ) { (void)userdata; @@ -126,6 +128,10 @@ int main( int argc, char * argv[] ) { pa_initialized = 1; pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16 | paNonInterleaved, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); + if ( pa_error == paSampleFormatNotSupported ) { + is_interleaved = 1; + pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); + } if ( pa_error != paNoError ) { fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." ); goto fail; @@ -144,7 +150,7 @@ int main( int argc, char * argv[] ) { while ( 1 ) { openmpt_module_error_clear( mod ); - count = openmpt_module_read_stereo( mod, SAMPLERATE, BUFFERSIZE, left, right ); + count = is_interleaved ? openmpt_module_read_interleaved_stereo( mod, SAMPLERATE, BUFFERSIZE, interleaved_buffer ) : 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 ) { @@ -156,7 +162,7 @@ int main( int argc, char * argv[] ) { break; } - pa_error = Pa_WriteStream( stream, buffers, (unsigned long)count ); + pa_error = is_interleaved ? Pa_WriteStream( stream, interleaved_buffer, (unsigned long)count ) : Pa_WriteStream( stream, buffers, (unsigned long)count ); if ( pa_error == paOutputUnderflowed ) { pa_error = paNoError; } diff --git a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_mem.c b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_mem.c index 6a4057505..dda6379dc 100644 --- a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_mem.c +++ b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_c_mem.c @@ -27,6 +27,8 @@ static int16_t left[BUFFERSIZE]; static int16_t right[BUFFERSIZE]; static int16_t * const buffers[2] = { left, right }; +static int16_t interleaved_buffer[BUFFERSIZE * 2]; +static int is_interleaved = 0; static void libopenmpt_example_logfunc( const char * message, void * userdata ) { (void)userdata; @@ -214,7 +216,12 @@ int main( int argc, char * argv[] ) { } pa_initialized = 1; + is_interleaved = 0; pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16 | paNonInterleaved, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); + if ( pa_error == paSampleFormatNotSupported ) { + is_interleaved = 1; + pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL ); + } if ( pa_error != paNoError ) { fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." ); goto fail; @@ -233,7 +240,7 @@ int main( int argc, char * argv[] ) { while ( 1 ) { openmpt_module_error_clear( mod ); - count = openmpt_module_read_stereo( mod, SAMPLERATE, BUFFERSIZE, left, right ); + count = is_interleaved ? openmpt_module_read_interleaved_stereo( mod, SAMPLERATE, BUFFERSIZE, interleaved_buffer ) : 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 ) { @@ -245,7 +252,7 @@ int main( int argc, char * argv[] ) { break; } - pa_error = Pa_WriteStream( stream, buffers, (unsigned long)count ); + pa_error = is_interleaved ? Pa_WriteStream( stream, interleaved_buffer, (unsigned long)count ) : Pa_WriteStream( stream, buffers, (unsigned long)count ); if ( pa_error == paOutputUnderflowed ) { pa_error = paNoError; } diff --git a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_cxx.cpp b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_cxx.cpp index b3eb4e70e..199308248 100644 --- a/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_cxx.cpp +++ b/Frameworks/OpenMPT/OpenMPT/examples/libopenmpt_example_cxx.cpp @@ -20,22 +20,18 @@ #include -#if (__cplusplus >= 201103L) #if defined(__clang__) #if ((__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) >= 40000) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec" #endif #endif -#endif #include -#if (__cplusplus >= 201103L) #if defined(__clang__) #if ((__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) >= 40000) #pragma clang diagnostic pop #endif #endif -#endif #if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) ) #if defined( __GNUC__ ) @@ -51,26 +47,53 @@ int main( int argc, char * argv[] ) { if ( argc != 2 ) { throw std::runtime_error( "Usage: libopenmpt_example_cxx SOMEMODULE" ); } - const std::size_t buffersize = 480; - const std::int32_t samplerate = 48000; - std::vector left( buffersize ); - std::vector right( buffersize ); - const float * const buffers[2] = { left.data(), right.data() }; + constexpr std::size_t buffersize = 480; + constexpr std::int32_t samplerate = 48000; std::ifstream file( argv[1], std::ios::binary ); openmpt::module mod( file ); portaudio::AutoSystem portaudio_initializer; portaudio::System & portaudio = portaudio::System::instance(); + std::vector left( buffersize ); + std::vector right( buffersize ); + std::vector interleaved_buffer( buffersize * 2 ); + bool is_interleaved = false; +#if defined(_MSC_VER) && defined(_PREFAST_) + // work-around bug in VS2019 MSVC 16.5.5 static analyzer + is_interleaved = false; 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 ); +#else + portaudio::BlockingStream stream = [&]() { + try { + is_interleaved = false; + 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 ); + return portaudio::BlockingStream( stream_parameters ); + } catch ( const portaudio::PaException & e ) { + if ( e.paError() != paSampleFormatNotSupported ) { + throw; + } + is_interleaved = true; + portaudio::DirectionSpecificStreamParameters outputstream_parameters( portaudio.defaultOutputDevice(), 2, portaudio::FLOAT32, true, portaudio.defaultOutputDevice().defaultHighOutputLatency(), 0 ); + portaudio::StreamParameters stream_parameters( portaudio::DirectionSpecificStreamParameters::null(), outputstream_parameters, samplerate, paFramesPerBufferUnspecified, paNoFlag ); + return portaudio::BlockingStream( stream_parameters ); + } + }(); +#endif stream.start(); while ( true ) { - std::size_t count = mod.read( samplerate, buffersize, left.data(), right.data() ); + std::size_t count = is_interleaved ? mod.read_interleaved_stereo( samplerate, buffersize, interleaved_buffer.data() ) : mod.read( samplerate, buffersize, left.data(), right.data() ); if ( count == 0 ) { break; } try { - stream.write( buffers, static_cast( count ) ); + if ( is_interleaved ) { + stream.write( interleaved_buffer.data(), static_cast( count ) ); + } else { + const float * const buffers[2] = { left.data(), right.data() }; + stream.write( buffers, static_cast( count ) ); + } } catch ( const portaudio::PaException & pa_exception ) { if ( pa_exception.paError() != paOutputUnderflowed ) { throw; diff --git a/Frameworks/OpenMPT/OpenMPT/include/minimp3/OpenMPT.txt b/Frameworks/OpenMPT/OpenMPT/include/minimp3/OpenMPT.txt index 31b85dc15..2cf182f4d 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/minimp3/OpenMPT.txt +++ b/Frameworks/OpenMPT/OpenMPT/include/minimp3/OpenMPT.txt @@ -1,4 +1,4 @@ minimp3 library from https://github.com/lieff/minimp3 -commit 977514a6dfc4960d819a103f43b358e58ac6c28f (2019-07-24) +commit 55da78cbeea5fb6757f8df672567714e1e8ca3e9 (2020-03-04) The following changes have been made: * minimp3.c has been added diff --git a/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.h b/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.h index 6015bb9fa..6fd645d7e 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.h +++ b/Frameworks/OpenMPT/OpenMPT/include/minimp3/minimp3.h @@ -12,7 +12,7 @@ typedef struct { - int frame_bytes, channels, hz, layer, bitrate_kbps; + int frame_bytes, frame_offset, channels, hz, layer, bitrate_kbps; } mp3dec_frame_info_t; typedef struct @@ -163,6 +163,7 @@ end: } #elif defined(__ARM_NEON) || defined(__aarch64__) #include +#define HAVE_SSE 0 #define HAVE_SIMD 1 #define VSTORE vst1q_f32 #define VLD vld1q_f32 @@ -180,6 +181,7 @@ static int have_simd() return 1; } #else /* SIMD checks... */ +#define HAVE_SSE 0 #define HAVE_SIMD 0 #ifdef MINIMP3_ONLY_SIMD #error MINIMP3_ONLY_SIMD used, but SSE/NEON not enabled @@ -1672,7 +1674,7 @@ static int mp3d_find_frame(const uint8_t *mp3, int mp3_bytes, int *free_format_b } } *ptr_frame_bytes = 0; - return i; + return mp3_bytes; } void mp3dec_init(mp3dec_t *dec) @@ -1709,6 +1711,7 @@ int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_s hdr = mp3 + i; memcpy(dec->header, hdr, HDR_SIZE); info->frame_bytes = i + frame_size; + info->frame_offset = i; info->channels = HDR_IS_MONO(hdr) ? 1 : 2; info->hz = hdr_sample_rate_hz(hdr); info->layer = 4 - HDR_GET_LAYER(hdr); @@ -1777,60 +1780,56 @@ int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_s #ifdef MINIMP3_FLOAT_OUTPUT void mp3dec_f32_to_s16(const float *in, int16_t *out, int num_samples) { - if(num_samples > 0) - { - int i = 0; + int i = 0; #if HAVE_SIMD - int aligned_count = num_samples & ~7; - - for(;i < aligned_count;i+=8) - { - static const f4 g_scale = { 32768.0f, 32768.0f, 32768.0f, 32768.0f }; - f4 a = VMUL(VLD(&in[i ]), g_scale); - f4 b = VMUL(VLD(&in[i+4]), g_scale); + int aligned_count = num_samples & ~7; + for(; i < aligned_count; i += 8) + { + static const f4 g_scale = { 32768.0f, 32768.0f, 32768.0f, 32768.0f }; + f4 a = VMUL(VLD(&in[i ]), g_scale); + f4 b = VMUL(VLD(&in[i+4]), g_scale); #if HAVE_SSE - static const f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; - static const f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; - __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), - _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); - out[i ] = _mm_extract_epi16(pcm8, 0); - out[i+1] = _mm_extract_epi16(pcm8, 1); - out[i+2] = _mm_extract_epi16(pcm8, 2); - out[i+3] = _mm_extract_epi16(pcm8, 3); - out[i+4] = _mm_extract_epi16(pcm8, 4); - out[i+5] = _mm_extract_epi16(pcm8, 5); - out[i+6] = _mm_extract_epi16(pcm8, 6); - out[i+7] = _mm_extract_epi16(pcm8, 7); + static const f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; + static const f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; + __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), + _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); + out[i ] = _mm_extract_epi16(pcm8, 0); + out[i+1] = _mm_extract_epi16(pcm8, 1); + out[i+2] = _mm_extract_epi16(pcm8, 2); + out[i+3] = _mm_extract_epi16(pcm8, 3); + out[i+4] = _mm_extract_epi16(pcm8, 4); + out[i+5] = _mm_extract_epi16(pcm8, 5); + out[i+6] = _mm_extract_epi16(pcm8, 6); + out[i+7] = _mm_extract_epi16(pcm8, 7); #else /* HAVE_SSE */ - int16x4_t pcma, pcmb; - a = VADD(a, VSET(0.5f)); - b = VADD(b, VSET(0.5f)); - pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, VSET(0))))); - pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, VSET(0))))); - vst1_lane_s16(out+i , pcma, 0); - vst1_lane_s16(out+i+1, pcma, 1); - vst1_lane_s16(out+i+2, pcma, 2); - vst1_lane_s16(out+i+3, pcma, 3); - vst1_lane_s16(out+i+4, pcmb, 0); - vst1_lane_s16(out+i+5, pcmb, 1); - vst1_lane_s16(out+i+6, pcmb, 2); - vst1_lane_s16(out+i+7, pcmb, 3); + int16x4_t pcma, pcmb; + a = VADD(a, VSET(0.5f)); + b = VADD(b, VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, VSET(0))))); + vst1_lane_s16(out+i , pcma, 0); + vst1_lane_s16(out+i+1, pcma, 1); + vst1_lane_s16(out+i+2, pcma, 2); + vst1_lane_s16(out+i+3, pcma, 3); + vst1_lane_s16(out+i+4, pcmb, 0); + vst1_lane_s16(out+i+5, pcmb, 1); + vst1_lane_s16(out+i+6, pcmb, 2); + vst1_lane_s16(out+i+7, pcmb, 3); #endif /* HAVE_SSE */ - } + } #endif /* HAVE_SIMD */ - for(; i < num_samples; i++) + for(; i < num_samples; i++) + { + float sample = in[i] * 32768.0f; + if (sample >= 32766.5) + out[i] = (int16_t) 32767; + else if (sample <= -32767.5) + out[i] = (int16_t)-32768; + else { - float sample = in[i] * 32768.0f; - if (sample >= 32766.5) - out[i] = (int16_t) 32767; - else if (sample <= -32767.5) - out[i] = (int16_t)-32768; - else - { - int16_t s = (int16_t)(sample + .5f); - s -= (s < 0); /* away from zero, to be compliant */ - out[i] = s; - } + int16_t s = (int16_t)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ + out[i] = s; } } } diff --git a/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt index 44eeb833b..6d32ad581 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt +++ b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/OpenMPT.txt @@ -1,15 +1,12 @@ This folder contains the stb_vorbis library from -https://github.com/nothings/stb/blob/master/stb_vorbis.c v1.17 -commit 130f28df68c3fdbf043c4275260ba1c870495b5b (2019-08-09) +https://github.com/nothings/stb/blob/master/stb_vorbis.c v1.20 +commit b42009b3b9d4ca35bc703f5310eedc74f584be58 (2020-07-13) 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. diff --git a/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c index eb9bf1cfc..75fabf7d6 100644 --- a/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c +++ b/Frameworks/OpenMPT/OpenMPT/include/stb_vorbis/stb_vorbis.c @@ -1,4 +1,4 @@ -// Ogg Vorbis audio decoder - v1.17 - public domain +// Ogg Vorbis audio decoder - v1.20 - public domain // http://nothings.org/stb_vorbis/ // // Original version written by Sean Barrett in 2007. @@ -26,20 +26,25 @@ // Terje Mathisen Niklas Frykholm Andy Hill // Casey Muratori John Bolton Gargaj // Laurent Gomila Marc LeBlanc Ronny Chevalier -// Bernhard Wodo Evan Balster alxprd@github +// Bernhard Wodo Evan Balster github:alxprd // Tom Beaumont Ingo Leitgeb Nicolas Guillemot // Phillip Bennefall Rohit Thiago Goulart -// manxorist@github saga musix github:infatum -// Timur Gagiev Maxwell Koo +// github:manxorist saga musix github:infatum +// Timur Gagiev Maxwell Koo Peter Waller +// github:audinowho Dougall Johnson David Reid +// github:Clownacy Pedro J. Estebanez Remi Verschelde // // Partial history: +// 1.20 - 2020-07-11 - several small fixes +// 1.19 - 2020-02-05 - warnings +// 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc. // 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure) // 1.16 - 2019-03-04 - fix warnings // 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found // 1.14 - 2018-02-11 - delete bogus dealloca usage // 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) // 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files -// 1.11 - 2017-07-23 - fix MinGW compilation +// 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 @@ -130,9 +135,20 @@ typedef struct int max_frame_size; } stb_vorbis_info; +typedef struct +{ + char *vendor; + + int comment_list_length; + char **comment_list; +} stb_vorbis_comment; + // get general information about the file extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); +// get ogg comments +extern stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f); + // get the last error detected (clears it, too) extern int stb_vorbis_get_error(stb_vorbis *f); @@ -565,7 +581,7 @@ enum STBVorbisError #if defined(_MSC_VER) || defined(__MINGW32__) #include #endif - #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) + #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__) #include #endif #else // STB_VORBIS_NO_CRT @@ -588,7 +604,9 @@ enum STBVorbisError #endif #define __forceinline #if 0 // OpenMPT + #ifndef alloca #define alloca __builtin_alloca + #endif #endif // OpenMPT #elif !defined(_MSC_VER) #if __GNUC__ @@ -763,6 +781,10 @@ struct stb_vorbis unsigned int temp_memory_required; unsigned int setup_temp_memory_required; + char *vendor; + int comment_list_length; + char **comment_list; + // input config #ifndef STB_VORBIS_NO_STDIO FILE *f; @@ -778,8 +800,11 @@ struct stb_vorbis uint8 push_mode; + // the page to seek to when seeking to start, may be zero uint32 first_audio_page_offset; + // p_first is the page on which the first audio packet ends + // (but not necessarily the page on which it starts) ProbedPage p_first, p_last; // memory management @@ -828,7 +853,7 @@ struct stb_vorbis int current_loc_valid; // per-blocksize precomputed data - + // twiddle factors float *A[2],*B[2],*C[2]; float *window[2]; @@ -893,10 +918,10 @@ static int error(vorb *f, enum STBVorbisError e) #if 0 // OpenMPT #define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) -#define temp_free(f,p) 0 +#define temp_free(f,p) (void)0 #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 +#define temp_free(f,p) (f->alloc.alloc_buffer ? (void)0 : free(p)) // OpenMPT #endif // OpenMPT #define temp_alloc_save(f) ((f)->temp_offset) #define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) @@ -918,7 +943,7 @@ static void *make_block_array(void *mem, int count, int size) static void *setup_malloc(vorb *f, int sz) { - sz = (sz+3) & ~3; + sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. f->setup_memory_required += sz; if (f->alloc.alloc_buffer) { void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; @@ -937,7 +962,7 @@ static void setup_free(vorb *f, void *p) static void *setup_temp_malloc(vorb *f, int sz) { - sz = (sz+3) & ~3; + sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. if (f->alloc.alloc_buffer) { if (f->temp_offset - sz < f->setup_offset) return NULL; f->temp_offset -= sz; @@ -949,7 +974,7 @@ static void *setup_temp_malloc(vorb *f, int sz) static void setup_temp_free(vorb *f, void *p, int sz) { if (f->alloc.alloc_buffer) { - f->temp_offset += (sz+3)&~3; + f->temp_offset += (sz+7)&~7; return; } free(p); @@ -1155,7 +1180,7 @@ static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) if (!c->sparse) { int k = 0; for (i=0; i < c->entries; ++i) - if (include_in_sort(c, lengths[i])) + if (include_in_sort(c, lengths[i])) c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); assert(k == c->sorted_entries); } else { @@ -1338,7 +1363,7 @@ static int getn(vorb *z, uint8 *data, int n) return 1; } - #ifndef STB_VORBIS_NO_STDIO + #ifndef STB_VORBIS_NO_STDIO if (fread(data, n, 1, z->f) == 1) return 1; else { @@ -1413,12 +1438,15 @@ static int capture_pattern(vorb *f) static int start_page_no_capturepattern(vorb *f) { uint32 loc0,loc1,n; + if (f->first_decode && !IS_PUSH_MODE(f)) { + f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4; + } // 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); + loc0 = get32(f); loc1 = get32(f); // @TODO: validate loc0,loc1 as valid positions? // stream serial number -- vorbis doesn't interleave, so discard @@ -1449,15 +1477,12 @@ static int start_page_no_capturepattern(vorb *f) } 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->p_first.page_end = f->p_first.page_start + len; + f->p_first.last_decoded_sample = loc0; } f->next_seg = 0; return TRUE; @@ -1548,6 +1573,16 @@ static int get8_packet(vorb *f) return x; } +static int get32_packet(vorb *f) +{ + uint32 x; + x = get8_packet(f); + x += get8_packet(f) << 8; + x += get8_packet(f) << 16; + x += (uint32) get8_packet(f) << 24; + return x; +} + static void flush_packet(vorb *f) { while (get8_packet_raw(f) != EOP); @@ -1578,7 +1613,8 @@ static uint32 get_bits(vorb *f, int n) f->valid_bits += 8; } } - if (f->valid_bits < 0) return 0; + + assert(f->valid_bits >= n); z = f->acc & ((1 << n)-1); f->acc >>= n; f->valid_bits -= n; @@ -1903,69 +1939,69 @@ static int predict_point(int x, int x0, int x1, int y0, int y1) // 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, + 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 }; @@ -2139,47 +2175,7 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int ++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 { + } else if (ch > 2) { while (pcount < part_read) { int z = r->begin + pcount*r->part_size; int c_inter = z % ch, p_inter = z/ch; @@ -2366,11 +2362,11 @@ void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) #if LIBVORBIS_MDCT // directly call the vorbis MDCT using an interface documented // by Jeff Roberts... useful for performance comparison -typedef struct +typedef struct { int n; int log2n; - + float *trig; int *bitrev; @@ -2389,7 +2385,7 @@ void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) 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 { + else { if (M2.n) __asm int 3; mdct_init(&M2, n); M = &M2; @@ -2802,7 +2798,7 @@ static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) d1[0] = u[k4+1]; d0[1] = u[k4+2]; d0[0] = u[k4+3]; - + d0 -= 4; d1 -= 4; bitrev += 2; @@ -2883,7 +2879,7 @@ static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) float p0,p1,p2,p3; p3 = e[6]*B[7] - e[7]*B[6]; - p2 = -e[6]*B[6] - e[7]*B[7]; + p2 = -e[6]*B[6] - e[7]*B[7]; d0[0] = p3; d1[3] = - p3; @@ -2891,7 +2887,7 @@ static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) d3[3] = p2; p1 = e[4]*B[5] - e[5]*B[4]; - p0 = -e[4]*B[4] - e[5]*B[5]; + p0 = -e[4]*B[4] - e[5]*B[5]; d0[1] = p1; d1[2] = - p1; @@ -2899,7 +2895,7 @@ static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) d3[2] = p0; p3 = e[2]*B[3] - e[3]*B[2]; - p2 = -e[2]*B[2] - e[3]*B[3]; + p2 = -e[2]*B[2] - e[3]*B[3]; d0[2] = p3; d1[1] = - p3; @@ -2907,7 +2903,7 @@ static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) d3[1] = p2; p1 = e[0]*B[1] - e[1]*B[0]; - p0 = -e[0]*B[0] - e[1]*B[1]; + p0 = -e[0]*B[0] - e[1]*B[1]; d0[3] = p1; d1[0] = - p1; @@ -3513,7 +3509,7 @@ static int vorbis_pump_first_frame(stb_vorbis *f) } #ifndef STB_VORBIS_NO_PUSHDATA_API -static int is_whole_packet_present(stb_vorbis *f, int end_page) +static int is_whole_packet_present(stb_vorbis *f) { // 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 @@ -3533,15 +3529,13 @@ static int is_whole_packet_present(stb_vorbis *f, int end_page) 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; + uint8 *q; int n; // check that we have the page header ready @@ -3567,8 +3561,6 @@ static int is_whole_packet_present(stb_vorbis *f, int end_page) 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); @@ -3585,6 +3577,7 @@ static int start_decoder(vorb *f) int longest_floorlist=0; // first page, first packet + f->first_decode = TRUE; if (!start_page(f)) return FALSE; // validate page flag @@ -3642,6 +3635,44 @@ static int start_decoder(vorb *f) if (!start_page(f)) return FALSE; if (!start_packet(f)) return FALSE; + + if (!next_segment(f)) return FALSE; + + if (get8_packet(f) != VORBIS_packet_comment) 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); + //file vendor + len = get32_packet(f); + f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1)); + if (f->vendor == NULL) return error(f, VORBIS_outofmem); + for(i=0; i < len; ++i) { + f->vendor[i] = get8_packet(f); + } + f->vendor[len] = (char)'\0'; + //user comments + f->comment_list_length = get32_packet(f); + f->comment_list = (char**)setup_malloc(f, sizeof(char*) * (f->comment_list_length)); + if (f->comment_list == NULL) return error(f, VORBIS_outofmem); + + for(i=0; i < f->comment_list_length; ++i) { + len = get32_packet(f); + f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1)); + if (f->comment_list[i] == NULL) return error(f, VORBIS_outofmem); + + for(j=0; j < len; ++j) { + f->comment_list[i][j] = get8_packet(f); + } + f->comment_list[i][len] = (char)'\0'; + } + + // framing_flag + x = get8_packet(f); + if (!(x & 1)) return error(f, VORBIS_invalid_setup); + + + skip(f, f->bytes_in_seg); + f->bytes_in_seg = 0; + do { len = next_segment(f); skip(f, len); @@ -3653,7 +3684,7 @@ static int start_decoder(vorb *f) #ifndef STB_VORBIS_NO_PUSHDATA_API if (IS_PUSH_MODE(f)) { - if (!is_whole_packet_present(f, TRUE)) { + if (!is_whole_packet_present(f)) { // convert error in ogg header to write type if (f->error == VORBIS_invalid_stream) f->error = VORBIS_invalid_setup; @@ -3912,7 +3943,7 @@ static int start_decoder(vorb *f) } else { stbv__floor_ordering p[31*8+2]; Floor1 *g = &f->floor_config[i].floor1; - int max_class = -1; + 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); @@ -3956,12 +3987,7 @@ static int start_decoder(vorb *f) 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 + int low = 0,hi = 0; neighbors(g->Xlist, j, &low,&hi); g->neighbors[j][0] = low; g->neighbors[j][1] = hi; @@ -4030,7 +4056,7 @@ static int start_decoder(vorb *f) 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; + 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)); @@ -4146,7 +4172,6 @@ static int start_decoder(vorb *f) 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); @@ -4155,7 +4180,17 @@ static int start_decoder(vorb *f) return error(f, VORBIS_outofmem); } - f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + // @TODO: stb_vorbis_seek_start expects first_audio_page_offset to point to a page + // without PAGEFLAG_continued_packet, so this either points to the first page, or + // the page after the end of the headers. It might be cleaner to point to a page + // in the middle of the headers, when that's the page where the first audio packet + // starts, but we'd have to also correctly skip the end of any continued packet in + // stb_vorbis_seek_start. + if (f->next_seg == -1) { + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + } else { + f->first_audio_page_offset = 0; + } return TRUE; } @@ -4163,6 +4198,13 @@ static int start_decoder(vorb *f) static void vorbis_deinit(stb_vorbis *p) { int i,j; + + setup_free(p, p->vendor); + for (i=0; i < p->comment_list_length; ++i) { + setup_free(p, p->comment_list[i]); + } + setup_free(p, p->comment_list); + if (p->residue_config) { for (i=0; i < p->residue_count; ++i) { Residue *r = p->residue_config+i; @@ -4228,7 +4270,7 @@ 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->alloc.alloc_buffer_length_in_bytes &= ~7; p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; } p->eof = 0; @@ -4262,6 +4304,15 @@ stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) return d; } +stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f) +{ + stb_vorbis_comment d; + d.vendor = f->vendor; + d.comment_list_length = f->comment_list_length; + d.comment_list = f->comment_list; + return d; +} + int stb_vorbis_get_error(stb_vorbis *f) { int e = f->error; @@ -4403,7 +4454,7 @@ int stb_vorbis_decode_frame_pushdata( f->error = VORBIS__no_error; // check that we have the entire packet in memory - if (!is_whole_packet_present(f, FALSE)) { + if (!is_whole_packet_present(f)) { *samples = 0; return 0; } @@ -4639,8 +4690,8 @@ 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; + uint32 delta, stream_length, padding, last_sample_limit; + double offset = 0.0, bytes_per_sample = 0.0; int probe = 0; // find the last page and validate the target sample @@ -4653,9 +4704,9 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) // 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; + last_sample_limit = 0; else - sample_number -= padding; + last_sample_limit = sample_number - padding; left = f->p_first; while (left.last_decoded_sample == ~0U) { @@ -4668,9 +4719,12 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) 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)) + if (last_sample_limit <= left.last_decoded_sample) { + if (stb_vorbis_seek_start(f)) { + if (f->current_loc > sample_number) + return error(f, VORBIS_seek_failed); return 1; + } return 0; } @@ -4687,10 +4741,10 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) // 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); + offset = left.page_start + bytes_per_sample * (last_sample_limit - 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; + double error = ((double) last_sample_limit - mid.last_decoded_sample) * bytes_per_sample; if (error >= 0 && error < 8000) error = 8000; if (error < 0 && error > -8000) error = -8000; offset += error * 2; @@ -4721,14 +4775,16 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) } // 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; + // and we're close enough (if it wasn't an interpolation probe). + if (mid.page_start == right.page_start) { + if (probe >= 2 || delta <= 65536) + break; + } else { + if (last_sample_limit < mid.last_decoded_sample) + right = mid; + else + left = mid; + } ++probe; } @@ -4844,8 +4900,8 @@ int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) flush_packet(f); } } - // the next frame will start with the sample - assert(f->current_loc == sample_number); + // the next frame should start with the sample + if (f->current_loc != sample_number) return error(f, VORBIS_seek_failed); return 1; } @@ -5022,7 +5078,7 @@ stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const st #else f = fopen(filename, "rb"); #endif - if (f) + if (f) return stb_vorbis_open_file(f, TRUE, error, alloc); if (error) *error = VORBIS_file_open_failure; return NULL; @@ -5085,7 +5141,7 @@ static int8 channel_position[7][6] = #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() + #define check_endianness() #else #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) #define check_endianness() @@ -5187,7 +5243,7 @@ static void convert_samples_short(int buf_c, short **buffer, int b_offset, int d int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) { - float **output; + float **output = NULL; int len = stb_vorbis_get_frame_float(f, NULL, &output); if (len > num_samples) len = num_samples; if (len) @@ -5410,20 +5466,20 @@ int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, in #endif // STB_VORBIS_NO_PULLDATA_API /* Version history - 1.17 - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13223, -13223 + 1.17 - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13222, -13223 found with Mayhem by ForAllSecure 1.16 - 2019-03-04 - fix warnings 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found 1.14 - 2018-02-11 - delete bogus dealloca usage 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files - 1.11 - 2017-07-23 - fix MinGW compilation + 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 '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 + 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 @@ -5479,38 +5535,38 @@ 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 +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 +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 +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, +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 +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 +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/.clang-format b/Frameworks/OpenMPT/OpenMPT/libopenmpt/.clang-format new file mode 100644 index 000000000..a50eb55a0 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/.clang-format @@ -0,0 +1,45 @@ +Language: Cpp +AccessModifierOffset: -2 +AlignEscapedNewlines: DontAlign +AllowShortFunctionsOnASingleLine: Empty +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: true +BreakConstructorInitializersBeforeComma: true +BreakStringLiterals: false +ColumnLimit: 0 +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +IndentCaseLabels: true +IndentWidth: 2 +MaxEmptyLinesToKeep: 3 +PointerAlignment: Middle +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: false +SpaceBeforeParens: ControlStatements +SpacesBeforeTrailingComments: 2 +SpacesInContainerLiterals: false +SpacesInParentheses: true +SpacesInSquareBrackets: true +Standard: Cpp11 +TabWidth: 2 +UseTab: ForIndentation diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/bindings/freebasic/libopenmpt.bi b/Frameworks/OpenMPT/OpenMPT/libopenmpt/bindings/freebasic/libopenmpt.bi index 059e0ae5b..6e83441b9 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/bindings/freebasic/libopenmpt.bi +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/bindings/freebasic/libopenmpt.bi @@ -480,7 +480,7 @@ Const OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR = -255 /'* \brief Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it - \param flags Ored mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. + \param flags Bit mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. \param data Beginning of the file data. \param size Size of the beginning of the file data. \param filesize Full size of the file data on disk. @@ -506,7 +506,7 @@ Declare Function openmpt_probe_file_header(ByVal flags As ULongInt, ByVal Data A /'* \brief Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it - \param flags Ored mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. + \param flags Bit mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. \param data Beginning of the file data. \param size Size of the beginning of the file data. \param logfunc Logging function where warning and errors are written. May be NULL. @@ -532,7 +532,7 @@ Declare Function openmpt_probe_file_header_without_filesize(ByVal flags As ULong /'* \brief Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it - \param flags Ored mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. + \param flags Bit mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. \param stream_callbacks Input stream callback operations. \param stream Input stream to scan. \param logfunc Logging function where warning and errors are written. May be NULL. @@ -1055,6 +1055,15 @@ Declare Function openmpt_module_get_metadata_keys_ Alias "openmpt_module_get_met '/ Declare Function openmpt_module_get_metadata_ Alias "openmpt_module_get_metadata" (ByVal module As openmpt_module Ptr, ByVal key As Const ZString Ptr) As Const ZString Ptr +/'* \brief Get the current estimated beats per minute (BPM). + + \param module The module handle to work on. + \remarks Many module formats lack time signature metadata. It is common that this estimate is off by a factor of two, but other multipliers are also possible. + \remarks Due to the nature of how module tempo works, the estimate may change slightly after switching libopenmpt's output to a different sample rate. + \return The current estimated BPM. +'/ +Declare Function openmpt_module_get_current_estimated_bpm(ByVal module As openmpt_module Ptr) As Double + /'* \brief Get the current speed \param module The module handle to work on. @@ -1356,6 +1365,11 @@ Declare Function openmpt_module_highlight_pattern_row_channel_ Alias "openmpt_mo - play.tempo_factor: Set a floating point tempo factor. "1.0" is the default tempo. - play.pitch_factor: Set a floating point pitch factor. "1.0" is the default pitch. - render.resampler.emulate_amiga: Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. + - render.resampler.emulate_amiga_type: Configures the filter type to use for the Amiga resampler. Supported values are: + - "auto": Filter type is chosen by the library and might change. This is the default. + - "a500": Amiga A500 filter. + - "a1200": Amiga A1200 filter. + - "unfiltered": BLEP synthesis without model-specific filters. The LED filter is ignored by this setting. This filter mode is considered to be experimental and might change in the future. - render.opl.volume_factor: Set volume factor applied to synthesized OPL sounds, relative to the default OPL volume. - dither: Set the dither algorithm that is used for the 16 bit versions of openmpt_module_read. Supported values are: - 0: No dithering. diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/foo_openmpt.txt b/Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/foo_openmpt.txt deleted file mode 100644 index 327dd1fa2..000000000 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/doc/foo_openmpt.txt +++ /dev/null @@ -1,9 +0,0 @@ - -foo_openmpt -=========== - -foo_openmpt is a module file (https://en.wikipedia.org/wiki/Module_file) decoder -component for foobar2000 >= 1.3.0. foo_openmpt is based on libopenmpt. - - -See https://lib.openmpt.org/ for documentation, FAQ and other details. diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md index 5b98fcf60..d8e3b424a 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/changelog.md @@ -5,131 +5,214 @@ Changelog {#changelog} For fully detailed change log, please see the source repository directly. This is just a high-level summary. -### libopenmpt 0.4.10 (2019-10-30) +### libopenmpt 0.5.3 (2020-10-25) - * The "date" metadata could contain a bogus date for some older IT files. - * Do not apply global volume ramping from initial global volume when seeking. + * [**Sec**] Possible hang if a MED file claimed to contain 256 songs. (r13704) - * MTM: Sample loop length was off by one. - * PSM: Sample loop length was off by one in most files. + * [**Bug**] libopenmpt: `openmpt::is_extension_supported2()` exported symbol + was missing (C++). + * [**Bug**] `openmpt::module::set_position_seconds` sometimes behaved as if + the song end was reached when seeking into a pattern loop and in some other + corner cases. - * mpg123: Update to v1.25.13 (2019-10-26). + * Increase threshold for ignoring panning commands from 820 to 830. + * Subsong names now fall back to the first pattern's name if empty. + * MO3: Avoid certain ModPlug hacks from being fixed up twice, which could lead + to e.g. very narrow pan swing range for old OpenMPT IT files saved with a + recent MO3 encoder version. + * MO3: Some files with corrupted envelope data could be rejected completely + (normally libopenmpt should fix up the envelope data). + * MO3: Song metadata didn't correctly identify MPTM as source format (it + appeared as IT instead). + * STM: Change tempo computation to behave like Scream Tracker 2.3 instead of + Scream Tracker 2.2, as the playback frequencies we use for sample playback + are closer to those of Scream Tracker 2.3. + * PLM: Percentage offset (Mxx) was slightly off. + * WOW: Fix loading of several files and harden WOW detection. -### libopenmpt 0.4.9 (2019-10-02) +### libopenmpt 0.5.2 (2020-08-30) - * [**Sec**] libmodplug: C API: Limit the length of strings copied to the - output buffer of `ModPlug_InstrumentName()` and `ModPlug_SampleName()` to 32 - bytes (including terminating null) as is done by original libmodplug. This - avoids potential buffer overflows in software relying on this limit instead - of querying the required buffer size beforehand. libopenmpt can return - strings longer than 32 bytes here beacuse the internal limit of 32 bytes - applies to strings encoded in arbitrary character encodings but the API - returns them converted to UTF-8, which can be longer. (reported by Antonio - Morales Maldonado of Semmle Security Research Team) (r12129) - ([CVE-2019-17113](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-17113)) - * [**Sec**] libmodplug: C++ API: Do not return 0 in - `CSoundFile::GetSampleName()` and `CSoundFile::GetInstrumentName()` when a - null output pointer is provided. This behaviour differed from libmodplug and - made it impossible to determine the required buffer size. (r12130) + * [**Change**] `Makefile` `CONFIG=emscripten` now supports + `EMSCRIPTEN_TARGET=all` which provides WebAssembly as well as fallback to + JavaScript in a single build. -### libopenmpt 0.4.8 (2019-09-30) + * [**Regression**] `Makefile` `CONFIG=emscripten` does not support + `EMSCRIPTEN_TARGET=asmjs` or `EMSCRIPTEN_TARGET=asmjs128m` any more because + support has been removed from current Emscripten versions. + * [**Regression**] Support for Emscripten versions older than 1.39.7 has been + removed. - * [**Sec**] Possible crash due to out-of-bounds read when playing an OPL note - with active filter in S3M or MPTM files (r12118). + * PP20: The first few bytes of some files were not decompressed properly, + making some files unplayable (depending on the original format). -### libopenmpt 0.4.7 (2019-09-23) +### libopenmpt 0.5.1 (2020-07-26) - * [**Bug**] Compilation fix for various platforms that do not provide - `std::aligned_alloc` in C++17 mode. The problematic dependency has been - removed. This should fix build problems on MinGW, OpenBSD, Haiku, and others - for good. + * [**Bug**] `libopenmpt/libopenmpt.h` failed to compile with + `LIBOPENMPT_NO_DEPRECATE` defined. - * J2B: Ignore notes with non-existing instrument (fixes Ending.j2b). + * MPTM: Qxy now retriggers OPL notes if new compatibility flag is set in file. + * MPTM: Bring back old OPL note end-of-envelope behaviour for files made with + OpenMPT 1.28. + * IT: Global volume slides with both nibbles set preferred the "slide up" + nibble over the "slide down" nibble in old OpenMPT versions, unlike other + slides. Such old files are now imported correctly again. + * IT: Fixed an edge case where, if the filter hit full cutoff / no resonance + on the first tick of a row where a new delayed note would be triggered, the + filter would be disabled even though it should stay active. Fixes trace.it + by maddie. + * OXM: Some sample loops were not imported correctly. + * XM: Out-of-range arpeggio clamping behaviour broke in OpenMPT 1.23.05.00. + The arpeggios in Binary World by Dakota now play correctly again. + * S3M: Support old-style sample pre-amp value in very early S3M files. + * S3M: Only force-enable fast slides for files ST 3.00. Previously, any S3M + file made with an ST3 version older than 3.20 enabled them. + * S3M: Only apply volume and middle-C speed on instrument change if the new + sample slot has sample data. + * MOD: Fix an infinite loop in GamerMan by MrGamer by playing non-ProTracker + MODs more like FT2 would. + * M15: Improve tracker detection heuristics to never assume SoundTracker 2.0 + if there is a huge number of Dxx commands, as that is a definite hint that + they should be treated as volume slides. Fixes Monty On The Run by + Master Blaster. + * MO3: Support OPL patches in MO3 files created from MPTM and S3M. + * DBM: If a global pattern command would be lost because both effect commands + in a cell would have to go into the regular effect column (e.g. a speed and + a tempo command), the lost command is now attempted to be written into a + different cell on the same row. Fixes "Party-Question V" by grogon. - * mpg123: Update to v1.25.12 (2019-08-24). - * ogg: Update to v1.3.4. (2019-08-31). - * flac: Update to v1.3.3. (2019-08-04). + * mpg123: Update to v1.26.3 (2020-07-16). + * stb_vorbis: Update v1.20 commit b42009b3b9d4ca35bc703f5310eedc74f584be58 + (2020-07-13). -### libopenmpt 0.4.6 (2019-08-10) +### libopenmpt 0.5.0 (2020-05-24) - * [**Bug**] Compilation fix for OpenBSD. - * [**Bug**] Compilation fix for NO_PLUGINS being defined. + * [**New**] OggMod compressed FastTracker 2 XM (OXM) modules are now + supported. + * [**New**] The emulated Amiga type when Amiga resampler emulation is enabled + can now be selected via ctl `render.resampler.emulate_amiga_type`. Possible + values are: `"auto"`, `"a500"`, `"a1200"`, and an experimental option + `"unfiltered"`. + * [**New**] libopenmpt: New API `openmpt::module::get_current_estimated_bpm()` + (C++), and `openmpt_module_get_current_estimated_bpm()` (C) which provides + accurate beats per minute information for module formats with time signature + and an educated guess based on speed and tempo for others. + * [**New**] libopenmpt: New type-aware ctl APIs that do not require memory + allocations and are thus realtime-safe: + `openmpt::module::ctl_get_boolean()`, `openmpt::module::ctl_get_integer()`, + `openmpt::module::ctl_get_floatingpoint()`, + `openmpt::module::ctl_get_text()`, `openmpt::module::ctl_set_boolean()`, + `openmpt::module::ctl_set_integer()`, + `openmpt::module::ctl_set_floatingpoint()` (C++), and + `openmpt_module_ctl_get_boolean()`, `openmpt_module_ctl_get_integer()`, + `openmpt_module_ctl_get_floatingpoint()`, `openmpt_module_ctl_get_text()`, + `openmpt_module_ctl_set_boolean()`, `openmpt_module_ctl_set_integer()`, + `openmpt_module_ctl_set_floatingpoint()` (C). + * [**New**] libopenmpt C++ New API `openmpt::is_extension_supported2()` which + takes a `std::string_view` parameter instead of `std::string`. + * [**New**] libopenmpt C++: New API + `openmpt::module::module(std::vector data)`, + `openmpt::module::module(const std::byte * data, std::size_t size)`, + `openmpt::module::module(const std::byte * beg, const std::byte * end)`. + * [**New**] libopenmpt C++: New API + `openmpt::probe_file_header(flags, const std::byte * data, std::size_t size, filesize)`, + `openmpt::probe_file_header(flags, const std::byte * data, std::size_t size)`. + * [**New**] libopenmpt_ext C++: New API + `openmpt::module_ext::module_ext(std::vector data)`, + `openmpt::module_ext::module_ext(const std::byte * data, std::size_t size)`, + `openmpt::module_ext::module_ext(std::vector data)`, + `openmpt::module_ext::module_ext(const std::uint8_t * data, std::size_t size)`. - * in_openmpt: Correct documentation. `openmpt-mpg123.dll` must be placed into - the Winamp directory. + * [**Change**] std::istream based file I/O has been speed up. + * [**Change**] Dependency on iconv on Linux has been removed. - * Detect IT files unpacked with early UNMO3 versions. + * [**Regression**] libmodplug: The libmodplug emulation layer has been removed + from the libopenmpt tree. Please use the separate `libopenmpt-modplug` + package instead. + * [**Regression**] foo_openmpt: foo_openmpt is discontinued. Please use + Kode54's fork foo_openmpt54: + . + * [**Regression**] Support for building with C++11 or C++14 has been removed. + C++17 is now required to build libopenmpt. + * [**Regression**] Support for client code using C++11 or C++ 14 has been + removed. C++17 is now required to build libopenmpt client applications. + * [**Regression**] Support for Visual Studio 2015 has been removed. + * [**Regression**] Support for GCC 4.8, 4.9, 5, 6 has been removed. + * [**Regression**] Support for Clang 3.6, 3.7, 3.8, 3.9, 4 has been removed. + * [**Regression**] Support for Emscripten versions older than 1.39.1 has been + removed. + * [**Regression**] Building with Android NDK older than NDK r18b is not + supported any more. + * [**Regression**] openmpt123: Support for SDL1 (but not SDL2) output has been + removed. + * [**Regression**] openmpt123: Support for SDL2 older than 2.0.4 has been + removed. + * [**Regression**] Windows XP and Windows Vista are no longer supported. + * [**Regression**] It is no longer possible to optionally use iconv for + character set conversions. - * mpg123: Update to v1.25.11 (2019-07-18). - * minimp3: Update to commit 977514a6dfc4960d819a103f43b358e58ac6c28f - (2019-07-24). - * miniz: Update to v2.1.0 (2019-05-05). - * stb_vorbis: Update to v1.17 (2019-08-09). + * [**Bug**] openmpt123: openmpt123 now honors the current locale and outputs + text appropriately. + * [**Bug**] openmpt123: Piping text output to other than console window + targets on Windows has been fixed. -### libopenmpt 0.4.5 (2019-05-27) + * Greatly improved MED import. Synthesized instruments are still not supported + but support was added for: Multisampled instruments, delta samples, more + pattern commands, Hold and Decay, multiple songs and many other small + changes. + * Improved OPL channel allocation when more than 18 notes are active, so that + channels that have completely faded out are prioritized over channels that + have already been released but have not faded out yet. + * Interactively triggering an OPL instrument could cause the first pattern + channel to no longer be played back correctly. + * Fix some inaccuracies in OPL emulator. + * Fix overflow of OPL amplification happening at a synth volume level of 510. + * End-of-sample pop reduction of surround channels was applied to front + channels instead, causing a pop on the front channels instead of removing it + on the back channels. + * IT: Disable retrigger with short notes quirk for modules saved with + Chibi Tracker, as it does not implement that quirk. + * IT: Instrument and sample panning should not override channel panning for + following notes. + * IT: SBx is now prioritized over Bxx commands that are to the left of it. + * IT: Duplicate Check Type "Sample" should only be applied if the instruments + match, too. + * IT: Duplicate Check Type "Note" should compare pattern notes, but it was + comparing the new pattern note against the old translated note. + * IT: Various fixes for envelope resetting. + * IT / S3M: When combining SBx and EEx effects, don't skip the first row of + the loop like in FastTracker 2. + * S3M: Empty pattern commands now affect effect memory as well. + * S3M: Offset beyond loop end wraps around to loop start like in + Scream Tracker 3 + GUS (previously it just keep playing from the loop start, + which is neither what GUS nor Sound Blaster drivers do). + * S3M: Notes cannot be retriggered after they have been cut. + * S3M: Fix portamento after note cut (fixes antediluvian_song.s3m). + * S3M / MOD: Previous note offset is no longer used for retriggered notes if + there was no instrument number next to the Qxy effect. + * MOD: Sample swapping now also works if the sample that is being swapped from + does not loop. Swapping to a non-looped sample now stops playback once the + swapped-from sample reaches its (loop) end. + * MOD: Fix early song ending due to ProTracker pattern jump quirk + (EEx + Dxx on same row) if infinite looping is disabled. + Fixes Haunted Tracks.mod by Triace. + * MOD: Previous note offset is no longer used for retriggered notes if there + was no instrument number next to the E9x effect. + * MOD: Vibrato type "ramp down" was upside down. + * XM: If a file contains patterns longer than 1024 rows, they are now clamped + to 1024 rows instead of 64 rows. + * XM: Do not reset note-off status on portamento if there is no instrument + number. - * [**Sec**] Possible crash during playback due out-of-bounds read in XM and - MT2 files (r11608). - ([CVE-2019-14380](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-14380)) - - * Breaking out of a sustain loop through Note-Off sometimes didn't continue in - the regular sample loop. - * Seeking did not stop notes playing with XM Key Off (Kxx) effect. - -### libopenmpt 0.4.4 (2019-04-07) - - * [**Bug**] Channel VU meters were swapped. - - * Startrekker: Clamp speed to 31 ticks per row. - * MTM: Ignore unused Exy commands on import. Command E5x (Set Finetune) is now - applied correctly. - * MOD: Sample swapping was always enabled since it has been separated from the - ProTracker 1/2 compatibility flag. Now it is always enabled for Amiga-style - modules and otherwise the old heuristic is used again. - - * stb_vorbis: Update to v1.16 (2019-03-05). - -### libopenmpt 0.4.3 (2019-02-11) - - * [**Sec**] Possible crash due to null-pointer access when doing a portamento - from an OPL instrument to an empty instrument note map slot (r11348). - ([CVE-2019-14381](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-14381)) - - * [**Bug**] libopenmpt did not compile on Apple platforms in C++17 mode. - - * IT: Various fixes for note-off + instrument number in Old Effects mode. - * MO3: Import IT row highlights as written by MO3 2.4.1.2 or newer. Required - for modules using modern tempo mode. - - * miniz: Update to v2.0.8 (2018-09-19). - * stb_vorbis: Update to v1.15 (2019-02-07). - -### libopenmpt 0.4.2 (2019-01-22) - - * [**Sec**] DSM: Assertion failure during file parsing with debug STLs - (r11209). - ([CVE-2019-14382](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-14382)) - * [**Sec**] J2B: Assertion failure during file parsing with debug STLs - (r11216). - ([CVE-2019-14383](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-14383)) - - * S3M: Allow volume change of OPL instruments after Note Cut. - -### libopenmpt 0.4.1 (2019-01-06) - - * [**Bug**] Binaries compiled for winold (Windows XP, Vista, 7, for CPUs - without SSE2 support) did not actually work on CPUs without SSE2 support. - * [**Bug**] libmodplug: Public symbols of the C++ API had `visibility=hidden` - set on non-MSVC systems, which made them not publicly accessible. - * [**Bug**] Project files for Windows 10 desktop builds on ARM and ARM64 - (`build/vs2017win10`) were missing from Windows source package. - * [**Bug**] MSVC project files in Windows source package lacked additional - files required to build DLLs. - - * MO3: Apply playback changes based on "ModPlug-made" header flag. - - * minimp3: Update to commit e9df0760e94044caded36a55d70ab4152134adc5 - (2018-12-23). + * mpg123: v1.26rc3. + * ogg: v1.3.4. + * vorbis: v1.3.6. + * zlib: v1.2.11. + * minimp3: commit 55da78cbeea5fb6757f8df672567714e1e8ca3e9 (2020-03-04). + * stb_vorbis: v1.19 commit 37b9b20fdec06c75a0493e0bb59e2d0f288bfb51 + (2020-02-05). + * miniz: v2.1.0. + * FLAC: v1.3.3. + * PortAudio: commit 799a6834a58592eadc5712cba73b35956dc51579 (2020-03-26). ### libopenmpt 0.4.0 (2018-12-23) diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/dependencies.md b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/dependencies.md index 936619017..d6eed5c61 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/dependencies.md +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/dependencies.md @@ -9,17 +9,16 @@ Dependencies ### libopenmpt * Supported compilers for building libopenmpt: - * **Microsoft Visual Studio 2015** or higher, running on a x86-64 build + * **Microsoft Visual Studio 2017** or higher, running on a x86-64 build system (other target systems are supported) - * **GCC 4.8** or higher - * **Clang 3.6** or higher - * **MinGW-W64 4.8** or higher (it is recommended to preferably use + * **GCC 7.1** or higher + * **Clang 5** or higher + * **MinGW-W64 7.1** or higher (it is recommended to preferably use posix threading model as opposed to win32 threading model, or at least have mingw-std-threads available otherwise) - * **emscripten 1.38.5** or higher + * **emscripten 1.39.1** or higher * **DJGPP GCC 7.2** or higher - * any other **C++11 compliant** compiler (full standard compliant mode is - known to work with GCC >= 5.1 and Clang) + * any other **C++17 compliant** compiler libopenmpt makes the following assumptions about the C++ implementation used for building: @@ -27,8 +26,8 @@ Dependencies static_assert) * `sizeof(char) == 1` (enforced by static_assert) * existence of `std::uintptr_t` (enforced by static_assert) - * `sizeof(float) == 4` (enforced by static_assert) - * `sizeof(double) == 8` (enforced by static_assert) + * in C++20 mode, `std::endian::little != std::endian::big` (enforced + by static_assert) * `wchar_t` encoding is either UTF-16 or UTF-32 (implicitly assumed) * representation of basic source character set is ASCII (implicitly assumed) @@ -48,8 +47,7 @@ Dependencies * Required compilers to use libopenmpt: * Any **C89** / **C99** / **C11** compatible compiler should work with the C API as long as a **C99** compatible **stdint.h** is available. - * Any **C++11** / **C++14** / **C++17** compatible compiler should work - with the C++ API. + * Any **C++17** compatible compiler should work with the C++ API. * **J2B** support requires an inflate (deflate decompression) implementation: * **zlib** * **miniz** can be used internally if no zlib is available. @@ -73,17 +71,16 @@ Dependencies ### openmpt123 * Supported compilers for building openmpt123: - * **Microsoft Visual Studio 2015** or higher, running on a x86-64 build + * **Microsoft Visual Studio 2017** or higher, running on a x86-64 build system (other target systems are supported) - * **GCC 4.8** or higher - * **Clang 3.6** or higher - * **MinGW-W64 4.8** or higher + * **GCC 7.1** or higher + * **Clang 5** or higher + * **MinGW-W64 7.1** or higher * **DJGPP GCC 7.2** or higher - * any **C++11 compliant** compiler + * any **C++17 compliant** compiler * Live sound output requires one of: * **PulseAudio** * **SDL 2** - * **SDL 1.2** * **PortAudio v19** * **Win32** * **liballegro 4.2** on DJGPP/DOS @@ -94,11 +91,6 @@ Optional dependencies ### libopenmpt - * Character set conversion can use one of: - * **Win32** - * **iconv** - * **C++11** codecvt_utf8 - instead of the internal conversion tables and functions. * **doxygen 1.8** or higher is required to build the documentation. ### openmpt123 @@ -119,6 +111,8 @@ Building the source packages additionally requires: * autoconf * autoconf-archive * automake + * awk (mawk) + * git * gzip * help2man * libtool diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/quickstart.md b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/quickstart.md index bc7563604..428138459 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/quickstart.md +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/dox/quickstart.md @@ -7,7 +7,7 @@ Quick Start {#quickstart} 1. Grab a `libopenmpt-VERSION.autotools.tar.gz` tarball. 2. Get dependencies: - - **gcc >= 4.8** or **clang >= 3.6** + - **gcc >= 7.1** or **clang >= 5** - **pkg-config >= 0.24** - **zlib** - **libogg**, **libvorbis**, **libvorbisfile** @@ -18,7 +18,7 @@ Quick Start {#quickstart} - **libFLAC** (required only by openmpt123) - **libsndfile** (required only by openmpt123) 3. *Optional*: - - **libSDL >= 1.2.x** (required only by openmpt123) + - **libSDL2 >= 2.0.4** (required only by openmpt123) 4. Run: ./configure @@ -29,20 +29,20 @@ Quick Start {#quickstart} ### Windows 1. Get dependencies: - - **Microsoft Visual Studio >= 2015** + - **Microsoft Visual Studio >= 2017** 2. *Optionally* get dependencies: - **Winamp SDK** - **XMPlay SDK** 3. Checkout `https://source.openmpt.org/svn/openmpt/trunk/OpenMPT/` . - 4. Open `build\vs2015\openmpt123.sln` or `build\vs2015\libopenmpt.sln` or `build\vs2015\xmp-openmpt.sln` or `build\vs2015\in_openmpt.sln` or `build\vs2015\foo_openmpt.sln` in *Microsoft Visual Studio 2015*. + 4. Open `build\vs2017\openmpt123.sln` or `build\vs2017\libopenmpt.sln` or `build\vs2017\xmp-openmpt.sln` or `build\vs2017\in_openmpt.sln` in *Microsoft Visual Studio 2017*. 5. Select appropriate configuration and build. Binaries are generated in `bin\` - 6. Drag a module onto `openmpt123.exe` or copy the player plugin DLLs (`in_openmpt.dll`, `xmp-openmpt.dll` or `foo_openmpt.dll`) into the respective player directory. + 6. Drag a module onto `openmpt123.exe` or copy the player plugin DLLs (`in_openmpt.dll` or `xmp-openmpt.dll`) into the respective player directory. ### Unix-like 1. Get dependencies: - **GNU make** - - **gcc >= 4.8** or **clang >= 3.6** + - **gcc >= 7.1** or **clang >= 5** - **pkg-config** - **zlib** - **libogg**, **libvorbis**, **libvorbisfile** @@ -52,7 +52,7 @@ Quick Start {#quickstart} - **libFLAC** (required only by openmpt123) - **libsndfile** (required only by openmpt123) 2. *Optional*: - - **libSDL >= 1.2.x** (required only by openmpt123) + - **libSDL2 >= 2.0.4** (required only by openmpt123) - **doxygen >= 1.8** - **help2man** 3. Run: diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/foo_openmpt.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/foo_openmpt.cpp deleted file mode 100644 index 0172bdd0d..000000000 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/foo_openmpt.cpp +++ /dev/null @@ -1,323 +0,0 @@ - -#if defined(_MSC_VER) -#pragma warning(disable:4091) -#endif - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable:4996) -#endif -#include "foobar2000/SDK/foobar2000.h" -#include "foobar2000/helpers/helpers.h" -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - -#include "libopenmpt.hpp" - -#include -#include -#include -#include - - - -// Declaration of your component's version information -// Since foobar2000 v1.0 having at least one of these in your DLL is mandatory to let the troubleshooter tell different versions of your component apart. -// Note that it is possible to declare multiple components within one DLL, but it's strongly recommended to keep only one declaration per DLL. -// As for 1.1, the version numbers are used by the component update finder to find updates; for that to work, you must have ONLY ONE declaration per DLL. If there are multiple declarations, the component is assumed to be outdated and a version number of "0" is assumed, to overwrite the component with whatever is currently on the site assuming that it comes with proper version numbers. -DECLARE_COMPONENT_VERSION("OpenMPT component", OPENMPT_API_VERSION_STRING ,"libopenmpt based module file player"); - - - -// This will prevent users from renaming your component around (important for proper troubleshooter behaviors) or loading multiple instances of it. -VALIDATE_COMPONENT_FILENAME("foo_openmpt.dll"); - - -// settings - - -// {FFD659CA-6AEA-479f-8E60-F03B297286FE} -static const GUID guid_openmpt_root = -{ 0xffd659ca, 0x6aea, 0x479f, { 0x8e, 0x60, 0xf0, 0x3b, 0x29, 0x72, 0x86, 0xfe } }; - - -// {E698E101-FD93-4e6c-AF02-AEC7E8631D8E} -static const GUID guid_openmpt_samplerate = -{ 0xe698e101, 0xfd93, 0x4e6c, { 0xaf, 0x2, 0xae, 0xc7, 0xe8, 0x63, 0x1d, 0x8e } }; - -// {E4026686-02F9-4805-A3FD-2EECA655A92C} -static const GUID guid_openmpt_channels = -{ 0xe4026686, 0x2f9, 0x4805, { 0xa3, 0xfd, 0x2e, 0xec, 0xa6, 0x55, 0xa9, 0x2c } }; - -// {7C845F81-9BA3-4a9a-9C94-C7056DFD1B57} -static const GUID guid_openmpt_gain = -{ 0x7c845f81, 0x9ba3, 0x4a9a, { 0x9c, 0x94, 0xc7, 0x5, 0x6d, 0xfd, 0x1b, 0x57 } }; - -// {EDB0E1E5-BD2E-475c-B2FB-8448C92F6F29} -static const GUID guid_openmpt_stereo = -{ 0xedb0e1e5, 0xbd2e, 0x475c, { 0xb2, 0xfb, 0x84, 0x48, 0xc9, 0x2f, 0x6f, 0x29 } }; - -// {9115A820-67F5-4d0a-B0FB-D312F7FBBAFF} -static const GUID guid_openmpt_repeat = -{ 0x9115a820, 0x67f5, 0x4d0a, { 0xb0, 0xfb, 0xd3, 0x12, 0xf7, 0xfb, 0xba, 0xff } }; - -// {EAAD5E60-F75B-4071-B308-9956362ECB69} -static const GUID guid_openmpt_filter = -{ 0xeaad5e60, 0xf75b, 0x4071, { 0xb3, 0x8, 0x99, 0x56, 0x36, 0x2e, 0xcb, 0x69 } }; - -// {0CF7E156-44E3-4587-A727-864B045BEDDD} -static const GUID guid_openmpt_amiga = -{ 0x0cf7e156, 0x44e3, 0x4587,{ 0xa7, 027, 0x86, 0x4b, 0x04, 0x5b, 0xed, 0xdd } }; - -// {497BF503-D825-4A02-BE5C-02DB8911B1DC} -static const GUID guid_openmpt_ramping = -{ 0x497bf503, 0xd825, 0x4a02, { 0xbe, 0x5c, 0x2, 0xdb, 0x89, 0x11, 0xb1, 0xdc } }; - - -static advconfig_branch_factory g_advconfigBranch("OpenMPT Component", guid_openmpt_root, advconfig_branch::guid_branch_decoding, 0); - -static advconfig_integer_factory cfg_samplerate("Samplerate [6000..96000] (Hz)" , guid_openmpt_samplerate, guid_openmpt_root, 0, 48000, 6000, 96000); -static advconfig_integer_factory cfg_channels ("Channels [1=mono, 2=stereo, 4=quad]" , guid_openmpt_channels , guid_openmpt_root, 0, 2, 1, 4); -static advconfig_string_factory_MT cfg_gain ("Gain [-12...12] (dB)" , guid_openmpt_gain , guid_openmpt_root, 0, "0.0"); -static advconfig_integer_factory cfg_stereo ("Stereo separation [0...200] (%)" , guid_openmpt_stereo , guid_openmpt_root, 0, 100, 0, 200); -static advconfig_string_factory_MT cfg_repeat ("Repeat [0=never, -1=forever, 1..] (#)" , guid_openmpt_repeat , guid_openmpt_root, 0, "0"); -static advconfig_integer_factory cfg_filter ("Interpolation filter length [1=nearest, 2=linear, 4=cubic, 8=sinc]", guid_openmpt_filter , guid_openmpt_root, 0, 8, 1, 8); -static advconfig_checkbox_factory cfg_amiga ("Use Amiga Resampler for Amiga modules" , guid_openmpt_amiga, guid_openmpt_root, 0, false); -static advconfig_string_factory_MT cfg_ramping ("Volume ramping [-1=default, 0=off, 1..10] (ms)" , guid_openmpt_ramping , guid_openmpt_root, 0, "-1"); - -struct foo_openmpt_settings { - int samplerate; - int channels; - int mastergain_millibel; - int stereoseparation; - int repeatcount; - int interpolationfilterlength; - int ramping; - bool use_amiga_resampler; - foo_openmpt_settings() { - - /* - samplerate = 48000; - channels = 2; - mastergain_millibel = 0; - stereoseparation = 100; - repeatcount = 0; - interpolationfilterlength = 8; - use_amiga_resampler = false; - ramping = -1; - */ - - pfc::string8 tmp; - - samplerate = static_cast( cfg_samplerate.get() ); - - channels = static_cast( cfg_channels.get() ); - if ( channels == 3 ) { - channels = 2; - } - - cfg_gain.get( tmp ); - mastergain_millibel = static_cast( pfc::string_to_float( tmp ) * 100.0 ); - - stereoseparation = static_cast( cfg_stereo.get() ); - - cfg_repeat.get( tmp ); - repeatcount = static_cast( pfc::atoi64_ex( tmp, ~0 ) ); - if ( repeatcount < -1 ) { - repeatcount = 0; - } - - interpolationfilterlength = static_cast( cfg_filter.get() ); - - use_amiga_resampler = cfg_amiga.get(); - - cfg_ramping.get( tmp ); - ramping = static_cast( pfc::atoi64_ex( tmp, ~0 ) ); - if ( ramping < -1 ) { - ramping = -1; - } - - } -}; - - - -// Note that input class does *not* implement virtual methods or derive from interface classes. -// Our methods get called over input framework templates. See input_singletrack_impl for descriptions of what each method does. -// input_stubs just provides stub implementations of mundane methods that are irrelevant for most implementations. -class input_openmpt : public input_stubs { -public: - void open(service_ptr_t p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort) { - if ( p_reason == input_open_info_write ) { - throw exception_io_unsupported_format(); // our input does not support retagging. - } - m_file = p_filehint; // p_filehint may be null, hence next line - input_open_file_helper(m_file,p_path,p_reason,p_abort); // if m_file is null, opens file with appropriate privileges for our operation (read/write for writing tags, read-only otherwise). - if ( m_file->get_size( p_abort ) >= (std::numeric_limits::max)() ) { - throw exception_io_unsupported_format(); - } - std::vector data( static_cast( m_file->get_size( p_abort ) ) ); - m_file->read( data.data(), data.size(), p_abort ); - try { - std::map< std::string, std::string > ctls; - ctls["seek.sync_samples"] = "1"; - mod = new openmpt::module( data, std::clog, ctls ); - } catch ( std::exception & /*e*/ ) { - throw exception_io_data(); - } - settings = foo_openmpt_settings(); - mod->set_repeat_count( settings.repeatcount ); - mod->set_render_param( openmpt::module::RENDER_MASTERGAIN_MILLIBEL, settings.mastergain_millibel ); - mod->set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, settings.stereoseparation ); - mod->set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, settings.interpolationfilterlength ); - mod->set_render_param( openmpt::module::RENDER_VOLUMERAMPING_STRENGTH, settings.ramping ); - mod->ctl_set( "render.resampler.emulate_amiga", settings.use_amiga_resampler ? "1" : "0" ); - } - void get_info(file_info & p_info,abort_callback & p_abort) { - p_info.set_length( mod->get_duration_seconds() ); - p_info.info_set_int( "samplerate", settings.samplerate ); - p_info.info_set_int( "channels", settings.channels ); - p_info.info_set_int( "bitspersample", 32 ); - std::vector keys = mod->get_metadata_keys(); - for ( std::vector::iterator key = keys.begin(); key != keys.end(); ++key ) { - if ( *key == "message_raw" ) { - continue; - } - p_info.meta_set( (*key).c_str(), mod->get_metadata( *key ).c_str() ); - } - } - t_filestats get_file_stats(abort_callback & p_abort) { - return m_file->get_stats(p_abort); - } - void decode_initialize(unsigned p_flags,abort_callback & p_abort) { - m_file->reopen(p_abort); // equivalent to seek to zero, except it also works on nonseekable streams - } - bool decode_run(audio_chunk & p_chunk,abort_callback & p_abort) { - if ( settings.channels == 1 ) { - - std::size_t count = mod->read( settings.samplerate, buffersize, left.data() ); - if ( count == 0 ) { - return false; - } - for ( std::size_t frame = 0; frame < count; frame++ ) { - buffer[frame*1+0] = left[frame]; - } - p_chunk.set_data_32( buffer.data(), count, settings.channels, settings.samplerate ); - return true; - - } else if ( settings.channels == 2 ) { - - std::size_t count = mod->read( settings.samplerate, buffersize, left.data(), right.data() ); - if ( count == 0 ) { - return false; - } - for ( std::size_t frame = 0; frame < count; frame++ ) { - buffer[frame*2+0] = left[frame]; - buffer[frame*2+1] = right[frame]; - } - p_chunk.set_data_32( buffer.data(), count, settings.channels, settings.samplerate ); - return true; - - } else if ( settings.channels == 4 ) { - - std::size_t count = mod->read( settings.samplerate, buffersize, left.data(), right.data(), rear_left.data(), rear_right.data() ); - if ( count == 0 ) { - return false; - } - for ( std::size_t frame = 0; frame < count; frame++ ) { - buffer[frame*4+0] = left[frame]; - buffer[frame*4+1] = right[frame]; - buffer[frame*4+2] = rear_left[frame]; - buffer[frame*4+3] = rear_right[frame]; - } - p_chunk.set_data_32( buffer.data(), count, settings.channels, settings.samplerate ); - return true; - - } else { - return false; - } - - } - void decode_seek(double p_seconds,abort_callback & p_abort) { - mod->set_position_seconds( p_seconds ); - } - bool decode_can_seek() { - return true; - } - bool decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta) { // deals with dynamic information such as VBR bitrates - return false; - } - bool decode_get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) { // deals with dynamic information such as track changes in live streams - return false; - } - void decode_on_idle(abort_callback & p_abort) { - m_file->on_idle( p_abort ); - } - void retag(const file_info & p_info,abort_callback & p_abort) { - throw exception_io_unsupported_format(); - } - static bool g_is_our_content_type(const char * p_content_type) { // match against supported mime types here - return false; - } - static bool g_is_our_path(const char * p_path,const char * p_extension) { - if ( !p_extension ) { - return false; - } - std::vector extensions = openmpt::get_supported_extensions(); - std::string ext = p_extension; - std::transform( ext.begin(), ext.end(), ext.begin(), tolower ); - return std::find( extensions.begin(), extensions.end(), ext ) != extensions.end(); - } - static GUID g_get_guid() { - // {B0B7CCC3-4520-44D3-B5F9-22EB9EBA7575} - static const GUID foo_openmpt_guid = { 0xb0b7ccc3, 0x4520, 0x44d3, { 0xb5, 0xf9, 0x22, 0xeb, 0x9e, 0xba, 0x75, 0x75 } }; - return foo_openmpt_guid; - } - static const char * g_get_name() { - return "OpenMPT Module Decoder"; - } -private: - service_ptr_t m_file; - static const std::size_t buffersize = 1024; - foo_openmpt_settings settings; - openmpt::module * mod; - std::vector left; - std::vector right; - std::vector rear_left; - std::vector rear_right; - std::vector buffer; -public: - input_openmpt() : mod(0), left(buffersize), right(buffersize), rear_left(buffersize), rear_right(buffersize), buffer(4*buffersize) {} - ~input_openmpt() { delete mod; mod = 0; } -}; - -static input_singletrack_factory_t g_input_openmpt_factory; - - -class input_file_type_v2_impl_openmpt : public input_file_type_v2 { -public: - input_file_type_v2_impl_openmpt() - : extensions( openmpt::get_supported_extensions() ) - { } - unsigned get_count() { - return static_cast( extensions.size() ); - } - bool is_associatable( unsigned idx ) { - return true; - } - void get_format_name( unsigned idx, pfc::string_base & out, bool isPlural ) { - if ( isPlural ) { - out = "OpenMPT compatible module files"; - } else { - out = "OpenMPT compatible module file"; - } - } - void get_extensions( unsigned idx, pfc::string_base & out ) { - out = extensions[idx].c_str(); - } -private: - std::vector extensions; -}; - -namespace { static service_factory_single_t g_filetypes; } diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp index f939860a7..1b752106c 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/in_openmpt.cpp @@ -165,7 +165,21 @@ static void apply_options() { self->mod->set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, self->settings.stereoseparation ); self->mod->set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, self->settings.interpolationfilterlength ); self->mod->set_render_param( openmpt::module::RENDER_VOLUMERAMPING_STRENGTH, self->settings.ramping ); - self->mod->ctl_set( "render.resampler.emulate_amiga", self->settings.use_amiga_resampler ? "1" : "0" ); + self->mod->ctl_set_boolean( "render.resampler.emulate_amiga", self->settings.use_amiga_resampler ? true : false ); + switch ( self->settings.amiga_filter_type ) { + case 0: + self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "auto" ); + break; + case 1: + self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "unfiltered" ); + break; + case 0xA500: + self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "a500" ); + break; + case 0xA1200: + self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "a1200" ); + break; + } } self->settings.save(); } @@ -196,7 +210,7 @@ static void config( HWND hwndParent ) { static void about( HWND hwndParent ) { std::ostringstream about; about << SHORT_TITLE << " version " << openmpt::string::get( "library_version" ) << " " << "(built " << openmpt::string::get( "build" ) << ")" << std::endl; - about << " Copyright (c) 2013-2019 OpenMPT developers (https://lib.openmpt.org/)" << std::endl; + about << " Copyright (c) 2013-2020 OpenMPT developers (https://lib.openmpt.org/)" << std::endl; about << " OpenMPT version " << openmpt::string::get( "core_version" ) << std::endl; about << std::endl; about << openmpt::string::get( "contact" ) << std::endl; @@ -225,7 +239,7 @@ static void quit() { } } -static int isourfile( const in_char * fn ) { +static int isourfile( const in_char * /* fn */ ) { return 0; } @@ -358,7 +372,7 @@ static void getfileinfo( const in_char * filename, in_char * title, int * length } } -static void eq_set( int on, char data[10], int preamp ) { +static void eq_set( int /* on */ , char /* data */ [10], int /* preamp */ ) { return; } diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.h index d41324885..5451483e9 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.h @@ -579,7 +579,7 @@ LIBOPENMPT_API size_t openmpt_probe_file_header_get_recommended_size(void); /*! \brief Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it * - * \param flags Ored mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. + * \param flags Bit mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. * \param data Beginning of the file data. * \param size Size of the beginning of the file data. * \param filesize Full size of the file data on disk. @@ -604,7 +604,7 @@ LIBOPENMPT_API size_t openmpt_probe_file_header_get_recommended_size(void); LIBOPENMPT_API int openmpt_probe_file_header( uint64_t flags, const void * data, size_t size, uint64_t filesize, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message ); /*! \brief Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it * - * \param flags Ored mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. + * \param flags Bit mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. * \param data Beginning of the file data. * \param size Size of the beginning of the file data. * \param logfunc Logging function where warning and errors are written. May be NULL. @@ -630,7 +630,7 @@ LIBOPENMPT_API int openmpt_probe_file_header_without_filesize( uint64_t flags, c /*! \brief Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it * - * \param flags Ored mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. + * \param flags Bit mask of OPENMPT_PROBE_FILE_HEADER_FLAGS_MODULES and OPENMPT_PROBE_FILE_HEADER_FLAGS_CONTAINERS, or OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT. * \param stream_callbacks Input stream callback operations. * \param stream Input stream to scan. * \param logfunc Logging function where warning and errors are written. May be NULL. @@ -1140,6 +1140,14 @@ LIBOPENMPT_API const char * openmpt_module_get_metadata_keys( openmpt_module * m */ LIBOPENMPT_API const char * openmpt_module_get_metadata( openmpt_module * mod, const char * key ); +/*! Get the current estimated beats per minute (BPM). + * + * \param mod The module handle to work on. + * \remarks Many module formats lack time signature metadata. It is common that this estimate is off by a factor of two, but other multipliers are also possible. + * \remarks Due to the nature of how module tempo works, the estimate may change slightly after switching libopenmpt's output to a different sample rate. + * \return The current estimated BPM. + */ +LIBOPENMPT_API double openmpt_module_get_current_estimated_bpm( openmpt_module * mod ); /*! \brief Get the current speed * * \param mod The module handle to work on. @@ -1396,35 +1404,79 @@ LIBOPENMPT_API const char * openmpt_module_highlight_pattern_row_channel( openmp * \param mod The module handle to work on. * \return A semicolon-separated list containing all supported ctl keys. * \remarks Currently supported ctl values are: - * - load.skip_samples: Set to "1" to avoid loading samples into memory - * - load.skip_patterns: Set to "1" to avoid loading patterns into memory - * - load.skip_plugins: Set to "1" to avoid loading plugins - * - load.skip_subsongs_init: Set to "1" to avoid pre-initializing sub-songs. Skipping results in faster module loading but slower seeking. - * - seek.sync_samples: Set to "1" to sync sample playback when using openmpt_module_set_position_seconds or openmpt_module_set_position_order_row. - * - subsong: The current subsong. Setting it has identical semantics as openmpt_module_select_subsong(), getting it returns the currently selected subsong. - * - play.at_end: Chooses the behaviour when the end of song is reached: + * - load.skip_samples (boolean): Set to "1" to avoid loading samples into memory + * - load.skip_patterns (boolean): Set to "1" to avoid loading patterns into memory + * - load.skip_plugins (boolean): Set to "1" to avoid loading plugins + * - load.skip_subsongs_init (boolean): Set to "1" to avoid pre-initializing sub-songs. Skipping results in faster module loading but slower seeking. + * - seek.sync_samples (boolean): Set to "1" to sync sample playback when using openmpt_module_set_position_seconds or openmpt_module_set_position_order_row. + * - subsong (integer): The current subsong. Setting it has identical semantics as openmpt_module_select_subsong(), getting it returns the currently selected subsong. + * - play.at_end (text): Chooses the behaviour when the end of song is reached: * - "fadeout": Fades the module out for a short while. Subsequent reads after the fadeout will return 0 rendered frames. * - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the song start or loop start. * - "stop": Returns 0 rendered frames when the song end is reached. Subsequent reads will return 0 rendered frames. - * - play.tempo_factor: Set a floating point tempo factor. "1.0" is the default tempo. - * - play.pitch_factor: Set a floating point pitch factor. "1.0" is the default pitch. - * - render.resampler.emulate_amiga: Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. - * - render.opl.volume_factor: Set volume factor applied to synthesized OPL sounds, relative to the default OPL volume. - * - dither: Set the dither algorithm that is used for the 16 bit versions of openmpt_module_read. Supported values are: + * - play.tempo_factor (floatingpoint): Set a floating point tempo factor. "1.0" is the default tempo. + * - play.pitch_factor (floatingpoint): Set a floating point pitch factor. "1.0" is the default pitch. + * - render.resampler.emulate_amiga (boolean): Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. + * - render.resampler.emulate_amiga_type (string): Configures the filter type to use for the Amiga resampler. Supported values are: + * - "auto": Filter type is chosen by the library and might change. This is the default. + * - "a500": Amiga A500 filter. + * - "a1200": Amiga A1200 filter. + * - "unfiltered": BLEP synthesis without model-specific filters. The LED filter is ignored by this setting. This filter mode is considered to be experimental and might change in the future. + * - render.opl.volume_factor (floatingpoint): Set volume factor applied to synthesized OPL sounds, relative to the default OPL volume. + * - dither (integer): Set the dither algorithm that is used for the 16 bit versions of openmpt_module_read. Supported values are: * - 0: No dithering. * - 1: Default mode. Chosen by OpenMPT code, might change. * - 2: Rectangular, 0.5 bit depth, no noise shaping (original ModPlug Tracker). * - 3: Rectangular, 1 bit depth, simple 1st order noise shaping */ LIBOPENMPT_API const char * openmpt_module_get_ctls( openmpt_module * mod ); + /*! \brief Get current ctl value * * \param mod The module handle to work on. * \param ctl The ctl key whose value should be retrieved. * \return The associated ctl value, or NULL on failure. * \sa openmpt_module_get_ctls + * \deprecated Please use openmpt_module_ctl_get_boolean(), openmpt_module_ctl_get_integer(), openmpt_module_ctl_get_floatingpoint(), or openmpt_module_ctl_get_text(). */ -LIBOPENMPT_API const char * openmpt_module_ctl_get( openmpt_module * mod, const char * ctl ); +LIBOPENMPT_API LIBOPENMPT_DEPRECATED const char * openmpt_module_ctl_get( openmpt_module * mod, const char * ctl ); +/*! \brief Get current ctl boolean value + * + * \param mod The module handle to work on. + * \param ctl The ctl key whose value should be retrieved. + * \return The associated ctl value, or NULL on failure. + * \sa openmpt_module_get_ctls + * \since 0.5.0 + */ +LIBOPENMPT_API int openmpt_module_ctl_get_boolean( openmpt_module * mod, const char * ctl ); +/*! \brief Get current ctl integer value + * + * \param mod The module handle to work on. + * \param ctl The ctl key whose value should be retrieved. + * \return The associated ctl value, or NULL on failure. + * \sa openmpt_module_get_ctls + * \since 0.5.0 + */ +LIBOPENMPT_API int64_t openmpt_module_ctl_get_integer( openmpt_module * mod, const char * ctl ); +/*! \brief Get current ctl floatingpoint value + * + * \param mod The module handle to work on. + * \param ctl The ctl key whose value should be retrieved. + * \return The associated ctl value, or NULL on failure. + * \sa openmpt_module_get_ctls + * \since 0.5.0 + */ +LIBOPENMPT_API double openmpt_module_ctl_get_floatingpoint( openmpt_module * mod, const char * ctl ); +/*! \brief Get current ctl string value + * + * \param mod The module handle to work on. + * \param ctl The ctl key whose value should be retrieved. + * \return The associated ctl value, or NULL on failure. + * \sa openmpt_module_get_ctls + * \since 0.5.0 + */ +LIBOPENMPT_API const char * openmpt_module_ctl_get_text( openmpt_module * mod, const char * ctl ); + /*! \brief Set ctl value * * \param mod The module handle to work on. @@ -1432,8 +1484,49 @@ LIBOPENMPT_API const char * openmpt_module_ctl_get( openmpt_module * mod, const * \param value The value that should be set. * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized. * \sa openmpt_module_get_ctls + * \deprecated Please use openmpt_module_ctl_set_boolean(), openmpt_module_ctl_set_integer(), openmpt_module_ctl_set_floatingpoint(), or openmpt_module_ctl_set_text(). */ -LIBOPENMPT_API int openmpt_module_ctl_set( openmpt_module * mod, const char * ctl, const char * value ); +LIBOPENMPT_API LIBOPENMPT_DEPRECATED int openmpt_module_ctl_set( openmpt_module * mod, const char * ctl, const char * value ); +/*! \brief Set ctl boolean value + * + * \param mod The module handle to work on. + * \param ctl The ctl key whose value should be set. + * \param value The value that should be set. + * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized. + * \sa openmpt_module_get_ctls + * \since 0.5.0 + */ +LIBOPENMPT_API int openmpt_module_ctl_set_boolean( openmpt_module * mod, const char * ctl, int value ); +/*! \brief Set ctl integer value + * + * \param mod The module handle to work on. + * \param ctl The ctl key whose value should be set. + * \param value The value that should be set. + * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized. + * \sa openmpt_module_get_ctls + * \since 0.5.0 + */ +LIBOPENMPT_API int openmpt_module_ctl_set_integer( openmpt_module * mod, const char * ctl, int64_t value ); +/*! \brief Set ctl floatingpoint value + * + * \param mod The module handle to work on. + * \param ctl The ctl key whose value should be set. + * \param value The value that should be set. + * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized. + * \sa openmpt_module_get_ctls + * \since 0.5.0 + */ +LIBOPENMPT_API int openmpt_module_ctl_set_floatingpoint( openmpt_module * mod, const char * ctl, double value ); +/*! \brief Set ctl string value + * + * \param mod The module handle to work on. + * \param ctl The ctl key whose value should be set. + * \param value The value that should be set. + * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized. + * \sa openmpt_module_get_ctls + * \since 0.5.0 + */ +LIBOPENMPT_API int openmpt_module_ctl_set_text( openmpt_module * mod, const char * ctl, const char * value ); /* remember to add new functions to both C and C++ interfaces and to increase OPENMPT_API_VERSION_MINOR */ diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp index f16ccd6e5..0e5bff0b1 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt.hpp @@ -17,8 +17,10 @@ #include #include #include +#include #include +#include #include /*! @@ -174,19 +176,19 @@ LIBOPENMPT_CXX_API std::uint32_t get_core_version(); namespace string { //! Return a verbose library version string from openmpt::string::get(). \deprecated Please use `"library_version"` directly. -LIBOPENMPT_DEPRECATED static const char library_version LIBOPENMPT_ATTR_DEPRECATED [] = "library_version"; +static const char library_version LIBOPENMPT_ATTR_DEPRECATED [] = "library_version"; //! Return a verbose library features string from openmpt::string::get(). \deprecated Please use `"library_features"` directly. -LIBOPENMPT_DEPRECATED static const char library_features LIBOPENMPT_ATTR_DEPRECATED [] = "library_features"; +static const char library_features LIBOPENMPT_ATTR_DEPRECATED [] = "library_features"; //! Return a verbose OpenMPT core version string from openmpt::string::get(). \deprecated Please use `"core_version"` directly. -LIBOPENMPT_DEPRECATED static const char core_version LIBOPENMPT_ATTR_DEPRECATED [] = "core_version"; +static const char core_version LIBOPENMPT_ATTR_DEPRECATED [] = "core_version"; //! Return information about the current build (e.g. the build date or compiler used) from openmpt::string::get(). \deprecated Please use `"build"` directly. -LIBOPENMPT_DEPRECATED static const char build LIBOPENMPT_ATTR_DEPRECATED [] = "build"; +static const char build LIBOPENMPT_ATTR_DEPRECATED [] = "build"; //! Return all contributors from openmpt::string::get(). \deprecated Please use `"credits"` directly. -LIBOPENMPT_DEPRECATED static const char credits LIBOPENMPT_ATTR_DEPRECATED [] = "credits"; +static const char credits LIBOPENMPT_ATTR_DEPRECATED [] = "credits"; //! Return contact information about libopenmpt from openmpt::string::get(). \deprecated Please use `"contact"` directly. -LIBOPENMPT_DEPRECATED static const char contact LIBOPENMPT_ATTR_DEPRECATED [] = "contact"; +static const char contact LIBOPENMPT_ATTR_DEPRECATED [] = "contact"; //! Return the libopenmpt license from openmpt::string::get(). \deprecated Please use `"license"` directly. -LIBOPENMPT_DEPRECATED static const char license LIBOPENMPT_ATTR_DEPRECATED [] = "license"; +static const char license LIBOPENMPT_ATTR_DEPRECATED [] = "license"; //! Get library related metadata. /*! @@ -231,8 +233,16 @@ LIBOPENMPT_CXX_API std::vector get_supported_extensions(); /*! \param extension file extension to query without a leading dot. The case is ignored. \return true if the extension is supported by libopenmpt, false otherwise. + \deprecated Please use openmpt::is_extension_supported2(). */ -LIBOPENMPT_CXX_API bool is_extension_supported( const std::string & extension ); +LIBOPENMPT_ATTR_DEPRECATED LIBOPENMPT_CXX_API bool is_extension_supported( const std::string & extension ); +//! Query whether a file extension is supported +/*! + \param extension file extension to query without a leading dot. The case is ignored. + \return true if the extension is supported by libopenmpt, false otherwise. + \since 0.5.0 +*/ +LIBOPENMPT_CXX_API bool is_extension_supported2( std::string_view extension ); //! Roughly scan the input stream to find out whether libopenmpt might be able to open it /*! @@ -253,7 +263,7 @@ LIBOPENMPT_CXX_API double could_open_probability( std::istream & stream, double /*! \deprecated Please use openmpt::could_open_probability(). */ -LIBOPENMPT_ATTR_DEPRECATED LIBOPENMPT_CXX_API LIBOPENMPT_DEPRECATED double could_open_propability( std::istream & stream, double effort = 1.0, std::ostream & log = std::clog ); +LIBOPENMPT_ATTR_DEPRECATED LIBOPENMPT_CXX_API double could_open_propability( std::istream & stream, double effort = 1.0, std::ostream & log = std::clog ); //! Get recommended header size for successfull format probing /*! @@ -283,7 +293,23 @@ enum probe_file_header_result { //! Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it /*! - \param flags Ored mask of openmpt::probe_file_header_flags_modules and openmpt::probe_file_header_flags_containers, or openmpt::probe_file_header_flags_default. + \param flags Bit mask of openmpt::probe_file_header_flags_modules and openmpt::probe_file_header_flags_containers, or openmpt::probe_file_header_flags_default. + \param data Beginning of the file data. + \param size Size of the beginning of the file data. + \param filesize Full size of the file data on disk. + \remarks It is recommended to provide openmpt::probe_file_header_get_recommended_size() bytes of data for data and size. If the file is smaller, only provide the filesize amount and set size and filesize to the file's size. + \remarks openmpt::could_open_probability() provides a more elaborate interface that might be required for special use cases. It is recommended to use openmpt::probe_file_header() though, if possible. + \retval probe_file_header_result_success The file will most likely be supported by libopenmpt. + \retval probe_file_header_result_failure The file is not supported by libopenmpt. + \retval probe_file_header_result_wantmoredata An answer could not be determined with the amount of data provided. + \sa openmpt::probe_file_header_get_recommended_size() + \sa openmpt::could_open_probability() + \since 0.5.0 +*/ +LIBOPENMPT_CXX_API int probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size, std::uint64_t filesize ); +//! Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it +/*! + \param flags Bit mask of openmpt::probe_file_header_flags_modules and openmpt::probe_file_header_flags_containers, or openmpt::probe_file_header_flags_default. \param data Beginning of the file data. \param size Size of the beginning of the file data. \param filesize Full size of the file data on disk. @@ -300,7 +326,23 @@ LIBOPENMPT_CXX_API int probe_file_header( std::uint64_t flags, const std::uint8_ //! Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it /*! - \param flags Ored mask of openmpt::probe_file_header_flags_modules and openmpt::probe_file_header_flags_containers, or openmpt::probe_file_header_flags_default. + \param flags Bit mask of openmpt::probe_file_header_flags_modules and openmpt::probe_file_header_flags_containers, or openmpt::probe_file_header_flags_default. + \param data Beginning of the file data. + \param size Size of the beginning of the file data. + \remarks It is recommended to use the overload of this function that also takes the filesize as parameter if at all possile. libopenmpt can provide more accurate answers if the filesize is known. + \remarks It is recommended to provide openmpt::probe_file_header_get_recommended_size() bytes of data for data and size. If the file is smaller, only provide the filesize amount and set size to the file's size. + \remarks openmpt::could_open_probability() provides a more elaborate interface that might be required for special use cases. It is recommended to use openmpt::probe_file_header() though, if possible. + \retval probe_file_header_result_success The file will most likely be supported by libopenmpt. + \retval probe_file_header_result_failure The file is not supported by libopenmpt. + \retval probe_file_header_result_wantmoredata An answer could not be determined with the amount of data provided. + \sa openmpt::probe_file_header_get_recommended_size() + \sa openmpt::could_open_probability() + \since 0.5.0 +*/ +LIBOPENMPT_CXX_API int probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size ); +//! Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it +/*! + \param flags Bit mask of openmpt::probe_file_header_flags_modules and openmpt::probe_file_header_flags_containers, or openmpt::probe_file_header_flags_default. \param data Beginning of the file data. \param size Size of the beginning of the file data. \remarks It is recommended to use the overload of this function that also takes the filesize as parameter if at all possile. libopenmpt can provide more accurate answers if the filesize is known. @@ -317,7 +359,7 @@ LIBOPENMPT_CXX_API int probe_file_header( std::uint64_t flags, const std::uint8_ //! Probe the provided bytes from the beginning of a file for supported file format headers to find out whether libopenmpt might be able to open it /*! - \param flags Ored mask of openmpt::probe_file_header_flags_modules and openmpt::probe_file_header_flags_containers, or openmpt::probe_file_header_flags_default. + \param flags Bit mask of openmpt::probe_file_header_flags_modules and openmpt::probe_file_header_flags_containers, or openmpt::probe_file_header_flags_default. \param stream Input stream to scan. \remarks stream is left in an unspecified state when this function returns. \remarks openmpt::could_open_probability() provides a more elaborate interface that might be required for special use cases. It is recommended to use openmpt::probe_file_header() though, if possible. @@ -417,6 +459,38 @@ public: \sa \ref libopenmpt_cpp_fileio */ module( std::istream & stream, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); + /*! + \param data Data to load the module from. + \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. + \param ctls A map of initial ctl values, see openmpt::module::get_ctls. + \throws openmpt::exception Throws an exception derived from openmpt::exception in case the provided file cannot be opened. + \remarks The input data can be discarded after an openmpt::module has been constructed successfully. + \sa \ref libopenmpt_cpp_fileio + \since 0.5.0 + */ + module( const std::vector & data, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); + /*! + \param beg Begin of data to load the module from. + \param end End of data to load the module from. + \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. + \param ctls A map of initial ctl values, see openmpt::module::get_ctls. + \throws openmpt::exception Throws an exception derived from openmpt::exception in case the provided file cannot be opened. + \remarks The input data can be discarded after an openmpt::module has been constructed successfully. + \sa \ref libopenmpt_cpp_fileio + \since 0.5.0 + */ + module( const std::byte * beg, const std::byte * end, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); + /*! + \param data Data to load the module from. + \param size Amount of data available. + \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. + \param ctls A map of initial ctl values, see openmpt::module::get_ctls. + \throws openmpt::exception Throws an exception derived from openmpt::exception in case the provided file cannot be opened. + \remarks The input data can be discarded after an openmpt::module has been constructed successfully. + \sa \ref libopenmpt_cpp_fileio + \since 0.5.0 + */ + module( const std::byte * data, std::size_t size, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); /*! \param data Data to load the module from. \param log Log where any warnings or errors are printed to. The lifetime of the reference has to be as long as the lifetime of the module instance. @@ -741,6 +815,13 @@ public: */ std::string get_metadata( const std::string & key ) const; + //! Get the current estimated beats per minute (BPM). + /*! + \remarks Many module formats lack time signature metadata. It is common that this estimate is off by a factor of two, but other multipliers are also possible. + \remarks Due to the nature of how module tempo works, the estimate may change slightly after switching libopenmpt's output to a different sample rate. + \return The current estimated BPM. + */ + double get_current_estimated_bpm() const; //! Get the current speed /*! \return The current speed in ticks per row. @@ -962,21 +1043,26 @@ public: /*! \return A vector containing all supported ctl keys. \remarks Currently supported ctl values are: - - load.skip_samples: Set to "1" to avoid loading samples into memory - - load.skip_patterns: Set to "1" to avoid loading patterns into memory - - load.skip_plugins: Set to "1" to avoid loading plugins - - load.skip_subsongs_init: Set to "1" to avoid pre-initializing sub-songs. Skipping results in faster module loading but slower seeking. - - seek.sync_samples: Set to "1" to sync sample playback when using openmpt::module::set_position_seconds or openmpt::module::set_position_order_row. - - subsong: The current subsong. Setting it has identical semantics as openmpt::module::select_subsong(), getting it returns the currently selected subsong. - - play.at_end: Chooses the behaviour when the end of song is reached: + - load.skip_samples (boolean): Set to "1" to avoid loading samples into memory + - load.skip_patterns (boolean): Set to "1" to avoid loading patterns into memory + - load.skip_plugins (boolean): Set to "1" to avoid loading plugins + - load.skip_subsongs_init (boolean): Set to "1" to avoid pre-initializing sub-songs. Skipping results in faster module loading but slower seeking. + - seek.sync_samples (boolean): Set to "1" to sync sample playback when using openmpt::module::set_position_seconds or openmpt::module::set_position_order_row. + - subsong (integer): The current subsong. Setting it has identical semantics as openmpt::module::select_subsong(), getting it returns the currently selected subsong. + - play.at_end (text): Chooses the behaviour when the end of song is reached: - "fadeout": Fades the module out for a short while. Subsequent reads after the fadeout will return 0 rendered frames. - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the song start or loop start. - "stop": Returns 0 rendered frames when the song end is reached. Subsequent reads will return 0 rendered frames. - - play.tempo_factor: Set a floating point tempo factor. "1.0" is the default tempo. - - play.pitch_factor: Set a floating point pitch factor. "1.0" is the default pitch. - - render.resampler.emulate_amiga: Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. - - render.opl.volume_factor: Set volume factor applied to synthesized OPL sounds, relative to the default OPL volume. - - dither: Set the dither algorithm that is used for the 16 bit versions of openmpt::module::read. Supported values are: + - play.tempo_factor (floatingpoint): Set a floating point tempo factor. "1.0" is the default tempo. + - play.pitch_factor (floatingpoint): Set a floating point pitch factor. "1.0" is the default pitch. + - render.resampler.emulate_amiga (boolean): Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. + - render.resampler.emulate_amiga_type (string): Configures the filter type to use for the Amiga resampler. Supported values are: + - "auto": Filter type is chosen by the library and might change. This is the default. + - "a500": Amiga A500 filter. + - "a1200": Amiga A1200 filter. + - "unfiltered": BLEP synthesis without model-specific filters. The LED filter is ignored by this setting. This filter mode is considered to be experimental and might change in the future. + - render.opl.volume_factor (floatingpoint): Set volume factor applied to synthesized OPL sounds, relative to the default OPL volume. + - dither (integer): Set the dither algorithm that is used for the 16 bit versions of openmpt::module::read. Supported values are: - 0: No dithering. - 1: Default mode. Chosen by OpenMPT code, might change. - 2: Rectangular, 0.5 bit depth, no noise shaping (original ModPlug Tracker). @@ -991,16 +1077,87 @@ public: \param ctl The ctl key whose value should be retrieved. \return The associated ctl value. \sa openmpt::module::get_ctls + \deprecated Please use openmpt::module::ctl_get_boolean(), openmpt::module::ctl_get_integer(), openmpt::module::ctl_get_floatingpoint(), or openmpt::module::ctl_get_text(). */ - std::string ctl_get( const std::string & ctl ) const; + LIBOPENMPT_ATTR_DEPRECATED std::string ctl_get( const std::string & ctl ) const; + //! Get current ctl boolean value + /*! + \param ctl The ctl key whose value should be retrieved. + \return The associated ctl value. + \sa openmpt::module::get_ctls + \since 0.5.0 + */ + bool ctl_get_boolean( std::string_view ctl ) const; + //! Get current ctl integer value + /*! + \param ctl The ctl key whose value should be retrieved. + \return The associated ctl value. + \sa openmpt::module::get_ctls + \since 0.5.0 + */ + std::int64_t ctl_get_integer( std::string_view ctl ) const; + //! Get current ctl floatingpoint value + /*! + \param ctl The ctl key whose value should be retrieved. + \return The associated ctl value. + \sa openmpt::module::get_ctls + \since 0.5.0 + */ + double ctl_get_floatingpoint( std::string_view ctl ) const; + //! Get current ctl text value + /*! + \param ctl The ctl key whose value should be retrieved. + \return The associated ctl value. + \sa openmpt::module::get_ctls + \since 0.5.0 + */ + std::string ctl_get_text( std::string_view ctl ) const; + //! Set ctl value /*! \param ctl The ctl key whose value should be set. \param value The value that should be set. \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls. \sa openmpt::module::get_ctls + \deprecated Please use openmpt::module::ctl_set_bool(), openmpt::module::ctl_set_int(), openmpt::module::ctl_set_float(), or openmpt::module::ctl_set_string(). */ - void ctl_set( const std::string & ctl, const std::string & value ); + LIBOPENMPT_ATTR_DEPRECATED void ctl_set( const std::string & ctl, const std::string & value ); + //! Set ctl boolean value + /*! + \param ctl The ctl key whose value should be set. + \param value The value that should be set. + \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls. + \sa openmpt::module::get_ctls + \since 0.5.0 + */ + void ctl_set_boolean( std::string_view ctl, bool value ); + //! Set ctl integer value + /*! + \param ctl The ctl key whose value should be set. + \param value The value that should be set. + \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls. + \sa openmpt::module::get_ctls + \since 0.5.0 + */ + void ctl_set_integer( std::string_view ctl, std::int64_t value ); + //! Set ctl floatingpoint value + /*! + \param ctl The ctl key whose value should be set. + \param value The value that should be set. + \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls. + \sa openmpt::module::get_ctls + \since 0.5.0 + */ + void ctl_set_floatingpoint( std::string_view ctl, double value ); + //! Set ctl text value + /*! + \param ctl The ctl key whose value should be set. + \param value The value that should be set. + \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls. + \sa openmpt::module::get_ctls + \since 0.5.0 + */ + void ctl_set_text( std::string_view ctl, std::string_view value ); // remember to add new functions to both C and C++ interfaces and to increase OPENMPT_API_VERSION_MINOR diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_c.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_c.cpp index 845e643bf..fff8a59fb 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_c.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_c.cpp @@ -285,7 +285,7 @@ uint32_t openmpt_get_library_version(void) { try { return openmpt::get_library_version(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__ ); + openmpt::report_exception( __func__ ); } return 0; } @@ -294,7 +294,7 @@ uint32_t openmpt_get_core_version(void) { try { return openmpt::get_core_version(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__ ); + openmpt::report_exception( __func__ ); } return 0; } @@ -303,7 +303,7 @@ void openmpt_free_string( const char * str ) { try { std::free( const_cast< char * >( str ) ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__ ); + openmpt::report_exception( __func__ ); } return; } @@ -315,7 +315,7 @@ const char * openmpt_get_string( const char * key ) { } return openmpt::strdup( openmpt::string::get( key ).c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__ ); + openmpt::report_exception( __func__ ); } return NULL; } @@ -335,7 +335,7 @@ const char * openmpt_get_supported_extensions(void) { } return openmpt::strdup( retval.c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__ ); + openmpt::report_exception( __func__ ); } return NULL; } @@ -347,7 +347,7 @@ int openmpt_is_extension_supported( const char * extension ) { } return openmpt::module_impl::is_extension_supported( extension ) ? 1 : 0; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__ ); + openmpt::report_exception( __func__ ); } return 0; } @@ -468,7 +468,7 @@ double openmpt_could_open_probability2( openmpt_stream_callbacks stream_callback openmpt::callback_stream_wrapper istream = { stream, stream_callbacks.read, stream_callbacks.seek, stream_callbacks.tell }; return openmpt::module_impl::could_open_probability( istream, effort, openmpt::helper::make_unique( logfunc ? logfunc : openmpt_log_func_default, loguser ) ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, logfunc, loguser, errfunc, erruser, error, error_message ); + openmpt::report_exception( __func__, logfunc, loguser, errfunc, erruser, error, error_message ); } return 0.0; } @@ -477,7 +477,7 @@ size_t openmpt_probe_file_header_get_recommended_size(void) { try { return openmpt::module_impl::probe_file_header_get_recommended_size(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__ ); + openmpt::report_exception( __func__ ); } return 0; } @@ -486,7 +486,7 @@ int openmpt_probe_file_header( uint64_t flags, const void * data, size_t size, u try { return openmpt::module_impl::probe_file_header( flags, data, size, filesize ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, logfunc, loguser, errfunc, erruser, error, error_message ); + openmpt::report_exception( __func__, logfunc, loguser, errfunc, erruser, error, error_message ); } return OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR; } @@ -495,7 +495,7 @@ int openmpt_probe_file_header_without_filesize( uint64_t flags, const void * dat try { return openmpt::module_impl::probe_file_header( flags, data, size ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, logfunc, loguser, errfunc, erruser, error, error_message ); + openmpt::report_exception( __func__, logfunc, loguser, errfunc, erruser, error, error_message ); } return OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR; } @@ -505,7 +505,7 @@ int openmpt_probe_file_header_from_stream( uint64_t flags, openmpt_stream_callba openmpt::callback_stream_wrapper istream = { stream, stream_callbacks.read, stream_callbacks.seek, stream_callbacks.tell }; return openmpt::module_impl::probe_file_header( flags, istream ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, logfunc, loguser, errfunc, erruser, error, error_message ); + openmpt::report_exception( __func__, logfunc, loguser, errfunc, erruser, error, error_message ); } return OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR; } @@ -543,7 +543,14 @@ openmpt_module * openmpt_module_create2( openmpt_stream_callbacks stream_callbac mod->impl = new openmpt::module_impl( istream, openmpt::helper::make_unique( mod->logfunc, mod->loguser ), ctls_map ); return mod; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod, error, error_message ); + #if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:6001) // false-positive: Using uninitialized memory 'mod'. + #endif // _MSC_VER + openmpt::report_exception( __func__, mod, error, error_message ); + #if defined(_MSC_VER) + #pragma warning(pop) + #endif // _MSC_VER } delete mod->impl; mod->impl = 0; @@ -554,7 +561,7 @@ openmpt_module * openmpt_module_create2( openmpt_stream_callbacks stream_callbac std::free( (void*)mod ); mod = NULL; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, 0, error, error_message ); + openmpt::report_exception( __func__, 0, error, error_message ); } return NULL; } @@ -591,7 +598,14 @@ openmpt_module * openmpt_module_create_from_memory2( const void * filedata, size mod->impl = new openmpt::module_impl( filedata, filesize, openmpt::helper::make_unique( mod->logfunc, mod->loguser ), ctls_map ); return mod; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod, error, error_message ); + #if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:6001) // false-positive: Using uninitialized memory 'mod'. + #endif // _MSC_VER + openmpt::report_exception( __func__, mod, error, error_message ); + #if defined(_MSC_VER) + #pragma warning(pop) + #endif // _MSC_VER } delete mod->impl; mod->impl = 0; @@ -602,7 +616,7 @@ openmpt_module * openmpt_module_create_from_memory2( const void * filedata, size std::free( (void*)mod ); mod = NULL; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, 0, error, error_message ); + openmpt::report_exception( __func__, 0, error, error_message ); } return NULL; } @@ -620,7 +634,7 @@ void openmpt_module_destroy( openmpt_module * mod ) { mod = NULL; return; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return; } @@ -632,7 +646,7 @@ void openmpt_module_set_log_func( openmpt_module * mod, openmpt_log_func logfunc mod->loguser = loguser; return; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return; } @@ -645,7 +659,7 @@ void openmpt_module_set_error_func( openmpt_module * mod, openmpt_error_func err mod->error = OPENMPT_ERROR_OK; return; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return; } @@ -655,7 +669,7 @@ int openmpt_module_error_get_last( openmpt_module * mod ) { openmpt::interface::check_soundfile( mod ); return mod->error; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return -1; } @@ -665,7 +679,7 @@ const char * openmpt_module_error_get_last_message( openmpt_module * mod ) { openmpt::interface::check_soundfile( mod ); return mod->error_message ? openmpt::strdup( mod->error_message ) : openmpt::strdup( "" ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } @@ -680,7 +694,7 @@ void openmpt_module_error_set_last( openmpt_module * mod, int error ) { } return; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return; } @@ -695,7 +709,7 @@ void openmpt_module_error_clear( openmpt_module * mod ) { } return; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return; } @@ -706,7 +720,7 @@ int openmpt_module_select_subsong( openmpt_module * mod, int32_t subsong ) { mod->impl->select_subsong( subsong ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -716,7 +730,7 @@ int32_t openmpt_module_get_selected_subsong( openmpt_module * mod ) { openmpt::interface::check_soundfile( mod ); return mod->impl->get_selected_subsong(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return -1; } @@ -727,7 +741,7 @@ int openmpt_module_set_repeat_count( openmpt_module * mod, int32_t repeat_count mod->impl->set_repeat_count( repeat_count ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -736,7 +750,7 @@ int32_t openmpt_module_get_repeat_count( openmpt_module * mod ) { openmpt::interface::check_soundfile( mod ); return mod->impl->get_repeat_count(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -746,7 +760,7 @@ double openmpt_module_get_duration_seconds( openmpt_module * mod ) { openmpt::interface::check_soundfile( mod ); return mod->impl->get_duration_seconds(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0.0; } @@ -756,7 +770,7 @@ double openmpt_module_set_position_seconds( openmpt_module * mod, double seconds openmpt::interface::check_soundfile( mod ); return mod->impl->set_position_seconds( seconds ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0.0; } @@ -765,7 +779,7 @@ double openmpt_module_get_position_seconds( openmpt_module * mod ) { openmpt::interface::check_soundfile( mod ); return mod->impl->get_position_seconds(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0.0; } @@ -775,7 +789,7 @@ double openmpt_module_set_position_order_row( openmpt_module * mod, int32_t orde openmpt::interface::check_soundfile( mod ); return mod->impl->set_position_order_row( order, row ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0.0; } @@ -787,7 +801,7 @@ int openmpt_module_get_render_param( openmpt_module * mod, int param, int32_t * *value = mod->impl->get_render_param( (openmpt::module::render_param)param ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -797,7 +811,7 @@ int openmpt_module_set_render_param( openmpt_module * mod, int param, int32_t va mod->impl->set_render_param( (openmpt::module::render_param)param, value ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -807,7 +821,7 @@ size_t openmpt_module_read_mono( openmpt_module * mod, int32_t samplerate, size_ openmpt::interface::check_soundfile( mod ); return mod->impl->read( samplerate, count, mono ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -816,7 +830,7 @@ size_t openmpt_module_read_stereo( openmpt_module * mod, int32_t samplerate, siz openmpt::interface::check_soundfile( mod ); return mod->impl->read( samplerate, count, left, right ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -825,7 +839,7 @@ size_t openmpt_module_read_quad( openmpt_module * mod, int32_t samplerate, size_ openmpt::interface::check_soundfile( mod ); return mod->impl->read( samplerate, count, left, right, rear_left, rear_right ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -834,7 +848,7 @@ size_t openmpt_module_read_float_mono( openmpt_module * mod, int32_t samplerate, openmpt::interface::check_soundfile( mod ); return mod->impl->read( samplerate, count, mono ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -843,7 +857,7 @@ size_t openmpt_module_read_float_stereo( openmpt_module * mod, int32_t samplerat openmpt::interface::check_soundfile( mod ); return mod->impl->read( samplerate, count, left, right ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -852,7 +866,7 @@ size_t openmpt_module_read_float_quad( openmpt_module * mod, int32_t samplerate, openmpt::interface::check_soundfile( mod ); return mod->impl->read( samplerate, count, left, right, rear_left, rear_right ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -861,7 +875,7 @@ size_t openmpt_module_read_interleaved_stereo( openmpt_module * mod, int32_t sam openmpt::interface::check_soundfile( mod ); return mod->impl->read_interleaved_stereo( samplerate, count, interleaved_stereo ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -870,7 +884,7 @@ size_t openmpt_module_read_interleaved_quad( openmpt_module * mod, int32_t sampl openmpt::interface::check_soundfile( mod ); return mod->impl->read_interleaved_quad( samplerate, count, interleaved_quad ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -879,7 +893,7 @@ size_t openmpt_module_read_interleaved_float_stereo( openmpt_module * mod, int32 openmpt::interface::check_soundfile( mod ); return mod->impl->read_interleaved_stereo( samplerate, count, interleaved_stereo ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -888,7 +902,7 @@ size_t openmpt_module_read_interleaved_float_quad( openmpt_module * mod, int32_t openmpt::interface::check_soundfile( mod ); return mod->impl->read_interleaved_quad( samplerate, count, interleaved_quad ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -909,7 +923,7 @@ const char * openmpt_module_get_metadata_keys( openmpt_module * mod ) { } return openmpt::strdup( retval.c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } @@ -919,168 +933,177 @@ const char * openmpt_module_get_metadata( openmpt_module * mod, const char * key openmpt::interface::check_pointer( key ); return openmpt::strdup( mod->impl->get_metadata( key ).c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } -LIBOPENMPT_API int32_t openmpt_module_get_current_speed( openmpt_module * mod ) { +double openmpt_module_get_current_estimated_bpm( openmpt_module * mod ) { + try { + openmpt::interface::check_soundfile( mod ); + return mod->impl->get_current_estimated_bpm(); + } catch ( ... ) { + openmpt::report_exception( __func__, mod ); + } + return 0.0; +} +int32_t openmpt_module_get_current_speed( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_speed(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_current_tempo( openmpt_module * mod ) { +int32_t openmpt_module_get_current_tempo( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_tempo(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_current_order( openmpt_module * mod ) { +int32_t openmpt_module_get_current_order( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_order(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_current_pattern( openmpt_module * mod ) { +int32_t openmpt_module_get_current_pattern( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_pattern(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_current_row( openmpt_module * mod ) { +int32_t openmpt_module_get_current_row( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_row(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_current_playing_channels( openmpt_module * mod ) { +int32_t openmpt_module_get_current_playing_channels( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_playing_channels(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API float openmpt_module_get_current_channel_vu_mono( openmpt_module * mod, int32_t channel ) { +float openmpt_module_get_current_channel_vu_mono( openmpt_module * mod, int32_t channel ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_channel_vu_mono( channel ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0.0; } -LIBOPENMPT_API float openmpt_module_get_current_channel_vu_left( openmpt_module * mod, int32_t channel ) { +float openmpt_module_get_current_channel_vu_left( openmpt_module * mod, int32_t channel ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_channel_vu_left( channel ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0.0; } -LIBOPENMPT_API float openmpt_module_get_current_channel_vu_right( openmpt_module * mod, int32_t channel ) { +float openmpt_module_get_current_channel_vu_right( openmpt_module * mod, int32_t channel ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_channel_vu_right( channel ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0.0; } -LIBOPENMPT_API float openmpt_module_get_current_channel_vu_rear_left( openmpt_module * mod, int32_t channel ) { +float openmpt_module_get_current_channel_vu_rear_left( openmpt_module * mod, int32_t channel ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_channel_vu_rear_left( channel ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0.0; } -LIBOPENMPT_API float openmpt_module_get_current_channel_vu_rear_right( openmpt_module * mod, int32_t channel ) { +float openmpt_module_get_current_channel_vu_rear_right( openmpt_module * mod, int32_t channel ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_current_channel_vu_rear_right( channel ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0.0; } -LIBOPENMPT_API int32_t openmpt_module_get_num_subsongs( openmpt_module * mod ) { +int32_t openmpt_module_get_num_subsongs( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_num_subsongs(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_num_channels( openmpt_module * mod ) { +int32_t openmpt_module_get_num_channels( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_num_channels(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_num_orders( openmpt_module * mod ) { +int32_t openmpt_module_get_num_orders( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_num_orders(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_num_patterns( openmpt_module * mod ) { +int32_t openmpt_module_get_num_patterns( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_num_patterns(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_num_instruments( openmpt_module * mod ) { +int32_t openmpt_module_get_num_instruments( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_num_instruments(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_num_samples( openmpt_module * mod ) { +int32_t openmpt_module_get_num_samples( openmpt_module * mod ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_num_samples(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API const char * openmpt_module_get_subsong_name( openmpt_module * mod, int32_t index ) { +const char * openmpt_module_get_subsong_name( openmpt_module * mod, int32_t index ) { try { openmpt::interface::check_soundfile( mod ); std::vector names = mod->impl->get_subsong_names(); @@ -1092,11 +1115,11 @@ LIBOPENMPT_API const char * openmpt_module_get_subsong_name( openmpt_module * mo } return openmpt::strdup( names[index].c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } -LIBOPENMPT_API const char * openmpt_module_get_channel_name( openmpt_module * mod, int32_t index ) { +const char * openmpt_module_get_channel_name( openmpt_module * mod, int32_t index ) { try { openmpt::interface::check_soundfile( mod ); std::vector names = mod->impl->get_channel_names(); @@ -1108,11 +1131,11 @@ LIBOPENMPT_API const char * openmpt_module_get_channel_name( openmpt_module * mo } return openmpt::strdup( names[index].c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } -LIBOPENMPT_API const char * openmpt_module_get_order_name( openmpt_module * mod, int32_t index ) { +const char * openmpt_module_get_order_name( openmpt_module * mod, int32_t index ) { try { openmpt::interface::check_soundfile( mod ); std::vector names = mod->impl->get_order_names(); @@ -1124,11 +1147,11 @@ LIBOPENMPT_API const char * openmpt_module_get_order_name( openmpt_module * mod, } return openmpt::strdup( names[index].c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } -LIBOPENMPT_API const char * openmpt_module_get_pattern_name( openmpt_module * mod, int32_t index ) { +const char * openmpt_module_get_pattern_name( openmpt_module * mod, int32_t index ) { try { openmpt::interface::check_soundfile( mod ); std::vector names = mod->impl->get_pattern_names(); @@ -1140,11 +1163,11 @@ LIBOPENMPT_API const char * openmpt_module_get_pattern_name( openmpt_module * mo } return openmpt::strdup( names[index].c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } -LIBOPENMPT_API const char * openmpt_module_get_instrument_name( openmpt_module * mod, int32_t index ) { +const char * openmpt_module_get_instrument_name( openmpt_module * mod, int32_t index ) { try { openmpt::interface::check_soundfile( mod ); std::vector names = mod->impl->get_instrument_names(); @@ -1156,11 +1179,11 @@ LIBOPENMPT_API const char * openmpt_module_get_instrument_name( openmpt_module * } return openmpt::strdup( names[index].c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } -LIBOPENMPT_API const char * openmpt_module_get_sample_name( openmpt_module * mod, int32_t index ) { +const char * openmpt_module_get_sample_name( openmpt_module * mod, int32_t index ) { try { openmpt::interface::check_soundfile( mod ); std::vector names = mod->impl->get_sample_names(); @@ -1172,77 +1195,77 @@ LIBOPENMPT_API const char * openmpt_module_get_sample_name( openmpt_module * mod } return openmpt::strdup( names[index].c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } -LIBOPENMPT_API int32_t openmpt_module_get_order_pattern( openmpt_module * mod, int32_t order ) { +int32_t openmpt_module_get_order_pattern( openmpt_module * mod, int32_t order ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_order_pattern( order ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API int32_t openmpt_module_get_pattern_num_rows( openmpt_module * mod, int32_t pattern ) { +int32_t openmpt_module_get_pattern_num_rows( openmpt_module * mod, int32_t pattern ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_pattern_num_rows( pattern ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API uint8_t openmpt_module_get_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command ) { +uint8_t openmpt_module_get_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command ) { try { openmpt::interface::check_soundfile( mod ); return mod->impl->get_pattern_row_channel_command( pattern, row, channel, command ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API const char * openmpt_module_format_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command ) { +const char * openmpt_module_format_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command ) { try { openmpt::interface::check_soundfile( mod ); return openmpt::strdup( mod->impl->format_pattern_row_channel_command( pattern, row, channel, command ).c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API const char * openmpt_module_highlight_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command ) { +const char * openmpt_module_highlight_pattern_row_channel_command( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, int command ) { try { openmpt::interface::check_soundfile( mod ); return openmpt::strdup( mod->impl->highlight_pattern_row_channel_command( pattern, row, channel, command ).c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API const char * openmpt_module_format_pattern_row_channel( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, size_t width, int pad ) { +const char * openmpt_module_format_pattern_row_channel( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, size_t width, int pad ) { try { openmpt::interface::check_soundfile( mod ); return openmpt::strdup( mod->impl->format_pattern_row_channel( pattern, row, channel, width, pad ? true : false ).c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } -LIBOPENMPT_API const char * openmpt_module_highlight_pattern_row_channel( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, size_t width, int pad ) { +const char * openmpt_module_highlight_pattern_row_channel( openmpt_module * mod, int32_t pattern, int32_t row, int32_t channel, size_t width, int pad ) { try { openmpt::interface::check_soundfile( mod ); return openmpt::strdup( mod->impl->highlight_pattern_row_channel( pattern, row, channel, width, pad ? true : false ).c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return 0; } @@ -1263,7 +1286,7 @@ const char * openmpt_module_get_ctls( openmpt_module * mod ) { } return openmpt::strdup( retval.c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); } return NULL; } @@ -1274,7 +1297,47 @@ const char * openmpt_module_ctl_get( openmpt_module * mod, const char * ctl ) { openmpt::interface::check_pointer( ctl ); return openmpt::strdup( mod->impl->ctl_get( ctl ).c_str() ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); + } + return NULL; +} +int openmpt_module_ctl_get_boolean( openmpt_module * mod, const char * ctl ) { + try { + openmpt::interface::check_soundfile( mod ); + openmpt::interface::check_pointer( ctl ); + return mod->impl->ctl_get_boolean( ctl ); + } catch ( ... ) { + openmpt::report_exception( __func__, mod ); + } + return 0; +} +int64_t openmpt_module_ctl_get_integer( openmpt_module * mod, const char * ctl ) { + try { + openmpt::interface::check_soundfile( mod ); + openmpt::interface::check_pointer( ctl ); + return mod->impl->ctl_get_integer( ctl ); + } catch ( ... ) { + openmpt::report_exception( __func__, mod ); + } + return 0; +} +double openmpt_module_ctl_get_floatingpoint( openmpt_module * mod, const char * ctl ) { + try { + openmpt::interface::check_soundfile( mod ); + openmpt::interface::check_pointer( ctl ); + return mod->impl->ctl_get_floatingpoint( ctl ); + } catch ( ... ) { + openmpt::report_exception( __func__, mod ); + } + return 0.0; +} +const char * openmpt_module_ctl_get_text( openmpt_module * mod, const char * ctl ) { + try { + openmpt::interface::check_soundfile( mod ); + openmpt::interface::check_pointer( ctl ); + return openmpt::strdup( mod->impl->ctl_get_text( ctl ).c_str() ); + } catch ( ... ) { + openmpt::report_exception( __func__, mod ); } return NULL; } @@ -1287,11 +1350,55 @@ int openmpt_module_ctl_set( openmpt_module * mod, const char * ctl, const char * mod->impl->ctl_set( ctl, value ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod ); + openmpt::report_exception( __func__, mod ); + } + return 0; +} +int openmpt_module_ctl_set_boolean( openmpt_module * mod, const char * ctl, int value ) { + try { + openmpt::interface::check_soundfile( mod ); + openmpt::interface::check_pointer( ctl ); + mod->impl->ctl_set_boolean( ctl, value ? true : false ); + return 1; + } catch ( ... ) { + openmpt::report_exception( __func__, mod ); + } + return 0; +} +int openmpt_module_ctl_set_integer( openmpt_module * mod, const char * ctl, int64_t value ) { + try { + openmpt::interface::check_soundfile( mod ); + openmpt::interface::check_pointer( ctl ); + mod->impl->ctl_set_integer( ctl, value ); + return 1; + } catch ( ... ) { + openmpt::report_exception( __func__, mod ); + } + return 0; +} +int openmpt_module_ctl_set_floatingpoint( openmpt_module * mod, const char * ctl, double value ) { + try { + openmpt::interface::check_soundfile( mod ); + openmpt::interface::check_pointer( ctl ); + mod->impl->ctl_set_floatingpoint( ctl, value ); + return 1; + } catch ( ... ) { + openmpt::report_exception( __func__, mod ); + } + return 0; +} +int openmpt_module_ctl_set_text( openmpt_module * mod, const char * ctl, const char * value ) { + try { + openmpt::interface::check_soundfile( mod ); + openmpt::interface::check_pointer( ctl ); + openmpt::interface::check_pointer( value ); + mod->impl->ctl_set_text( ctl, value ); + return 1; + } catch ( ... ) { + openmpt::report_exception( __func__, mod ); } return 0; } - openmpt_module_ext * openmpt_module_ext_create( openmpt_stream_callbacks stream_callbacks, void * stream, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message, const openmpt_module_initial_ctl * ctls ) { try { @@ -1326,9 +1433,16 @@ openmpt_module_ext * openmpt_module_ext_create( openmpt_stream_callbacks stream_ mod->impl = mod_ext->impl; return mod_ext; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod, error, error_message ); + openmpt::report_exception( __func__, mod, error, error_message ); } - delete mod_ext->impl; + #if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:6001) // false-positive: Using uninitialized memory 'mod_ext'. + #endif // _MSC_VER + delete mod_ext->impl; + #if defined(_MSC_VER) + #pragma warning(pop) + #endif // _MSC_VER mod_ext->impl = 0; mod->impl = 0; if ( mod->error_message ) { @@ -1338,7 +1452,7 @@ openmpt_module_ext * openmpt_module_ext_create( openmpt_stream_callbacks stream_ std::free( (void*)mod_ext ); mod_ext = NULL; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, 0, error, error_message ); + openmpt::report_exception( __func__, 0, error, error_message ); } return NULL; } @@ -1375,9 +1489,16 @@ openmpt_module_ext * openmpt_module_ext_create_from_memory( const void * filedat mod->impl = mod_ext->impl; return mod_ext; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod, error, error_message ); + openmpt::report_exception( __func__, mod, error, error_message ); } - delete mod_ext->impl; + #if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:6001) // false-positive: Using uninitialized memory 'mod_ext'. + #endif // _MSC_VER + delete mod_ext->impl; + #if defined(_MSC_VER) + #pragma warning(pop) + #endif // _MSC_VER mod_ext->impl = 0; mod->impl = 0; if ( mod->error_message ) { @@ -1387,7 +1508,7 @@ openmpt_module_ext * openmpt_module_ext_create_from_memory( const void * filedat std::free( (void*)mod_ext ); mod_ext = NULL; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, 0, error, error_message ); + openmpt::report_exception( __func__, 0, error, error_message ); } return NULL; } @@ -1407,7 +1528,7 @@ void openmpt_module_ext_destroy( openmpt_module_ext * mod_ext ) { mod_ext = NULL; return; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return; } @@ -1418,7 +1539,7 @@ openmpt_module * openmpt_module_ext_get_module( openmpt_module_ext * mod_ext ) { openmpt_module * mod = &mod_ext->mod; return mod; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return NULL; } @@ -1430,7 +1551,7 @@ static int get_pattern_row_channel_volume_effect_type( openmpt_module_ext * mod_ openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_pattern_row_channel_volume_effect_type( pattern, row, channel ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return -1; } @@ -1439,163 +1560,163 @@ static int get_pattern_row_channel_effect_type( openmpt_module_ext * mod_ext, in openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_pattern_row_channel_effect_type( pattern, row, channel ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return -1; } -int set_current_speed( openmpt_module_ext * mod_ext, int32_t speed ) { +static int set_current_speed( openmpt_module_ext * mod_ext, int32_t speed ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->set_current_speed( speed ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } -int set_current_tempo( openmpt_module_ext * mod_ext, int32_t tempo ) { +static int set_current_tempo( openmpt_module_ext * mod_ext, int32_t tempo ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->set_current_tempo( tempo ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } -int set_tempo_factor( openmpt_module_ext * mod_ext, double factor ) { +static int set_tempo_factor( openmpt_module_ext * mod_ext, double factor ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->set_tempo_factor( factor ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } -double get_tempo_factor( openmpt_module_ext * mod_ext ) { +static double get_tempo_factor( openmpt_module_ext * mod_ext ) { try { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_tempo_factor(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0.0; } -int set_pitch_factor( openmpt_module_ext * mod_ext, double factor ) { +static int set_pitch_factor( openmpt_module_ext * mod_ext, double factor ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->set_pitch_factor( factor ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } -double get_pitch_factor( openmpt_module_ext * mod_ext ) { +static double get_pitch_factor( openmpt_module_ext * mod_ext ) { try { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_pitch_factor(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0.0; } -int set_global_volume( openmpt_module_ext * mod_ext, double volume ) { +static int set_global_volume( openmpt_module_ext * mod_ext, double volume ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->set_global_volume( volume ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } -double get_global_volume( openmpt_module_ext * mod_ext ) { +static double get_global_volume( openmpt_module_ext * mod_ext ) { try { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_global_volume(); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0.0; } -int set_channel_volume( openmpt_module_ext * mod_ext, int32_t channel, double volume ) { +static int set_channel_volume( openmpt_module_ext * mod_ext, int32_t channel, double volume ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->set_channel_volume( channel, volume ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } -double get_channel_volume( openmpt_module_ext * mod_ext, int32_t channel ) { +static double get_channel_volume( openmpt_module_ext * mod_ext, int32_t channel ) { try { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_channel_volume( channel ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0.0; } -int set_channel_mute_status( openmpt_module_ext * mod_ext, int32_t channel, int mute ) { +static int set_channel_mute_status( openmpt_module_ext * mod_ext, int32_t channel, int mute ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->set_channel_mute_status( channel, mute ? true : false ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } -int get_channel_mute_status( openmpt_module_ext * mod_ext, int32_t channel ) { +static int get_channel_mute_status( openmpt_module_ext * mod_ext, int32_t channel ) { try { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_channel_mute_status( channel ) ? 1 : 0; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return -1; } -int set_instrument_mute_status( openmpt_module_ext * mod_ext, int32_t instrument, int mute ) { +static int set_instrument_mute_status( openmpt_module_ext * mod_ext, int32_t instrument, int mute ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->set_instrument_mute_status( instrument, mute ? true : false ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } -int get_instrument_mute_status( openmpt_module_ext * mod_ext, int32_t instrument ) { +static int get_instrument_mute_status( openmpt_module_ext * mod_ext, int32_t instrument ) { try { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->get_instrument_mute_status( instrument ) ? 1 : 0; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return -1; } -int32_t play_note( openmpt_module_ext * mod_ext, int32_t instrument, int32_t note, double volume, double panning ) { +static int32_t play_note( openmpt_module_ext * mod_ext, int32_t instrument, int32_t note, double volume, double panning ) { try { openmpt::interface::check_soundfile( mod_ext ); return mod_ext->impl->play_note( instrument, note, volume, panning ); } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return -1; } -int stop_note( openmpt_module_ext * mod_ext, int32_t channel ) { +static int stop_note( openmpt_module_ext * mod_ext, int32_t channel ) { try { openmpt::interface::check_soundfile( mod_ext ); mod_ext->impl->stop_note( channel ); return 1; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } @@ -1657,7 +1778,7 @@ int openmpt_module_ext_get_interface( openmpt_module_ext * mod_ext, const char * } return result; } catch ( ... ) { - openmpt::report_exception( __FUNCTION__, mod_ext ? &mod_ext->mod : NULL ); + openmpt::report_exception( __func__, mod_ext ? &mod_ext->mod : NULL ); } return 0; } diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_config.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_config.h index 0323ab1e0..01104732b 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_config.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_config.h @@ -148,6 +148,8 @@ #else #define LIBOPENMPT_DEPRECATED #endif +#else +#define LIBOPENMPT_DEPRECATED #endif #ifndef __cplusplus @@ -164,37 +166,14 @@ LIBOPENMPT_DEPRECATED static const int LIBOPENMPT_DEPRECATED_STRING_CONSTANT = 0 #ifdef __cplusplus -#ifndef LIBOPENMPT_ASSUME_CPLUSPLUS_DEPRECATED -/* handle known broken compilers here by defining LIBOPENMPT_ASSUME_CPLUSPLUS_DEPRECATED appropriately */ -#endif - #if defined(LIBOPENMPT_ASSUME_CPLUSPLUS) -#ifndef LIBOPENMPT_ASSUME_CPLUSPLUS_DEPRECATED -#define LIBOPENMPT_ASSUME_CPLUSPLUS_DEPRECATED LIBOPENMPT_ASSUME_CPLUSPLUS -#endif #endif #if !defined(LIBOPENMPT_NO_DEPRECATE) -#if defined(LIBOPENMPT_ASSUME_CPLUSPLUS_DEPRECATED) -#if (LIBOPENMPT_ASSUME_CPLUSPLUS_DEPRECATED >= 201402L) #define LIBOPENMPT_ATTR_DEPRECATED [[deprecated]] -#undef LIBOPENMPT_DEPRECATED -#define LIBOPENMPT_DEPRECATED #else #define LIBOPENMPT_ATTR_DEPRECATED #endif -#elif (__cplusplus >= 201402L) -#define LIBOPENMPT_ATTR_DEPRECATED [[deprecated]] -#undef LIBOPENMPT_DEPRECATED -#define LIBOPENMPT_DEPRECATED -#else -#define LIBOPENMPT_ATTR_DEPRECATED -#endif -#else -#undef LIBOPENMPT_DEPRECATED -#define LIBOPENMPT_DEPRECATED -#define LIBOPENMPT_ATTR_DEPRECATED -#endif #endif diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_cxx.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_cxx.cpp index e2eac37fc..956354b72 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_cxx.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_cxx.cpp @@ -126,6 +126,9 @@ std::vector get_supported_extensions() { bool is_extension_supported( const std::string & extension ) { return openmpt::module_impl::is_extension_supported( extension ); } +bool is_extension_supported2( std::string_view extension ) { + return openmpt::module_impl::is_extension_supported( extension ); +} double could_open_probability( std::istream & stream, double effort, std::ostream & log ) { return openmpt::module_impl::could_open_probability( stream, effort, openmpt::helper::make_unique( log ) ); @@ -137,9 +140,15 @@ double could_open_propability( std::istream & stream, double effort, std::ostrea std::size_t probe_file_header_get_recommended_size() { return openmpt::module_impl::probe_file_header_get_recommended_size(); } +int probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size, std::uint64_t filesize ) { + return openmpt::module_impl::probe_file_header( flags, data, size, filesize ); +} int probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size, std::uint64_t filesize ) { return openmpt::module_impl::probe_file_header( flags, data, size, filesize ); } +int probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size ) { + return openmpt::module_impl::probe_file_header( flags, data, size ); +} int probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size ) { return openmpt::module_impl::probe_file_header( flags, data, size ); } @@ -177,6 +186,18 @@ module::module( std::istream & stream, std::ostream & log, const std::map< std:: impl = new module_impl( stream, openmpt::helper::make_unique( log ), ctls ); } +module::module( const std::vector & data, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { + impl = new module_impl( data, openmpt::helper::make_unique( log ), ctls ); +} + +module::module( const std::byte * beg, const std::byte * end, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { + impl = new module_impl( beg, end - beg, openmpt::helper::make_unique( log ), ctls ); +} + +module::module( const std::byte * data, std::size_t size, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { + impl = new module_impl( data, size, openmpt::helper::make_unique( log ), ctls ); +} + module::module( const std::vector & data, std::ostream & log, const std::map< std::string, std::string > & ctls ) : impl(0) { impl = new module_impl( data, openmpt::helper::make_unique( log ), ctls ); } @@ -284,6 +305,9 @@ std::string module::get_metadata( const std::string & key ) const { return impl->get_metadata( key ); } +double module::get_current_estimated_bpm() const { + return impl->get_current_estimated_bpm(); +} std::int32_t module::get_current_speed() const { return impl->get_current_speed(); } @@ -385,25 +409,67 @@ std::string module::highlight_pattern_row_channel( std::int32_t pattern, std::in std::vector module::get_ctls() const { return impl->get_ctls(); } + std::string module::ctl_get( const std::string & ctl ) const { return impl->ctl_get( ctl ); } +bool module::ctl_get_boolean( std::string_view ctl ) const { + return impl->ctl_get_boolean( ctl ); +} +std::int64_t module::ctl_get_integer( std::string_view ctl ) const { + return impl->ctl_get_integer( ctl ); +} +double module::ctl_get_floatingpoint( std::string_view ctl ) const { + return impl->ctl_get_floatingpoint( ctl ); +} +std::string module::ctl_get_text( std::string_view ctl ) const { + return impl->ctl_get_text( ctl ); +} + void module::ctl_set( const std::string & ctl, const std::string & value ) { impl->ctl_set( ctl, value ); } +void module::ctl_set_boolean( std::string_view ctl, bool value ) { + impl->ctl_set_boolean( ctl, value ); +} +void module::ctl_set_integer( std::string_view ctl, std::int64_t value ) { + impl->ctl_set_integer( ctl, value ); +} +void module::ctl_set_floatingpoint( std::string_view ctl, double value ) { + impl->ctl_set_floatingpoint( ctl, value ); +} +void module::ctl_set_text( std::string_view ctl, std::string_view value ) { + impl->ctl_set_text( ctl, value ); +} module_ext::module_ext( std::istream & stream, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { ext_impl = new module_ext_impl( stream, openmpt::helper::make_unique( log ), ctls ); set_impl( ext_impl ); } +module_ext::module_ext( const std::vector & data, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { + ext_impl = new module_ext_impl( data, openmpt::helper::make_unique( log ), ctls ); + set_impl( ext_impl ); +} module_ext::module_ext( const std::vector & data, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { ext_impl = new module_ext_impl( data, openmpt::helper::make_unique( log ), ctls ); set_impl( ext_impl ); } +module_ext::module_ext( const std::vector & data, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { + ext_impl = new module_ext_impl( data, openmpt::helper::make_unique( log ), ctls ); + set_impl( ext_impl ); +} +module_ext::module_ext( const std::uint8_t * data, std::size_t size, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { + ext_impl = new module_ext_impl( data, size, openmpt::helper::make_unique( log ), ctls ); + set_impl( ext_impl ); +} module_ext::module_ext( const char * data, std::size_t size, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { ext_impl = new module_ext_impl( data, size, openmpt::helper::make_unique( log ), ctls ); set_impl( ext_impl ); } +module_ext::module_ext( const std::byte * data, std::size_t size, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { + ext_impl = new module_ext_impl( data, size, openmpt::helper::make_unique( log ), ctls ); + set_impl( ext_impl ); +} module_ext::module_ext( const void * data, std::size_t size, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) { ext_impl = new module_ext_impl( data, size, openmpt::helper::make_unique( log ), ctls ); set_impl( ext_impl ); diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext.hpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext.hpp index 686eaeeed..87772cf0e 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext.hpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext.hpp @@ -44,7 +44,11 @@ private: void operator = ( const module_ext & ); public: module_ext( std::istream & stream, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); + module_ext( const std::vector & data, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); + module_ext( const std::vector & data, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); module_ext( const std::vector & data, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); + module_ext( const std::byte * data, std::size_t size, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); + module_ext( const std::uint8_t * data, std::size_t size, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); module_ext( const char * data, std::size_t size, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); module_ext( const void * data, std::size_t size, std::ostream & log = std::clog, const std::map< std::string, std::string > & ctls = detail::initial_ctls_map() ); virtual ~module_ext(); diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.cpp index 9301579c2..a231a108c 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.cpp @@ -28,12 +28,18 @@ namespace openmpt { module_ext_impl::module_ext_impl( std::istream & stream, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( stream, std::move(log), ctls ) { ctor(); } + module_ext_impl::module_ext_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( data, std::move(log), ctls ) { + ctor(); + } module_ext_impl::module_ext_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( data, std::move(log), ctls ) { ctor(); } module_ext_impl::module_ext_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( data, std::move(log), ctls ) { ctor(); } + module_ext_impl::module_ext_impl( const std::byte * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( data, size, std::move(log), ctls ) { + ctor(); + } module_ext_impl::module_ext_impl( const std::uint8_t * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : module_impl( data, size, std::move(log), ctls ) { ctor(); } diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.hpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.hpp index 68fad85cf..7cc0a3a22 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.hpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_ext_impl.hpp @@ -33,8 +33,10 @@ class module_ext_impl public: module_ext_impl( callback_stream_wrapper stream, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( std::istream & stream, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); + module_ext_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); + module_ext_impl( const std::byte * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( const std::uint8_t * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( const char * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_ext_impl( const void * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp index 1f74f1c7b..74f407a31 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.cpp @@ -41,15 +41,7 @@ OPENMPT_NAMESPACE_BEGIN #if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT #if defined(_WIN32_WINNT) #if (_WIN32_WINNT < 0x0602) -#if MPT_COMPILER_MSVC -#pragma message("Warning: libopenmpt for WinRT is built with reduced functionality. Please #define _WIN32_WINNT 0x0602.") -#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG -#warning "Warning: libopenmpt for WinRT is built with reduced functionality. Please #define _WIN32_WINNT 0x0602." -#else -// There is no portable way to display a warning. -// Try to provoke a warning with an unused variable. -static int Warning_libopenmpt_for_WinRT_is_built_with_reduced_functionality_Please_define_WIN32_WINNT_0x0602; -#endif +MPT_WARNING("Warning: libopenmpt for WinRT is built with reduced functionality. Please #define _WIN32_WINNT 0x0602.") #endif // _WIN32_WINNT #endif // _WIN32_WINNT #endif // MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT @@ -67,29 +59,17 @@ static int Warning_libopenmpt_for_WinRT_is_built_with_reduced_functionality_Plea #endif // MPT_BUILD_MSVC #if MPT_PLATFORM_MULTITHREADED && MPT_MUTEX_NONE -#if MPT_COMPILER_MSVC -#pragma message("Warning: libopenmpt built in non thread-safe mode because mutexes are not supported by the C++ standard library available.") -#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG -#warning "Warning: libopenmpt built in non thread-safe mode because mutexes are not supported by the C++ standard library available." -#else -// There is no portable way to display a warning. -// Try to provoke a warning with an unused variable. -static int Warning_libopenmpt_built_in_non_thread_safe_mode_because_mutexes_are_not_supported_by_the_CPlusPlus_standard_library_available; -#endif +MPT_WARNING("Warning: libopenmpt built in non thread-safe mode because mutexes are not supported by the C++ standard library available.") #endif // MPT_MUTEX_NONE #if (defined(__MINGW32__) || defined(__MINGW64__)) && !defined(_GLIBCXX_HAS_GTHREADS) && !defined(MPT_WITH_MINGWSTDTHREADS) -#if MPT_COMPILER_MSVC -#pragma message("Warning: Building libopenmpt with MinGW-w64 without std::thread support is not recommended and is deprecated. Please use MinGW-w64 with posix threading model (as opposed to win32 threading model), or build with mingw-std-threads.") -#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG -#warning "Warning: Building libopenmpt with MinGW-w64 without std::thread support is not recommended and is deprecated. Please use MinGW-w64 with posix threading model (as opposed to win32 threading model), or build with mingw-std-threads." -#else -// There is no portable way to display a warning. -// Try to provoke a warning with an unused variable. -static int Warning_Building_libopenmpt_with_MinGW_w64_without_std_thread_support_is_not_recommended_ans_is_deprecated_Please_use_MinGW_w64_with_posix_threading_model_as_opposed_to_win32_threading_model_or_build_with_mingw_std_threads; -#endif +MPT_WARNING("Warning: Building libopenmpt with MinGW-w64 without std::thread support is not recommended and is deprecated. Please use MinGW-w64 with posix threading model (as opposed to win32 threading model), or build with mingw-std-threads.") #endif // MINGW +#if MPT_CLANG_AT_LEAST(5,0,0) && defined(__powerpc__) && !defined(__powerpc64__) +MPT_WARNING("Warning: libopenmpt is known to trigger bad code generation with Clang 5 or later on powerpc (32bit) when using -O3. See .") +#endif + #endif // !MPT_BUILD_SILENCE_LIBOPENMPT_CONFIGURATION_WARNINGS #if defined(MPT_ASSERT_HANDLER_NEEDED) && !defined(ENABLE_TESTS) @@ -99,12 +79,12 @@ MPT_NOINLINE void AssertHandler(const mpt::source_location &loc, const char *exp if(msg) { mpt::log::Logger().SendLogMessage(loc, LogError, "ASSERT", - U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetASCII, msg) + U_(" (") + mpt::ToUnicode(mpt::CharsetASCII, expr) + U_(")") + U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetSource, msg) + U_(" (") + mpt::ToUnicode(mpt::CharsetSource, expr) + U_(")") ); } else { mpt::log::Logger().SendLogMessage(loc, LogError, "ASSERT", - U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetASCII, expr) + U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetSource, expr) ); } #if defined(MPT_BUILD_FATAL_ASSERTS) @@ -161,19 +141,19 @@ static std::string get_library_version_string() { } static std::string get_library_features_string() { - return mpt::ToCharset(mpt::CharsetUTF8, mpt::String::Trim(Build::GetBuildFeaturesString())); + return mpt::ToCharset(mpt::Charset::UTF8, mpt::String::Trim(Build::GetBuildFeaturesString())); } static std::string get_core_version_string() { - return mpt::ToCharset(mpt::CharsetUTF8, Build::GetVersionStringExtended()); + return mpt::ToCharset(mpt::Charset::UTF8, Build::GetVersionStringExtended()); } static std::string get_source_url_string() { - return mpt::ToCharset(mpt::CharsetUTF8, SourceInfo::Current().GetUrlWithRevision()); + return mpt::ToCharset(mpt::Charset::UTF8, SourceInfo::Current().GetUrlWithRevision()); } static std::string get_source_date_string() { - return mpt::ToCharset(mpt::CharsetUTF8, SourceInfo::Current().Date()); + return mpt::ToCharset(mpt::Charset::UTF8, SourceInfo::Current().Date()); } static std::string get_source_revision_string() { @@ -182,35 +162,35 @@ static std::string get_source_revision_string() { } static std::string get_build_string() { - return mpt::ToCharset(mpt::CharsetUTF8, Build::GetBuildDateString()); + return mpt::ToCharset(mpt::Charset::UTF8, Build::GetBuildDateString()); } static std::string get_build_compiler_string() { - return mpt::ToCharset(mpt::CharsetUTF8, Build::GetBuildCompilerString()); + return mpt::ToCharset(mpt::Charset::UTF8, Build::GetBuildCompilerString()); } static std::string get_credits_string() { - return mpt::ToCharset(mpt::CharsetUTF8, Build::GetFullCreditsString()); + return mpt::ToCharset(mpt::Charset::UTF8, Build::GetFullCreditsString()); } static std::string get_contact_string() { - return mpt::ToCharset(mpt::CharsetUTF8, U_("Forum: ") + Build::GetURL(Build::Url::Forum)); + return mpt::ToCharset(mpt::Charset::UTF8, U_("Forum: ") + Build::GetURL(Build::Url::Forum)); } static std::string get_license_string() { - return mpt::ToCharset(mpt::CharsetUTF8, Build::GetLicenseString()); + return mpt::ToCharset(mpt::Charset::UTF8, Build::GetLicenseString()); } static std::string get_url_string() { - return mpt::ToCharset(mpt::CharsetUTF8, Build::GetURL(Build::Url::Website)); + return mpt::ToCharset(mpt::Charset::UTF8, Build::GetURL(Build::Url::Website)); } static std::string get_support_forum_url_string() { - return mpt::ToCharset(mpt::CharsetUTF8, Build::GetURL(Build::Url::Forum)); + return mpt::ToCharset(mpt::Charset::UTF8, Build::GetURL(Build::Url::Forum)); } static std::string get_bugtracker_url_string() { - return mpt::ToCharset(mpt::CharsetUTF8, Build::GetURL(Build::Url::Bugtracker)); + return mpt::ToCharset(mpt::Charset::UTF8, Build::GetURL(Build::Url::Bugtracker)); } std::string get_string( const std::string & key ) { @@ -295,7 +275,7 @@ public: } private: void AddToLog( LogLevel level, const mpt::ustring & text ) const override { - destination.log( mpt::ToCharset( mpt::CharsetUTF8, LogLevelToString( level ) + U_(": ") + text ) ); + destination.log( mpt::ToCharset( mpt::Charset::UTF8, LogLevelToString( level ) + U_(": ") + text ) ); } }; // class log_forwarder @@ -312,14 +292,14 @@ std::vector > loader_log::GetMessages() const { return m_Messages; } void loader_log::AddToLog( LogLevel level, const mpt::ustring & text ) const { - m_Messages.push_back( std::make_pair( level, mpt::ToCharset( mpt::CharsetUTF8, text ) ) ); + m_Messages.push_back( std::make_pair( level, mpt::ToCharset( mpt::Charset::UTF8, text ) ) ); } void module_impl::PushToCSoundFileLog( const std::string & text ) const { - m_sndFile->AddToLog( LogError, mpt::ToUnicode( mpt::CharsetUTF8, text ) ); + m_sndFile->AddToLog( LogError, mpt::ToUnicode( mpt::Charset::UTF8, text ) ); } void module_impl::PushToCSoundFileLog( int loglevel, const std::string & text ) const { - m_sndFile->AddToLog( static_cast( loglevel ), mpt::ToUnicode( mpt::CharsetUTF8, text ) ); + m_sndFile->AddToLog( static_cast( loglevel ), mpt::ToUnicode( mpt::Charset::UTF8, text ) ); } module_impl::subsong_data::subsong_data( double duration, std::int32_t start_row, std::int32_t start_order, std::int32_t sequence ) @@ -369,6 +349,19 @@ static std::int32_t resamplingmode_to_filterlength(ResamplingMode mode) { } } +static Resampling::AmigaFilter translate_amiga_filter_type( module_impl::amiga_filter_type amiga_type ) { + switch (amiga_type ) { + case module_impl::amiga_filter_type::a500: + return Resampling::AmigaFilter::A500; + case module_impl::amiga_filter_type::a1200: + case module_impl::amiga_filter_type::auto_filter: + default: + return Resampling::AmigaFilter::A1200; + case module_impl::amiga_filter_type::unfiltered: + return Resampling::AmigaFilter::Unfiltered; + } +} + static void ramping_to_mixersettings( MixerSettings & settings, int ramping ) { if ( ramping == -1 ) { settings.SetVolumeRampUpMicroseconds( MixerSettings().GetVolumeRampUpMicroseconds() ); @@ -382,7 +375,7 @@ static void ramping_to_mixersettings( MixerSettings & settings, int ramping ) { } } static void mixersettings_to_ramping( int & ramping, const MixerSettings & settings ) { - std::int32_t ramp_us = std::max( settings.GetVolumeRampUpMicroseconds(), settings.GetVolumeRampDownMicroseconds() ); + std::int32_t ramp_us = std::max( settings.GetVolumeRampUpMicroseconds(), settings.GetVolumeRampDownMicroseconds() ); if ( ( settings.GetVolumeRampUpMicroseconds() == MixerSettings().GetVolumeRampUpMicroseconds() ) && ( settings.GetVolumeRampDownMicroseconds() == MixerSettings().GetVolumeRampDownMicroseconds() ) ) { ramping = -1; } else if ( ramp_us <= 0 ) { @@ -393,7 +386,7 @@ static void mixersettings_to_ramping( int & ramping, const MixerSettings & setti } std::string module_impl::mod_string_to_utf8( const std::string & encoded ) const { - return mpt::ToCharset( mpt::CharsetUTF8, m_sndFile->GetCharsetInternal(), encoded ); + return mpt::ToCharset( mpt::Charset::UTF8, m_sndFile->GetCharsetInternal(), encoded ); } void module_impl::apply_mixer_settings( std::int32_t samplerate, int channels ) { bool samplerate_changed = static_cast( m_sndFile->m_MixerSettings.gdwMixingFreq ) != samplerate; @@ -440,11 +433,11 @@ bool module_impl::has_subsongs_inited() const { return !m_subsongs.empty(); } void module_impl::ctor( const std::map< std::string, std::string > & ctls ) { - m_sndFile = mpt::make_unique(); + m_sndFile = std::make_unique(); m_loaded = false; m_mixer_initialized = false; - m_Dither = mpt::make_unique( mpt::global_prng() ); - m_LogForwarder = mpt::make_unique( *m_Log ); + m_Dither = std::make_unique( mpt::global_prng() ); + m_LogForwarder = std::make_unique( *m_Log ); m_sndFile->SetCustomLog( m_LogForwarder.get() ); m_current_subsong = 0; m_currentPositionSeconds = 0.0; @@ -486,7 +479,7 @@ void module_impl::load( const FileReader & file, const std::map< std::string, st std::vector > loaderMessages = loaderlog.GetMessages(); for ( const auto & msg : loaderMessages ) { PushToCSoundFileLog( msg.first, msg.second ); - m_loaderMessages.push_back( mpt::ToCharset( mpt::CharsetUTF8, LogLevelToString( msg.first ) ) + std::string(": ") + msg.second ); + m_loaderMessages.push_back( mpt::ToCharset( mpt::Charset::UTF8, LogLevelToString( msg.first ) ) + std::string(": ") + msg.second ); } // init CSoundFile state that corresponds to ctls for ( const auto & ctl : ctls ) { @@ -500,11 +493,11 @@ std::size_t module_impl::read_wrapper( std::size_t count, std::int16_t * left, s m_sndFile->ResetMixStat(); m_sndFile->m_bIsRendering = ( m_ctl_play_at_end != song_end_action::fadeout_song ); std::size_t count_read = 0; + std::int16_t * const buffers[4] = { left, right, rear_left, rear_right }; + AudioReadTargetGainBuffer> target( audio_buffer_planar( buffers, planar_audio_buffer_valid_channels( buffers, std::size( buffers) ), count ), *m_Dither, m_Gain ); while ( count > 0 ) { - std::int16_t * const buffers[4] = { left + count_read, right + count_read, rear_left + count_read, rear_right + count_read }; - AudioReadTargetGainBuffer target(*m_Dither, 0, buffers, m_Gain); std::size_t count_chunk = m_sndFile->Read( - static_cast( std::min( count, std::numeric_limits::max() / 2 / 4 / 4 ) ), // safety margin / samplesize / channels + static_cast( std::min( static_cast( count ), static_cast( std::numeric_limits::max() / 2 / 4 / 4 ) ) ), // safety margin / samplesize / channels target ); if ( count_chunk == 0 ) { @@ -523,11 +516,11 @@ std::size_t module_impl::read_wrapper( std::size_t count, float * left, float * m_sndFile->ResetMixStat(); m_sndFile->m_bIsRendering = ( m_ctl_play_at_end != song_end_action::fadeout_song ); std::size_t count_read = 0; + float * const buffers[4] = { left, right, rear_left, rear_right }; + AudioReadTargetGainBuffer> target( audio_buffer_planar( buffers, planar_audio_buffer_valid_channels( buffers, std::size( buffers) ), count ), *m_Dither, m_Gain ); while ( count > 0 ) { - float * const buffers[4] = { left + count_read, right + count_read, rear_left + count_read, rear_right + count_read }; - AudioReadTargetGainBuffer target(*m_Dither, 0, buffers, m_Gain); std::size_t count_chunk = m_sndFile->Read( - static_cast( std::min( count, std::numeric_limits::max() / 2 / 4 / 4 ) ), // safety margin / samplesize / channels + static_cast( std::min( static_cast( count ), static_cast( std::numeric_limits::max() / 2 / 4 / 4 ) ) ), // safety margin / samplesize / channels target ); if ( count_chunk == 0 ) { @@ -546,10 +539,10 @@ std::size_t module_impl::read_interleaved_wrapper( std::size_t count, std::size_ m_sndFile->ResetMixStat(); m_sndFile->m_bIsRendering = ( m_ctl_play_at_end != song_end_action::fadeout_song ); std::size_t count_read = 0; + AudioReadTargetGainBuffer> target( audio_buffer_interleaved( interleaved, channels, count ), *m_Dither, m_Gain ); while ( count > 0 ) { - AudioReadTargetGainBuffer target(*m_Dither, interleaved + count_read * channels, 0, m_Gain); std::size_t count_chunk = m_sndFile->Read( - static_cast( std::min( count, std::numeric_limits::max() / 2 / 4 / 4 ) ), // safety margin / samplesize / channels + static_cast( std::min( static_cast( count ), static_cast( std::numeric_limits::max() / 2 / 4 / 4 ) ) ), // safety margin / samplesize / channels target ); if ( count_chunk == 0 ) { @@ -568,10 +561,10 @@ std::size_t module_impl::read_interleaved_wrapper( std::size_t count, std::size_ m_sndFile->ResetMixStat(); m_sndFile->m_bIsRendering = ( m_ctl_play_at_end != song_end_action::fadeout_song ); std::size_t count_read = 0; + AudioReadTargetGainBuffer> target( audio_buffer_interleaved( interleaved, channels, count ), *m_Dither, m_Gain ); while ( count > 0 ) { - AudioReadTargetGainBuffer target(*m_Dither, interleaved + count_read * channels, 0, m_Gain); std::size_t count_chunk = m_sndFile->Read( - static_cast( std::min( count, std::numeric_limits::max() / 2 / 4 / 4 ) ), // safety margin / samplesize / channels + static_cast( std::min( static_cast( count ), static_cast( std::numeric_limits::max() / 2 / 4 / 4 ) ) ), // safety margin / samplesize / channels target ); if ( count_chunk == 0 ) { @@ -593,17 +586,14 @@ std::vector module_impl::get_supported_extensions() { std::copy( extensions.begin(), extensions.end(), std::back_insert_iterator >( retval ) ); return retval; } -bool module_impl::is_extension_supported( const char * extension ) { +bool module_impl::is_extension_supported( std::string_view extension ) { return CSoundFile::IsExtensionSupported( extension ); } -bool module_impl::is_extension_supported( const std::string & extension ) { - return CSoundFile::IsExtensionSupported( extension.c_str() ); -} double module_impl::could_open_probability( const OpenMPT::FileReader & file, double effort, std::unique_ptr log ) { try { if ( effort >= 0.8 ) { - std::unique_ptr sndFile = mpt::make_unique(); - std::unique_ptr logForwarder = mpt::make_unique( *log ); + std::unique_ptr sndFile = std::make_unique(); + std::unique_ptr logForwarder = std::make_unique( *log ); sndFile->SetCustomLog( logForwarder.get() ); if ( !sndFile->Create( file, CSoundFile::loadCompleteModule ) ) { return 0.0; @@ -611,8 +601,8 @@ double module_impl::could_open_probability( const OpenMPT::FileReader & file, do sndFile->Destroy(); return 1.0; } else if ( effort >= 0.6 ) { - std::unique_ptr sndFile = mpt::make_unique(); - std::unique_ptr logForwarder = mpt::make_unique( *log ); + std::unique_ptr sndFile = std::make_unique(); + std::unique_ptr logForwarder = std::make_unique( *log ); sndFile->SetCustomLog( logForwarder.get() ); if ( !sndFile->Create( file, CSoundFile::loadNoPatternOrPluginData ) ) { return 0.0; @@ -620,8 +610,8 @@ double module_impl::could_open_probability( const OpenMPT::FileReader & file, do sndFile->Destroy(); return 0.8; } else if ( effort >= 0.2 ) { - std::unique_ptr sndFile = mpt::make_unique(); - std::unique_ptr logForwarder = mpt::make_unique( *log ); + std::unique_ptr sndFile = std::make_unique(); + std::unique_ptr logForwarder = std::make_unique( *log ); sndFile->SetCustomLog( logForwarder.get() ); if ( !sndFile->Create( file, CSoundFile::onlyVerifyHeader ) ) { return 0.0; @@ -669,9 +659,27 @@ double module_impl::could_open_probability( std::istream & stream, double effort std::size_t module_impl::probe_file_header_get_recommended_size() { return CSoundFile::ProbeRecommendedSize; } +int module_impl::probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size, std::uint64_t filesize ) { + int result = 0; + switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( data, size ), &filesize ) ) { + case CSoundFile::ProbeSuccess: + result = probe_file_header_result_success; + break; + case CSoundFile::ProbeFailure: + result = probe_file_header_result_failure; + break; + case CSoundFile::ProbeWantMoreData: + result = probe_file_header_result_wantmoredata; + break; + default: + throw exception("internal error"); + break; + } + return result; +} int module_impl::probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size, std::uint64_t filesize ) { int result = 0; - switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( data ), size ), &filesize ) ) { + switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( data ), size ), &filesize ) ) { case CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; @@ -689,7 +697,25 @@ int module_impl::probe_file_header( std::uint64_t flags, const std::uint8_t * da } int module_impl::probe_file_header( std::uint64_t flags, const void * data, std::size_t size, std::uint64_t filesize ) { int result = 0; - switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::void_cast( data ), size ), &filesize ) ) { + switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::void_cast( data ), size ), &filesize ) ) { + case CSoundFile::ProbeSuccess: + result = probe_file_header_result_success; + break; + case CSoundFile::ProbeFailure: + result = probe_file_header_result_failure; + break; + case CSoundFile::ProbeWantMoreData: + result = probe_file_header_result_wantmoredata; + break; + default: + throw exception("internal error"); + break; + } + return result; +} +int module_impl::probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size ) { + int result = 0; + switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( data, size ), nullptr ) ) { case CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; @@ -707,7 +733,7 @@ int module_impl::probe_file_header( std::uint64_t flags, const void * data, std: } int module_impl::probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size ) { int result = 0; - switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( data ), size ), nullptr ) ) { + switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( data ), size ), nullptr ) ) { case CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; @@ -725,7 +751,7 @@ int module_impl::probe_file_header( std::uint64_t flags, const std::uint8_t * da } int module_impl::probe_file_header( std::uint64_t flags, const void * data, std::size_t size ) { int result = 0; - switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::void_cast( data ), size ), nullptr ) ) { + switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::void_cast( data ), size ), nullptr ) ) { case CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; @@ -767,7 +793,7 @@ int module_impl::probe_file_header( std::uint64_t flags, std::istream & stream ) size_read += read_count; size_toread -= read_count; } - switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( buffer ), size_read ), seekable ? &filesize : nullptr ) ) { + switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( buffer ), size_read ), seekable ? &filesize : nullptr ) ) { case CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; @@ -807,7 +833,7 @@ int module_impl::probe_file_header( std::uint64_t flags, callback_stream_wrapper break; } } - switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( buffer ), size_read ), seekable ? &filesize : nullptr ) ) { + switch ( CSoundFile::Probe( static_cast( flags ), mpt::span( mpt::byte_cast( buffer ), size_read ), seekable ? &filesize : nullptr ) ) { case CSoundFile::ProbeSuccess: result = probe_file_header_result_success; break; @@ -838,6 +864,11 @@ module_impl::module_impl( std::istream & stream, std::unique_ptr load( make_FileReader( &stream ), ctls ); apply_libopenmpt_defaults(); } +module_impl::module_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { + ctor( ctls ); + load( make_FileReader( mpt::as_span( data ) ), ctls ); + apply_libopenmpt_defaults(); +} module_impl::module_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { ctor( ctls ); load( make_FileReader( mpt::as_span( data ) ), ctls ); @@ -845,7 +876,12 @@ module_impl::module_impl( const std::vector & data, std::unique_pt } module_impl::module_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { ctor( ctls ); - load( make_FileReader( mpt::byte_cast< mpt::span< const mpt::byte > >( mpt::as_span( data ) ) ), ctls ); + load( make_FileReader( mpt::byte_cast< mpt::span< const std::byte > >( mpt::as_span( data ) ) ), ctls ); + apply_libopenmpt_defaults(); +} +module_impl::module_impl( const std::byte * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { + ctor( ctls ); + load( make_FileReader( mpt::as_span( data, size ) ), ctls ); apply_libopenmpt_defaults(); } module_impl::module_impl( const std::uint8_t * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { @@ -855,12 +891,12 @@ module_impl::module_impl( const std::uint8_t * data, std::size_t size, std::uniq } module_impl::module_impl( const char * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { ctor( ctls ); - load( make_FileReader( mpt::byte_cast< mpt::span< const mpt::byte > >( mpt::as_span( data, size ) ) ), ctls ); + load( make_FileReader( mpt::byte_cast< mpt::span< const std::byte > >( mpt::as_span( data, size ) ) ), ctls ); apply_libopenmpt_defaults(); } module_impl::module_impl( const void * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ) : m_Log(std::move(log)) { ctor( ctls ); - load( make_FileReader( mpt::as_span( mpt::void_cast< const mpt::byte * >( data ), size ) ), ctls ); + load( make_FileReader( mpt::as_span( mpt::void_cast< const std::byte * >( data ), size ) ), ctls ); apply_libopenmpt_defaults(); } module_impl::~module_impl() { @@ -924,7 +960,7 @@ std::size_t module_impl::read( std::int32_t samplerate, std::size_t count, std:: throw openmpt::exception("null pointer"); } apply_mixer_settings( samplerate, 1 ); - count = read_wrapper( count, mono, 0, 0, 0 ); + count = read_wrapper( count, mono, nullptr, nullptr, nullptr ); m_currentPositionSeconds += static_cast( count ) / static_cast( samplerate ); return count; } @@ -933,7 +969,7 @@ std::size_t module_impl::read( std::int32_t samplerate, std::size_t count, std:: throw openmpt::exception("null pointer"); } apply_mixer_settings( samplerate, 2 ); - count = read_wrapper( count, left, right, 0, 0 ); + count = read_wrapper( count, left, right, nullptr, nullptr ); m_currentPositionSeconds += static_cast( count ) / static_cast( samplerate ); return count; } @@ -951,7 +987,7 @@ std::size_t module_impl::read( std::int32_t samplerate, std::size_t count, float throw openmpt::exception("null pointer"); } apply_mixer_settings( samplerate, 1 ); - count = read_wrapper( count, mono, 0, 0, 0 ); + count = read_wrapper( count, mono, nullptr, nullptr, nullptr ); m_currentPositionSeconds += static_cast( count ) / static_cast( samplerate ); return count; } @@ -960,7 +996,7 @@ std::size_t module_impl::read( std::int32_t samplerate, std::size_t count, float throw openmpt::exception("null pointer"); } apply_mixer_settings( samplerate, 2 ); - count = read_wrapper( count, left, right, 0, 0 ); + count = read_wrapper( count, left, right, nullptr, nullptr ); m_currentPositionSeconds += static_cast( count ) / static_cast( samplerate ); return count; } @@ -1012,7 +1048,7 @@ std::size_t module_impl::read_interleaved_quad( std::int32_t samplerate, std::si double module_impl::get_duration_seconds() const { - std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : mpt::make_unique( get_subsongs() ); + std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : std::make_unique( get_subsongs() ); const subsongs_type & subsongs = has_subsongs_inited() ? m_subsongs : *subsongs_temp; if ( m_current_subsong == all_subsongs ) { // Play all subsongs consecutively. @@ -1025,7 +1061,7 @@ double module_impl::get_duration_seconds() const { return subsongs[m_current_subsong].duration; } void module_impl::select_subsong( std::int32_t subsong ) { - std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : mpt::make_unique( get_subsongs() ); + std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : std::make_unique( get_subsongs() ); const subsongs_type & subsongs = has_subsongs_inited() ? m_subsongs : *subsongs_temp; if ( subsong != all_subsongs && ( subsong < 0 || subsong >= static_cast( subsongs.size() ) ) ) { throw openmpt::exception("invalid subsong"); @@ -1052,7 +1088,7 @@ double module_impl::get_position_seconds() const { return m_currentPositionSeconds; } double module_impl::set_position_seconds( double seconds ) { - std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : mpt::make_unique( get_subsongs() ); + std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : std::make_unique( get_subsongs() ); const subsongs_type & subsongs = has_subsongs_inited() ? m_subsongs : *subsongs_temp; const subsong_data * subsong = 0; double base_seconds = 0.0; @@ -1070,11 +1106,11 @@ double module_impl::set_position_seconds( double seconds ) { } else { subsong = &subsongs[m_current_subsong]; } - GetLengthType t = m_sndFile->GetLength( eNoAdjust, GetLengthTarget( seconds ).StartPos( static_cast( subsong->sequence ), static_cast( subsong->start_order ), static_cast( subsong->start_row ) ) ).back(); + GetLengthType t = m_sndFile->GetLength( m_ctl_seek_sync_samples ? eAdjustSamplePositions : eAdjust, GetLengthTarget( seconds ).StartPos( static_cast( subsong->sequence ), static_cast( subsong->start_order ), static_cast( subsong->start_row ) ) ).back(); m_sndFile->m_PlayState.m_nCurrentOrder = t.lastOrder; m_sndFile->SetCurrentOrder( t.lastOrder ); m_sndFile->m_PlayState.m_nNextRow = t.lastRow; - m_currentPositionSeconds = base_seconds + m_sndFile->GetLength( m_ctl_seek_sync_samples ? eAdjustSamplePositions : eAdjust, GetLengthTarget( t.lastOrder, t.lastRow ).StartPos( static_cast( subsong->sequence ), static_cast( subsong->start_order ), static_cast( subsong->start_row ) ) ).back().duration; + m_currentPositionSeconds = base_seconds + t.duration; return m_currentPositionSeconds; } double module_impl::set_position_order_row( std::int32_t order, std::int32_t row ) { @@ -1113,60 +1149,114 @@ std::vector module_impl::get_metadata_keys() const { "warnings", }; } +std::string module_impl::get_message_instruments() const { + std::string retval; + std::string tmp; + bool valid = false; + for ( INSTRUMENTINDEX i = 1; i <= m_sndFile->GetNumInstruments(); ++i ) { + std::string instname = m_sndFile->GetInstrumentName( i ); + if ( !instname.empty() ) { + valid = true; + } + tmp += instname; + tmp += "\n"; + } + if ( valid ) { + retval = tmp; + } + return retval; +} +std::string module_impl::get_message_samples() const { + std::string retval; + std::string tmp; + bool valid = false; + for ( SAMPLEINDEX i = 1; i <= m_sndFile->GetNumSamples(); ++i ) { + std::string samplename = m_sndFile->GetSampleName( i ); + if ( !samplename.empty() ) { + valid = true; + } + tmp += samplename; + tmp += "\n"; + } + if ( valid ) { + retval = tmp; + } + return retval; +} std::string module_impl::get_metadata( const std::string & key ) const { if ( key == std::string("type") ) { - return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->m_modFormat.type ); + return mpt::ToCharset(mpt::Charset::UTF8, m_sndFile->m_modFormat.type ); } else if ( key == std::string("type_long") ) { - return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->m_modFormat.formatName ); + return mpt::ToCharset(mpt::Charset::UTF8, m_sndFile->m_modFormat.formatName ); } else if ( key == std::string("originaltype") ) { - return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->m_modFormat.originalType ); + return mpt::ToCharset(mpt::Charset::UTF8, m_sndFile->m_modFormat.originalType ); } else if ( key == std::string("originaltype_long") ) { - return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->m_modFormat.originalFormatName ); + return mpt::ToCharset(mpt::Charset::UTF8, m_sndFile->m_modFormat.originalFormatName ); } else if ( key == std::string("container") ) { - return mpt::ToCharset(mpt::CharsetUTF8, CSoundFile::ModContainerTypeToString( m_sndFile->GetContainerType() ) ); + return mpt::ToCharset(mpt::Charset::UTF8, CSoundFile::ModContainerTypeToString( m_sndFile->GetContainerType() ) ); } else if ( key == std::string("container_long") ) { - return mpt::ToCharset(mpt::CharsetUTF8, CSoundFile::ModContainerTypeToTracker( m_sndFile->GetContainerType() ) ); + return mpt::ToCharset(mpt::Charset::UTF8, CSoundFile::ModContainerTypeToTracker( m_sndFile->GetContainerType() ) ); } else if ( key == std::string("tracker") ) { - return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->m_modFormat.madeWithTracker ); + return mpt::ToCharset(mpt::Charset::UTF8, m_sndFile->m_modFormat.madeWithTracker ); } else if ( key == std::string("artist") ) { - return mpt::ToCharset( mpt::CharsetUTF8, m_sndFile->m_songArtist ); + return mpt::ToCharset( mpt::Charset::UTF8, m_sndFile->m_songArtist ); } else if ( key == std::string("title") ) { return mod_string_to_utf8( m_sndFile->GetTitle() ); } else if ( key == std::string("date") ) { if ( m_sndFile->GetFileHistory().empty() || !m_sndFile->GetFileHistory().back().HasValidDate() ) { return std::string(); } - return mpt::ToCharset(mpt::CharsetUTF8, m_sndFile->GetFileHistory().back().AsISO8601() ); + return mpt::ToCharset(mpt::Charset::UTF8, m_sndFile->GetFileHistory().back().AsISO8601() ); } else if ( key == std::string("message") ) { std::string retval = m_sndFile->m_songMessage.GetFormatted( SongMessage::leLF ); if ( retval.empty() ) { - std::string tmp; - bool valid = false; - for ( INSTRUMENTINDEX i = 1; i <= m_sndFile->GetNumInstruments(); ++i ) { - std::string instname = m_sndFile->GetInstrumentName( i ); - if ( !instname.empty() ) { - valid = true; - } - tmp += instname; - tmp += "\n"; - } - if ( valid ) { - retval = tmp; - } - } - if ( retval.empty() ) { - std::string tmp; - bool valid = false; - for ( SAMPLEINDEX i = 1; i <= m_sndFile->GetNumSamples(); ++i ) { - std::string samplename = m_sndFile->GetSampleName( i ); - if ( !samplename.empty() ) { - valid = true; - } - tmp += samplename; - tmp += "\n"; - } - if ( valid ) { - retval = tmp; + switch ( m_sndFile->GetMessageHeuristic() ) { + case ModMessageHeuristicOrder::Instruments: + retval = get_message_instruments(); + break; + case ModMessageHeuristicOrder::Samples: + retval = get_message_samples(); + break; + case ModMessageHeuristicOrder::InstrumentsSamples: + if ( retval.empty() ) { + retval = get_message_instruments(); + } + if ( retval.empty() ) { + retval = get_message_samples(); + } + break; + case ModMessageHeuristicOrder::SamplesInstruments: + if ( retval.empty() ) { + retval = get_message_samples(); + } + if ( retval.empty() ) { + retval = get_message_instruments(); + } + break; + case ModMessageHeuristicOrder::BothInstrumentsSamples: + { + std::string message_instruments = get_message_instruments(); + std::string message_samples = get_message_samples(); + if ( !message_instruments.empty() ) { + retval += std::move( message_instruments ); + } + if ( !message_samples.empty() ) { + retval += std::move( message_samples ); + } + } + break; + case ModMessageHeuristicOrder::BothSamplesInstruments: + { + std::string message_instruments = get_message_instruments(); + std::string message_samples = get_message_samples(); + if ( !message_samples.empty() ) { + retval += std::move( message_samples ); + } + if ( !message_instruments.empty() ) { + retval += std::move( message_instruments ); + } + } + break; } } return mod_string_to_utf8( retval ); @@ -1189,6 +1279,9 @@ std::string module_impl::get_metadata( const std::string & key ) const { return ""; } +double module_impl::get_current_estimated_bpm() const { + return m_sndFile->GetCurrentBPM(); +} std::int32_t module_impl::get_current_speed() const { return m_sndFile->m_PlayState.m_nMusicSpeed; } @@ -1250,7 +1343,7 @@ float module_impl::get_current_channel_vu_rear_right( std::int32_t channel ) con } std::int32_t module_impl::get_num_subsongs() const { - std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : mpt::make_unique( get_subsongs() ); + std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : std::make_unique( get_subsongs() ); const subsongs_type & subsongs = has_subsongs_inited() ? m_subsongs : *subsongs_temp; return static_cast( subsongs.size() ); } @@ -1272,10 +1365,17 @@ std::int32_t module_impl::get_num_samples() const { std::vector module_impl::get_subsong_names() const { std::vector retval; - std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : mpt::make_unique( get_subsongs() ); + std::unique_ptr subsongs_temp = has_subsongs_inited() ? std::unique_ptr() : std::make_unique( get_subsongs() ); const subsongs_type & subsongs = has_subsongs_inited() ? m_subsongs : *subsongs_temp; + retval.reserve( subsongs.size() ); for ( const auto & subsong : subsongs ) { - retval.push_back( mod_string_to_utf8( m_sndFile->Order( static_cast( subsong.sequence ) ).GetName() ) ); + const auto & order = m_sndFile->Order( static_cast( subsong.sequence ) ); + retval.push_back( mpt::ToCharset( mpt::Charset::UTF8, order.GetName() ) ); + if ( retval.back().empty() ) { + // use first pattern name instead + if ( order.IsValidPat( static_cast( subsong.start_order ) ) ) + retval.back() = mpt::ToCharset( mpt::Charset::UTF8, m_sndFile->GetCharsetInternal(), m_sndFile->Patterns[ order[ subsong.start_order ] ].GetName() ); + } } return retval; } @@ -1404,7 +1504,7 @@ std::pair< std::string, std::string > module_impl::format_and_highlight_pattern_ switch ( cmd ) { case module::command_note: return std::make_pair( - ( cell.IsNote() || cell.IsSpecialNote() ) ? mpt::ToCharset( mpt::CharsetUTF8, m_sndFile->GetNoteName( cell.note, cell.instr ) ) : std::string("...") + ( cell.IsNote() || cell.IsSpecialNote() ) ? mpt::ToCharset( mpt::Charset::UTF8, m_sndFile->GetNoteName( cell.note, cell.instr ) ) : std::string("...") , ( cell.IsNote() ) ? std::string("nnn") : cell.IsSpecialNote() ? std::string("mmm") : std::string("...") ); @@ -1473,7 +1573,7 @@ std::pair< std::string, std::string > module_impl::format_and_highlight_pattern_ const ModCommand & cell = *pattern.GetpModCommand( static_cast( r ), static_cast( c ) ); text.clear(); high.clear(); - text += ( cell.IsNote() || cell.IsSpecialNote() ) ? mpt::ToCharset( mpt::CharsetUTF8, m_sndFile->GetNoteName( cell.note, cell.instr ) ) : std::string("..."); + text += ( cell.IsNote() || cell.IsSpecialNote() ) ? mpt::ToCharset( mpt::Charset::UTF8, m_sndFile->GetNoteName( cell.note, cell.instr ) ) : std::string("..."); high += ( cell.IsNote() ) ? std::string("nnn") : cell.IsSpecialNote() ? std::string("mmm") : std::string("..."); if ( ( width == 0 ) || ( width >= 6 ) ) { text += std::string(" "); @@ -1510,25 +1610,39 @@ std::string module_impl::highlight_pattern_row_channel( std::int32_t p, std::int return format_and_highlight_pattern_row_channel( p, r, c, width, pad ).second; } -std::vector module_impl::get_ctls() const { - return - { - "load.skip_samples", - "load.skip_patterns", - "load.skip_plugins", - "load.skip_subsongs_init", - "seek.sync_samples", - "subsong", - "play.tempo_factor", - "play.pitch_factor", - "play.at_end", - "render.resampler.emulate_amiga", - "render.opl.volume_factor", - "dither", +std::pair module_impl::get_ctl_infos() const { + static constexpr ctl_info ctl_infos[] = { + { "load.skip_samples", ctl_type::boolean }, + { "load.skip_patterns", ctl_type::boolean }, + { "load.skip_plugins", ctl_type::boolean }, + { "load.skip_subsongs_init", ctl_type::boolean }, + { "seek.sync_samples", ctl_type::boolean }, + { "subsong", ctl_type::integer }, + { "play.tempo_factor", ctl_type::floatingpoint }, + { "play.pitch_factor", ctl_type::floatingpoint }, + { "play.at_end", ctl_type::text }, + { "render.resampler.emulate_amiga", ctl_type::boolean }, + { "render.resampler.emulate_amiga_type", ctl_type::text }, + { "render.opl.volume_factor", ctl_type::floatingpoint }, + { "dither", ctl_type::integer } }; + return std::make_pair(std::begin(ctl_infos), std::end(ctl_infos)); } + +std::vector module_impl::get_ctls() const { + std::vector result; + auto ctl_infos = get_ctl_infos(); + result.reserve(std::distance(ctl_infos.first, ctl_infos.second)); + for ( std::ptrdiff_t i = 0; i < std::distance(ctl_infos.first, ctl_infos.second); ++i ) { + result.push_back(ctl_infos.first[i].name); + } + return result; +} + std::string module_impl::ctl_get( std::string ctl, bool throw_if_unknown ) const { if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds char rightmost = ctl.back(); if ( rightmost == '!' || rightmost == '?' ) { if ( rightmost == '!' ) { @@ -1539,20 +1653,189 @@ std::string module_impl::ctl_get( std::string ctl, bool throw_if_unknown ) const ctl = ctl.substr( 0, ctl.length() - 1 ); } } + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl"); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + ctl); + } else { + return std::string(); + } + } + std::string result; + switch ( found_ctl->type ) { + case ctl_type::boolean: + return mpt::fmt::val( ctl_get_boolean( ctl, throw_if_unknown ) ); + break; + case ctl_type::integer: + return mpt::fmt::val( ctl_get_integer( ctl, throw_if_unknown ) ); + break; + case ctl_type::floatingpoint: + return mpt::fmt::val( ctl_get_floatingpoint( ctl, throw_if_unknown ) ); + break; + case ctl_type::text: + return ctl_get_text( ctl, throw_if_unknown ); + break; + } + return result; +} +bool module_impl::ctl_get_boolean( std::string_view ctl, bool throw_if_unknown ) const { + if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds + char rightmost = ctl.back(); + if ( rightmost == '!' || rightmost == '?' ) { + if ( rightmost == '!' ) { + throw_if_unknown = true; + } else if ( rightmost == '?' ) { + throw_if_unknown = false; + } + ctl = ctl.substr( 0, ctl.length() - 1 ); + } + } + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl"); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + std::string(ctl)); + } else { + return false; + } + } + if ( found_ctl->type != ctl_type::boolean ) { + throw openmpt::exception("wrong ctl value type"); + } if ( ctl == "" ) { throw openmpt::exception("empty ctl"); } else if ( ctl == "load.skip_samples" || ctl == "load_skip_samples" ) { - return mpt::fmt::val( m_ctl_load_skip_samples ); + return m_ctl_load_skip_samples; } else if ( ctl == "load.skip_patterns" || ctl == "load_skip_patterns" ) { - return mpt::fmt::val( m_ctl_load_skip_patterns ); + return m_ctl_load_skip_patterns; } else if ( ctl == "load.skip_plugins" ) { - return mpt::fmt::val( m_ctl_load_skip_plugins ); + return m_ctl_load_skip_plugins; } else if ( ctl == "load.skip_subsongs_init" ) { - return mpt::fmt::val( m_ctl_load_skip_subsongs_init ); + return m_ctl_load_skip_subsongs_init; } else if ( ctl == "seek.sync_samples" ) { - return mpt::fmt::val( m_ctl_seek_sync_samples ); + return m_ctl_seek_sync_samples; + } else if ( ctl == "render.resampler.emulate_amiga" ) { + return ( m_sndFile->m_Resampler.m_Settings.emulateAmiga != Resampling::AmigaFilter::Off ); + } else { + MPT_ASSERT_NOTREACHED(); + return false; + } +} +std::int64_t module_impl::ctl_get_integer( std::string_view ctl, bool throw_if_unknown ) const { + if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds + char rightmost = ctl.back(); + if ( rightmost == '!' || rightmost == '?' ) { + if ( rightmost == '!' ) { + throw_if_unknown = true; + } else if ( rightmost == '?' ) { + throw_if_unknown = false; + } + ctl = ctl.substr( 0, ctl.length() - 1 ); + } + } + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl"); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + std::string(ctl)); + } else { + return 0; + } + } + if ( found_ctl->type != ctl_type::integer ) { + throw openmpt::exception("wrong ctl value type"); + } + if ( ctl == "" ) { + throw openmpt::exception("empty ctl"); } else if ( ctl == "subsong" ) { - return mpt::fmt::val( get_selected_subsong() ); + return get_selected_subsong(); + } else if ( ctl == "dither" ) { + return static_cast( m_Dither->GetMode() ); + } else { + MPT_ASSERT_NOTREACHED(); + return 0; + } +} +double module_impl::ctl_get_floatingpoint( std::string_view ctl, bool throw_if_unknown ) const { + if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds + char rightmost = ctl.back(); + if ( rightmost == '!' || rightmost == '?' ) { + if ( rightmost == '!' ) { + throw_if_unknown = true; + } else if ( rightmost == '?' ) { + throw_if_unknown = false; + } + ctl = ctl.substr( 0, ctl.length() - 1 ); + } + } + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl"); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + std::string(ctl)); + } else { + return 0.0; + } + } + if ( found_ctl->type != ctl_type::floatingpoint ) { + throw openmpt::exception("wrong ctl value type"); + } + if ( ctl == "" ) { + throw openmpt::exception("empty ctl"); + } else if ( ctl == "play.tempo_factor" ) { + if ( !is_loaded() ) { + return 1.0; + } + return 65536.0 / m_sndFile->m_nTempoFactor; + } else if ( ctl == "play.pitch_factor" ) { + if ( !is_loaded() ) { + return 1.0; + } + return m_sndFile->m_nFreqFactor / 65536.0; + } else if ( ctl == "render.opl.volume_factor" ) { + return static_cast( m_sndFile->m_OPLVolumeFactor ) / static_cast( m_sndFile->m_OPLVolumeFactorScale ); + } else { + MPT_ASSERT_NOTREACHED(); + return 0.0; + } +} +std::string module_impl::ctl_get_text( std::string_view ctl, bool throw_if_unknown ) const { + if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds + char rightmost = ctl.back(); + if ( rightmost == '!' || rightmost == '?' ) { + if ( rightmost == '!' ) { + throw_if_unknown = true; + } else if ( rightmost == '?' ) { + throw_if_unknown = false; + } + ctl = ctl.substr( 0, ctl.length() - 1 ); + } + } + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl"); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + std::string(ctl)); + } else { + return std::string(); + } + } + if ( ctl == "" ) { + throw openmpt::exception("empty ctl"); } else if ( ctl == "play.at_end" ) { switch ( m_ctl_play_at_end ) { @@ -1565,32 +1848,29 @@ std::string module_impl::ctl_get( std::string ctl, bool throw_if_unknown ) const default: return std::string(); } - } else if ( ctl == "play.tempo_factor" ) { - if ( !is_loaded() ) { - return "1.0"; + } else if ( ctl == "render.resampler.emulate_amiga_type" ) { + switch ( m_ctl_render_resampler_emulate_amiga_type ) { + case amiga_filter_type::a500: + return "a500"; + case amiga_filter_type::a1200: + return "a1200"; + case amiga_filter_type::unfiltered: + return "unfiltered"; + case amiga_filter_type::auto_filter: + return "auto"; + default: + return std::string(); } - return mpt::fmt::val( 65536.0 / m_sndFile->m_nTempoFactor ); - } else if ( ctl == "play.pitch_factor" ) { - if ( !is_loaded() ) { - return "1.0"; - } - return mpt::fmt::val( m_sndFile->m_nFreqFactor / 65536.0 ); - } else if ( ctl == "render.resampler.emulate_amiga" ) { - return mpt::fmt::val( m_sndFile->m_Resampler.m_Settings.emulateAmiga ); - } else if ( ctl == "render.opl.volume_factor" ) { - return mpt::fmt::val( static_cast( m_sndFile->m_OPLVolumeFactor ) / static_cast( m_sndFile->m_OPLVolumeFactorScale ) ); - } else if ( ctl == "dither" ) { - return mpt::fmt::val( static_cast( m_Dither->GetMode() ) ); } else { - if ( throw_if_unknown ) { - throw openmpt::exception("unknown ctl: " + ctl); - } else { - return std::string(); - } + MPT_ASSERT_NOTREACHED(); + return std::string(); } } + void module_impl::ctl_set( std::string ctl, const std::string & value, bool throw_if_unknown ) { if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds char rightmost = ctl.back(); if ( rightmost == '!' || rightmost == '?' ) { if ( rightmost == '!' ) { @@ -1601,35 +1881,152 @@ void module_impl::ctl_set( std::string ctl, const std::string & value, bool thro ctl = ctl.substr( 0, ctl.length() - 1 ); } } - if ( ctl == "" ) { - throw openmpt::exception("empty ctl: := " + value); - } else if ( ctl == "load.skip_samples" || ctl == "load_skip_samples" ) { - m_ctl_load_skip_samples = ConvertStrTo( value ); - } else if ( ctl == "load.skip_patterns" || ctl == "load_skip_patterns" ) { - m_ctl_load_skip_patterns = ConvertStrTo( value ); - } else if ( ctl == "load.skip_plugins" ) { - m_ctl_load_skip_plugins = ConvertStrTo( value ); - } else if ( ctl == "load.skip_subsongs_init" ) { - m_ctl_load_skip_subsongs_init = ConvertStrTo( value ); - } else if ( ctl == "seek.sync_samples" ) { - m_ctl_seek_sync_samples = ConvertStrTo( value ); - } else if ( ctl == "subsong" ) { - select_subsong( ConvertStrTo( value ) ); - } else if ( ctl == "play.at_end" ) { - if ( value == "fadeout" ) { - m_ctl_play_at_end = song_end_action::fadeout_song; - } else if(value == "continue") { - m_ctl_play_at_end = song_end_action::continue_song; - } else if(value == "stop") { - m_ctl_play_at_end = song_end_action::stop_song; + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl: := " + value); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + ctl + " := " + value); } else { - throw openmpt::exception("unknown song end action:" + value); + return; } + } + switch ( found_ctl->type ) { + case ctl_type::boolean: + ctl_set_boolean( ctl, ConvertStrTo( value ), throw_if_unknown ); + break; + case ctl_type::integer: + ctl_set_integer( ctl, ConvertStrTo( value ), throw_if_unknown ); + break; + case ctl_type::floatingpoint: + ctl_set_floatingpoint( ctl, ConvertStrTo( value ), throw_if_unknown ); + break; + case ctl_type::text: + ctl_set_text( ctl, value, throw_if_unknown ); + break; + } +} +void module_impl::ctl_set_boolean( std::string_view ctl, bool value, bool throw_if_unknown ) { + if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds + char rightmost = ctl.back(); + if ( rightmost == '!' || rightmost == '?' ) { + if ( rightmost == '!' ) { + throw_if_unknown = true; + } else if ( rightmost == '?' ) { + throw_if_unknown = false; + } + ctl = ctl.substr( 0, ctl.length() - 1 ); + } + } + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl: := " + mpt::fmt::val( value ) ); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + std::string(ctl) + " := " + mpt::fmt::val(value)); + } else { + return; + } + } + if ( ctl == "" ) { + throw openmpt::exception("empty ctl: := " + mpt::fmt::val( value ) ); + } else if ( ctl == "load.skip_samples" || ctl == "load_skip_samples" ) { + m_ctl_load_skip_samples = value; + } else if ( ctl == "load.skip_patterns" || ctl == "load_skip_patterns" ) { + m_ctl_load_skip_patterns = value; + } else if ( ctl == "load.skip_plugins" ) { + m_ctl_load_skip_plugins = value; + } else if ( ctl == "load.skip_subsongs_init" ) { + m_ctl_load_skip_subsongs_init = value; + } else if ( ctl == "seek.sync_samples" ) { + m_ctl_seek_sync_samples = value; + } else if ( ctl == "render.resampler.emulate_amiga" ) { + CResamplerSettings newsettings = m_sndFile->m_Resampler.m_Settings; + const bool enabled = value; + if ( enabled ) + newsettings.emulateAmiga = translate_amiga_filter_type( m_ctl_render_resampler_emulate_amiga_type ); + else + newsettings.emulateAmiga = Resampling::AmigaFilter::Off; + if ( newsettings != m_sndFile->m_Resampler.m_Settings ) { + m_sndFile->SetResamplerSettings( newsettings ); + } + } else { + MPT_ASSERT_NOTREACHED(); + } +} +void module_impl::ctl_set_integer( std::string_view ctl, std::int64_t value, bool throw_if_unknown ) { + if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds + char rightmost = ctl.back(); + if ( rightmost == '!' || rightmost == '?' ) { + if ( rightmost == '!' ) { + throw_if_unknown = true; + } else if ( rightmost == '?' ) { + throw_if_unknown = false; + } + ctl = ctl.substr( 0, ctl.length() - 1 ); + } + } + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl: := " + mpt::fmt::val( value ) ); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + std::string(ctl) + " := " + mpt::fmt::val(value)); + } else { + return; + } + } + + if ( ctl == "" ) { + throw openmpt::exception("empty ctl: := " + mpt::fmt::val( value ) ); + } else if ( ctl == "subsong" ) { + select_subsong( mpt::saturate_cast( value ) ); + } else if ( ctl == "dither" ) { + int dither = mpt::saturate_cast( value ); + if ( dither < 0 || dither >= NumDitherModes ) { + dither = DitherDefault; + } + m_Dither->SetMode( static_cast( dither ) ); + } else { + MPT_ASSERT_NOTREACHED(); + } +} +void module_impl::ctl_set_floatingpoint( std::string_view ctl, double value, bool throw_if_unknown ) { + if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds + char rightmost = ctl.back(); + if ( rightmost == '!' || rightmost == '?' ) { + if ( rightmost == '!' ) { + throw_if_unknown = true; + } else if ( rightmost == '?' ) { + throw_if_unknown = false; + } + ctl = ctl.substr( 0, ctl.length() - 1 ); + } + } + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl: := " + mpt::fmt::val( value ) ); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + std::string(ctl) + " := " + mpt::fmt::val(value)); + } else { + return; + } + } + + if ( ctl == "" ) { + throw openmpt::exception("empty ctl: := " + mpt::fmt::val( value ) ); } else if ( ctl == "play.tempo_factor" ) { if ( !is_loaded() ) { return; } - double factor = ConvertStrTo( value ); + double factor = value; if ( factor <= 0.0 || factor > 4.0 ) { throw openmpt::exception("invalid tempo factor"); } @@ -1639,33 +2036,77 @@ void module_impl::ctl_set( std::string ctl, const std::string & value, bool thro if ( !is_loaded() ) { return; } - double factor = ConvertStrTo( value ); + double factor = value; if ( factor <= 0.0 || factor > 4.0 ) { throw openmpt::exception("invalid pitch factor"); } m_sndFile->m_nFreqFactor = mpt::saturate_round( 65536.0 * factor ); m_sndFile->RecalculateSamplesPerTick(); - } else if ( ctl == "render.resampler.emulate_amiga" ) { - CResamplerSettings newsettings = m_sndFile->m_Resampler.m_Settings; - newsettings.emulateAmiga = ConvertStrTo( value ); - if ( newsettings != m_sndFile->m_Resampler.m_Settings ) { - m_sndFile->SetResamplerSettings( newsettings ); - } } else if ( ctl == "render.opl.volume_factor" ) { - m_sndFile->m_OPLVolumeFactor = mpt::saturate_round( ConvertStrTo( value ) * static_cast( m_sndFile->m_OPLVolumeFactorScale ) ); - } else if ( ctl == "dither" ) { - int dither = ConvertStrTo( value ); - if ( dither < 0 || dither >= NumDitherModes ) { - dither = DitherDefault; - } - m_Dither->SetMode( static_cast( dither ) ); + m_sndFile->m_OPLVolumeFactor = mpt::saturate_round( value * static_cast( m_sndFile->m_OPLVolumeFactorScale ) ); } else { - if ( throw_if_unknown ) { - throw openmpt::exception("unknown ctl: " + ctl + " := " + value); - } else { - // ignore + MPT_ASSERT_NOTREACHED(); + } +} +void module_impl::ctl_set_text( std::string_view ctl, std::string_view value, bool throw_if_unknown ) { + if ( !ctl.empty() ) { + // cppcheck false-positive + // cppcheck-suppress containerOutOfBounds + char rightmost = ctl.back(); + if ( rightmost == '!' || rightmost == '?' ) { + if ( rightmost == '!' ) { + throw_if_unknown = true; + } else if ( rightmost == '?' ) { + throw_if_unknown = false; + } + ctl = ctl.substr( 0, ctl.length() - 1 ); } } + auto found_ctl = std::find_if(get_ctl_infos().first, get_ctl_infos().second, [&](const ctl_info & info) -> bool { return info.name == ctl; }); + if ( found_ctl == get_ctl_infos().second ) { + if ( ctl == "" ) { + throw openmpt::exception("empty ctl: := " + std::string( value ) ); + } else if ( throw_if_unknown ) { + throw openmpt::exception("unknown ctl: " + std::string(ctl) + " := " + std::string(value)); + } else { + return; + } + } + + if ( ctl == "" ) { + throw openmpt::exception("empty ctl: := " + std::string( value ) ); + } else if ( ctl == "play.at_end" ) { + if ( value == "fadeout" ) { + m_ctl_play_at_end = song_end_action::fadeout_song; + } else if(value == "continue") { + m_ctl_play_at_end = song_end_action::continue_song; + } else if(value == "stop") { + m_ctl_play_at_end = song_end_action::stop_song; + } else { + throw openmpt::exception("unknown song end action:" + std::string(value)); + } + } else if ( ctl == "render.resampler.emulate_amiga_type" ) { + if ( value == "a500" ) { + m_ctl_render_resampler_emulate_amiga_type = amiga_filter_type::a500; + } else if ( value == "a1200" ) { + m_ctl_render_resampler_emulate_amiga_type = amiga_filter_type::a1200; + } else if ( value == "unfiltered" ) { + m_ctl_render_resampler_emulate_amiga_type = amiga_filter_type::unfiltered; + } else if ( value == "auto" ) { + m_ctl_render_resampler_emulate_amiga_type = amiga_filter_type::auto_filter; + } else { + throw openmpt::exception( "invalid amiga filter type" ); + } + if ( m_sndFile->m_Resampler.m_Settings.emulateAmiga != Resampling::AmigaFilter::Off ) { + CResamplerSettings newsettings = m_sndFile->m_Resampler.m_Settings; + newsettings.emulateAmiga = translate_amiga_filter_type( m_ctl_render_resampler_emulate_amiga_type ); + if ( newsettings != m_sndFile->m_Resampler.m_Settings ) { + m_sndFile->SetResamplerSettings( newsettings ); + } + } + } else { + MPT_ASSERT_NOTREACHED(); + } } } // namespace openmpt diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.hpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.hpp index 299417be3..1e440a848 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.hpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_impl.hpp @@ -15,6 +15,7 @@ #include #include +#include #if defined(_MSC_VER) #pragma warning(push) @@ -31,7 +32,8 @@ class FileReader; } // namespace detail typedef detail::FileReader FileReader; class CSoundFile; -class Dither; +template class DitherChannels; +using Dither = DitherChannels<4>; } // namespace OpenMPT namespace openmpt { @@ -71,6 +73,14 @@ struct callback_stream_wrapper { }; // struct callback_stream_wrapper class module_impl { +public: + enum class amiga_filter_type { + a500, + a1200, + unfiltered, + auto_filter, + }; + protected: struct subsong_data { double duration; @@ -88,7 +98,18 @@ protected: stop_song, }; - static const std::int32_t all_subsongs = -1; + static constexpr std::int32_t all_subsongs = -1; + + enum class ctl_type { + boolean, + integer, + floatingpoint, + text, + }; + struct ctl_info { + const char * name; + ctl_type type; + }; std::unique_ptr m_Log; std::unique_ptr m_LogForwarder; @@ -101,6 +122,7 @@ protected: subsongs_type m_subsongs; float m_Gain; song_end_action m_ctl_play_at_end; + amiga_filter_type m_ctl_render_resampler_emulate_amiga_type = amiga_filter_type::auto_filter; bool m_ctl_load_skip_samples; bool m_ctl_load_skip_patterns; bool m_ctl_load_skip_plugins; @@ -124,26 +146,31 @@ protected: std::size_t read_wrapper( std::size_t count, float * left, float * right, float * rear_left, float * rear_right ); std::size_t read_interleaved_wrapper( std::size_t count, std::size_t channels, std::int16_t * interleaved ); std::size_t read_interleaved_wrapper( std::size_t count, std::size_t channels, float * interleaved ); + std::string get_message_instruments() const; + std::string get_message_samples() const; std::pair< std::string, std::string > format_and_highlight_pattern_row_channel_command( std::int32_t p, std::int32_t r, std::int32_t c, int command ) const; std::pair< std::string, std::string > format_and_highlight_pattern_row_channel( std::int32_t p, std::int32_t r, std::int32_t c, std::size_t width, bool pad ) const; static double could_open_probability( const OpenMPT::FileReader & file, double effort, std::unique_ptr log ); public: static std::vector get_supported_extensions(); - static bool is_extension_supported( const char * extension ); - static bool is_extension_supported( const std::string & extension ); + static bool is_extension_supported( std::string_view extension ); static double could_open_probability( callback_stream_wrapper stream, double effort, std::unique_ptr log ); static double could_open_probability( std::istream & stream, double effort, std::unique_ptr log ); static std::size_t probe_file_header_get_recommended_size(); + static int probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size, std::uint64_t filesize ); static int probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size, std::uint64_t filesize ); static int probe_file_header( std::uint64_t flags, const void * data, std::size_t size, std::uint64_t filesize ); + static int probe_file_header( std::uint64_t flags, const std::byte * data, std::size_t size ); static int probe_file_header( std::uint64_t flags, const std::uint8_t * data, std::size_t size ); static int probe_file_header( std::uint64_t flags, const void * data, std::size_t size ); static int probe_file_header( std::uint64_t flags, std::istream & stream ); static int probe_file_header( std::uint64_t flags, callback_stream_wrapper stream ); module_impl( callback_stream_wrapper stream, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( std::istream & stream, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); + module_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( const std::vector & data, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); + module_impl( const std::byte * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( const std::uint8_t * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( const char * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); module_impl( const void * data, std::size_t size, std::unique_ptr log, const std::map< std::string, std::string > & ctls ); @@ -171,6 +198,7 @@ public: std::size_t read_interleaved_quad( std::int32_t samplerate, std::size_t count, float * interleaved_quad ); std::vector get_metadata_keys() const; std::string get_metadata( const std::string & key ) const; + double get_current_estimated_bpm() const; std::int32_t get_current_speed() const; std::int32_t get_current_tempo() const; std::int32_t get_current_order() const; @@ -201,9 +229,18 @@ public: std::string highlight_pattern_row_channel_command( std::int32_t p, std::int32_t r, std::int32_t c, int cmd ) const; std::string format_pattern_row_channel( std::int32_t p, std::int32_t r, std::int32_t c, std::size_t width, bool pad ) const; std::string highlight_pattern_row_channel( std::int32_t p, std::int32_t r, std::int32_t c, std::size_t width, bool pad ) const; + std::pair get_ctl_infos() const; std::vector get_ctls() const; std::string ctl_get( std::string ctl, bool throw_if_unknown = true ) const; + bool ctl_get_boolean( std::string_view ctl, bool throw_if_unknown = true ) const; + std::int64_t ctl_get_integer( std::string_view ctl, bool throw_if_unknown = true ) const; + double ctl_get_floatingpoint( std::string_view ctl, bool throw_if_unknown = true ) const; + std::string ctl_get_text( std::string_view ctl, bool throw_if_unknown = true ) const; void ctl_set( std::string ctl, const std::string & value, bool throw_if_unknown = true ); + void ctl_set_boolean( std::string_view ctl, bool value, bool throw_if_unknown = true ); + void ctl_set_integer( std::string_view ctl, std::int64_t value, bool throw_if_unknown = true ); + void ctl_set_floatingpoint( std::string_view ctl, double value, bool throw_if_unknown = true ); + void ctl_set_text( std::string_view ctl, std::string_view value, bool throw_if_unknown = true ); }; // class module_impl namespace helper { diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.cpp index 780d494ef..59a4930f4 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.cpp @@ -67,13 +67,14 @@ protected: CSliderCtrl m_SliderCtrlGain; CComboBox m_ComboBoxInterpolation; CButton m_CheckBoxAmigaResampler; + CComboBox m_ComboBoxAmigaFilter; CComboBox m_ComboBoxRepeat; CSliderCtrl m_SliderCtrlStereoSeparation; CComboBox m_ComboBoxRamping; public: - CSettingsDialog( libopenmpt_settings * s_, CString title, CWnd * parent = NULL ) + CSettingsDialog( libopenmpt_settings * s_, CString title, CWnd * parent = nullptr ) : CDialog( IDD_SETTINGS, parent ) , s( s_ ) , m_Title( title ) @@ -91,6 +92,7 @@ protected: DDX_Control( pDX, IDC_SLIDER_GAIN, m_SliderCtrlGain ); DDX_Control( pDX, IDC_COMBO_INTERPOLATION, m_ComboBoxInterpolation ); DDX_Control( pDX, IDC_CHECK_AMIGA_RESAMPLER, m_CheckBoxAmigaResampler ); + DDX_Control( pDX, IDC_COMBO_AMIGA_FILTER, m_ComboBoxAmigaFilter ); DDX_Control( pDX, IDC_COMBO_REPEAT, m_ComboBoxRepeat ); DDX_Control( pDX, IDC_SLIDER_STEREOSEPARATION, m_SliderCtrlStereoSeparation ); DDX_Control( pDX, IDC_COMBO_RAMPING, m_ComboBoxRamping ); @@ -98,9 +100,7 @@ protected: afx_msg BOOL OnInitDialog() override { - if ( !CDialog::OnInitDialog() ) { - return false; - } + CDialog::OnInitDialog(); SetWindowText( m_Title ); EnableToolTips(); @@ -125,7 +125,7 @@ protected: m_ComboBoxSamplerate.SelectString( 0, L"Default" ); } for ( int index = 0; index < m_ComboBoxSamplerate.GetCount(); ++index ) { - if ( m_ComboBoxSamplerate.GetItemData( index ) == s->samplerate ) { + if ( static_cast( m_ComboBoxSamplerate.GetItemData( index ) ) == s->samplerate ) { m_ComboBoxSamplerate.SetCurSel( index ); selected = true; } @@ -145,7 +145,7 @@ protected: m_ComboBoxChannels.SelectString( 0, L"Default" ); } for ( int index = 0; index < m_ComboBoxChannels.GetCount(); ++index ) { - if ( m_ComboBoxChannels.GetItemData( index ) == s->channels ) { + if ( static_cast( m_ComboBoxChannels.GetItemData( index ) ) == s->channels ) { m_ComboBoxChannels.SetCurSel( index ); selected = true; } @@ -166,7 +166,7 @@ protected: m_ComboBoxInterpolation.SetItemData( m_ComboBoxInterpolation.AddString( L"4 Tap (Cubic)" ), 4 ); m_ComboBoxInterpolation.SetItemData( m_ComboBoxInterpolation.AddString( L"8 Tap (Polyphase FIR)" ), 8 ); for ( int index = 0; index < m_ComboBoxInterpolation.GetCount(); ++index ) { - if ( m_ComboBoxInterpolation.GetItemData( index ) == s->interpolationfilterlength ) { + if ( static_cast( m_ComboBoxInterpolation.GetItemData( index ) ) == s->interpolationfilterlength ) { m_ComboBoxInterpolation.SetCurSel( index ); selected = true; } @@ -176,13 +176,28 @@ protected: } m_CheckBoxAmigaResampler.SetCheck( s->use_amiga_resampler ? BST_CHECKED : BST_UNCHECKED ); + selected = false; + m_ComboBoxAmigaFilter.EnableWindow( s->use_amiga_resampler ? TRUE : FALSE ); + m_ComboBoxAmigaFilter.SetItemData( m_ComboBoxAmigaFilter.AddString( L"Default" ), 0 ); + m_ComboBoxAmigaFilter.SetItemData( m_ComboBoxAmigaFilter.AddString( L"A500 Filter" ), 0xA500 ); + m_ComboBoxAmigaFilter.SetItemData( m_ComboBoxAmigaFilter.AddString( L"A1200 Filter" ), 0xA1200 ); + m_ComboBoxAmigaFilter.SetItemData( m_ComboBoxAmigaFilter.AddString( L"Unfiltered" ), 1 ); + for ( int index = 0; index < m_ComboBoxAmigaFilter.GetCount(); ++index ) { + if ( static_cast( m_ComboBoxAmigaFilter.GetItemData( index ) ) == s->amiga_filter_type ) { + m_ComboBoxAmigaFilter.SetCurSel( index ); + selected = true; + } + } + if ( !selected ) { + m_ComboBoxAmigaFilter.SelectString( 0, L"Default" ); + } selected = false; - m_ComboBoxRepeat.SetItemData( m_ComboBoxRepeat.AddString( L"Forever" ), -1 ); + m_ComboBoxRepeat.SetItemData( m_ComboBoxRepeat.AddString( L"Forever" ), static_cast( -1 ) ); m_ComboBoxRepeat.SetItemData( m_ComboBoxRepeat.AddString( L"Never" ), 0 ); m_ComboBoxRepeat.SetItemData( m_ComboBoxRepeat.AddString( L"Once" ), 1 ); for ( int index = 0; index < m_ComboBoxRepeat.GetCount(); ++index ) { - if ( m_ComboBoxRepeat.GetItemData( index ) == s->repeatcount ) { + if ( static_cast( m_ComboBoxRepeat.GetItemData( index ) ) == s->repeatcount ) { m_ComboBoxRepeat.SetCurSel( index ); selected = true; } @@ -198,7 +213,7 @@ protected: m_SliderCtrlStereoSeparation.SetPos( s->stereoseparation ); selected = false; - m_ComboBoxRamping.SetItemData( m_ComboBoxRamping.AddString( L"Default" ), -1 ); + m_ComboBoxRamping.SetItemData( m_ComboBoxRamping.AddString( L"Default" ), static_cast( -1 ) ); m_ComboBoxRamping.SetItemData( m_ComboBoxRamping.AddString( L"Off" ), 0 ); m_ComboBoxRamping.SetItemData( m_ComboBoxRamping.AddString( L"1 ms" ), 1 ); m_ComboBoxRamping.SetItemData( m_ComboBoxRamping.AddString( L"2 ms" ), 2 ); @@ -206,7 +221,7 @@ protected: m_ComboBoxRamping.SetItemData( m_ComboBoxRamping.AddString( L"5 ms" ), 5 ); m_ComboBoxRamping.SetItemData( m_ComboBoxRamping.AddString( L"10 ms" ), 10 ); for ( int index = 0; index < m_ComboBoxRamping.GetCount(); ++index ) { - if ( m_ComboBoxRamping.GetItemData( index ) == s->ramping ) { + if ( static_cast( m_ComboBoxRamping.GetItemData( index ) ) == s->ramping ) { m_ComboBoxRamping.SetCurSel( index ); selected = true; } @@ -230,6 +245,7 @@ protected: s->interpolationfilterlength = m_ComboBoxInterpolation.GetItemData( m_ComboBoxInterpolation.GetCurSel() ); s->use_amiga_resampler = ( m_CheckBoxAmigaResampler.GetCheck() != BST_UNCHECKED ) ? 1 : 0; + s->amiga_filter_type = m_ComboBoxAmigaFilter.GetItemData( m_ComboBoxAmigaFilter.GetCurSel() ); s->repeatcount = m_ComboBoxRepeat.GetItemData( m_ComboBoxRepeat.GetCurSel() ); @@ -270,10 +286,15 @@ protected: return TRUE; } + void OnAmigaResamplerChanged() { + m_ComboBoxAmigaFilter.EnableWindow( IsDlgButtonChecked( IDC_CHECK_AMIGA_RESAMPLER ) != BST_UNCHECKED ? TRUE : FALSE ); + } + }; BEGIN_MESSAGE_MAP(CSettingsDialog, CDialog) ON_NOTIFY_EX_RANGE(TTN_NEEDTEXT, 0, 0xFFFF, &CSettingsDialog::OnToolTipText) + ON_COMMAND( IDC_CHECK_AMIGA_RESAMPLER, &CSettingsDialog::OnAmigaResamplerChanged ) END_MESSAGE_MAP() diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.rc b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.rc index ebd7bbe4b..a618fd3be 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.rc +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_gui.rc @@ -50,12 +50,12 @@ END // Dialog // -IDD_SETTINGS DIALOGEX 0, 0, 201, 183 +IDD_SETTINGS DIALOGEX 0, 0, 201, 200 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - DEFPUSHBUTTON "&OK",IDOK,78,162,54,14 - PUSHBUTTON "&Cancel",IDCANCEL,138,162,54,14 + DEFPUSHBUTTON "&OK",IDOK,78,180,54,14 + PUSHBUTTON "&Cancel",IDCANCEL,138,180,54,14 LTEXT "&Samplerate",IDC_STATIC,6,6,60,12,SS_CENTERIMAGE COMBOBOX IDC_COMBO_SAMPLERATE,72,6,120,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "C&hannels",IDC_STATIC,6,24,60,12,SS_CENTERIMAGE @@ -66,13 +66,15 @@ BEGIN COMBOBOX IDC_COMBO_INTERPOLATION,72,60,120,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP CONTROL "Use &Amiga resampler for Amiga modules",IDC_CHECK_AMIGA_RESAMPLER, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,78,186,12 - LTEXT "&Repeat",IDC_STATIC,6,96,60,12,SS_CENTERIMAGE - COMBOBOX IDC_COMBO_REPEAT,72,96,120,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "S&tereo Separation",IDC_STATIC,6,114,60,12,SS_CENTERIMAGE - CONTROL "",IDC_SLIDER_STEREOSEPARATION,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | TBS_TOOLTIPS | WS_TABSTOP,72,114,126,15 - LTEXT "&Volume Ramping",IDC_STATIC,6,132,60,12,SS_CENTERIMAGE - COMBOBOX IDC_COMBO_RAMPING,72,132,120,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,6,156,186,1 + LTEXT "Amiga &Filter Type:",IDC_STATIC,6,96,60,12,SS_CENTERIMAGE + COMBOBOX IDC_COMBO_AMIGA_FILTER,72,96,120,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "&Repeat",IDC_STATIC,6,114,60,12,SS_CENTERIMAGE + COMBOBOX IDC_COMBO_REPEAT,72,114,120,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "S&tereo Separation",IDC_STATIC,6,132,60,12,SS_CENTERIMAGE + CONTROL "",IDC_SLIDER_STEREOSEPARATION,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | TBS_TOOLTIPS | WS_TABSTOP,72,132,126,15 + LTEXT "&Volume Ramping",IDC_STATIC,6,151,60,12,SS_CENTERIMAGE + COMBOBOX IDC_COMBO_RAMPING,72,151,120,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,6,175,186,1 END IDD_FILEINFO DIALOGEX 0, 0, 310, 174 @@ -97,7 +99,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 194 TOPMARGIN, 7 - BOTTOMMARGIN, 176 + BOTTOMMARGIN, 193 END IDD_FILEINFO, DIALOG diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_settings.hpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_settings.hpp index 6ef9744a2..b1cbf5b17 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_settings.hpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_plugin_settings.hpp @@ -29,6 +29,7 @@ struct libopenmpt_settings { int mastergain_millibel; int stereoseparation; int use_amiga_resampler; + int amiga_filter_type; int repeatcount; int interpolationfilterlength; int ramping; @@ -55,7 +56,7 @@ protected: regkey = HKEY(); } } - virtual void write_setting( const std::string & /* key */ , const std::wstring & keyW, int val ) { + virtual void write_setting( const std::string & /* key */, const std::wstring & keyW, int val ) { HKEY regkey = HKEY(); if ( RegCreateKeyEx( HKEY_CURRENT_USER, ( L"Software\\libopenmpt\\" + subkey ).c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, ®key, NULL ) == ERROR_SUCCESS ) { DWORD v = val; @@ -81,6 +82,7 @@ public: repeatcount = 0; interpolationfilterlength = 8; use_amiga_resampler = 0; + amiga_filter_type = 0; ramping = -1; vis_allow_scroll = 1; changed = 0; @@ -94,7 +96,8 @@ public: read_setting( subkey, "StereoSeparation_Percent", stereoseparation ); read_setting( subkey, "RepeatCount", repeatcount ); read_setting( subkey, "InterpolationFilterLength", interpolationfilterlength ); - read_setting( subkey, "UseAmigaResampler", use_amiga_resampler); + read_setting( subkey, "UseAmigaResampler", use_amiga_resampler ); + read_setting( subkey, "AmigaFilterType", amiga_filter_type ); read_setting( subkey, "VolumeRampingStrength", ramping ); read_setting( subkey, "VisAllowScroll", vis_allow_scroll ); #undef read_setting @@ -108,7 +111,8 @@ public: write_setting( subkey, "StereoSeparation_Percent", stereoseparation ); write_setting( subkey, "RepeatCount", repeatcount ); write_setting( subkey, "InterpolationFilterLength", interpolationfilterlength ); - write_setting( subkey, "UseAmigaResampler", use_amiga_resampler); + write_setting( subkey, "UseAmigaResampler", use_amiga_resampler ); + write_setting( subkey, "AmigaFilterType", amiga_filter_type ); write_setting( subkey, "VolumeRampingStrength", ramping ); write_setting( subkey, "VisAllowScroll", vis_allow_scroll ); #undef write_setting diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h index ad3941ed7..5045bd800 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.h @@ -17,9 +17,9 @@ /*! \brief libopenmpt major version number */ #define OPENMPT_API_VERSION_MAJOR 0 /*! \brief libopenmpt minor version number */ -#define OPENMPT_API_VERSION_MINOR 4 +#define OPENMPT_API_VERSION_MINOR 5 /*! \brief libopenmpt patch version number */ -#define OPENMPT_API_VERSION_PATCH 10 +#define OPENMPT_API_VERSION_PATCH 3 /*! \brief libopenmpt pre-release tag */ #define OPENMPT_API_VERSION_PREREL "" /*! \brief libopenmpt pre-release flag */ diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk index 1dc902690..4c3613743 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.mk @@ -1,8 +1,8 @@ LIBOPENMPT_VERSION_MAJOR=0 -LIBOPENMPT_VERSION_MINOR=4 -LIBOPENMPT_VERSION_PATCH=10 +LIBOPENMPT_VERSION_MINOR=5 +LIBOPENMPT_VERSION_PATCH=3 LIBOPENMPT_VERSION_PREREL= -LIBOPENMPT_LTVER_CURRENT=1 -LIBOPENMPT_LTVER_REVISION=10 -LIBOPENMPT_LTVER_AGE=1 +LIBOPENMPT_LTVER_CURRENT=2 +LIBOPENMPT_LTVER_REVISION=3 +LIBOPENMPT_LTVER_AGE=2 diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc index 52fca64be..4f122945c 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/libopenmpt_version.rc @@ -192,7 +192,7 @@ BEGIN VALUE "FileDescription", VER_FILEDESC_STR VALUE "FileVersion", VER_FILEVERSION_STR VALUE "InternalName", VER_FILENAME_STR - VALUE "LegalCopyright", "Copyright 2004-2019 OpenMPT contributors, Copyright 1997-2003 Olivier Lapicque" + VALUE "LegalCopyright", "Copyright 2004-2020 OpenMPT contributors, Copyright 1997-2003 Olivier Lapicque" VALUE "OriginalFilename", VER_FILENAME_STR VALUE "ProductName", "libopenmpt" VALUE "ProductVersion", VER_FILEVERSION_STR diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/resource.h b/Frameworks/OpenMPT/OpenMPT/libopenmpt/resource.h index c4d4a2121..980115f2a 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/resource.h +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/resource.h @@ -12,6 +12,7 @@ #define IDC_SLIDER_STEREOSEPARATION 1006 #define IDC_COMBO_RAMPING 1007 #define IDC_FILEINFO 1008 +#define IDC_COMBO_AMIGA_FILTER 1008 #define IDC_CHECK_AMIGA_RESAMPLER 1009 // Next default values for new objects diff --git a/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp b/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp index c210d7f7f..56334e4bc 100644 --- a/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/libopenmpt/xmp-openmpt.cpp @@ -219,7 +219,7 @@ static inline Tstring StringReplace( Tstring str, const Tstring2 & oldStr_, cons } static std::string StringUpperCase( std::string str ) { - std::transform( str.begin(), str.end(), str.begin(), std::toupper ); + std::transform( str.begin(), str.end(), str.begin(), []( char c ) { return static_cast( std::toupper( c ) ); } ); return str; } @@ -247,6 +247,7 @@ static void save_settings_to_map( std::map & result, const libo result[ "RepeatCount" ] = s.repeatcount; result[ "InterpolationFilterLength" ] = s.interpolationfilterlength; result[ "UseAmigaResampler" ] = s.use_amiga_resampler; + result[ "AmigaFilterType" ] = s.amiga_filter_type; result[ "VolumeRampingStrength" ] = s.ramping; } @@ -265,6 +266,7 @@ static void load_settings_from_map( libopenmpt::plugin::settings & s, const std: load_map_setting( map, "RepeatCount", s.repeatcount ); load_map_setting( map, "InterpolationFilterLength", s.interpolationfilterlength ); load_map_setting( map, "UseAmigaResampler", s.use_amiga_resampler ); + load_map_setting( map, "AmigaFilterType", s.amiga_filter_type ); load_map_setting( map, "VolumeRampingStrength", s.ramping ); } @@ -305,7 +307,21 @@ static void apply_options() { self->mod->set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, self->settings.stereoseparation ); self->mod->set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, self->settings.interpolationfilterlength ); self->mod->set_render_param( openmpt::module::RENDER_VOLUMERAMPING_STRENGTH, self->settings.ramping ); - self->mod->ctl_set( "render.resampler.emulate_amiga", self->settings.use_amiga_resampler ? "1" : "0" ); + self->mod->ctl_set_boolean( "render.resampler.emulate_amiga", self->settings.use_amiga_resampler ? true : false ); + switch ( self->settings.amiga_filter_type ) { + case 0: + self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "auto" ); + break; + case 1: + self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "unfiltered" ); + break; + case 0xA500: + self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "a500" ); + break; + case 0xA1200: + self->mod->ctl_set_text( "render.resampler.emulate_amiga_type", "a1200" ); + break; + } } } @@ -374,8 +390,8 @@ static void WINAPI ShortcutHandler( DWORD id ) { break; } - self->tempo_factor = std::min ( 48, std::max( -48, self->tempo_factor ) ); - self->pitch_factor = std::min ( 48, std::max( -48, self->pitch_factor ) ); + self->tempo_factor = std::min( 48, std::max( -48, self->tempo_factor ) ); + self->pitch_factor = std::min( 48, std::max( -48, self->pitch_factor ) ); const double tempo_factor = std::pow( 2.0, self->tempo_factor / 24.0 ); const double pitch_factor = std::pow( 2.0, self->pitch_factor / 24.0 ); @@ -449,7 +465,7 @@ static void clear_current_timeinfo() { static void WINAPI openmpt_About( HWND win ) { std::ostringstream about; about << SHORT_TITLE << " version " << openmpt::string::get( "library_version" ) << " " << "(built " << openmpt::string::get( "build" ) << ")" << std::endl; - about << " Copyright (c) 2013-2019 OpenMPT developers (https://lib.openmpt.org/)" << std::endl; + about << " Copyright (c) 2013-2020 OpenMPT developers (https://lib.openmpt.org/)" << std::endl; about << " OpenMPT version " << openmpt::string::get( "core_version" ) << std::endl; about << std::endl; about << openmpt::string::get( "contact" ) << std::endl; @@ -502,7 +518,7 @@ std::streambuf::int_type xmplay_streambuf::underflow() { char * base = &buffer.front(); char * start = base; if ( eback() == base ) { - std::size_t put_back_count = std::min( put_back, egptr() - base ); + std::size_t put_back_count = std::min( put_back, static_cast( egptr() - base ) ); std::memmove( base, egptr() - put_back_count, put_back_count ); start += put_back_count; } @@ -673,7 +689,7 @@ static char * build_xmplay_tags( const openmpt::module & mod ) { return result; } -static float * build_xmplay_length( const openmpt::module & mod ) { +static float * build_xmplay_length( const openmpt::module & /* mod */ ) { float * result = static_cast( xmpfmisc->Alloc( sizeof( float ) * self->subsong_lengths.size() ) ); if ( !result ) { return nullptr; @@ -749,6 +765,7 @@ static std::string sanitize_xmplay_multiline_string( const std::string & str ) { // check if a file is playable by this plugin // more thorough checks can be saved for the GetFileInfo and Open functions static BOOL WINAPI openmpt_CheckFile( const char * filename, XMPFILE file ) { + static_cast( filename ); try { #ifdef USE_XMPLAY_FILE_IO #ifdef USE_XMPLAY_ISTREAM @@ -791,6 +808,7 @@ static BOOL WINAPI openmpt_CheckFile( const char * filename, XMPFILE file ) { } static DWORD WINAPI openmpt_GetFileInfo( const char * filename, XMPFILE file, float * * length, char * * tags ) { + static_cast( filename ); try { std::map< std::string, std::string > ctls { @@ -867,6 +885,7 @@ static DWORD WINAPI openmpt_GetFileInfo( const char * filename, XMPFILE file, fl // open a file for playback // return: 0=failed, 1=success, 2=success and XMPlay can close the file static DWORD WINAPI openmpt_Open( const char * filename, XMPFILE file ) { + static_cast( filename ); xmpopenmpt_lock guard; reset_options(); try { @@ -1134,7 +1153,7 @@ static DWORD WINAPI openmpt_Process( float * dstbuf, DWORD count ) { std::size_t frames_to_render = frames; std::size_t frames_rendered = 0; while ( frames_to_render > 0 ) { - std::size_t frames_chunk = std::min( frames_to_render, ( self->samplerate + 99 ) / 100 ); // 100 Hz timing info update interval + std::size_t frames_chunk = std::min( frames_to_render, static_cast( ( self->samplerate + 99 ) / 100 ) ); // 100 Hz timing info update interval switch ( self->num_channels ) { case 1: { @@ -1294,18 +1313,18 @@ static BOOL WINAPI VisOpen(DWORD colors[3]) { viscolors[col_global] = invert_color( viscolors[col_background] ); const int r = viscolors[col_text].r, g = viscolors[col_text].g, b = viscolors[col_text].b; - viscolors[col_empty].r = (r + viscolors[col_background].r) / 2; - viscolors[col_empty].g = (g + viscolors[col_background].g) / 2; - viscolors[col_empty].b = (b + viscolors[col_background].b) / 2; + viscolors[col_empty].r = static_cast( (r + viscolors[col_background].r) / 2 ); + viscolors[col_empty].g = static_cast( (g + viscolors[col_background].g) / 2 ); + viscolors[col_empty].b = static_cast( (b + viscolors[col_background].b) / 2 ); viscolors[col_empty].a = 0; #define MIXCOLOR(col, c1, c2, c3) { \ viscolors[col] = viscolors[col_text]; \ int mix = viscolors[col].c1 + 0xA0; \ - viscolors[col].c1 = mix; \ + viscolors[col].c1 = static_cast( mix ); \ if ( mix > 0xFF ) { \ - viscolors[col].c2 = std::max( c2 - viscolors[col].c1 / 2, 0 ); \ - viscolors[col].c3 = std::max( c3 - viscolors[col].c1 / 2, 0 ); \ + viscolors[col].c2 = std::max( static_cast( c2 - viscolors[col].c1 / 2 ), std::uint8_t(0) ); \ + viscolors[col].c3 = std::max( static_cast( c3 - viscolors[col].c1 / 2 ), std::uint8_t(0) ); \ viscolors[col].c1 = 0xFF; \ } } @@ -1336,13 +1355,15 @@ static void WINAPI VisClose() { DeleteFont( visfont ); DeleteBitmap( visbitmap ); - DeleteDC( visDC ); + if ( visDC ) { + DeleteDC( visDC ); + } } -static void WINAPI VisSize(HDC dc, SIZE *size) { +static void WINAPI VisSize( HDC /* dc */ , SIZE * /* size */ ) { xmpopenmpt_lock guard; last_pattern = -1; // Force redraw } -static BOOL WINAPI VisRender(DWORD *buf, SIZE size, DWORD flags) { +static BOOL WINAPI VisRender( DWORD * /* buf */ , SIZE /* size */ , DWORD /* flags */ ) { xmpopenmpt_lock guard; return FALSE; } @@ -1392,7 +1413,7 @@ static BOOL WINAPI VisRenderDC( HDC dc, SIZE size, DWORD flags ) { const std::size_t channels = self->mod->get_num_channels(); const std::size_t rows = self->mod->get_pattern_num_rows( pattern ); - const std::size_t num_half_chars = std::max( 2 * size.cx / text_size.cx, 8 ) - 8; + const std::size_t num_half_chars = std::max( static_cast( 2 * size.cx / text_size.cx ), std::size_t(8) ) - 8; const std::size_t num_rows = size.cy / text_size.cy; // Spaces between pattern components are half width, full space at channel end @@ -1428,7 +1449,9 @@ static BOOL WINAPI VisRenderDC( HDC dc, SIZE size, DWORD flags ) { if ( !visDC || last_pattern != pattern ) { DeleteBitmap( visbitmap ); - DeleteDC( visDC ); + if ( visDC ) { + DeleteDC( visDC ); + } visDC = CreateCompatibleDC( dc ); visbitmap = CreateCompatibleBitmap( dc, pattern_width, pattern_height ); @@ -1565,13 +1588,13 @@ static BOOL WINAPI VisRenderDC( HDC dc, SIZE size, DWORD flags ) { if ( offset_x < 0 ) { src_offset_x -= offset_x; - pattern_width = std::min( pattern_width + offset_x, size.cx ); + pattern_width = std::min( static_cast( pattern_width + offset_x ), static_cast( size.cx ) ); offset_x = 0; } if ( offset_y < 0 ) { src_offset_y -= offset_y; - pattern_height = std::min( pattern_height + offset_y, size.cy ); + pattern_height = std::min( static_cast( pattern_height + offset_y ), static_cast( size.cy ) ); offset_y = 0; } @@ -1589,7 +1612,7 @@ static BOOL WINAPI VisRenderDC( HDC dc, SIZE size, DWORD flags ) { return TRUE; } -static void WINAPI VisButton(DWORD x, DWORD y) { +static void WINAPI VisButton( DWORD /* x */ , DWORD /* y */ ) { //xmpopenmpt_lock guard; } @@ -1643,7 +1666,14 @@ static char * file_formats; static void xmp_openmpt_on_dll_load() { ZeroMemory( &xmpopenmpt_mutex, sizeof( xmpopenmpt_mutex ) ); - InitializeCriticalSection( &xmpopenmpt_mutex ); + #if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:28125) // The function 'InitializeCriticalSection' must be called from within a try/except block: The requirement might be conditional. + #endif // _MSC_VER + InitializeCriticalSection( &xmpopenmpt_mutex ); + #if defined(_MSC_VER) + #pragma warning(pop) + #endif // _MSC_VER std::vector extensions = openmpt::get_supported_extensions(); std::string filetypes_string = "OpenMPT"; filetypes_string.push_back('\0'); diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/.clang-format b/Frameworks/OpenMPT/OpenMPT/openmpt123/.clang-format new file mode 100644 index 000000000..a50eb55a0 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/.clang-format @@ -0,0 +1,45 @@ +Language: Cpp +AccessModifierOffset: -2 +AlignEscapedNewlines: DontAlign +AllowShortFunctionsOnASingleLine: Empty +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: true +BreakConstructorInitializersBeforeComma: true +BreakStringLiterals: false +ColumnLimit: 0 +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +IndentCaseLabels: true +IndentWidth: 2 +MaxEmptyLinesToKeep: 3 +PointerAlignment: Middle +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: false +SpaceBeforeParens: ControlStatements +SpacesBeforeTrailingComments: 2 +SpacesInContainerLiterals: false +SpacesInParentheses: true +SpacesInSquareBrackets: true +Standard: Cpp11 +TabWidth: 2 +UseTab: ForIndentation diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp index b5b4b516d..faae040d1 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.cpp @@ -8,7 +8,7 @@ */ static const char * const license = -"Copyright (c) 2004-2019, OpenMPT contributors" "\n" +"Copyright (c) 2004-2020, OpenMPT contributors" "\n" "Copyright (c) 1997-2003, Olivier Lapicque" "\n" "All rights reserved." "\n" "" "\n" @@ -44,13 +44,16 @@ static const char * const license = #include #include #include +#include #include #include #include #include +#include #include #include +#include #include #include #include @@ -60,6 +63,7 @@ static const char * const license = #if defined(__DJGPP__) #include +#include #include #include #include @@ -76,9 +80,6 @@ static const char * const license = #include #include #else -#if defined(MPT_NEEDS_THREADS) -#include -#endif #include #include #include @@ -99,7 +100,6 @@ static const char * const license = #include "openmpt123_allegro42.hpp" #include "openmpt123_portaudio.hpp" #include "openmpt123_pulseaudio.hpp" -#include "openmpt123_sdl.hpp" #include "openmpt123_sdl2.hpp" #include "openmpt123_waveout.hpp" @@ -129,12 +129,33 @@ struct show_version_number_exception : public std::exception { struct show_long_version_number_exception : public std::exception { }; +#if defined( WIN32 ) +bool IsConsole( DWORD stdHandle ) { + HANDLE hStd = GetStdHandle( stdHandle ); + if ( ( hStd != NULL ) && ( hStd != INVALID_HANDLE_VALUE ) ) { + DWORD mode = 0; + if ( GetConsoleMode( hStd, &mode ) != FALSE ) { + return true; + } + } + return false; +} +#endif + bool IsTerminal( int fd ) { #if defined( WIN32 ) - return true - && ( _isatty( fd ) ? true : false ) - && GetConsoleWindow() != NULL - ; + if ( !_isatty( fd ) ) { + return false; + } + DWORD stdHandle = 0; + if ( fd == 0 ) { + stdHandle = STD_INPUT_HANDLE; + } else if ( fd == 1 ) { + stdHandle = STD_OUTPUT_HANDLE; + } else if ( fd == 2 ) { + stdHandle = STD_ERROR_HANDLE; + } + return IsConsole( stdHandle ); #else return isatty( fd ) ? true : false; #endif @@ -166,10 +187,10 @@ static void set_input_mode() { class file_audio_stream_raii : public file_audio_stream_base { private: - file_audio_stream_base * impl; + std::unique_ptr impl; public: file_audio_stream_raii( const commandlineflags & flags, const std::string & filename, std::ostream & log ) - : impl(0) + : impl(nullptr) { if ( !flags.force_overwrite ) { std::ifstream testfile( filename, std::ios::binary ); @@ -180,18 +201,18 @@ public: if ( false ) { // nothing } else if ( flags.output_extension == "raw" ) { - impl = new raw_stream_raii( filename, flags, log ); + impl = std::make_unique( filename, flags, log ); #ifdef MPT_WITH_MMIO } else if ( flags.output_extension == "wav" ) { - impl = new mmio_stream_raii( filename, flags, log ); + impl = std::make_unique( filename, flags, log ); #endif #ifdef MPT_WITH_FLAC } else if ( flags.output_extension == "flac" ) { - impl = new flac_stream_raii( filename, flags, log ); + impl = std::make_unique( filename, flags, log ); #endif #ifdef MPT_WITH_SNDFILE } else { - impl = new sndfile_stream_raii( filename, flags, log ); + impl = std::make_unique( filename, flags, log ); #endif } if ( !impl ) { @@ -199,10 +220,7 @@ public: } } virtual ~file_audio_stream_raii() { - if ( impl ) { - delete impl; - impl = 0; - } + return; } void write_metadata( std::map metadata ) override { impl->write_metadata( metadata ); @@ -441,7 +459,7 @@ static std::string seconds_to_string( double time ) { static void show_info( std::ostream & log, bool verbose ) { log << "openmpt123" << " v" << OPENMPT123_VERSION_STRING << ", libopenmpt " << openmpt::string::get( "library_version" ) << " (" << "OpenMPT " << openmpt::string::get( "core_version" ) << ")" << std::endl; - log << "Copyright (c) 2013-2019 OpenMPT developers " << std::endl; + log << "Copyright (c) 2013-2020 OpenMPT developers " << std::endl; if ( !verbose ) { log << std::endl; return; @@ -496,18 +514,6 @@ static void show_info( std::ostream & log, bool verbose ) { log << "API: " << static_cast( sdlver.major ) << "." << static_cast( sdlver.minor ) << "." << static_cast( sdlver.patch ) << ""; log << " " << std::endl; #endif -#ifdef MPT_WITH_SDL - const SDL_version * linked_sdlver = SDL_Linked_Version(); - log << " libSDL "; - if ( linked_sdlver ) { - log << static_cast( linked_sdlver->major ) << "." << static_cast( linked_sdlver->minor ) << "." << static_cast( linked_sdlver->patch ) << " "; - } - SDL_version sdlver; - std::memset( &sdlver, 0, sizeof( SDL_version ) ); - SDL_VERSION( &sdlver ); - log << "(API: " << static_cast( sdlver.major ) << "." << static_cast( sdlver.minor ) << "." << static_cast( sdlver.patch ) << ")"; - log << " " << std::endl; -#endif #ifdef MPT_WITH_PULSEAUDIO log << " " << "libpulse, libpulse-simple" << " (headers " << pa_get_headers_version() << ", API " << PA_API_VERSION << ", PROTOCOL " << PA_PROTOCOL_VERSION << ", library " << ( pa_get_library_version() ? pa_get_library_version() : "unknown" ) << ") " << std::endl; #endif @@ -530,7 +536,7 @@ static void show_info( std::ostream & log, bool verbose ) { static void show_man_version( textout & log ) { log << "openmpt123" << " v" << OPENMPT123_VERSION_STRING << std::endl; log << std::endl; - log << "Copyright (c) 2013-2019 OpenMPT developers " << std::endl; + log << "Copyright (c) 2013-2020 OpenMPT developers " << std::endl; } static void show_short_version( textout & log ) { @@ -711,33 +717,6 @@ static void show_help_keyboard( textout & log ) { } -template < typename T, typename Tmod > -T ctl_get( Tmod & mod, const std::string & ctl ) { - T result = T(); - try { - std::istringstream str; - str.imbue( std::locale::classic() ); - str.str( mod.ctl_get( ctl ) ); - str >> std::fixed >> std::setprecision(16) >> result; - } catch ( const openmpt::exception & ) { - // ignore - } - return result; -} - -template < typename T, typename Tmod > -void ctl_set( Tmod & mod, const std::string & ctl, const T & val ) { - try { - std::ostringstream str; - str.imbue( std::locale::classic() ); - str << std::fixed << std::setprecision(16) << val; - mod.ctl_set( ctl, str.str() ); - } catch ( const openmpt::exception & ) { - // ignore - } - return; -} - template < typename Tmod > static void apply_mod_settings( commandlineflags & flags, Tmod & mod ) { flags.separation = std::max( flags.separation, std::int32_t( 0 ) ); @@ -753,12 +732,17 @@ static void apply_mod_settings( commandlineflags & flags, Tmod & mod ) { mod.set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, flags.separation ); mod.set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, flags.filtertaps ); mod.set_render_param( openmpt::module::RENDER_VOLUMERAMPING_STRENGTH, flags.ramping ); - ctl_set( mod, "play.tempo_factor", tempo_flag_to_double( flags.tempo ) ); - ctl_set( mod, "play.pitch_factor", pitch_flag_to_double( flags.pitch ) ); - std::ostringstream dither_str; - dither_str.imbue( std::locale::classic() ); - dither_str << flags.dither; - mod.ctl_set( "dither", dither_str.str() ); + try { + mod.ctl_set_floatingpoint( "play.tempo_factor", tempo_flag_to_double( flags.tempo ) ); + } catch ( const openmpt::exception & ) { + // ignore + } + try { + mod.ctl_set_floatingpoint( "play.pitch_factor", pitch_flag_to_double( flags.pitch ) ); + } catch ( const openmpt::exception & ) { + // ignore + } + mod.ctl_set_integer( "dither", flags.dither ); } struct prev_file { int count; prev_file( int c ) : count(c) { } }; @@ -1036,9 +1020,9 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto log.writeout(); std::size_t bufsize; - if ( flags.mode == ModeUI ) { + if ( flags.mode == Mode::UI ) { bufsize = std::min( flags.ui_redraw_interval, flags.period ) * flags.samplerate / 1000; - } else if ( flags.mode == ModeBatch ) { + } else if ( flags.mode == Mode::Batch ) { bufsize = flags.period * flags.samplerate / 1000; } else { bufsize = 1024; @@ -1101,28 +1085,11 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto log.writeout(); -#if defined( WIN32 ) - HANDLE hStdErr = NULL; - COORD coord_cursor = COORD(); - if ( multiline ) { - log.flush(); - hStdErr = GetStdHandle( STD_ERROR_HANDLE ); - if ( hStdErr ) { - CONSOLE_SCREEN_BUFFER_INFO csbi; - ZeroMemory( &csbi, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) ); - GetConsoleScreenBufferInfo( hStdErr, &csbi ); - coord_cursor = csbi.dwCursorPosition; - coord_cursor.X = 1; - coord_cursor.Y -= lines; - } - } -#endif - double cpu_smooth = 0.0; while ( true ) { - if ( flags.mode == ModeUI ) { + if ( flags.mode == Mode::UI ) { #if defined( __DJGPP__ ) @@ -1133,6 +1100,15 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto } } +#elif defined( WIN32 ) && defined( UNICODE ) + + while ( _kbhit() ) { + wint_t c = _getwch(); + if ( !handle_keypress( c, flags, mod, audio_stream ) ) { + return; + } + } + #elif defined( WIN32 ) while ( _kbhit() ) { @@ -1215,16 +1191,7 @@ void render_loop( commandlineflags & flags, Tmod & mod, double & duration, texto } if ( multiline ) { -#if defined( WIN32 ) - log.flush(); - if ( hStdErr ) { - SetConsoleCursorPosition( hStdErr, coord_cursor ); - } -#else - for ( int line = 0; line < lines; ++line ) { - log << "\x1b[1A"; - } -#endif + log.cursor_up( lines ); log << std::endl; if ( flags.show_meters ) { draw_meters( log, meter, flags ); @@ -1506,7 +1473,7 @@ void render_mod_file( commandlineflags & flags, const std::string & filename, st log.writeout(); - if ( flags.mode != ModeProbe && flags.mode != ModeInfo ) { + if ( flags.mode != Mode::Probe && flags.mode != Mode::Info ) { mod.set_repeat_count( flags.repeatcount ); apply_mod_settings( flags, mod ); } @@ -1576,13 +1543,13 @@ void render_mod_file( commandlineflags & flags, const std::string & filename, st log.writeout(); - if ( flags.filenames.size() == 1 || flags.mode == ModeRender ) { + if ( flags.filenames.size() == 1 || flags.mode == Mode::Render ) { audio_stream.write_metadata( get_metadata( mod ) ); } else { audio_stream.write_updated_metadata( get_metadata( mod ) ); } - if ( flags.mode == ModeProbe || flags.mode == ModeInfo ) { + if ( flags.mode == Mode::Probe || flags.mode == Mode::Info ) { return; } @@ -2018,15 +1985,15 @@ static commandlineflags parse_openmpt123( const std::vector & args, } else if ( arg == "--license" ) { throw show_license_exception(); } else if ( arg == "--probe" ) { - flags.mode = ModeProbe; + flags.mode = Mode::Probe; } else if ( arg == "--info" ) { - flags.mode = ModeInfo; + flags.mode = Mode::Info; } else if ( arg == "--ui" ) { - flags.mode = ModeUI; + flags.mode = Mode::UI; } else if ( arg == "--batch" ) { - flags.mode = ModeBatch; + flags.mode = Mode::Batch; } else if ( arg == "--render" ) { - flags.mode = ModeRender; + flags.mode = Mode::Render; } else if ( arg == "--terminal-width" && nextarg != "" ) { std::istringstream istr( nextarg ); istr >> flags.terminal_width; @@ -2072,9 +2039,6 @@ static commandlineflags parse_openmpt123( const std::vector & args, #if defined( MPT_WITH_SDL2 ) drivers << " " << "sdl2" << std::endl; #endif -#if defined( MPT_WITH_SDL ) - drivers << " " << "sdl" << std::endl; -#endif #if defined( MPT_WITH_PORTAUDIO ) drivers << " " << "portaudio" << std::endl; #endif @@ -2245,21 +2209,33 @@ static commandlineflags parse_openmpt123( const std::vector & args, #if defined(WIN32) -class ConsoleCP_utf8_raii { +class FD_utf8_raii { private: - const UINT oldCP; - const UINT oldOutputCP; + FILE * file; + int old_mode; public: - ConsoleCP_utf8_raii() - : oldCP(GetConsoleCP()) - , oldOutputCP(GetConsoleOutputCP()) + FD_utf8_raii( FILE * file, bool set_utf8 ) + : file(file) + , old_mode(-1) { - SetConsoleCP( 65001 ); // UTF-8 - SetConsoleOutputCP( 65001 ); // UTF-8 + if ( set_utf8 ) { + fflush( file ); + #if defined(UNICODE) + old_mode = _setmode( _fileno( file ), _O_U8TEXT ); + #else + old_mode = _setmode( _fileno( file ), _O_TEXT ); + #endif + if ( old_mode == -1 ) { + throw exception( "failed to set TEXT mode on file descriptor" ); + } + } } - ~ConsoleCP_utf8_raii() { - SetConsoleCP( oldCP ); - SetConsoleOutputCP( oldOutputCP ); + ~FD_utf8_raii() + { + if ( old_mode != -1 ) { + fflush( file ); + old_mode = _setmode( _fileno( file ), old_mode ); + } } }; @@ -2268,7 +2244,7 @@ private: FILE * file; int old_mode; public: - FD_binary_raii(FILE * file, bool set_binary) + FD_binary_raii( FILE * file, bool set_binary ) : file(file) , old_mode(-1) { @@ -2306,12 +2282,14 @@ static int main( int argc, char * argv [] ) { #endif #if defined(WIN32) - ConsoleCP_utf8_raii console_cp; + FD_utf8_raii stdin_utf8_guard( stdin, true ); + FD_utf8_raii stdout_utf8_guard( stdout, true ); + FD_utf8_raii stderr_utf8_guard( stderr, true ); #endif textout_dummy dummy_log; #if defined(WIN32) - textout_console std_out( GetStdHandle( STD_OUTPUT_HANDLE ) ); - textout_console std_err( GetStdHandle( STD_ERROR_HANDLE ) ); + textout_ostream_console std_out( std::wcout, STD_OUTPUT_HANDLE ); + textout_ostream_console std_err( std::wclog, STD_ERROR_HANDLE ); #else textout_ostream std_out( std::cout ); textout_ostream std_err( std::clog ); @@ -2398,16 +2376,16 @@ static int main( int argc, char * argv [] ) { // setup terminal #if !defined(WIN32) if ( stdin_can_ui ) { - if ( flags.mode == ModeUI ) { + if ( flags.mode == Mode::UI ) { set_input_mode(); } } #endif - textout & log = flags.quiet ? *static_cast( &dummy_log ) : *static_cast( stdout_can_ui ? &std_out : &std_err ); + textout & log = flags.quiet ? static_cast( dummy_log ) : static_cast( stdout_can_ui ? std_out : std_err ); show_info( log, flags.verbose ); - + if ( !flags.warnings.empty() ) { log << flags.warnings << std::endl; } @@ -2418,24 +2396,30 @@ static int main( int argc, char * argv [] ) { log.writeout(); - std::random_device rd; - std::seed_seq seq{ rd(), static_cast( std::time( NULL ) ) }; - std::default_random_engine prng( seq ); + std::default_random_engine prng; + try { + std::random_device rd; + std::seed_seq seq{ rd(), static_cast( std::time( NULL ) ) }; + prng = std::default_random_engine{ seq }; + } catch ( const std::exception & ) { + std::seed_seq seq{ static_cast( std::time( NULL ) ) }; + prng = std::default_random_engine{ seq }; + } std::srand( std::uniform_int_distribution()( prng ) ); switch ( flags.mode ) { - case ModeProbe: { + case Mode::Probe: { for ( const auto & filename : flags.filenames ) { probe_file( flags, filename, log ); flags.playlist_index++; } } break; - case ModeInfo: { + case Mode::Info: { void_audio_stream dummy; render_files( flags, log, dummy, prng ); } break; - case ModeUI: - case ModeBatch: { + case Mode::UI: + case Mode::Batch: { if ( flags.use_stdout ) { flags.apply_default_buffer_sizes(); stdout_stream_raii stdout_audio_stream; @@ -2454,11 +2438,6 @@ static int main( int argc, char * argv [] ) { sdl2_stream_raii sdl2_stream( flags, log ); render_files( flags, log, sdl2_stream, prng ); #endif -#if defined( MPT_WITH_SDL ) - } else if ( flags.driver == "sdl" || flags.driver.empty() ) { - sdl_stream_raii sdl_stream( flags, log ); - render_files( flags, log, sdl_stream, prng ); -#endif #if defined( MPT_WITH_PORTAUDIO ) } else if ( flags.driver == "portaudio" || flags.driver.empty() ) { portaudio_stream_raii portaudio_stream( flags, log ); @@ -2482,7 +2461,7 @@ static int main( int argc, char * argv [] ) { } } } break; - case ModeRender: { + case Mode::Render: { for ( const auto & filename : flags.filenames ) { flags.apply_default_buffer_sizes(); file_audio_stream_raii file_audio_stream( flags, filename + std::string(".") + flags.output_extension, log ); @@ -2490,7 +2469,7 @@ static int main( int argc, char * argv [] ) { flags.playlist_index++; } } break; - case ModeNone: + case Mode::None: break; } @@ -2515,12 +2494,6 @@ static int main( int argc, char * argv [] ) { std_err.writeout(); return 1; #endif -#ifdef MPT_WITH_SDL - } catch ( sdl_exception & e ) { - std_err << "SDL error: " << e.what() << std::endl; - std_err.writeout(); - return 1; -#endif #ifdef MPT_WITH_SDL2 } catch ( sdl2_exception & e ) { std_err << "SDL2 error: " << e.what() << std::endl; diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.hpp index 89104c9e4..89a28bf67 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123.hpp @@ -31,84 +31,743 @@ struct args_error_exception { struct show_help_keyboard_exception { }; #if defined(WIN32) +bool IsConsole( DWORD stdHandle ); +#endif +bool IsTerminal( int fd ); -std::string wstring_to_utf8( const std::wstring & unicode_string ) { - int required_size = WideCharToMultiByte( CP_UTF8, 0, unicode_string.c_str(), -1, NULL, 0, NULL, NULL ); - if ( required_size <= 0 ) { - return std::string(); - } - std::vector utf8_buf( required_size ); - WideCharToMultiByte( CP_UTF8, 0, unicode_string.c_str(), -1, &utf8_buf[0], required_size, NULL, NULL ); - return &utf8_buf[0]; -} -std::wstring utf8_to_wstring( const std::string & utf8_string ) { - int required_size = MultiByteToWideChar( CP_UTF8, 0, utf8_string.c_str(), -1, NULL, 0 ); - if ( required_size <= 0 ) { - return std::wstring(); - } - std::vector unicode_buf( required_size ); - MultiByteToWideChar( CP_UTF8, 0, utf8_string.data(), -1, &unicode_buf[0], required_size ); - return &unicode_buf[0]; -} - -std::string wstring_to_locale( const std::wstring & unicode_string ) { - int required_size = WideCharToMultiByte( CP_ACP, 0, unicode_string.c_str(), -1, NULL, 0, NULL, NULL ); - if ( required_size <= 0 ) { - return std::string(); - } - std::vector locale_buf( required_size ); - WideCharToMultiByte( CP_ACP, 0, unicode_string.c_str(), -1, &locale_buf[0], required_size, NULL, NULL ); - return &locale_buf[0]; -} - -std::wstring locale_to_wstring( const std::string & locale_string ) { - int required_size = MultiByteToWideChar( CP_ACP, 0, locale_string.c_str(), -1, NULL, 0 ); - if ( required_size <= 0 ) { - return std::wstring(); - } - std::vector unicode_buf( required_size ); - MultiByteToWideChar( CP_ACP, 0, locale_string.data(), -1, &unicode_buf[0], required_size ); - return &unicode_buf[0]; -} - -#endif // WIN32 - -#if defined(MPT_NEEDS_THREADS) #if defined(WIN32) -class mutex { -private: - CRITICAL_SECTION impl; -public: - mutex() { InitializeCriticalSection(&impl); } - ~mutex() { DeleteCriticalSection(&impl); } - void lock() { EnterCriticalSection(&impl); } - void unlock() { LeaveCriticalSection(&impl); } -}; -#else -class mutex { -private: - pthread_mutex_t impl; -public: - mutex() { - pthread_mutexattr_t attr; - pthread_mutexattr_init( &attr ); - pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_NORMAL ); - pthread_mutex_init( &impl, &attr ); - pthread_mutexattr_destroy( &attr ); +static inline std::string wstring_to_utf8( const std::wstring & unicode_string ) { + std::string utf8_string; + int source_length = ( unicode_string.size() < static_cast( std::numeric_limits::max() ) ) ? static_cast( unicode_string.size() ) : std::numeric_limits::max(); + int required_size = WideCharToMultiByte( CP_UTF8, 0, unicode_string.data(), source_length, NULL, 0, NULL, NULL ); + if ( required_size > 0 ) { + utf8_string.resize( required_size ); + WideCharToMultiByte( CP_UTF8, 0, unicode_string.data(), source_length, utf8_string.data(), required_size, NULL, NULL ); } - ~mutex() { pthread_mutex_destroy( &impl ); } - void lock() { pthread_mutex_lock( &impl ); } - void unlock() { pthread_mutex_unlock( &impl ); } + return utf8_string; +} + +static inline std::wstring utf8_to_wstring( const std::string & utf8_string ) { + std::wstring unicode_string; + int source_length = ( utf8_string.size() < static_cast( std::numeric_limits::max() ) ) ? static_cast( utf8_string.size() ) : std::numeric_limits::max(); + int required_size = MultiByteToWideChar( CP_UTF8, 0, utf8_string.data(), source_length, NULL, 0 ); + if ( required_size > 0 ) { + unicode_string.resize( required_size ); + MultiByteToWideChar( CP_UTF8, 0, utf8_string.data(), source_length, unicode_string.data(), required_size ); + } + return unicode_string; +} + +static inline std::string wstring_to_locale( const std::wstring & unicode_string ) { + std::string locale_string; + int source_length = ( unicode_string.size() < static_cast( std::numeric_limits::max() ) ) ? static_cast( unicode_string.size() ) : std::numeric_limits::max(); + int required_size = WideCharToMultiByte( CP_ACP, 0, unicode_string.data(), source_length, NULL, 0, NULL, NULL ); + if ( required_size > 0 ) { + locale_string.resize( required_size ); + WideCharToMultiByte( CP_ACP, 0, unicode_string.data(), source_length, locale_string.data(), required_size, NULL, NULL ); + } + return locale_string; +} + +static inline std::wstring locale_to_wstring( const std::string & locale_string ) { + std::wstring unicode_string; + int source_length = ( locale_string.size() < static_cast( std::numeric_limits::max() ) ) ? static_cast( locale_string.size() ) : std::numeric_limits::max(); + int required_size = MultiByteToWideChar( CP_ACP, 0, locale_string.data(), source_length, NULL, 0 ); + if ( required_size > 0 ) { + unicode_string.resize( required_size ); + MultiByteToWideChar( CP_ACP, 0, locale_string.data(), source_length, unicode_string.data(), required_size ); + } + return unicode_string; +} + + + +#endif + + + +/* +default 1:1 mapping +static constexpr char32_t 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 +}; +*/ + +static constexpr char32_t 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 }; -#endif +static constexpr char32_t 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 constexpr char32_t 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 +}; + +static constexpr char32_t CharsetTableCP850[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,0x00F8,0x00a3,0x00D8,0x00D7,0x0192, + 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x00AE,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb, + 0x2591,0x2592,0x2593,0x2502,0x2524,0x00C1,0x00C2,0x00C0,0x00A9,0x2563,0x2551,0x2557,0x255d,0x00A2,0x00A5,0x2510, + 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x00E3,0x00C3,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x00A4, + 0x00F0,0x00D0,0x00CA,0x00CB,0x00C8,0x0131,0x00CD,0x00CE,0x00CF,0x2518,0x250c,0x2588,0x2584,0x00A6,0x00CC,0x2580, + 0x00D3,0x00df,0x00D4,0x00D2,0x00F5,0x00D5,0x00b5,0x00FE,0x00DE,0x00DA,0x00DB,0x00D9,0x00FD,0x00DD,0x00AF,0x00B4, + 0x00AD,0x00b1,0x2017,0x00BE,0x00B6,0x00A7,0x00f7,0x00B8,0x00b0,0x00A8,0x00b7,0x00B9,0x00B3,0x00b2,0x25a0,0x00a0 +}; + + +#if defined(__DJGPP__) +using widechar = char32_t; +using widestring = std::u32string; +static constexpr widechar wide_default_replacement = 0xFFFD; +#else // !__DJGPP__ +using widechar = wchar_t; +using widestring = std::wstring; +static constexpr widechar wide_default_replacement = L'\uFFFD'; +#endif // !__DJGPP__ + + +static inline widestring From8bit(const std::string &str, const char32_t (&table)[256], widechar replacement = wide_default_replacement) +{ + widestring res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + std::size_t c = static_cast(static_cast(str[i])); + if(c < std::size(table)) + { + res.push_back(static_cast(table[c])); + } else + { + res.push_back(replacement); + } + } + return res; +} + +static inline std::string To8bit(const widestring &str, const char32_t (&table)[256], char replacement = '?') +{ + std::string res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + char32_t c = static_cast(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 < std::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 < std::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; +} + +static inline widestring FromAscii(const std::string &str, widechar replacement = wide_default_replacement) +{ + widestring res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + std::uint8_t c = str[i]; + if(c <= 0x7f) + { + res.push_back(static_cast(static_cast(c))); + } else + { + res.push_back(replacement); + } + } + return res; +} + +static inline std::string ToAscii(const widestring &str, char replacement = '?') +{ + std::string res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + char32_t c = static_cast(str[i]); + if(c <= 0x7f) + { + res.push_back(static_cast(static_cast(c))); + } else + { + res.push_back(replacement); + } + } + return res; +} + +static inline widestring FromISO_8859_1(const std::string &str, widechar replacement = wide_default_replacement) +{ + static_cast( replacement ); + widestring res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + std::uint8_t c = str[i]; + res.push_back(static_cast(static_cast(c))); + } + return res; +} + +static inline std::string ToISO_8859_1(const widestring &str, char replacement = '?') +{ + std::string res; + res.reserve(str.length()); + for(std::size_t i = 0; i < str.length(); ++i) + { + char32_t c = static_cast(str[i]); + if(c <= 0xff) + { + res.push_back(static_cast(static_cast(c))); + } else + { + res.push_back(replacement); + } + } + return res; +} + + +#if !defined(__DJGPP__) && !defined(__EMSCRIPTEN__) + +// Note: +// +// std::codecvt::out in LLVM libc++ does not advance in and out pointers when +// running into a non-convertible character. 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 out, 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 inline 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; + using codecvt_type = std::codecvt; + 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 inline 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; + using codecvt_type = std::codecvt; + 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 inline std::wstring FromLocaleCpp(const std::string &str, wchar_t replacement) +{ + try + { + std::locale locale(""); // user locale + return LocaleDecode(str, locale, replacement); + } catch ( const std::bad_alloc & ) + { + throw; + } catch(...) + { + // nothing + } + try + { + std::locale locale; // current c++ locale + return LocaleDecode(str, locale, replacement); + } catch ( const std::bad_alloc & ) + { + throw; + } catch(...) + { + // nothing + } + try + { + std::locale locale = std::locale::classic(); // "C" locale + return LocaleDecode(str, locale, replacement); + } catch ( const std::bad_alloc & ) + { + throw; + } catch(...) + { + // nothing + } + assert(0); + return FromAscii(str, replacement); // fallback +} + +static inline std::string ToLocaleCpp(const std::wstring &str, char replacement) +{ + try + { + std::locale locale(""); // user locale + return LocaleEncode(str, locale, replacement); + } catch ( const std::bad_alloc & ) + { + throw; + } catch(...) + { + // nothing + } + try + { + std::locale locale; // current c++ locale + return LocaleEncode(str, locale, replacement); + } catch ( const std::bad_alloc & ) + { + throw; + } catch(...) + { + // nothing + } + try + { + std::locale locale = std::locale::classic(); // "C" locale + return LocaleEncode(str, locale, replacement); + } catch ( const std::bad_alloc & ) + { + throw; + } catch(...) + { + // nothing + } + assert(0); + return ToAscii(str, replacement); // fallback +} + + +static inline std::wstring FromLocale(const std::string &str, wchar_t replacement = L'\uFFFD') +{ + return FromLocaleCpp(str, replacement); +} + +static inline std::string ToLocale(const std::wstring &str, char replacement = '?') +{ + return ToLocaleCpp(str, replacement); +} + + + +#endif // !__DJGPP__ && !__EMSCRIPTEN + + + +static inline widestring FromUTF8(const std::string &str, widechar replacement = wide_default_replacement) +{ + const std::string &in = str; + + widestring out; + + // state: + std::size_t charsleft = 0; + char32_t ucs4 = 0; + + for ( std::uint8_t 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 ) { + if constexpr ( sizeof( widechar ) == 2 ) { + if ( ucs4 > 0x1fffff ) { + out.push_back( replacement ); + ucs4 = 0; + charsleft = 0; + } + if ( ucs4 <= 0xffff ) { + out.push_back( static_cast(ucs4) ); + } else { + std::uint32_t surrogate = static_cast(ucs4) - 0x10000; + std::uint16_t hi_sur = static_cast( ( 0x36 << 10 ) | ( (surrogate>>10) & ((1<<10)-1) ) ); + std::uint16_t 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 inline std::string ToUTF8(const widestring &str, char replacement = '?') +{ + const widestring &in = str; + + std::string out; + + for ( std::size_t i=0; i( wc ); + if ( i + 1 < in.length() ) { + // check for surrogate pair + std::uint16_t hi_sur = in[i+0]; + std::uint16_t 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; + } + + std::uint8_t 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<( lines ); } }; @@ -145,55 +809,140 @@ public: return; } public: - void write( const std::string & /* text */ ) override { - return; + void writeout() override { + static_cast( pop() ); } }; class textout_ostream : public textout { private: std::ostream & s; +#if defined(__DJGPP__) + std::uint16_t active_codepage; + std::uint16_t system_codepage; +#endif public: textout_ostream( std::ostream & s_ ) : s(s_) +#if defined(__DJGPP__) + , active_codepage(437) + , system_codepage(437) +#endif { + #if defined(__DJGPP__) + __dpmi_regs regs; + std::memset( ®s, 0, sizeof( __dpmi_regs ) ); + regs.x.ax = 0x6601; + if ( __dpmi_int( 0x21, ®s ) == 0 ) { + int cf = ( regs.x.flags >> 0 ) & 1; + if ( cf == 0 ) { + active_codepage = regs.x.bx; + system_codepage = regs.x.dx; + } + } + #endif return; } virtual ~textout_ostream() { - writeout(); + writeout_impl(); + } +private: + void writeout_impl() { + std::string text = pop(); + if ( text.length() > 0 ) { + #if defined(__DJGPP__) + if ( active_codepage == 0 ) { + s << To8bit( FromUTF8( text ), CharsetTableCP437 ); + } else if ( active_codepage == 437 ) { + s << To8bit( FromUTF8( text ), CharsetTableCP437 ); + } else if ( active_codepage == 850 ) { + s << To8bit( FromUTF8( text ), CharsetTableCP850 ); + } else if ( system_codepage == 437 ) { + s << To8bit( FromUTF8( text ), CharsetTableCP437 ); + } else if ( system_codepage == 850 ) { + s << To8bit( FromUTF8( text ), CharsetTableCP850 ); + } else { + s << To8bit( FromUTF8( text ), CharsetTableCP437 ); + } + #elif defined(__EMSCRIPTEN__) + s << text; + #elif defined(WIN32) + s << ToLocale( FromUTF8( text ) ); + #else + s << ToLocale( FromUTF8( text ) ); + #endif + s.flush(); + } } public: - void write( const std::string & text ) override { - s << text; - } void writeout() override { - textout::writeout(); + writeout_impl(); + } + void cursor_up( std::size_t lines ) override { s.flush(); + for ( std::size_t line = 0; line < lines; ++line ) { + *this << "\x1b[1A"; + } } }; #if defined(WIN32) -class textout_console : public textout { +class textout_ostream_console : public textout { private: + std::wostream & s; HANDLE handle; + bool console; public: - textout_console( HANDLE handle_ ) - : handle(handle_) + textout_ostream_console( std::wostream & s_, DWORD stdHandle_ ) + : s(s_) + , handle(GetStdHandle( stdHandle_ )) + , console(IsConsole( stdHandle_ )) { return; } - virtual ~textout_console() { - writeout(); + virtual ~textout_ostream_console() { + writeout_impl(); + } +private: + void writeout_impl() { + std::string text = pop(); + if ( text.length() > 0 ) { + if ( console ) { + #if defined(UNICODE) + std::wstring wtext = utf8_to_wstring( text ); + WriteConsole( handle, wtext.data(), static_cast( wtext.size() ), NULL, NULL ); + #else + std::string ltext = wstring_to_locale( utf8_to_wstring( text ) ); + WriteConsole( handle, ltext.data(), static_cast( ltext.size() ), NULL, NULL ); + #endif + } else { + #if defined(UNICODE) + s << utf8_to_wstring( text ); + #else + s << wstring_to_locale( utf8_to_wstring( text ) ); + #endif + s.flush(); + } + } } public: - void write( const std::string & text ) override { - #if defined(UNICODE) - std::wstring wtext = utf8_to_wstring( text ); - WriteConsole( handle, wtext.data(), static_cast( wtext.size() ), NULL, NULL ); - #else - WriteConsole( handle, text.data(), static_cast( text.size() ), NULL, NULL ); - #endif + void writeout() override { + writeout_impl(); + } + void cursor_up( std::size_t lines ) override { + if ( console ) { + s.flush(); + CONSOLE_SCREEN_BUFFER_INFO csbi; + ZeroMemory( &csbi, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) ); + COORD coord_cursor = COORD(); + if ( GetConsoleScreenBufferInfo( handle, &csbi ) != FALSE ) { + coord_cursor = csbi.dwCursorPosition; + coord_cursor.X = 1; + coord_cursor.Y -= static_cast( lines ); + SetConsoleCursorPosition( handle, coord_cursor ); + } + } } }; @@ -232,25 +981,23 @@ static inline std::string get_extension( std::string filename ) { return ""; } -bool IsTerminal( int fd ); - -enum Mode { - ModeNone, - ModeProbe, - ModeInfo, - ModeUI, - ModeBatch, - ModeRender +enum class Mode { + None, + Probe, + Info, + UI, + Batch, + Render }; static inline std::string mode_to_string( Mode mode ) { switch ( mode ) { - case ModeNone: return "none"; break; - case ModeProbe: return "probe"; break; - case ModeInfo: return "info"; break; - case ModeUI: return "ui"; break; - case ModeBatch: return "batch"; break; - case ModeRender: return "render"; break; + case Mode::None: return "none"; break; + case Mode::Probe: return "probe"; break; + case Mode::Info: return "info"; break; + case Mode::UI: return "ui"; break; + case Mode::Batch: return "batch"; break; + case Mode::Render: return "render"; break; } return ""; } @@ -322,7 +1069,7 @@ struct commandlineflags { } } commandlineflags() { - mode = ModeUI; + mode = Mode::UI; ui_redraw_interval = default_high; driver = ""; device = ""; @@ -358,11 +1105,17 @@ struct commandlineflags { terminal_height = 23; #endif #if defined(WIN32) - CONSOLE_SCREEN_BUFFER_INFO csbi; - ZeroMemory( &csbi, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) ); - GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &csbi ); - terminal_width = csbi.dwSize.X - 1; - terminal_height = 23; //csbi.dwSize.Y - 1; + terminal_width = 72; + terminal_height = 23; + HANDLE hStdOutput = GetStdHandle( STD_OUTPUT_HANDLE ); + if ( ( hStdOutput != NULL ) && ( hStdOutput != INVALID_HANDLE_VALUE ) ) { + CONSOLE_SCREEN_BUFFER_INFO csbi; + ZeroMemory( &csbi, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) ); + if ( GetConsoleScreenBufferInfo( hStdOutput, &csbi ) != FALSE ) { + terminal_width = std::min( static_cast( 1 + csbi.srWindow.Right - csbi.srWindow.Left ), static_cast( csbi.dwSize.X ) ); + terminal_height = std::min( static_cast( 1 + csbi.srWindow.Bottom - csbi.srWindow.Top ), static_cast( csbi.dwSize.Y ) ); + } + } #else // WIN32 if ( isatty( STDERR_FILENO ) ) { if ( std::getenv( "COLUMNS" ) ) { @@ -435,45 +1188,45 @@ struct commandlineflags { } } show_ui = canUI; - if ( mode == ModeNone ) { + if ( mode == Mode::None ) { if ( canUI ) { - mode = ModeUI; + mode = Mode::UI; } else { - mode = ModeBatch; + mode = Mode::Batch; } } - if ( mode == ModeUI && !canUI ) { + if ( mode == Mode::UI && !canUI ) { throw args_error_exception(); } if ( show_progress && !canProgress ) { throw args_error_exception(); } switch ( mode ) { - case ModeNone: + case Mode::None: throw args_error_exception(); break; - case ModeProbe: + case Mode::Probe: show_ui = false; show_progress = false; show_meters = false; show_channel_meters = false; show_pattern = false; break; - case ModeInfo: + case Mode::Info: show_ui = false; show_progress = false; show_meters = false; show_channel_meters = false; show_pattern = false; break; - case ModeUI: + case Mode::UI: break; - case ModeBatch: + case Mode::Batch: show_meters = false; show_channel_meters = false; show_pattern = false; break; - case ModeRender: + case Mode::Render: show_meters = false; show_channel_meters = false; show_pattern = false; @@ -499,13 +1252,13 @@ struct commandlineflags { if ( output_extension == "auto" ) { output_extension = ""; } - if ( mode != ModeRender && !output_extension.empty() ) { + if ( mode != Mode::Render && !output_extension.empty() ) { throw args_error_exception(); } - if ( mode == ModeRender && !output_filename.empty() ) { + if ( mode == Mode::Render && !output_filename.empty() ) { throw args_error_exception(); } - if ( mode != ModeRender && !output_filename.empty() ) { + if ( mode != Mode::Render && !output_filename.empty() ) { output_extension = get_extension( output_filename ); } if ( output_extension.empty() ) { @@ -555,77 +1308,6 @@ public: } }; -class write_buffers_blocking_wrapper : public write_buffers_interface { -protected: - std::size_t channels; - std::size_t sampleQueueMaxFrames; - std::deque sampleQueue; -protected: - virtual ~write_buffers_blocking_wrapper() { - return; - } -protected: - write_buffers_blocking_wrapper( const commandlineflags & flags ) - : channels(flags.channels) - , sampleQueueMaxFrames(0) - { - return; - } - void set_queue_size_frames( std::size_t frames ) { - sampleQueueMaxFrames = frames; - } - template < typename Tsample > - Tsample pop_queue() { - float val = 0.0f; - if ( !sampleQueue.empty() ) { - val = sampleQueue.front(); - sampleQueue.pop_front(); - } - return convert_sample_to( val ); - } - template < typename Tsample > - void fill_buffer( Tsample * buf, std::size_t framesToRender ) { - for ( std::size_t frame = 0; frame < framesToRender; ++frame ) { - for ( std::size_t channel = 0; channel < channels; ++channel ) { - *buf = pop_queue(); - buf++; - } - } - } -private: - void wait_for_queue_space() { - while ( sampleQueue.size() >= sampleQueueMaxFrames * channels ) { - unlock(); - sleep( 1 ); - lock(); - } - } -public: - void write( const std::vector buffers, std::size_t frames ) override { - lock(); - for ( std::size_t frame = 0; frame < frames; ++frame ) { - for ( std::size_t channel = 0; channel < channels; ++channel ) { - wait_for_queue_space(); - sampleQueue.push_back( buffers[channel][frame] ); - } - } - unlock(); - } - void write( const std::vector buffers, std::size_t frames ) override { - lock(); - for ( std::size_t frame = 0; frame < frames; ++frame ) { - for ( std::size_t channel = 0; channel < channels; ++channel ) { - wait_for_queue_space(); - sampleQueue.push_back( buffers[channel][frame] * (1.0f/32768.0f) ); - } - } - unlock(); - } - virtual void lock() = 0; - virtual void unlock() = 0; - bool sleep( int ms ) override = 0; -}; - class write_buffers_polling_wrapper : public write_buffers_interface { protected: std::size_t channels; diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_config.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_config.hpp index 9558215ad..7cba28fbc 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_config.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_config.hpp @@ -10,6 +10,36 @@ #ifndef OPENMPT123_CONFIG_HPP #define OPENMPT123_CONFIG_HPP +#define MPT_PP_DEFER(m, ...) m(__VA_ARGS__) +#define MPT_PP_STRINGIFY(x) #x +#define MPT_PP_JOIN_HELPER(a, b) a ## b +#define MPT_PP_JOIN(a, b) MPT_PP_JOIN_HELPER(a, b) +#define MPT_PP_UNIQUE_IDENTIFIER(prefix) MPT_PP_JOIN(prefix , __LINE__) + +#if defined(__clang__) +#define MPT_WARNING(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text)) +#define MPT_WARNING_STATEMENT(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text)) +#elif defined(_MSC_VER) +#define MPT_WARNING(text) __pragma(message(__FILE__ "(" MPT_PP_DEFER(MPT_PP_STRINGIFY, __LINE__) "): Warning: " text)) +#define MPT_WARNING_STATEMENT(text) __pragma(message(__FILE__ "(" MPT_PP_DEFER(MPT_PP_STRINGIFY, __LINE__) "): Warning: " text)) +#elif defined(__GNUC__) +#define MPT_WARNING(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text)) +#define MPT_WARNING_STATEMENT(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text)) +#else +#define MPT_WARNING(text) \ + static inline int MPT_PP_UNIQUE_IDENTIFIER(MPT_WARNING_NAME) () noexcept { \ + int warning [[deprecated("Warning: " text)]] = 0; \ + return warning; \ + } \ +/**/ +#define MPT_WARNING_STATEMENT(text) \ + int MPT_PP_UNIQUE_IDENTIFIER(MPT_WARNING_NAME) = [](){ \ + int warning [[deprecated("Warning: " text)]] = 0; \ + return warning; \ + }() \ +/**/ +#endif + #if defined(HAVE_CONFIG_H) // wrapper for autoconf macros #include "config.h" @@ -40,12 +70,6 @@ #define MPT_WITH_MMIO #endif // WIN32 -#if defined(_MSC_VER) - -#pragma warning( disable : 4996 ) // 'foo': The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _foo. See online help for details. - -#endif // _MSC_VER - #if defined(MPT_BUILD_MSVC) #define MPT_WITH_FLAC @@ -57,16 +81,6 @@ #endif // MPT_BUILD_MSVC -#if defined(MPT_WITH_SDL) && defined(MPT_WITH_SDL2) -#error "MPT_WITH_SDL2 and MPT_WITH_SDL are mutually exclusive." -#endif - -#if defined(MPT_WITH_SDL) -#ifndef MPT_NEEDS_THREADS -#define MPT_NEEDS_THREADS -#endif -#endif - #define OPENMPT123_VERSION_STRING OPENMPT_API_VERSION_STRING #endif // OPENMPT123_CONFIG_HPP diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_flac.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_flac.hpp index bc5b75628..969c6530d 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_flac.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_flac.hpp @@ -21,9 +21,16 @@ typedef _off_t off_t; #endif #endif +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif #include #include #include +#if defined(__clang__) +#pragma clang diagnostic pop +#endif namespace openmpt123 { diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_mmio.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_mmio.hpp index cb44587bd..a7f0f31ad 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_mmio.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_mmio.hpp @@ -47,10 +47,10 @@ public: ZeroMemory( &waveformatex, sizeof( WAVEFORMATEX ) ); waveformatex.cbSize = 0; waveformatex.wFormatTag = flags.use_float ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM; - waveformatex.nChannels = flags.channels; + waveformatex.nChannels = static_cast( flags.channels ); waveformatex.nSamplesPerSec = flags.samplerate; waveformatex.wBitsPerSample = flags.use_float ? 32 : 16; - waveformatex.nBlockAlign = flags.channels * ( waveformatex.wBitsPerSample / 8 ); + waveformatex.nBlockAlign = static_cast( flags.channels * ( waveformatex.wBitsPerSample / 8 ) ); waveformatex.nAvgBytesPerSec = waveformatex.nSamplesPerSec * waveformatex.nBlockAlign; #if defined(WIN32) && defined(UNICODE) diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_portaudio.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_portaudio.hpp index 75065142e..4444c3c0c 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_portaudio.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_portaudio.hpp @@ -120,18 +120,18 @@ public: streamparameters.suggestedLatency = flags.buffer * 0.001; } unsigned long framesperbuffer = 0; - if ( flags.mode != ModeUI ) { + if ( flags.mode != Mode::UI ) { framesperbuffer = paFramesPerBufferUnspecified; flags.period = 50; - flags.period = std::min( flags.period, flags.buffer / 3 ); + flags.period = std::min( flags.period, flags.buffer / 3 ); } else if ( flags.period == default_high ) { framesperbuffer = paFramesPerBufferUnspecified; flags.period = 50; - flags.period = std::min( flags.period, flags.buffer / 3 ); + flags.period = std::min( flags.period, flags.buffer / 3 ); } else if ( flags.period == default_low ) { framesperbuffer = paFramesPerBufferUnspecified; flags.period = 10; - flags.period = std::min( flags.period, flags.buffer / 3 ); + flags.period = std::min( flags.period, flags.buffer / 3 ); } else { framesperbuffer = flags.period * flags.samplerate / 1000; } @@ -187,7 +187,7 @@ private: template void write_frames( const Tsample * buffer, std::size_t frames ) { while ( frames > 0 ) { - unsigned long chunk_frames = static_cast( std::min( frames, std::numeric_limits::max() ) ); + unsigned long chunk_frames = static_cast( std::min( static_cast( frames ), static_cast( std::numeric_limits::max() ) ) ); check_portaudio_error( Pa_WriteStream( stream, buffer, chunk_frames ) ); buffer += chunk_frames * channels; frames -= chunk_frames; @@ -196,7 +196,7 @@ private: template void write_frames( std::vector buffers, std::size_t frames ) { while ( frames > 0 ) { - unsigned long chunk_frames = static_cast( std::min( frames, std::numeric_limits::max() ) ); + unsigned long chunk_frames = static_cast( std::min( static_cast( frames ), static_cast( std::numeric_limits::max() ) ) ); check_portaudio_error( Pa_WriteStream( stream, buffers.data(), chunk_frames ) ); for ( std::size_t channel = 0; channel < channels; ++channel ) { buffers[channel] += chunk_frames; diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl.hpp deleted file mode 100644 index 55d64e04a..000000000 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl.hpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * openmpt123_sdl.hpp - * ------------------ - * Purpose: libopenmpt command line player - * Notes : (currently none) - * Authors: OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - -#ifndef OPENMPT123_SDL_HPP -#define OPENMPT123_SDL_HPP - -#include "openmpt123_config.hpp" -#include "openmpt123.hpp" - -#if defined(MPT_WITH_SDL) - -#include -#ifdef main -#undef main -#endif -#ifdef SDL_main -#undef SDL_main -#endif - -namespace openmpt123 { - -struct sdl_exception : public exception { - sdl_exception( int /*code*/ ) : exception( "SDL error" ) { } -}; - -class sdl_stream_raii : public write_buffers_blocking_wrapper { -private: - std::ostream & log; - std::size_t channels; -protected: - void check_sdl_error( int e ) { - if ( e < 0 ) { - throw sdl_exception( e ); - return; - } - } - std::uint32_t round_up_power2(std::uint32_t x) - { - std::uint32_t result = 1; - while ( result < x ) { - result *= 2; - } - return result; - } -public: - sdl_stream_raii( commandlineflags & flags, std::ostream & log_ ) - : write_buffers_blocking_wrapper(flags) - , log(log_) - , channels(flags.channels) - { - if ( flags.buffer == default_high ) { - flags.buffer = 160; - } else if ( flags.buffer == default_low ) { - flags.buffer = 80; - } - if ( flags.period == default_high ) { - flags.period = 20; - } else if ( flags.period == default_low ) { - flags.period = 10; - } - flags.apply_default_buffer_sizes(); - check_sdl_error( SDL_Init( SDL_INIT_NOPARACHUTE | SDL_INIT_TIMER | SDL_INIT_AUDIO ) ); - SDL_AudioSpec audiospec; - std::memset( &audiospec, 0, sizeof( SDL_AudioSpec ) ); - audiospec.freq = flags.samplerate; - audiospec.format = AUDIO_S16SYS; - audiospec.channels = flags.channels; - audiospec.silence = 0; - audiospec.samples = round_up_power2( ( flags.buffer * flags.samplerate ) / ( 1000 * 2 ) ); - audiospec.size = audiospec.samples * audiospec.channels * sizeof( std::int16_t ); - audiospec.callback = &sdl_callback_wrapper; - audiospec.userdata = this; - if ( flags.verbose ) { - log << "SDL:" << std::endl; - log << " latency: " << ( audiospec.samples * 2.0 / flags.samplerate ) << " (2 * " << audiospec.samples << ")" << std::endl; - log << std::endl; - } - set_queue_size_frames( round_up_power2( ( flags.buffer * flags.samplerate ) / ( 1000 * 2 ) ) ); - check_sdl_error( SDL_OpenAudio( &audiospec, NULL ) ); - SDL_PauseAudio( 0 ); - } - ~sdl_stream_raii() { - SDL_PauseAudio( 1 ); - SDL_CloseAudio(); - SDL_Quit(); - } -private: - static void sdl_callback_wrapper( void * userdata, Uint8 * stream, int len ) { - return reinterpret_cast( userdata )->sdl_callback( stream, len ); - } - void sdl_callback( Uint8 * stream, int len ) { - std::size_t framesToRender = len / sizeof( std::int16_t ) / channels; - for ( std::size_t frame = 0; frame < framesToRender; ++frame ) { - for ( std::size_t channel = 0; channel < channels; ++channel ) { - std::int16_t sample = pop_queue(); - std::memcpy( stream, &sample, sizeof( std::int16_t ) ); - stream += sizeof( std::int16_t ); - } - } - } -public: - bool pause() override { - SDL_PauseAudio( 1 ); - return true; - } - bool unpause() override { - SDL_PauseAudio( 0 ); - return true; - } - void lock() override { - SDL_LockAudio(); - } - void unlock() override { - SDL_UnlockAudio(); - } - bool sleep( int ms ) override { - SDL_Delay( ms ); - return true; - } -}; - -} // namespace openmpt123 - -#endif // MPT_WITH_SDL - -#endif // OPENMPT123_SDL_HPP - diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl2.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl2.hpp index 307d67ffe..5acab163d 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl2.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_sdl2.hpp @@ -15,24 +15,41 @@ #if defined(MPT_WITH_SDL2) +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wimplicit-fallthrough" +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // __clang__ #include +#if defined(__clang__) +#pragma clang diagnostic pop +#endif // __clang__ #ifdef main #undef main #endif #ifdef SDL_main #undef SDL_main #endif +#if (SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 4)) +MPT_WARNING("Support for SDL2 < 2.0.4 has been deprecated and will be removed in a future openmpt123 version.") +#endif namespace openmpt123 { struct sdl2_exception : public exception { - sdl2_exception( int /*code*/ ) : exception( "SDL2 error" ) { } +private: + static std::string text_from_code( int code ) { + std::ostringstream s; + s << code; + return s.str(); + } +public: + sdl2_exception( int code, const char * error ) : exception( text_from_code( code ) + " (" + ( error ? std::string(error) : std::string("NULL") ) + ")" ) { } }; static void check_sdl2_error( int e ) { if ( e < 0 ) { - throw sdl2_exception( e ); - return; + throw sdl2_exception( e, SDL_GetError() ); } } @@ -46,13 +63,16 @@ public: } }; -class sdl2_stream_raii : public write_buffers_blocking_wrapper { +class sdl2_stream_raii : public write_buffers_interface { private: std::ostream & log; sdl2_raii sdl2; int dev; std::size_t channels; bool use_float; + std::size_t sampleQueueMaxFrames; + std::vector sampleBufFloat; + std::vector sampleBufInt; protected: std::uint32_t round_up_power2(std::uint32_t x) { @@ -64,12 +84,12 @@ protected: } public: sdl2_stream_raii( commandlineflags & flags, std::ostream & log_ ) - : write_buffers_blocking_wrapper(flags) - , log(log_) + : log(log_) , sdl2( SDL_INIT_NOPARACHUTE | SDL_INIT_TIMER | SDL_INIT_AUDIO ) , dev(-1) , channels(flags.channels) , use_float(flags.use_float) + , sampleQueueMaxFrames(0) { if ( flags.buffer == default_high ) { flags.buffer = 160; @@ -90,14 +110,14 @@ public: audiospec.silence = 0; audiospec.samples = round_up_power2( ( flags.buffer * flags.samplerate ) / ( 1000 * 2 ) ); audiospec.size = audiospec.samples * audiospec.channels * ( flags.use_float ? sizeof( float ) : sizeof( std::int16_t ) ); - audiospec.callback = &sdl2_callback_wrapper; - audiospec.userdata = this; + audiospec.callback = NULL; + audiospec.userdata = NULL; if ( flags.verbose ) { log << "SDL2:" << std::endl; log << " latency: " << ( audiospec.samples * 2.0 / flags.samplerate ) << " (2 * " << audiospec.samples << ")" << std::endl; log << std::endl; } - set_queue_size_frames( round_up_power2( ( flags.buffer * flags.samplerate ) / ( 1000 * 2 ) ) ); + sampleQueueMaxFrames = round_up_power2( ( flags.buffer * flags.samplerate ) / ( 1000 * 2 ) ); SDL_AudioSpec audiospec_obtained; std::memset( &audiospec_obtained, 0, sizeof( SDL_AudioSpec ) ); std::memcpy( &audiospec_obtained, &audiospec, sizeof( SDL_AudioSpec ) ); @@ -114,24 +134,45 @@ public: SDL_CloseAudioDevice( dev ); } private: - static void sdl2_callback_wrapper( void * userdata, Uint8 * stream, int len ) { - return reinterpret_cast( userdata )->sdl2_callback( stream, len ); + std::size_t get_num_writeable_frames() { + std::size_t num_queued_frames = SDL_GetQueuedAudioSize( dev ) / ( use_float ? sizeof( float ) : sizeof( std::int16_t ) ) / channels; + if ( num_queued_frames > sampleQueueMaxFrames ) { + return 0; + } + return sampleQueueMaxFrames - num_queued_frames; } - void sdl2_callback( Uint8 * stream, int len ) { - return ( use_float ? sdl2_callback_impl( stream, len ) : sdl2_callback_impl( stream, len ) ); - } - template < typename Tsample > - void sdl2_callback_impl( Uint8 * stream, int len ) { - std::size_t framesToRender = len / sizeof( Tsample ) / channels; - for ( std::size_t frame = 0; frame < framesToRender; ++frame ) { - for ( std::size_t channel = 0; channel < channels; ++channel ) { - Tsample sample = pop_queue(); - std::memcpy( stream, &sample, sizeof( Tsample ) ); - stream += sizeof( Tsample ); + template + void write_frames( const Tsample * buffer, std::size_t frames ) { + while ( frames > 0 ) { + std::size_t chunk_frames = std::min( frames, get_num_writeable_frames() ); + if ( chunk_frames > 0 ) { + check_sdl2_error( SDL_QueueAudio( dev, buffer, chunk_frames * channels * ( use_float ? sizeof( float ) : sizeof( std::int16_t ) ) ) ); + frames -= chunk_frames; + buffer += chunk_frames * channels; + } else { + SDL_Delay( 1 ); } } } public: + void write( const std::vector buffers, std::size_t frames ) override { + sampleBufFloat.clear(); + for ( std::size_t frame = 0; frame < frames; ++frame ) { + for ( std::size_t channel = 0; channel < channels; ++channel ) { + sampleBufFloat.push_back( buffers[channel][frame] ); + } + } + write_frames( sampleBufFloat.data(), frames ); + } + void write( const std::vector buffers, std::size_t frames ) override { + sampleBufInt.clear(); + for ( std::size_t frame = 0; frame < frames; ++frame ) { + for ( std::size_t channel = 0; channel < channels; ++channel ) { + sampleBufInt.push_back( buffers[channel][frame] ); + } + } + write_frames( sampleBufInt.data(), frames ); + } bool pause() override { SDL_PauseAudioDevice( dev, 1 ); return true; @@ -140,12 +181,6 @@ public: SDL_PauseAudioDevice( dev, 0 ); return true; } - void lock() override { - SDL_LockAudioDevice( dev ); - } - void unlock() override { - SDL_UnlockAudioDevice( dev ); - } bool sleep( int ms ) override { SDL_Delay( ms ); return true; diff --git a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_waveout.hpp b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_waveout.hpp index e19a0640d..5598f709a 100644 --- a/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_waveout.hpp +++ b/Frameworks/OpenMPT/OpenMPT/openmpt123/openmpt123_waveout.hpp @@ -53,7 +53,7 @@ public: WAVEFORMATEX wfx; ZeroMemory( &wfx, sizeof( wfx ) ); wfx.wFormatTag = flags.use_float ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM; - wfx.nChannels = flags.channels; + wfx.nChannels = static_cast( flags.channels ); wfx.nSamplesPerSec = flags.samplerate; wfx.wBitsPerSample = flags.use_float ? 32 : 16; wfx.nBlockAlign = ( wfx.wBitsPerSample / 8 ) * wfx.nChannels; diff --git a/Frameworks/OpenMPT/OpenMPT/soundbase/Dither.cpp b/Frameworks/OpenMPT/OpenMPT/soundbase/Dither.cpp new file mode 100644 index 000000000..ac1130cf0 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundbase/Dither.cpp @@ -0,0 +1,34 @@ +/* + * Dither.cpp + * ---------- + * Purpose: Dithering when converting to lower resolution sample formats. + * Notes : (currently none) + * Authors: Olivier Lapicque + * OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" + +#include "Dither.h" + +#include "../common/misc_util.h" + + +OPENMPT_NAMESPACE_BEGIN + + +mpt::ustring DitherNames::GetModeName(DitherMode mode) +{ + switch(mode) + { + case DitherNone : return U_("no" ); break; + case DitherDefault: return U_("default"); break; + case DitherModPlug: return U_("0.5 bit"); break; + case DitherSimple : return U_("1 bit" ); break; + default : return U_("" ); break; + } +} + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundbase/Dither.h b/Frameworks/OpenMPT/OpenMPT/soundbase/Dither.h new file mode 100644 index 000000000..319f63671 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundbase/Dither.h @@ -0,0 +1,330 @@ +/* + * Dither.h + * -------- + * Purpose: Dithering when converting to lower resolution sample formats. + * Notes : (currently none) + * Authors: Olivier Lapicque + * OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" + + +#include "SampleTypes.h" +#include "SampleFormatConverters.h" +#include "../common/mptRandom.h" + + +OPENMPT_NAMESPACE_BEGIN + + +enum DitherMode +{ + DitherNone = 0, + DitherDefault = 1, // chosen by OpenMPT code, might change + DitherModPlug = 2, // rectangular, 0.5 bit depth, no noise shaping (original ModPlug Tracker) + DitherSimple = 3, // rectangular, 1 bit depth, simple 1st order noise shaping + NumDitherModes +}; + + +struct Dither_None +{ +public: + template + MPT_FORCEINLINE MixSampleInt process(MixSampleInt sample, Trng &) + { + return sample; + } + template + MPT_FORCEINLINE MixSampleFloat process(MixSampleFloat sample, Trng &) + { + return sample; + } +}; + + +struct Dither_ModPlug +{ +public: + template + MPT_FORCEINLINE MixSampleInt process(MixSampleInt sample, Trng &rng) + { + if constexpr(targetbits == 0) + { + MPT_UNREFERENCED_PARAMETER(rng); + return sample; + } else if constexpr(targetbits + MixSampleIntTraits::mix_headroom_bits() + 1 >= 32) + { + MPT_UNREFERENCED_PARAMETER(rng); + return sample; + } else + { + sample += mpt::rshift_signed(static_cast(mpt::random(rng)), (targetbits + MixSampleIntTraits::mix_headroom_bits() + 1)); + return sample; + } + } + template + MPT_FORCEINLINE MixSampleFloat process(MixSampleFloat sample, Trng &prng) + { + SC::ConvertToFixedPoint conv1; + SC::ConvertFixedPoint conv2; + return conv2(process(conv1(sample), prng)); + } +}; + + +template +struct Dither_SimpleImpl +{ +private: + int32 error = 0; +public: + template + MPT_FORCEINLINE MixSampleInt process(MixSampleInt sample, Trng &prng) + { + if constexpr(targetbits == 0) + { + MPT_UNREFERENCED_PARAMETER(prng); + return sample; + } else + { + static_assert(sizeof(MixSampleInt) == 4); + constexpr int rshift = (32-targetbits) - MixSampleIntTraits::mix_headroom_bits(); + if constexpr(rshift <= 1) + { + MPT_UNREFERENCED_PARAMETER(prng); + // nothing to dither + return sample; + } else + { + constexpr int rshiftpositive = (rshift > 1) ? rshift : 1; // work-around warnings about negative shift with C++14 compilers + constexpr int round_mask = ~((1<(prng, noise_bits) + mpt::random(prng, noise_bits)) >> 1; + } else + { + unoise = mpt::random(prng, noise_bits); + } + int noise = static_cast(unoise) - noise_bias; // un-bias + int val = sample; + if constexpr(shaped) + { + val += (e >> 1); + } + int rounded = (val + noise + round_offset) & round_mask;; + e = val - rounded; + sample = rounded; + error = e; + return sample; + } + } + } + template + MPT_FORCEINLINE MixSampleFloat process(MixSampleFloat sample, Trng &prng) + { + SC::ConvertToFixedPoint conv1; + SC::ConvertFixedPoint conv2; + return conv2(process(conv1(sample), prng)); + } +}; + +using Dither_Simple = Dither_SimpleImpl<>; + + +template +class MultiChannelDither +{ +private: + std::array DitherChannels; +public: + void Reset() + { + DitherChannels.fill(Tdither()); + } + template + MPT_FORCEINLINE MixSampleInt process(std::size_t channel, MixSampleInt sample, Trng &prng) + { + return DitherChannels[channel].template process(sample, prng); + } + template + MPT_FORCEINLINE MixSampleFloat process(std::size_t channel, MixSampleFloat sample, Trng &prng) + { + return DitherChannels[channel].template process(sample, prng); + } +}; + + +template +class DitherTemplate; + +template +class DitherTemplate + : public MultiChannelDither +{ + struct {} prng; +public: + template + DitherTemplate(Trd &) + { + return; + } + template + MPT_FORCEINLINE MixSampleInt process(std::size_t channel, MixSampleInt sample) + { + return MultiChannelDither::template process(channel, sample, prng); + } + template + MPT_FORCEINLINE MixSampleFloat process(std::size_t channel, MixSampleFloat sample) + { + return MultiChannelDither::template process(channel, sample, prng); + } +}; + +template +class DitherTemplate + : public MultiChannelDither +{ +private: + mpt::rng::modplug_dither prng; +public: + template + DitherTemplate(Trd &) + : prng(0, 0) + { + return; + } + template + MPT_FORCEINLINE MixSampleInt process(std::size_t channel, MixSampleInt sample) + { + return MultiChannelDither::template process(channel, sample, prng); + } + template + MPT_FORCEINLINE MixSampleFloat process(std::size_t channel, MixSampleFloat sample) + { + return MultiChannelDither::template process(channel, sample, prng); + } +}; + +template +class DitherTemplate + : public MultiChannelDither +{ +private: + mpt::fast_prng prng; +public: + template + DitherTemplate(Trd & rd) + : prng(rd) + { + return; + } + template + MPT_FORCEINLINE MixSampleInt process(std::size_t channel, MixSampleInt sample) + { + return MultiChannelDither::template process(channel, sample, prng); + } + template + MPT_FORCEINLINE MixSampleFloat process(std::size_t channel, MixSampleFloat sample) + { + return MultiChannelDither::template process(channel, sample, prng); + } +}; + + +class DitherNames +{ +public: + static mpt::ustring GetModeName(DitherMode mode); +}; + + +template +class DitherChannels + : public DitherNames +{ + +private: + + DitherTemplate ditherNone; + DitherTemplate ditherModPlug; + DitherTemplate ditherSimple; + + DitherMode mode = DitherDefault; + +public: + + template + DitherChannels(Trd & rd) + : ditherNone(rd) + , ditherModPlug(rd) + , ditherSimple(rd) + { + return; + } + void Reset() + { + ditherModPlug.Reset(); + ditherSimple.Reset(); + } + + DitherTemplate & NoDither() + { + MPT_ASSERT(mode == DitherNone); + return ditherNone; + } + DitherTemplate & DefaultDither() + { + MPT_ASSERT(mode == DitherDefault); + return ditherModPlug; + } + DitherTemplate & ModPlugDither() + { + MPT_ASSERT(mode == DitherModPlug); + return ditherModPlug; + } + DitherTemplate & SimpleDither() + { + MPT_ASSERT(mode == DitherSimple); + return ditherSimple; + } + + template + auto WithDither(Tfn fn) + { + switch(GetMode()) + { + case DitherNone: return fn(NoDither()); break; + case DitherModPlug: return fn(ModPlugDither()); break; + case DitherSimple: return fn(SimpleDither()); break; + case DitherDefault: return fn(DefaultDither()); break; + default: return fn(DefaultDither()); break; + } + } + + void SetMode(DitherMode mode_) + { + mode = mode_; + } + DitherMode GetMode() const + { + return mode; + } + +}; + + +using Dither = DitherChannels<4>; + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleBuffer.h b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleBuffer.h new file mode 100644 index 000000000..718d1636f --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleBuffer.h @@ -0,0 +1,135 @@ +/* + * SampleBuffer.h + * -------------- + * Purpose: C++2b audio_buffer-like thingy. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include "BuildSettings.h" + +#include "../common/mptBaseMacros.h" +#include "../common/mptBaseTypes.h" + + +OPENMPT_NAMESPACE_BEGIN + + +template +struct audio_buffer_planar +{ +public: + using sample_type = SampleType; +private: + SampleType * const * m_buffers; + std::size_t m_channels; + std::size_t m_frames; + std::size_t m_offset; +public: + constexpr audio_buffer_planar(SampleType * const * buffers, std::size_t channels, std::size_t frames) + : m_buffers(buffers) + , m_channels(channels) + , m_frames(frames) + , m_offset(0) + { + return; + } + SampleType & operator()(std::size_t channel, std::size_t frame) + { + return m_buffers[channel][m_offset + frame]; + } + const SampleType & operator()(std::size_t channel, std::size_t frame) const + { + return m_buffers[channel][m_offset + frame]; + } + std::size_t size_channels() const noexcept + { + return m_channels; + } + std::size_t size_frames() const noexcept + { + return m_frames; + } + audio_buffer_planar & advance(std::size_t numFrames) + { + m_offset += numFrames; + m_frames -= numFrames; + return *this; + } +}; + +template +struct audio_buffer_interleaved +{ +public: + using sample_type = SampleType; +private: + SampleType * m_buffer; + std::size_t m_channels; + std::size_t m_frames; +public: + constexpr audio_buffer_interleaved(SampleType* buffer, std::size_t channels, std::size_t frames) + : m_buffer(buffer) + , m_channels(channels) + , m_frames(frames) + { + return; + } + SampleType * data() + { + return m_buffer; + } + const SampleType * data() const + { + return m_buffer; + } + SampleType & operator()(std::size_t channel, std::size_t frame) + { + return m_buffer[m_channels * frame + channel]; + } + const SampleType & operator()(std::size_t channel, std::size_t frame) const + { + return m_buffer[m_channels * frame + channel]; + } + std::size_t size_channels() const noexcept + { + return m_channels; + } + std::size_t size_frames() const noexcept + { + return m_frames; + } + audio_buffer_interleaved & advance(std::size_t numFrames) + { + m_buffer += size_channels() * numFrames; + m_frames -= numFrames; + return *this; + } +}; + +template +std::size_t planar_audio_buffer_valid_channels(SampleType * const * buffers, std::size_t maxChannels) +{ + std::size_t channel; + for(channel = 0; channel < maxChannels; ++channel) + { + if(!buffers[channel]) + { + break; + } + } + return channel; +} + +template +BufferType advance_audio_buffer(BufferType buf, std::size_t numFrames) +{ + MPT_ASSERT(numFrames <= buf.size_frames()); + return buf.advance(numFrames); +} + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormat.h b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormat.h index c2ca79fd3..32d0de222 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormat.h +++ b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormat.h @@ -18,80 +18,132 @@ OPENMPT_NAMESPACE_BEGIN struct int24; -enum SampleFormatEnum +enum SampleFormatEnum : uint8 { - SampleFormatUnsigned8 = 8, // do not change value (for compatibility with old configuration settings) + SampleFormatUnsigned8 = 9, // do not change value (for compatibility with old configuration settings) + SampleFormatInt8 = 8, // do not change value (for compatibility with old configuration settings) SampleFormatInt16 = 16, // do not change value (for compatibility with old configuration settings) SampleFormatInt24 = 24, // do not change value (for compatibility with old configuration settings) SampleFormatInt32 = 32, // do not change value (for compatibility with old configuration settings) SampleFormatFloat32 = 32 + 128, // do not change value (for compatibility with old configuration settings) + SampleFormatFloat64 = 64 + 128, // do not change value (for compatibility with old configuration settings) SampleFormatInvalid = 0 }; +template +Container AllSampleFormats() +{ + return { SampleFormatFloat64, SampleFormatFloat32, SampleFormatInt32, SampleFormatInt24, SampleFormatInt16, SampleFormatInt8, SampleFormatUnsigned8 }; +} + +template +Container DefaultSampleFormats() +{ + return { SampleFormatFloat32, SampleFormatInt32, SampleFormatInt24, SampleFormatInt16, SampleFormatInt8 }; +} + template struct SampleFormatTraits; -template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatUnsigned8; } }; -template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt16; } }; -template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt24; } }; -template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt32; } }; -template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatFloat32; } }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatUnsigned8; } }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt8; } }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt16; } }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt24; } }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatInt32; } }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatFloat32; } }; +template<> struct SampleFormatTraits { static MPT_CONSTEXPR11_FUN SampleFormatEnum sampleFormat() { return SampleFormatFloat64; } }; template struct SampleFormatToType; -template<> struct SampleFormatToType { typedef uint8 type; }; -template<> struct SampleFormatToType { typedef int16 type; }; -template<> struct SampleFormatToType { typedef int24 type; }; -template<> struct SampleFormatToType { typedef int32 type; }; -template<> struct SampleFormatToType { typedef float type; }; +template<> struct SampleFormatToType { typedef uint8 type; }; +template<> struct SampleFormatToType { typedef int8 type; }; +template<> struct SampleFormatToType { typedef int16 type; }; +template<> struct SampleFormatToType { typedef int24 type; }; +template<> struct SampleFormatToType { typedef int32 type; }; +template<> struct SampleFormatToType { typedef float type; }; +template<> struct SampleFormatToType { typedef double type; }; -struct SampleFormat +class SampleFormat { + +private: + SampleFormatEnum value; - MPT_CONSTEXPR11_FUN SampleFormat(SampleFormatEnum v = SampleFormatInvalid) : value(v) { } - MPT_CONSTEXPR11_FUN bool operator == (SampleFormat other) const { return value == other.value; } - MPT_CONSTEXPR11_FUN bool operator != (SampleFormat other) const { return value != other.value; } - MPT_CONSTEXPR11_FUN bool operator == (SampleFormatEnum other) const { return value == other; } - MPT_CONSTEXPR11_FUN bool operator != (SampleFormatEnum other) const { return value != other; } - MPT_CONSTEXPR11_FUN operator SampleFormatEnum () const + + template + static MPT_CONSTEXPR14_FUN SampleFormatEnum Sanitize(T x) noexcept { - return value; + using uT = typename std::make_unsigned::type; + uT ux = x; + if(ux == static_cast(-8)) + { + ux = 8+1; + } + // float|64|32|16|8|?|?|unsigned + ux &= 0b11111001; + return static_cast(ux); } - MPT_CONSTEXPR11_FUN bool IsValid() const + +public: + + MPT_CONSTEXPR11_FUN SampleFormat() noexcept + : value(SampleFormatInvalid) + { + } + + MPT_CONSTEXPR14_FUN SampleFormat(SampleFormatEnum v) noexcept + : value(Sanitize(v)) + { + } + + MPT_CONSTEXPR11_FUN bool IsValid() const noexcept { return value != SampleFormatInvalid; } - MPT_CONSTEXPR11_FUN bool IsUnsigned() const + + MPT_CONSTEXPR11_FUN bool IsUnsigned() const noexcept { return IsValid() && (value == SampleFormatUnsigned8); } - MPT_CONSTEXPR11_FUN bool IsFloat() const + MPT_CONSTEXPR11_FUN bool IsFloat() const noexcept { - return IsValid() && (value == SampleFormatFloat32); + return IsValid() && ((value == SampleFormatFloat32) || (value == SampleFormatFloat64)); } - MPT_CONSTEXPR11_FUN bool IsInt() const + MPT_CONSTEXPR11_FUN bool IsInt() const noexcept { - return IsValid() && (value != SampleFormatFloat32); + return IsValid() && ((value != SampleFormatFloat32) && (value != SampleFormatFloat64)); } - MPT_CONSTEXPR11_FUN uint8 GetBitsPerSample() const + MPT_CONSTEXPR11_FUN uint8 GetBitsPerSample() const noexcept { return !IsValid() ? 0 : (value == SampleFormatUnsigned8) ? 8 : + (value == SampleFormatInt8) ? 8 : (value == SampleFormatInt16) ? 16 : (value == SampleFormatInt24) ? 24 : (value == SampleFormatInt32) ? 32 : (value == SampleFormatFloat32) ? 32 : + (value == SampleFormatFloat64) ? 64 : 0; } + MPT_CONSTEXPR11_FUN operator SampleFormatEnum () const noexcept + { + return value; + } + // backward compatibility, conversion to/from integers - MPT_CONSTEXPR11_FUN operator int () const { return value; } - MPT_CONSTEXPR11_FUN SampleFormat(int v) : value(SampleFormatEnum(v)) { } - MPT_CONSTEXPR11_FUN operator long () const { return value; } - MPT_CONSTEXPR11_FUN SampleFormat(long v) : value(SampleFormatEnum(v)) { } - MPT_CONSTEXPR11_FUN operator unsigned int () const { return value; } - MPT_CONSTEXPR11_FUN SampleFormat(unsigned int v) : value(SampleFormatEnum(v)) { } - MPT_CONSTEXPR11_FUN operator unsigned long () const { return value; } - MPT_CONSTEXPR11_FUN SampleFormat(unsigned long v) : value(SampleFormatEnum(v)) { } + static MPT_CONSTEXPR14_FUN SampleFormat FromInt(int x) noexcept + { + return SampleFormat(Sanitize(x)); + } + static MPT_CONSTEXPR14_FUN int ToInt(SampleFormat x) noexcept + { + return x.value; + } + MPT_CONSTEXPR11_FUN int AsInt() const noexcept + { + return value; + } + }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatConverters.h b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatConverters.h index 88c7a24d6..7b53de1e8 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatConverters.h +++ b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatConverters.h @@ -36,16 +36,16 @@ namespace SC { // SC = _S_ample_C_onversion #if MPT_COMPILER_MSVC -#if defined(_M_IX86) && !(defined(_M_X64)) && (_M_IX86_FP < 2) -#define MPT_SC_AVOID_FLOOR 1 +#define MPT_SC_AVOID_ROUND 1 #else -#define MPT_SC_AVOID_FLOOR 0 -#endif -#else -#define MPT_SC_AVOID_FLOOR 0 +#define MPT_SC_AVOID_ROUND 0 #endif - +#if MPT_SC_AVOID_ROUND +#define MPT_SC_FASTROUND(x) std::floor((x) + 0.5f) +#else +#define MPT_SC_FASTROUND(x) mpt::round(x) +#endif #if MPT_COMPILER_SHIFT_SIGNED @@ -54,57 +54,26 @@ namespace SC { // SC = _S_ample_C_onversion #else -//#define MPT_SC_USE_MULDIV - -#ifdef MPT_SC_USE_MULDIV - -// just use mul and div - -template -MPT_FORCEINLINE auto rshift_signed_muldiv(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; - return x / (static_cast(1) << y); -} - -template -MPT_FORCEINLINE auto lshift_signed_muldiv(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; - return x * (static_cast(1) << y); -} - -#define MPT_SC_RSHIFT_SIGNED(val, shift) SC::rshift_signed_muldiv((val), (shift)) -#define MPT_SC_LSHIFT_SIGNED(val, shift) SC::lshift_signed_muldiv((val), (shift)) - -#else - #define MPT_SC_RSHIFT_SIGNED(val, shift) mpt::rshift_signed((val), (shift)) #define MPT_SC_LSHIFT_SIGNED(val, shift) mpt::lshift_signed((val), (shift)) #endif -#endif - // Every sample decoding functor has to typedef its input_t and output_t -// and has to provide a static const input_inc member +// and has to provide a static constexpr input_inc member // which describes by how many input_t elements inBuf has to be incremented between invocations. // input_inc is normally 1 except when decoding e.g. bigger sample values -// from multiple mpt::byte values. +// from multiple std::byte values. // decodes signed 7bit values stored as signed int8 struct DecodeInt7 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int8 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 1; + static constexpr std::size_t input_inc = 1; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return Clamp(mpt::byte_cast(*inBuf), static_cast(-64), static_cast(63)) * 2; @@ -113,9 +82,9 @@ struct DecodeInt7 struct DecodeInt8 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int8 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 1; + static constexpr std::size_t input_inc = 1; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return mpt::byte_cast(*inBuf); @@ -124,9 +93,9 @@ struct DecodeInt8 struct DecodeUint8 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int8 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 1; + static constexpr std::size_t input_inc = 1; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return static_cast(int(mpt::byte_cast(*inBuf)) - 128); @@ -135,9 +104,9 @@ struct DecodeUint8 struct DecodeInt8Delta { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int8 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 1; + static constexpr std::size_t input_inc = 1; uint8 delta; DecodeInt8Delta() : delta(0) { } MPT_FORCEINLINE output_t operator() (const input_t *inBuf) @@ -150,9 +119,9 @@ struct DecodeInt8Delta template struct DecodeInt16 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int16 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 2; + static constexpr std::size_t input_inc = 2; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return (mpt::byte_cast(inBuf[loByteIndex]) | (mpt::byte_cast(inBuf[hiByteIndex]) << 8)) - offset; @@ -162,9 +131,9 @@ struct DecodeInt16 template struct DecodeInt16Delta { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int16 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 2; + static constexpr std::size_t input_inc = 2; uint16 delta; DecodeInt16Delta() : delta(0) { } MPT_FORCEINLINE output_t operator() (const input_t *inBuf) @@ -176,9 +145,9 @@ struct DecodeInt16Delta struct DecodeInt16Delta8 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int16 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 2; + static constexpr std::size_t input_inc = 2; uint16 delta; DecodeInt16Delta8() : delta(0) { } MPT_FORCEINLINE output_t operator() (const input_t *inBuf) @@ -194,9 +163,9 @@ struct DecodeInt16Delta8 template struct DecodeInt24 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int32 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 3; + static constexpr std::size_t input_inc = 3; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return ((mpt::byte_cast(inBuf[loByteIndex]) << 8) | (mpt::byte_cast(inBuf[midByteIndex]) << 16) | (mpt::byte_cast(inBuf[hiByteIndex]) << 24)) - offset; @@ -206,9 +175,9 @@ struct DecodeInt24 template struct DecodeInt32 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int32 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 4; + static constexpr std::size_t input_inc = 4; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return (mpt::byte_cast(inBuf[loLoByteIndex]) | (mpt::byte_cast(inBuf[loHiByteIndex]) << 8) | (mpt::byte_cast(inBuf[hiLoByteIndex]) << 16) | (mpt::byte_cast(inBuf[hiHiByteIndex]) << 24)) - offset; @@ -218,9 +187,9 @@ struct DecodeInt32 template struct DecodeInt64 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef int64 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 8; + static constexpr std::size_t input_inc = 8; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return (uint64(0) @@ -239,9 +208,9 @@ struct DecodeInt64 template struct DecodeFloat32 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef float32 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 4; + static constexpr std::size_t input_inc = 4; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return IEEE754binary32LE(inBuf[loLoByteIndex], inBuf[loHiByteIndex], inBuf[hiLoByteIndex], inBuf[hiHiByteIndex]); @@ -251,9 +220,9 @@ struct DecodeFloat32 template struct DecodeScaledFloat32 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef float32 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 4; + static constexpr std::size_t input_inc = 4; float factor; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { @@ -269,9 +238,9 @@ struct DecodeScaledFloat32 template struct DecodeFloat64 { - typedef mpt::byte input_t; + typedef std::byte input_t; typedef float64 output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 8; + static constexpr std::size_t input_inc = 8; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return IEEE754binary64LE(inBuf[b0], inBuf[b1], inBuf[b2], inBuf[b3], inBuf[b4], inBuf[b5], inBuf[b6], inBuf[b7]); @@ -283,7 +252,7 @@ struct DecodeIdentity { typedef Tsample input_t; typedef Tsample output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = 1; + static constexpr std::size_t input_inc = 1; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) { return *inBuf; @@ -340,6 +309,98 @@ struct Convert } }; +template <> +struct Convert +{ + typedef int8 input_t; + typedef uint8 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return static_cast(val+0x80); + } +}; + +template <> +struct Convert +{ + typedef int16 input_t; + typedef uint8 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return static_cast(static_cast(MPT_SC_RSHIFT_SIGNED(val, 8))+0x80); + } +}; + +template <> +struct Convert +{ + typedef int24 input_t; + typedef uint8 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return static_cast(static_cast(MPT_SC_RSHIFT_SIGNED(static_cast(val), 16))+0x80); + } +}; + +template <> +struct Convert +{ + typedef int32 input_t; + typedef uint8 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return static_cast(static_cast(MPT_SC_RSHIFT_SIGNED(val, 24))+0x80); + } +}; + +template <> +struct Convert +{ + typedef int64 input_t; + typedef uint8 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return static_cast(static_cast(MPT_SC_RSHIFT_SIGNED(val, 56))+0x80); + } +}; + +template <> +struct Convert +{ + typedef float32 input_t; + typedef uint8 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + Limit(val, -1.0f, 1.0f); + val *= 128.0f; + return static_cast(mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val)))+0x80); + } +}; + +template <> +struct Convert +{ + typedef double input_t; + typedef uint8 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + Limit(val, -1.0, 1.0); + val *= 128.0; + return static_cast(mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val)))+0x80); + } +}; + +template <> +struct Convert +{ + typedef uint8 input_t; + typedef int8 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return static_cast(static_cast(val)-0x80); + } +}; + template <> struct Convert { @@ -393,12 +454,31 @@ struct Convert { Limit(val, -1.0f, 1.0f); val *= 128.0f; -#if MPT_SC_AVOID_FLOOR - // MSVC with x87 floating point math calls floor for the more intuitive version - return mpt::saturate_cast(MPT_SC_RSHIFT_SIGNED(static_cast(val * 2.0f + 1.0f), 1)); -#else - return mpt::saturate_cast(static_cast(std::floor(val + 0.5f))); -#endif + return mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val))); + } +}; + +template <> +struct Convert +{ + typedef double input_t; + typedef int8 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + Limit(val, -1.0, 1.0); + val *= 128.0; + return mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val))); + } +}; + +template <> +struct Convert +{ + typedef uint8 input_t; + typedef int16 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return static_cast(MPT_SC_LSHIFT_SIGNED(static_cast(val)-0x80, 8)); } }; @@ -455,30 +535,7 @@ struct Convert { Limit(val, -1.0f, 1.0f); val *= 32768.0f; -#if MPT_SC_AVOID_FLOOR - // MSVC with x87 floating point math calls floor for the more intuitive version - return mpt::saturate_cast(MPT_SC_RSHIFT_SIGNED(static_cast(val * 2.0f + 1.0f), 1)); -#else - return mpt::saturate_cast(static_cast(std::floor(val + 0.5f))); -#endif - } -}; - -template <> -struct Convert -{ - typedef double input_t; - typedef int8 output_t; - MPT_FORCEINLINE output_t operator() (input_t val) - { - Limit(val, -1.0, 1.0); - val *= 128.0; -#if MPT_SC_AVOID_FLOOR - // MSVC with x87 floating point math calls floor for the more intuitive version - return mpt::saturate_cast(MPT_SC_RSHIFT_SIGNED(static_cast(val * 2.0 + 1.0), 1)); -#else - return mpt::saturate_cast(static_cast(std::floor(val + 0.5))); -#endif + return mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val))); } }; @@ -491,12 +548,18 @@ struct Convert { Limit(val, -1.0, 1.0); val *= 32768.0; -#if MPT_SC_AVOID_FLOOR - // MSVC with x87 floating point math calls floor for the more intuitive version - return mpt::saturate_cast(MPT_SC_RSHIFT_SIGNED(static_cast(val * 2.0 + 1.0), 1)); -#else - return mpt::saturate_cast(static_cast(std::floor(val + 0.5))); -#endif + return mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val))); + } +}; + +template <> +struct Convert +{ + typedef uint8 input_t; + typedef int24 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return static_cast(MPT_SC_LSHIFT_SIGNED(static_cast(val)-0x80, 16)); } }; @@ -544,6 +607,43 @@ struct Convert } }; +template <> +struct Convert +{ + typedef float32 input_t; + typedef int24 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + Limit(val, -1.0f, 1.0f); + val *= 2147483648.0f; + return static_cast(MPT_SC_RSHIFT_SIGNED(mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val))), 8)); + } +}; + +template <> +struct Convert +{ + typedef double input_t; + typedef int24 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + Limit(val, -1.0, 1.0); + val *= 2147483648.0; + return static_cast(MPT_SC_RSHIFT_SIGNED(mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val))), 8)); + } +}; + +template <> +struct Convert +{ + typedef uint8 input_t; + typedef int32 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return static_cast(MPT_SC_LSHIFT_SIGNED(static_cast(val)-0x80, 24)); + } +}; + template <> struct Convert { @@ -597,12 +697,7 @@ struct Convert { Limit(val, -1.0f, 1.0f); val *= 2147483648.0f; -#if MPT_SC_AVOID_FLOOR - // MSVC with x87 floating point math calls floor for the more intuitive version - return mpt::saturate_cast(MPT_SC_RSHIFT_SIGNED(static_cast(val * 2.0f + 1.0f), 1)); -#else - return mpt::saturate_cast(static_cast(std::floor(val + 0.5f))); -#endif + return mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val))); } }; @@ -615,12 +710,18 @@ struct Convert { Limit(val, -1.0, 1.0); val *= 2147483648.0; -#if MPT_SC_AVOID_FLOOR - // MSVC with x87 floating point math calls floor for the more intuitive version - return mpt::saturate_cast(MPT_SC_RSHIFT_SIGNED(static_cast(val * 2.0 + 1.0), 1)); -#else - return mpt::saturate_cast(static_cast(std::floor(val + 0.5))); -#endif + return mpt::saturate_cast(static_cast(MPT_SC_FASTROUND(val))); + } +}; + +template <> +struct Convert +{ + typedef uint8 input_t; + typedef int64 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return MPT_SC_LSHIFT_SIGNED(static_cast(val)-0x80, 56); } }; @@ -677,7 +778,7 @@ struct Convert { Limit(val, -1.0f, 1.0f); val *= static_cast(uint64(1)<<63); - return mpt::saturate_cast(std::floor(val + 0.5f)); + return mpt::saturate_cast(MPT_SC_FASTROUND(val)); } }; @@ -690,7 +791,18 @@ struct Convert { Limit(val, -1.0, 1.0); val *= static_cast(uint64(1)<<63); - return mpt::saturate_cast(std::floor(val + 0.5)); + return mpt::saturate_cast(MPT_SC_FASTROUND(val)); + } +}; + +template <> +struct Convert +{ + typedef uint8 input_t; + typedef float32 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return (static_cast(val)-0x80) * (1.0f / static_cast(static_cast(1)<<7)); } }; @@ -716,6 +828,17 @@ struct Convert } }; +template <> +struct Convert +{ + typedef int24 input_t; + typedef float32 output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return val * (1.0f / static_cast(static_cast(1)<<23)); + } +}; + template <> struct Convert { @@ -738,6 +861,17 @@ struct Convert } }; +template <> +struct Convert +{ + typedef uint8 input_t; + typedef double output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return (static_cast(val)-0x80) * (1.0 / static_cast(static_cast(1)<<7)); + } +}; + template <> struct Convert { @@ -760,6 +894,17 @@ struct Convert } }; +template <> +struct Convert +{ + typedef int24 input_t; + typedef double output_t; + MPT_FORCEINLINE output_t operator() (input_t val) + { + return val * (1.0 / static_cast(static_cast(1)<<23)); + } +}; + template <> struct Convert { @@ -805,19 +950,19 @@ struct Convert }; -template +template struct ConvertFixedPoint; -template -struct ConvertFixedPoint +template +struct ConvertFixedPoint { typedef int32 input_t; typedef uint8 output_t; - static const int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; + static constexpr int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); - STATIC_ASSERT(shiftBits >= 1); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); + static_assert(shiftBits >= 1); val = MPT_SC_RSHIFT_SIGNED((val + (1<<(shiftBits-1))), shiftBits); // round if(val < int8_min) val = int8_min; if(val > int8_max) val = int8_max; @@ -825,16 +970,16 @@ struct ConvertFixedPoint } }; -template -struct ConvertFixedPoint +template +struct ConvertFixedPoint { typedef int32 input_t; typedef int8 output_t; - static const int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; + static constexpr int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); - STATIC_ASSERT(shiftBits >= 1); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); + static_assert(shiftBits >= 1); val = MPT_SC_RSHIFT_SIGNED((val + (1<<(shiftBits-1))), shiftBits); // round if(val < int8_min) val = int8_min; if(val > int8_max) val = int8_max; @@ -842,16 +987,16 @@ struct ConvertFixedPoint } }; -template -struct ConvertFixedPoint +template +struct ConvertFixedPoint { typedef int32 input_t; typedef int16 output_t; - static const int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; + static constexpr int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); - STATIC_ASSERT(shiftBits >= 1); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); + static_assert(shiftBits >= 1); val = MPT_SC_RSHIFT_SIGNED((val + (1<<(shiftBits-1))), shiftBits); // round if(val < int16_min) val = int16_min; if(val > int16_max) val = int16_max; @@ -859,16 +1004,16 @@ struct ConvertFixedPoint } }; -template -struct ConvertFixedPoint +template +struct ConvertFixedPoint { typedef int32 input_t; typedef int24 output_t; - static const int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; + static constexpr int shiftBits = fractionalBits + 1 - sizeof(output_t) * 8; MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); - STATIC_ASSERT(shiftBits >= 1); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); + static_assert(shiftBits >= 1); val = MPT_SC_RSHIFT_SIGNED((val + (1<<(shiftBits-1))), shiftBits); // round if(val < int24_min) val = int24_min; if(val > int24_max) val = int24_max; @@ -876,20 +1021,20 @@ struct ConvertFixedPoint } }; -template -struct ConvertFixedPoint +template +struct ConvertFixedPoint { typedef int32 input_t; typedef int32 output_t; MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); return static_cast(Clamp(val, static_cast(-((1<(1< -struct ConvertFixedPoint +template +struct ConvertFixedPoint { typedef int32 input_t; typedef float32 output_t; @@ -901,17 +1046,26 @@ struct ConvertFixedPoint } MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); - MPT_CONSTANT_IF(clipOutput) - { - float32 out = val * factor; - if(out < -1.0f) out = -1.0f; - if(out > 1.0f) out = 1.0f; - return out; - } else - { - return val * factor; - } + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); + return val * factor; + } +}; + +template +struct ConvertFixedPoint +{ + typedef int32 input_t; + typedef float64 output_t; + const double factor; + MPT_FORCEINLINE ConvertFixedPoint() + : factor( 1.0 / static_cast(1 << fractionalBits) ) + { + return; + } + MPT_FORCEINLINE output_t operator() (input_t val) + { + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); + return val * factor; } }; @@ -924,25 +1078,39 @@ struct ConvertToFixedPoint { typedef uint8 input_t; typedef int32 output_t; - static const int shiftBits = fractionalBits + 1 - sizeof(input_t) * 8; + static constexpr int shiftBits = fractionalBits + 1 - sizeof(input_t) * 8; MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); - STATIC_ASSERT(shiftBits >= 1); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); + static_assert(shiftBits >= 1); return MPT_SC_LSHIFT_SIGNED(static_cast(static_cast(val)-0x80), shiftBits); } }; +template +struct ConvertToFixedPoint +{ + typedef int8 input_t; + typedef int32 output_t; + static constexpr int shiftBits = fractionalBits + 1 - sizeof(input_t) * 8; + MPT_FORCEINLINE output_t operator() (input_t val) + { + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); + static_assert(shiftBits >= 1); + return MPT_SC_LSHIFT_SIGNED(static_cast(val), shiftBits); + } +}; + template struct ConvertToFixedPoint { typedef int16 input_t; typedef int32 output_t; - static const int shiftBits = fractionalBits + 1 - sizeof(input_t) * 8; + static constexpr int shiftBits = fractionalBits + 1 - sizeof(input_t) * 8; MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); - STATIC_ASSERT(shiftBits >= 1); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); + static_assert(shiftBits >= 1); return MPT_SC_LSHIFT_SIGNED(static_cast(val), shiftBits); } }; @@ -952,11 +1120,11 @@ struct ConvertToFixedPoint { typedef int24 input_t; typedef int32 output_t; - static const int shiftBits = fractionalBits + 1 - sizeof(input_t) * 8; + static constexpr int shiftBits = fractionalBits + 1 - sizeof(input_t) * 8; MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); - STATIC_ASSERT(shiftBits >= 1); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); + static_assert(shiftBits >= 1); return MPT_SC_LSHIFT_SIGNED(static_cast(val), shiftBits); } }; @@ -968,7 +1136,7 @@ struct ConvertToFixedPoint typedef int32 output_t; MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); return MPT_SC_RSHIFT_SIGNED(static_cast(val), (sizeof(input_t)*8-1-fractionalBits)); } }; @@ -986,8 +1154,26 @@ struct ConvertToFixedPoint } MPT_FORCEINLINE output_t operator() (input_t val) { - STATIC_ASSERT(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); - return mpt::saturate_cast(std::floor(val * factor + 0.5f)); + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); + return mpt::saturate_cast(MPT_SC_FASTROUND(val * factor)); + } +}; + +template +struct ConvertToFixedPoint +{ + typedef float64 input_t; + typedef int32 output_t; + const double factor; + MPT_FORCEINLINE ConvertToFixedPoint() + : factor( static_cast(1 << fractionalBits) ) + { + return; + } + MPT_FORCEINLINE output_t operator() (input_t val) + { + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(input_t)*8-1); + return mpt::saturate_cast(MPT_SC_FASTROUND(val * factor)); } }; @@ -1000,7 +1186,7 @@ struct ConversionChain { typedef typename Func1::input_t input_t; typedef typename Func2::output_t output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = Func1::input_inc; + static constexpr std::size_t input_inc = Func1::input_inc; Func1 func1; Func2 func2; MPT_FORCEINLINE output_t operator() (const input_t *inBuf) @@ -1016,6 +1202,69 @@ struct ConversionChain }; +template +struct ClipFixed +{ + typedef Tfixed input_t; + typedef Tfixed output_t; + MPT_FORCEINLINE Tfixed operator() (Tfixed val) + { + static_assert(fractionalBits >= 0 && fractionalBits <= sizeof(output_t)*8-1); + if constexpr(clipOutput) + { + constexpr Tfixed clip_max = (Tfixed(1) << fractionalBits) - Tfixed(1); + constexpr Tfixed clip_min = Tfixed(0) - (Tfixed(1) << fractionalBits); + if(val < clip_min) val = clip_min; + if(val > clip_max) val = clip_max; + return val; + } else + { + return val; + } + } +}; + + +template +struct ClipFloat; + +template +struct ClipFloat +{ + typedef float input_t; + typedef float output_t; + MPT_FORCEINLINE float operator() (float val) + { + if constexpr(clipOutput) + { + if(val < -1.0f) val = -1.0f; + if(val > 1.0f) val = 1.0f; + return val; + } else + { + return val; + } + } +}; + +template +struct ClipFloat +{ + typedef double input_t; + typedef double output_t; + MPT_FORCEINLINE double operator() (double val) + { + if constexpr(clipOutput) + { + if(val < -1.0) val = -1.0; + if(val > 1.0) val = 1.0; + return val; + } else + { + return val; + } + } +}; template @@ -1149,7 +1398,7 @@ struct NormalizationChain typedef typename Func1::output_t normalize_t; typedef typename Normalize::peak_t peak_t; typedef typename Func2::output_t output_t; - static MPT_CONSTEXPR11_VAR std::size_t input_inc = Func1::input_inc; + static constexpr std::size_t input_inc = Func1::input_inc; Func1 func1; Normalize normalize; Func2 func2; @@ -1181,6 +1430,7 @@ struct NormalizationChain #undef MPT_SC_RSHIFT_SIGNED #undef MPT_SC_LSHIFT_SIGNED +#undef MPT_SC_FASTROUND diff --git a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatCopy.h b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatCopy.h index 85906b237..c7547df2b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatCopy.h +++ b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleFormatCopy.h @@ -15,6 +15,7 @@ #include "../common/Endianness.h" #include "SampleFormatConverters.h" +#include "SampleFormat.h" OPENMPT_NAMESPACE_BEGIN @@ -77,28 +78,109 @@ void CopyInterleavedSampleStreams(typename SampleConversion::output_t * MPT_REST } - -template -void ConvertInterleavedFixedPointToInterleaved(Tsample * MPT_RESTRICT p, const Tfixed * MPT_RESTRICT mixbuffer, std::size_t channels, std::size_t count) +template +void ConvertBufferMixFixedToBuffer(TOutBuf outBuf, TInBuf inBuf, Tdither & dither, std::size_t channels, std::size_t count) { - SC::ConvertFixedPoint conv; - count *= channels; - for(std::size_t i = 0; i < count; ++i) - { - p[i] = conv(mixbuffer[i]); - } -} - -template -void ConvertInterleavedFixedPointToNonInterleaved(Tsample * const * const MPT_RESTRICT buffers, const Tfixed * MPT_RESTRICT mixbuffer, std::size_t channels, std::size_t count) -{ - SC::ConvertFixedPoint conv; + using TOutSample = typename std::remove_const::type; + using TInSample = typename std::remove_const::type; + MPT_ASSERT(inBuf.size_channels() >= channels); + MPT_ASSERT(outBuf.size_channels() >= channels); + MPT_ASSERT(inBuf.size_frames() >= count); + MPT_ASSERT(outBuf.size_frames() >= count); + constexpr int ditherBits = SampleFormat(SampleFormatTraits::sampleFormat()).IsInt() + ? SampleFormat(SampleFormatTraits::sampleFormat()).GetBitsPerSample() + : 0; + SC::ClipFixed clip; + SC::ConvertFixedPoint conv; for(std::size_t i = 0; i < count; ++i) { for(std::size_t channel = 0; channel < channels; ++channel) { - buffers[channel][i] = conv(*mixbuffer); - mixbuffer++; + outBuf(channel, i) = conv(clip(dither.template process(channel, inBuf(channel, i)))); + } + } +} + + +template +void ConvertBufferToBufferMixFixed(TOutBuf outBuf, TInBuf inBuf, std::size_t channels, std::size_t count) +{ + using TOutSample = typename std::remove_const::type; + using TInSample = typename std::remove_const::type; + MPT_ASSERT(inBuf.size_channels() >= channels); + MPT_ASSERT(outBuf.size_channels() >= channels); + MPT_ASSERT(inBuf.size_frames() >= count); + MPT_ASSERT(outBuf.size_frames() >= count); + SC::ConvertToFixedPoint conv; + for(std::size_t i = 0; i < count; ++i) + { + for(std::size_t channel = 0; channel < channels; ++channel) + { + outBuf(channel, i) = conv(inBuf(channel, i)); + } + } +} + + +template +void ConvertBufferMixFloatToBuffer(TOutBuf outBuf, TInBuf inBuf, Tdither & dither, std::size_t channels, std::size_t count) +{ + using TOutSample = typename std::remove_const::type; + using TInSample = typename std::remove_const::type; + MPT_ASSERT(inBuf.size_channels() >= channels); + MPT_ASSERT(outBuf.size_channels() >= channels); + MPT_ASSERT(inBuf.size_frames() >= count); + MPT_ASSERT(outBuf.size_frames() >= count); + constexpr int ditherBits = SampleFormat(SampleFormatTraits::sampleFormat()).IsInt() + ? SampleFormat(SampleFormatTraits::sampleFormat()).GetBitsPerSample() + : 0; + SC::ClipFloat clip; + SC::Convert conv; + for(std::size_t i = 0; i < count; ++i) + { + for(std::size_t channel = 0; channel < channels; ++channel) + { + outBuf(channel, i) = conv(clip(dither.template process(channel, inBuf(channel, i)))); + } + } +} + + +template +void ConvertBufferToBufferMixFloat(TOutBuf outBuf, TInBuf inBuf, std::size_t channels, std::size_t count) +{ + using TOutSample = typename std::remove_const::type; + using TInSample = typename std::remove_const::type; + MPT_ASSERT(inBuf.size_channels() >= channels); + MPT_ASSERT(outBuf.size_channels() >= channels); + MPT_ASSERT(inBuf.size_frames() >= count); + MPT_ASSERT(outBuf.size_frames() >= count); + SC::Convert conv; + for(std::size_t i = 0; i < count; ++i) + { + for(std::size_t channel = 0; channel < channels; ++channel) + { + outBuf(channel, i) = conv(inBuf(channel, i)); + } + } +} + + +template +void ConvertBufferToBuffer(TOutBuf outBuf, TInBuf inBuf, std::size_t channels, std::size_t count) +{ + using TOutSample = typename std::remove_const::type; + using TInSample = typename std::remove_const::type; + MPT_ASSERT(inBuf.size_channels() >= channels); + MPT_ASSERT(outBuf.size_channels() >= channels); + MPT_ASSERT(inBuf.size_frames() >= count); + MPT_ASSERT(outBuf.size_frames() >= count); + SC::Convert conv; + for(std::size_t i = 0; i < count; ++i) + { + for(std::size_t channel = 0; channel < channels; ++channel) + { + outBuf(channel, i) = conv(inBuf(channel, i)); } } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundbase/SampleTypes.h b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleTypes.h new file mode 100644 index 000000000..a88e98cfb --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundbase/SampleTypes.h @@ -0,0 +1,59 @@ +/* + * SampleTypes.h + * ------------- + * Purpose: Basic audio sample types. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include "BuildSettings.h" + +#include "../common/mptBaseMacros.h" +#include "../common/mptBaseTypes.h" + +#include +#include + + +OPENMPT_NAMESPACE_BEGIN + + +using AudioSampleInt = int16; +using AudioSampleFloat = nativefloat; + +using AudioSample = mpt::select_type::is_hard, AudioSampleFloat, AudioSampleInt>::type; + + +template +struct FixedPointSampleTraits +{ + static_assert(std::is_integral::value); + static_assert(std::is_signed::value); + static_assert((sizeof(Tsample) * 8u) - 1 > MIX_HEADROOM_BITS); + static_assert((sizeof(Tsample) * 8u) - 1 > FILTER_HEADROOM_BITS); + using sample_type = Tsample; + enum class sample_type_strong : sample_type {}; + static constexpr int mix_headroom_bits() noexcept { return static_cast(MIX_HEADROOM_BITS); } + static constexpr int mix_precision_bits() noexcept { return static_cast((sizeof(Tsample) * 8) - MIX_HEADROOM_BITS); } // including sign bit + static constexpr int mix_fractional_bits() noexcept { return static_cast((sizeof(Tsample) * 8) - 1 - MIX_HEADROOM_BITS); } // excluding sign bit + static constexpr sample_type mix_clip_max() noexcept { return ((sample_type(1) << mix_fractional_bits()) - sample_type(1)); } + static constexpr sample_type mix_clip_min() noexcept { return -((sample_type(1) << mix_fractional_bits()) - sample_type(1)); } + static constexpr int filter_headroom_bits() noexcept { return static_cast(FILTER_HEADROOM_BITS); } + static constexpr int filter_precision_bits() noexcept { return static_cast((sizeof(Tsample) * 8) - FILTER_HEADROOM_BITS); } // including sign bit + static constexpr int filter_fractional_bits() noexcept { return static_cast((sizeof(Tsample) * 8) - 1 - FILTER_HEADROOM_BITS); } // excluding sign bit + template + static constexpr Tfloat mix_scale() noexcept { return static_cast(sample_type(1) << mix_fractional_bits()); } +}; + +using MixSampleIntTraits = FixedPointSampleTraits; + +using MixSampleInt = MixSampleIntTraits::sample_type; +using MixSampleFloat = AudioSampleFloat; + +using MixSample = mpt::select_type::is_hard, MixSampleFloat, MixSampleInt>::type; + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.cpp b/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.cpp index 228236d9e..0da829da2 100644 --- a/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.cpp +++ b/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.cpp @@ -10,7 +10,8 @@ #include "stdafx.h" -#include "../sounddsp/DSP.h" +#include "DSP.h" +#include "../soundbase/SampleTypes.h" #include OPENMPT_NAMESPACE_BEGIN @@ -171,8 +172,8 @@ void CMegaBass::Initialize(bool bReset, DWORD MixingFreq) int32 a1 = 0, b0 = 1024, b1 = 0; int nXBassCutOff = 50 + (m_Settings.m_nXBassRange+2) * 20; int nXBassGain = m_Settings.m_nXBassDepth; - nXBassGain = mpt::clamp(nXBassGain, 2, 8); - nXBassCutOff = mpt::clamp(nXBassCutOff, 60, 600); + nXBassGain = std::clamp(nXBassGain, 2, 8); + nXBassCutOff = std::clamp(nXBassCutOff, 60, 600); ShelfEQ(1024, a1, b0, b1, nXBassCutOff, MixingFreq, 1.0f + (1.0f/16.0f) * (0x300 >> nXBassGain), 1.0f, @@ -418,6 +419,62 @@ void CSurround::SetSurroundParameters(uint32 nDepth, uint32 nDelay) } +BitCrushSettings::BitCrushSettings() + : m_Bits(8) +{ + return; +} + + +BitCrush::BitCrush() +{ +} + + +void BitCrush::Initialize(bool bReset, DWORD MixingFreq) +{ + MPT_UNREFERENCED_PARAMETER(bReset); + MPT_UNREFERENCED_PARAMETER(MixingFreq); +} + + +void BitCrush::Process(int * MixSoundBuffer, int * MixRearBuffer, int count, uint32 nChannels) +{ + if(m_Settings.m_Bits <= 0) + { + return; + } + if(m_Settings.m_Bits > MixSampleIntTraits::mix_precision_bits()) + { + return; + } + unsigned int mask = ~((1u << (MixSampleIntTraits::mix_precision_bits() - m_Settings.m_Bits)) - 1u); + if(nChannels == 4) + { + for(int frame = 0; frame < count; ++frame) + { + MixSoundBuffer[frame*2 + 0] &= mask; + MixSoundBuffer[frame*2 + 1] &= mask; + MixRearBuffer[frame*2 + 0] &= mask; + MixRearBuffer[frame*2 + 1] &= mask; + } + } else if(nChannels == 2) + { + for(int frame = 0; frame < count; ++frame) + { + MixSoundBuffer[frame*2 + 0] &= mask; + MixSoundBuffer[frame*2 + 1] &= mask; + } + } else if(nChannels == 1) + { + for(int frame = 0; frame < count; ++frame) + { + MixSoundBuffer[frame] &= mask; + } + } +} + + #else diff --git a/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.h b/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.h index 982ba6a1f..825db80f6 100644 --- a/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.h +++ b/Frameworks/OpenMPT/OpenMPT/sounddsp/DSP.h @@ -43,6 +43,13 @@ public: }; +struct BitCrushSettings +{ + int m_Bits; + BitCrushSettings(); +}; + + class CSurround { public: @@ -116,6 +123,19 @@ public: }; +class BitCrush +{ +public: + BitCrushSettings m_Settings; +public: + BitCrush(); +public: + void SetSettings(const BitCrushSettings &settings) { m_Settings = settings; } + void Initialize(bool bReset, DWORD MixingFreq); + void Process(int * MixSoundBuffer, int * MixRearBuffer, int count, uint32 nChannels); +}; + + #endif // NO_DSP diff --git a/Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.cpp b/Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.cpp index 1b39297fe..5b3f32d1e 100644 --- a/Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.cpp +++ b/Frameworks/OpenMPT/OpenMPT/sounddsp/EQ.cpp @@ -27,7 +27,7 @@ OPENMPT_NAMESPACE_BEGIN -static const UINT gEqLinearToDB[33] = +static constexpr UINT gEqLinearToDB[33] = { 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 52, 55, 58, 61, @@ -35,7 +35,7 @@ static const UINT gEqLinearToDB[33] = 160, 172, 184, 196, 208, 220, 232, 244, 256 }; -static const EQBANDSTRUCT gEQDefaults[MAX_EQ_BANDS*2] = +static constexpr EQBANDSTRUCT gEQDefaults[MAX_EQ_BANDS*2] = { // Default: Flat EQ {0,0,0,0,0, 0,0,0,0, 1, 120, false}, @@ -69,7 +69,7 @@ static const EQBANDSTRUCT gEQDefaults[MAX_EQ_BANDS*2] = #define PBS_Y1 DWORD PTR [eax + EQBANDSTRUCT.y1] #define PBS_Y2 DWORD PTR [eax + EQBANDSTRUCT.y2] -static void EQFilter(EQBANDSTRUCT *pbs, float32 *pbuffer, UINT nCount) +static void X86_EQFilter(EQBANDSTRUCT *pbs, float32 *pbuffer, UINT nCount) { _asm { mov eax, pbs // eax = pbs @@ -115,96 +115,11 @@ EQ_Loop: } -#ifdef ENABLE_X86_AMD - -static void AMD_StereoEQ(EQBANDSTRUCT *pbl, EQBANDSTRUCT *pbr, float32 *pbuffer, UINT nCount) -{ - float tmp[16]; - - _asm { - mov eax, pbl - mov edx, pbr - mov ebx, pbuffer - mov ecx, nCount - lea edi, [tmp+8] - and edi, 0xfffffff8 - movd mm7, [eax+EQBANDSTRUCT.a0] - movd mm0, [edx+EQBANDSTRUCT.a0] - movd mm6, [eax+EQBANDSTRUCT.a1] - movd mm1, [edx+EQBANDSTRUCT.a1] - punpckldq mm7, mm0 - punpckldq mm6, mm1 - movq [edi], mm7 // [edi] = a0 - movq [edi+8], mm6 // [edi+8] = a1 - movd mm5, [eax+EQBANDSTRUCT.a2] - movd mm0, [edx+EQBANDSTRUCT.a2] - movd mm4, [eax+EQBANDSTRUCT.b1] - movd mm1, [edx+EQBANDSTRUCT.b1] - movd mm3, [eax+EQBANDSTRUCT.b2] - movd mm2, [edx+EQBANDSTRUCT.b2] - punpckldq mm5, mm0 - punpckldq mm4, mm1 - punpckldq mm3, mm2 - movq [edi+16], mm5 // [edi+16] = a2 - movq [edi+24], mm4 // [edi+24] = b1 - movq [edi+32], mm3 // [edi+32] = b2 - movd mm4, [eax+EQBANDSTRUCT.x1] - movd mm0, [edx+EQBANDSTRUCT.x1] - movd mm5, [eax+EQBANDSTRUCT.x2] - movd mm1, [edx+EQBANDSTRUCT.x2] - punpckldq mm4, mm0 // mm4 = x1 - punpckldq mm5, mm1 // mm5 = x2 - movd mm6, [eax+EQBANDSTRUCT.y1] - movd mm2, [edx+EQBANDSTRUCT.y1] - movd mm7, [eax+EQBANDSTRUCT.y2] - movd mm3, [edx+EQBANDSTRUCT.y2] - punpckldq mm6, mm2 // mm6 = y1 - punpckldq mm7, mm3 // mm7 = y2 -mainloop: - movq mm0, [ebx] - movq mm3, [edi+8] - add ebx, 8 - movq mm1, [edi+16] - pfmul mm3, mm4 // x1 * a1 - movq mm2, [edi+32] - pfmul mm1, mm5 // x2 * a2 - movq mm5, mm4 // x2 = x1 - pfmul mm2, mm7 // y2 * b2 - movq mm7, mm6 // y2 = y1 - pfmul mm6, [edi+24] // y1 * b1 - movq mm4, mm0 // x1 = x - pfmul mm0, [edi] // x * a0 - pfadd mm6, mm1 // x2*a2 + y1*b1 - pfadd mm6, mm2 // x2*a2 + y1*b1 + y2*b2 - pfadd mm6, mm3 // x1*a1 + x2*a2 + y1*b1 + y2*b2 - pfadd mm6, mm0 // x*a0 + x1*a1 + x2*a2 + y1*b1 + y2*b2 - dec ecx - movq [ebx-8], mm6 - jnz mainloop - movd [eax+EQBANDSTRUCT.x1], mm4 - punpckhdq mm4, mm4 - movd [eax+EQBANDSTRUCT.x2], mm5 - punpckhdq mm5, mm5 - movd [eax+EQBANDSTRUCT.y1], mm6 - punpckhdq mm6, mm6 - movd [eax+EQBANDSTRUCT.y2], mm7 - punpckhdq mm7, mm7 - movd [edx+EQBANDSTRUCT.x1], mm4 - movd [edx+EQBANDSTRUCT.x2], mm5 - movd [edx+EQBANDSTRUCT.y1], mm6 - movd [edx+EQBANDSTRUCT.y2], mm7 - emms - } -} - -#endif // ENABLE_X86_AMD - - #if defined(ENABLE_X86) && defined(ENABLE_SSE) static void SSE_StereoEQ(EQBANDSTRUCT *pbl, EQBANDSTRUCT *pbr, float32 *pbuffer, UINT nCount) { - static const float gk1 = 1.0f; + static constexpr float gk1 = 1.0f; _asm { mov eax, pbl mov edx, pbr @@ -299,7 +214,7 @@ done:; #pragma warning(pop) #endif // MPT_COMPILER_MSVC -#else +#endif static void EQFilter(EQBANDSTRUCT *pbs, float32 *pbuffer, UINT nCount) { @@ -315,15 +230,21 @@ static void EQFilter(EQBANDSTRUCT *pbs, float32 *pbuffer, UINT nCount) } } -#endif - void CEQ::ProcessMono(int *pbuffer, float *MixFloatBuffer, UINT nCount) { MonoMixToFloat(pbuffer, MixFloatBuffer, nCount, 1.0f/MIXING_SCALEF); for (UINT b=0; b -#endif #ifdef ENABLE_SSE2 #include #endif @@ -31,35 +28,21 @@ OPENMPT_NAMESPACE_BEGIN #ifndef NO_REVERB -#ifdef ENABLE_MMX -// Load two 32-bit values -static MPT_FORCEINLINE __m64 Load64MMX(const int32 *x) { return _mm_set_pi32(x[1], x[0]); } -// Load four 16-bit values -static MPT_FORCEINLINE __m64 Load64MMX(const LR16 (&x)[2]) { return Load64MMX(&x->lr); } -// Store 64-bit value from register (MSVC does not have_mm_cvtsi64_si64x) - macro to avoid emms warnings -#define Store64MMX(dst, src) \ - MPT_DO \ - { \ - STATIC_ASSERT(sizeof((dst)[0]) == 4); \ - (dst)[0] = _mm_cvtsi64_si32(src); \ - (dst)[1] = _mm_cvtsi64_si32(_mm_unpackhi_pi32(src, src)); \ - } MPT_WHILE_0 -#endif #ifdef ENABLE_SSE2 // Load two 32-bit values static MPT_FORCEINLINE __m128i Load64SSE(const int32 *x) { return _mm_loadl_epi64(reinterpret_cast(x)); } // Load four 16-bit values -static MPT_FORCEINLINE __m128i Load64SSE(const LR16 (&x)[2]) { return _mm_loadl_epi64(reinterpret_cast(&x)); } +static MPT_FORCEINLINE __m128i Load64SSE(const LR16 (&x)[2]) { return _mm_loadl_epi64(&reinterpret_cast(x)); } // Store two 32-bit or four 16-bit values from register static MPT_FORCEINLINE void Store64SSE(int32 *dst, __m128i src) { return _mm_storel_epi64(reinterpret_cast<__m128i *>(dst), src); } -static MPT_FORCEINLINE void Store64SSE(LR16 *dst, __m128i src) { return _mm_storel_epi64(reinterpret_cast<__m128i *>(dst), src); } +static MPT_FORCEINLINE void Store64SSE(LR16 (&dst)[2], __m128i src) { return _mm_storel_epi64(&reinterpret_cast<__m128i &>(dst), src); } #endif CReverb::CReverb() { // Shared reverb state - InitMixBuffer(MixReverbBuffer, static_cast(mpt::size(MixReverbBuffer))); + InitMixBuffer(MixReverbBuffer, static_cast(std::size(MixReverbBuffer))); // Reverb mix buffers MemsetZero(g_RefDelay); @@ -92,64 +75,56 @@ static int32 mBToLinear(int32 scale, int32 value_mB) return mpt::saturate_round(mBToLinear(value_mB) * scale); } - -struct SNDMIX_REVERB_PROPERTIES +static constexpr std::pair ReverbPresets[NUM_REVERBTYPES] = { - int32 lRoom; // [-10000, 0] default: -10000 mB - int32 lRoomHF; // [-10000, 0] default: 0 mB - float flDecayTime; // [0.1, 20.0] default: 1.0 s - float flDecayHFRatio; // [0.1, 2.0] default: 0.5 - int32 lReflections; // [-10000, 1000] default: -10000 mB - float flReflectionsDelay; // [0.0, 0.3] default: 0.02 s - int32 lReverb; // [-10000, 2000] default: -10000 mB - float flReverbDelay; // [0.0, 0.1] default: 0.04 s - float flDiffusion; // [0.0, 100.0] default: 100.0 % - float flDensity; // [0.0, 100.0] default: 100.0 % + // Examples simulating General MIDI 2'musical' reverb presets + // Name (Decay time) Description + // Plate (1.3s) A plate reverb simulation. + {{ -1000, -200, 1.30f,0.90f, 0,0.002f, 0,0.010f,100.0f, 75.0f }, "GM Plate"}, + // Small Room (1.1s) A small size room with a length of 5m or so. + {{ -1000, -600, 1.10f,0.83f, -400,0.005f, 500,0.010f,100.0f,100.0f }, "GM Small Room"}, + // Medium Room (1.3s) A medium size room with a length of 10m or so. + {{ -1000, -600, 1.30f,0.83f, -1000,0.010f, -200,0.020f,100.0f,100.0f }, "GM Medium Room"}, + // Large Room (1.5s) A large size room suitable for live performances. + {{ -1000, -600, 1.50f,0.83f, -1600,0.020f, -1000,0.040f,100.0f,100.0f }, "GM Large Room"}, + // Medium Hall (1.8s) A medium size concert hall. + {{ -1000, -600, 1.80f,0.70f, -1300,0.015f, -800,0.030f,100.0f,100.0f }, "GM Medium Hall"}, + // Large Hall (1.8s) A large size concert hall suitable for a full orchestra. + {{ -1000, -600, 1.80f,0.70f, -2000,0.030f, -1400,0.060f,100.0f,100.0f }, "GM Large Hall"}, + + {{ -1000, -100, 1.49f,0.83f, -2602,0.007f, 200,0.011f,100.0f,100.0f }, "Generic"}, + {{ -1000,-6000, 0.17f,0.10f, -1204,0.001f, 207,0.002f,100.0f,100.0f }, "Padded Cell"}, + {{ -1000, -454, 0.40f,0.83f, -1646,0.002f, 53,0.003f,100.0f,100.0f }, "Room"}, + {{ -1000,-1200, 1.49f,0.54f, -370,0.007f, 1030,0.011f,100.0f, 60.0f }, "Bathroom"}, + {{ -1000,-6000, 0.50f,0.10f, -1376,0.003f, -1104,0.004f,100.0f,100.0f }, "Living Room"}, + {{ -1000, -300, 2.31f,0.64f, -711,0.012f, 83,0.017f,100.0f,100.0f }, "Stone Room"}, + {{ -1000, -476, 4.32f,0.59f, -789,0.020f, -289,0.030f,100.0f,100.0f }, "Auditorium"}, + {{ -1000, -500, 3.92f,0.70f, -1230,0.020f, -2,0.029f,100.0f,100.0f }, "Concert Hall"}, + {{ -1000, 0, 2.91f,1.30f, -602,0.015f, -302,0.022f,100.0f,100.0f }, "Cave"}, + {{ -1000, -698, 7.24f,0.33f, -1166,0.020f, 16,0.030f,100.0f,100.0f }, "Arena"}, + {{ -1000,-1000,10.05f,0.23f, -602,0.020f, 198,0.030f,100.0f,100.0f }, "Hangar"}, + {{ -1000,-4000, 0.30f,0.10f, -1831,0.002f, -1630,0.030f,100.0f,100.0f }, "Carpeted Hallway"}, + {{ -1000, -300, 1.49f,0.59f, -1219,0.007f, 441,0.011f,100.0f,100.0f }, "Hallway"}, + {{ -1000, -237, 2.70f,0.79f, -1214,0.013f, 395,0.020f,100.0f,100.0f }, "Stone Corridor"}, + {{ -1000, -270, 1.49f,0.86f, -1204,0.007f, -4,0.011f,100.0f,100.0f }, "Alley"}, + {{ -1000,-3300, 1.49f,0.54f, -2560,0.162f, -613,0.088f, 79.0f,100.0f }, "Forest"}, + {{ -1000, -800, 1.49f,0.67f, -2273,0.007f, -2217,0.011f, 50.0f,100.0f }, "City"}, + {{ -1000,-2500, 1.49f,0.21f, -2780,0.300f, -2014,0.100f, 27.0f,100.0f }, "Mountains"}, + {{ -1000,-1000, 1.49f,0.83f,-10000,0.061f, 500,0.025f,100.0f,100.0f }, "Quarry"}, + {{ -1000,-2000, 1.49f,0.50f, -2466,0.179f, -2514,0.100f, 21.0f,100.0f }, "Plain"}, + {{ -1000, 0, 1.65f,1.50f, -1363,0.008f, -1153,0.012f,100.0f,100.0f }, "Parking Lot"}, + {{ -1000,-1000, 2.81f,0.14f, 429,0.014f, 648,0.021f, 80.0f, 60.0f }, "Sewer Pipe"}, + {{ -1000,-4000, 1.49f,0.10f, -449,0.007f, 1700,0.011f,100.0f,100.0f }, "Underwater"}, }; -struct SNDMIX_RVBPRESET +mpt::ustring GetReverbPresetName(uint32 preset) { - SNDMIX_REVERB_PROPERTIES Preset; - const MPT_UCHAR_TYPE *name; -}; + return (preset < NUM_REVERBTYPES) ? mpt::ToUnicode(mpt::Charset::ASCII, ReverbPresets[preset].second) : mpt::ustring{}; +} - -static const SNDMIX_RVBPRESET gRvbPresets[NUM_REVERBTYPES] = +const SNDMIX_REVERB_PROPERTIES *GetReverbPreset(uint32 preset) { - {{ SNDMIX_REVERB_PRESET_PLATE }, UL_("GM Plate")}, - {{ SNDMIX_REVERB_PRESET_SMALLROOM }, UL_("GM Small Room")}, - {{ SNDMIX_REVERB_PRESET_MEDIUMROOM }, UL_("GM Medium Room")}, - {{ SNDMIX_REVERB_PRESET_LARGEROOM }, UL_("GM Large Room")}, - {{ SNDMIX_REVERB_PRESET_MEDIUMHALL }, UL_("GM Medium Hall")}, - {{ SNDMIX_REVERB_PRESET_LARGEHALL }, UL_("GM Large Hall")}, - {{ SNDMIX_REVERB_PRESET_GENERIC }, UL_("Generic")}, - {{ SNDMIX_REVERB_PRESET_PADDEDCELL }, UL_("Padded Cell")}, - {{ SNDMIX_REVERB_PRESET_ROOM }, UL_("Room")}, - {{ SNDMIX_REVERB_PRESET_BATHROOM }, UL_("Bathroom")}, - {{ SNDMIX_REVERB_PRESET_LIVINGROOM }, UL_("Living Room")}, - {{ SNDMIX_REVERB_PRESET_STONEROOM }, UL_("Stone Room")}, - {{ SNDMIX_REVERB_PRESET_AUDITORIUM }, UL_("Auditorium")}, - {{ SNDMIX_REVERB_PRESET_CONCERTHALL }, UL_("Concert Hall")}, - {{ SNDMIX_REVERB_PRESET_CAVE }, UL_("Cave")}, - {{ SNDMIX_REVERB_PRESET_ARENA }, UL_("Arena")}, - {{ SNDMIX_REVERB_PRESET_HANGAR }, UL_("Hangar")}, - {{ SNDMIX_REVERB_PRESET_CARPETEDHALLWAY }, UL_("Carpeted Hallway")}, - {{ SNDMIX_REVERB_PRESET_HALLWAY }, UL_("Hallway")}, - {{ SNDMIX_REVERB_PRESET_STONECORRIDOR }, UL_("Stone Corridor")}, - {{ SNDMIX_REVERB_PRESET_ALLEY }, UL_("Alley")}, - {{ SNDMIX_REVERB_PRESET_FOREST }, UL_("Forest")}, - {{ SNDMIX_REVERB_PRESET_CITY }, UL_("City")}, - {{ SNDMIX_REVERB_PRESET_MOUNTAINS }, UL_("Mountains")}, - {{ SNDMIX_REVERB_PRESET_QUARRY }, UL_("Quarry")}, - {{ SNDMIX_REVERB_PRESET_PLAIN }, UL_("Plain")}, - {{ SNDMIX_REVERB_PRESET_PARKINGLOT }, UL_("Parking Lot")}, - {{ SNDMIX_REVERB_PRESET_SEWERPIPE }, UL_("Sewer Pipe")}, - {{ SNDMIX_REVERB_PRESET_UNDERWATER }, UL_("Underwater")}, -}; - -mpt::ustring GetReverbPresetName(uint32 nPreset) -{ - return (nPreset < NUM_REVERBTYPES) ? mpt::ustring(gRvbPresets[nPreset].name) : mpt::ustring(); + return (preset < NUM_REVERBTYPES) ? &ReverbPresets[preset].first : nullptr; } ////////////////////////////////////////////////////////////////////////// @@ -301,7 +276,7 @@ void CReverb::Shutdown() void CReverb::Initialize(bool bReset, uint32 MixingFreq) { if (m_Settings.m_nReverbType >= NUM_REVERBTYPES) m_Settings.m_nReverbType = 0; - const SNDMIX_REVERB_PROPERTIES *rvbPreset = &gRvbPresets[m_Settings.m_nReverbType].Preset; + const SNDMIX_REVERB_PROPERTIES *rvbPreset = &ReverbPresets[m_Settings.m_nReverbType].first; if ((rvbPreset != m_currentPreset) || (bReset)) { @@ -418,7 +393,7 @@ mixsample_t *CReverb::GetReverbSendBuffer(uint32 nSamples) // Reverb -void CReverb::Process(mixsample_t *MixSoundBuffer, uint32 nSamples) +void CReverb::Process(MixSampleInt *MixSoundBuffer, uint32 nSamples) { if((!gnReverbSend) && (!gnReverbSamples)) { // no data is sent to reverb and reverb decayed completely @@ -618,28 +593,27 @@ void CReverb::ReverbProcessPostFiltering2x(const int32 * MPT_RESTRICT pRvb, int3 // Stereo Add + DC removal void CReverb::ReverbProcessPostFiltering1x(const int32 * MPT_RESTRICT pRvb, int32 * MPT_RESTRICT pDry, uint32 nSamples) { -#ifdef ENABLE_MMX - if(GetProcSupport() & PROCSUPPORT_MMX) +#ifdef ENABLE_SSE2 + if(GetProcSupport() & PROCSUPPORT_SSE2) { - __m64 nDCRRvb_Y1 = Load64MMX(gnDCRRvb_Y1); - __m64 nDCRRvb_X1 = Load64MMX(gnDCRRvb_X1); - __m64 in = _mm_set1_pi32(0); + __m128i nDCRRvb_Y1 = Load64SSE(gnDCRRvb_Y1); + __m128i nDCRRvb_X1 = Load64SSE(gnDCRRvb_X1); + __m128i in = _mm_set1_epi32(0); while(nSamples--) { - in = Load64MMX(pRvb); + in = Load64SSE(pRvb); pRvb += 2; // x(n-1) - x(n) - __m64 diff = _mm_sub_pi32(nDCRRvb_X1, in); - nDCRRvb_X1 = _mm_add_pi32(nDCRRvb_Y1, _mm_sub_pi32(_mm_srai_pi32(diff, DCR_AMOUNT + 1), diff)); - __m64 out = _mm_add_pi32(Load64MMX(pDry), nDCRRvb_X1); - nDCRRvb_Y1 = _mm_sub_pi32(nDCRRvb_X1, _mm_srai_pi32(nDCRRvb_X1, DCR_AMOUNT)); + __m128i diff = _mm_sub_epi32(nDCRRvb_X1, in); + nDCRRvb_X1 = _mm_add_epi32(nDCRRvb_Y1, _mm_sub_epi32(_mm_srai_epi32(diff, DCR_AMOUNT + 1), diff)); + __m128i out = _mm_add_epi32(Load64SSE(pDry), nDCRRvb_X1); + nDCRRvb_Y1 = _mm_sub_epi32(nDCRRvb_X1, _mm_srai_epi32(nDCRRvb_X1, DCR_AMOUNT)); nDCRRvb_X1 = in; - Store64MMX(pDry, out); + Store64SSE(pDry, out); pDry += 2; } - Store64MMX(gnDCRRvb_X1, in); - Store64MMX(gnDCRRvb_Y1, nDCRRvb_Y1); - _mm_empty(); + Store64SSE(gnDCRRvb_X1, in); + Store64SSE(gnDCRRvb_Y1, nDCRRvb_Y1); return; } #endif @@ -681,24 +655,23 @@ void CReverb::ReverbProcessPostFiltering1x(const int32 * MPT_RESTRICT pRvb, int3 void CReverb::ReverbDCRemoval(int32 * MPT_RESTRICT pBuffer, uint32 nSamples) { -#ifdef ENABLE_MMX - if(GetProcSupport() & PROCSUPPORT_MMX) +#ifdef ENABLE_SSE2 + if(GetProcSupport() & PROCSUPPORT_SSE2) { - __m64 nDCRRvb_Y1 = Load64MMX(gnDCRRvb_Y1); - __m64 nDCRRvb_X1 = Load64MMX(gnDCRRvb_X1); + __m128i nDCRRvb_Y1 = Load64SSE(gnDCRRvb_Y1); + __m128i nDCRRvb_X1 = Load64SSE(gnDCRRvb_X1); while(nSamples--) { - __m64 in = Load64MMX(pBuffer); - __m64 diff = _mm_sub_pi32(nDCRRvb_X1, in); - __m64 out = _mm_add_pi32(nDCRRvb_Y1, _mm_sub_pi32(_mm_srai_pi32(diff, DCR_AMOUNT + 1), diff)); - Store64MMX(pBuffer, out); + __m128i in = Load64SSE(pBuffer); + __m128i diff = _mm_sub_epi32(nDCRRvb_X1, in); + __m128i out = _mm_add_epi32(nDCRRvb_Y1, _mm_sub_epi32(_mm_srai_epi32(diff, DCR_AMOUNT + 1), diff)); + Store64SSE(pBuffer, out); pBuffer += 2; - nDCRRvb_Y1 = _mm_sub_pi32(out, _mm_srai_pi32(out, DCR_AMOUNT)); + nDCRRvb_Y1 = _mm_sub_epi32(out, _mm_srai_epi32(out, DCR_AMOUNT)); nDCRRvb_X1 = in; } - Store64MMX(gnDCRRvb_X1, nDCRRvb_X1); - Store64MMX(gnDCRRvb_Y1, nDCRRvb_Y1); - _mm_empty(); + Store64SSE(gnDCRRvb_X1, nDCRRvb_X1); + Store64SSE(gnDCRRvb_Y1, nDCRRvb_Y1); return; } #endif @@ -773,34 +746,6 @@ void CReverb::ProcessPreDelay(SWRvbRefDelay * MPT_RESTRICT pPreDelay, const int3 pPreDelay->History.lr = _mm_cvtsi128_si32(history); return; } -#endif -#ifdef ENABLE_MMX - if(GetProcSupport() & PROCSUPPORT_MMX) - { - __m64 coeffs = _mm_cvtsi32_si64(pPreDelay->nCoeffs.lr); - __m64 history = _mm_cvtsi32_si64(pPreDelay->History.lr); - __m64 preDifCoeffs = _mm_cvtsi32_si64(pPreDelay->nPreDifCoeffs.lr); - while(nSamples--) - { - __m64 in32 = Load64MMX(pIn); // 16-bit unsaturated reverb input [ r | l ] - __m64 inSat = _mm_packs_pi32(in32, in32); // [ r | l | r | l ] (16-bit saturated) - pIn += 2; - // Low-pass - __m64 lp = _mm_mulhi_pi16(_mm_subs_pi16(history, inSat), coeffs); - __m64 preDif = _mm_cvtsi32_si64(pPreDelay->PreDifBuffer[preDifPos].lr); - history = _mm_adds_pi16(_mm_adds_pi16(lp, lp), inSat); - // Pre-Diffusion - preDifPos = (preDifPos + 1) & SNDMIX_PREDIFFUSION_DELAY_MASK; - delayPos = (delayPos + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; - __m64 preDif2 = _mm_subs_pi16(history, _mm_mulhi_pi16(preDif, preDifCoeffs)); - pPreDelay->PreDifBuffer[preDifPos].lr = _mm_cvtsi64_si32(preDif2); - pPreDelay->RefDelayBuffer[delayPos].lr = _mm_cvtsi64_si32(_mm_adds_pi16(_mm_mulhi_pi16(preDifCoeffs, preDif2), preDif)); - } - pPreDelay->nPreDifPos = preDifPos; - pPreDelay->History.lr = _mm_cvtsi64_si32(history); - _mm_empty(); - return; - } #endif const int32 coeffsL = pPreDelay->nCoeffs.c.l, coeffsR = pPreDelay->nCoeffs.c.r; const int32 preDifCoeffsL = pPreDelay->nPreDifCoeffs.c.l, preDifCoeffsR = pPreDelay->nPreDifCoeffs.c.r; @@ -895,75 +840,6 @@ void CReverb::ProcessReflections(SWRvbRefDelay * MPT_RESTRICT pPreDelay, LR16 * } return; } -#endif -#ifdef ENABLE_MMX - if(GetProcSupport() & PROCSUPPORT_MMX) - { - // First stage - uint32 numSamples = nSamples; - const LR16 *refDelayBuffer = pPreDelay->RefDelayBuffer; - int pos1 = pPreDelay->nDelayPos - pPreDelay->Reflections[0].Delay - 1; - int pos2 = pPreDelay->nDelayPos - pPreDelay->Reflections[1].Delay - 1; - int pos3 = pPreDelay->nDelayPos - pPreDelay->Reflections[2].Delay - 1; - int pos4 = pPreDelay->nDelayPos - pPreDelay->Reflections[3].Delay - 1; - __m64 gain1 = Load64MMX(pPreDelay->Reflections[0].Gains); - __m64 gain2 = Load64MMX(pPreDelay->Reflections[1].Gains); - __m64 gain3 = Load64MMX(pPreDelay->Reflections[2].Gains); - __m64 gain4 = Load64MMX(pPreDelay->Reflections[3].Gains); - while(numSamples--) - { - pos1 = (pos1 + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; - pos2 = (pos2 + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; - pos3 = (pos3 + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; - pos4 = (pos4 + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; - __m64 ref1 = _mm_cvtsi32_si64(refDelayBuffer[pos1].lr); // [0 | 0 | r | l ] - __m64 ref2 = _mm_cvtsi32_si64(refDelayBuffer[pos2].lr); - __m64 ref3 = _mm_cvtsi32_si64(refDelayBuffer[pos3].lr); - __m64 ref4 = _mm_cvtsi32_si64(refDelayBuffer[pos4].lr); - __m64 refOut = _mm_srai_pi32(_mm_add_pi32( - _mm_add_pi32(_mm_madd_pi16(_mm_unpacklo_pi32(ref1, ref1), gain1), _mm_madd_pi16(_mm_unpacklo_pi32(ref2, ref2), gain2)), - _mm_add_pi32(_mm_madd_pi16(_mm_unpacklo_pi32(ref3, ref3), gain3), _mm_madd_pi16(_mm_unpacklo_pi32(ref4, ref4), gain4))), - 15); - pRefOut->lr = _mm_cvtsi64_si32(_mm_packs_pi32(refOut, refOut)); - pRefOut++; - } - - // Second stage - numSamples = nSamples; - pRefOut -= nSamples; - - __m64 refGain = _mm_unpacklo_pi16(_mm_cvtsi32_si64(pPreDelay->ReflectionsGain.lr), _mm_cvtsi32_si64(0)); // [0 | g_r | 0 | g_l] - refGain = _mm_srai_pi32(refGain, 3); // For 28-bit final output: 16+15-3 = 28 - int pos5 = pPreDelay->nDelayPos - pPreDelay->Reflections[4].Delay - 1; - int pos6 = pPreDelay->nDelayPos - pPreDelay->Reflections[5].Delay - 1; - int pos7 = pPreDelay->nDelayPos - pPreDelay->Reflections[6].Delay - 1; - __m64 gain5 = Load64MMX(pPreDelay->Reflections[4].Gains); - __m64 gain6 = Load64MMX(pPreDelay->Reflections[5].Gains); - __m64 gain7 = Load64MMX(pPreDelay->Reflections[6].Gains); - while(numSamples--) - { - pos5 = (pos5 + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; - pos6 = (pos6 + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; - pos7 = (pos7 + 1) & SNDMIX_REFLECTIONS_DELAY_MASK; - __m64 ref5 = _mm_cvtsi32_si64(refDelayBuffer[pos5].lr); // [0 | 0 | r | l ] - __m64 ref6 = _mm_cvtsi32_si64(refDelayBuffer[pos6].lr); - __m64 ref7 = _mm_cvtsi32_si64(refDelayBuffer[pos7].lr); - __m64 refPrev = _mm_cvtsi32_si64(pRefOut->lr); // output of previous reflections - __m64 refOut = _mm_srai_pi32(_mm_add_pi32( - _mm_add_pi32(_mm_madd_pi16(_mm_unpacklo_pi32(ref5, ref5), gain5), _mm_madd_pi16(_mm_unpacklo_pi32(ref7, ref7), gain7)), - _mm_madd_pi16(_mm_unpacklo_pi32(ref6, ref6), gain6)), - 15); - refOut = _mm_adds_pi16(_mm_packs_pi32(refOut, refOut), refPrev); - pRefOut->lr = _mm_cvtsi64_si32(refOut); // late reverb stereo input - pRefOut++; - __m64 out = _mm_madd_pi16(_mm_unpacklo_pi16(refOut, refOut), refGain); // Apply reflections gain - // At this, point, this is the only output of the reverb - Store64MMX(pOut, out); - pOut += 2; - } - _mm_empty(); - return; - } #endif int pos[7]; for(int i = 0; i < 7; i++) @@ -1077,73 +953,6 @@ void CReverb::ProcessLateReverb(SWLateReverb * MPT_RESTRICT pReverb, LR16 * MPT_ pReverb->nDelayPos = delayPos; return; } -#endif -#ifdef ENABLE_MMX - if(GetProcSupport() & PROCSUPPORT_MMX) - { - int delayPos = pReverb->nDelayPos & RVBDLY_MASK; - __m64 rvbOutGains = Load64MMX(pReverb->RvbOutGains); - __m64 difCoeffs = Load64MMX(pReverb->nDifCoeffs); - __m64 decayLP = Load64MMX(pReverb->nDecayLP); - __m64 lpHistory = Load64MMX(pReverb->LPHistory); - while(nSamples--) - { - __m64 refIn = _mm_cvtsi32_si64(pRefOut->lr); // 16-bit stereo input - pRefOut++; - - __m64 delay2 = _mm_unpacklo_pi32( - _mm_cvtsi32_si64(pReverb->Delay2[DELAY_OFFSET(RVBDLY2L_LEN)].lr), - _mm_cvtsi32_si64(pReverb->Delay2[DELAY_OFFSET(RVBDLY2R_LEN)].lr)); - - // Unsigned to avoid sign extension - uint16 diff1L = pReverb->Diffusion1[DELAY_OFFSET(RVBDIF1L_LEN)].c.l; - uint16 diff1R = pReverb->Diffusion1[DELAY_OFFSET(RVBDIF1R_LEN)].c.r; - int32 diffusion1 = diff1L | (diff1R << 16); // diffusion1 history - - uint16 diff2L = pReverb->Diffusion2[DELAY_OFFSET(RVBDIF2L_LEN)].c.l; - uint16 diff2R = pReverb->Diffusion2[DELAY_OFFSET(RVBDIF2R_LEN)].c.r; - int32 diffusion2 = diff2L | (diff2R << 16); // diffusion2 history - - __m64 lpDecay = _mm_mulhi_pi16(_mm_subs_pi16(lpHistory, delay2), decayLP); - lpHistory = _mm_adds_pi16(_mm_adds_pi16(lpDecay, lpDecay), delay2); // Low-passed decay - - // Apply decay gain - __m64 histDecay = _mm_srai_pi32(_mm_madd_pi16(Load64MMX(pReverb->nDecayDC), lpHistory), 15); - __m64 histDecayIn = _mm_adds_pi16(_mm_packs_pi32(histDecay, histDecay), _mm_srai_pi16(_mm_unpacklo_pi32(refIn, refIn), 2)); - __m64 histDecayInDiff = _mm_subs_pi16(histDecayIn, _mm_mulhi_pi16(_mm_cvtsi32_si64(diffusion1), difCoeffs)); - pReverb->Diffusion1[delayPos].lr = _mm_cvtsi64_si32(histDecayInDiff); - - __m64 delay1Out = _mm_adds_pi16(_mm_mulhi_pi16(difCoeffs, histDecayInDiff), _mm_cvtsi32_si64(diffusion1)); - // Insert the diffusion output in the reverb delay line - pReverb->Delay1[delayPos].lr = _mm_cvtsi64_si32(delay1Out); - __m64 histDecayInDelay = _mm_adds_pi16(histDecayIn, _mm_unpacklo_pi32(delay1Out, delay1Out)); - - // Input to second diffuser - __m64 delay1 = _mm_unpacklo_pi32( - _mm_cvtsi32_si64(pReverb->Delay1[DELAY_OFFSET(RVBDLY1L_LEN)].lr), - _mm_cvtsi32_si64(pReverb->Delay1[DELAY_OFFSET(RVBDLY1R_LEN)].lr)); - - __m64 delay1Gains = _mm_srai_pi32(_mm_madd_pi16(delay1, Load64MMX(pReverb->Dif2InGains)), 15); - __m64 delay1GainsSat = _mm_packs_pi32(delay1Gains, delay1Gains); - __m64 histDelay1 = _mm_subs_pi16(_mm_adds_pi16(histDecayInDelay, delay1), delay1GainsSat); // accumulate with reverb output - __m64 diff2out = _mm_subs_pi16(delay1GainsSat, _mm_mulhi_pi16(_mm_cvtsi32_si64(diffusion2), difCoeffs)); - __m64 diff2outCoeffs = _mm_mulhi_pi16(difCoeffs, diff2out); - pReverb->Diffusion2[delayPos].lr = _mm_cvtsi64_si32(diff2out); - - __m64 mixOut = Load64MMX(pMixOut); - __m64 delay2out = _mm_adds_pi16(diff2outCoeffs, _mm_cvtsi32_si64(diffusion2)); - pReverb->Delay2[delayPos].lr = _mm_cvtsi64_si32(delay2out); - delayPos = (delayPos + 1) & RVBDLY_MASK; - // Accumulate with reverb output - __m64 out = _mm_add_pi32(_mm_madd_pi16(_mm_adds_pi16(histDelay1, delay2out), rvbOutGains), mixOut); - Store64MMX(pMixOut, out); - pMixOut += 2; - } - Store64MMX(&pReverb->LPHistory[0].lr, lpHistory); - pReverb->nDelayPos = delayPos; - _mm_empty(); - return; - } #endif int delayPos = pReverb->nDelayPos & RVBDLY_MASK; while(nSamples--) diff --git a/Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.h b/Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.h index 8a777ea55..2118c9a8d 100644 --- a/Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.h +++ b/Frameworks/OpenMPT/OpenMPT/sounddsp/Reverb.h @@ -21,10 +21,6 @@ OPENMPT_NAMESPACE_BEGIN //////////////////////////////////////////////////////////////////////// // Reverberation -#define NUM_REVERBTYPES 29 - -mpt::ustring GetReverbPresetName(uint32 nPreset); - ///////////////////////////////////////////////////////////////////////////// // // SW Reverb structures @@ -142,9 +138,9 @@ public: // Shared reverb state private: - mixsample_t MixReverbBuffer[MIXBUFFERSIZE * 2]; + MixSampleInt MixReverbBuffer[MIXBUFFERSIZE * 2]; public: - mixsample_t gnRvbROfsVol = 0, gnRvbLOfsVol = 0; + MixSampleInt gnRvbROfsVol = 0, gnRvbLOfsVol = 0; private: const SNDMIX_REVERB_PROPERTIES *m_currentPreset = nullptr; @@ -176,10 +172,10 @@ public: void Initialize(bool bReset, uint32 MixingFreq); // can be called multiple times or never (if no data is sent to reverb) - mixsample_t *GetReverbSendBuffer(uint32 nSamples); + MixSampleInt *GetReverbSendBuffer(uint32 nSamples); // call once after all data has been sent. - void Process(mixsample_t *MixSoundBuffer, uint32 nSamples); + void Process(MixSampleInt *MixSoundBuffer, uint32 nSamples); private: void Shutdown(); @@ -204,79 +200,26 @@ private: // I3DL2 reverb presets // -#define SNDMIX_REVERB_PRESET_DEFAULT \ --10000, 0, 1.00f,0.50f,-10000,0.020f,-10000,0.040f,100.0f,100.0f +struct SNDMIX_REVERB_PROPERTIES +{ + int32 lRoom; // [-10000, 0] default: -10000 mB + int32 lRoomHF; // [-10000, 0] default: 0 mB + float flDecayTime; // [0.1, 20.0] default: 1.0 s + float flDecayHFRatio; // [0.1, 2.0] default: 0.5 + int32 lReflections; // [-10000, 1000] default: -10000 mB + float flReflectionsDelay; // [0.0, 0.3] default: 0.02 s + int32 lReverb; // [-10000, 2000] default: -10000 mB + float flReverbDelay; // [0.0, 0.1] default: 0.04 s + float flDiffusion; // [0.0, 100.0] default: 100.0 % + float flDensity; // [0.0, 100.0] default: 100.0 % +}; -#define SNDMIX_REVERB_PRESET_GENERIC \ - -1000, -100, 1.49f,0.83f, -2602,0.007f, 200,0.011f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_PADDEDCELL \ - -1000,-6000, 0.17f,0.10f, -1204,0.001f, 207,0.002f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_ROOM \ - -1000, -454, 0.40f,0.83f, -1646,0.002f, 53,0.003f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_BATHROOM \ - -1000,-1200, 1.49f,0.54f, -370,0.007f, 1030,0.011f,100.0f, 60.0f -#define SNDMIX_REVERB_PRESET_LIVINGROOM \ - -1000,-6000, 0.50f,0.10f, -1376,0.003f, -1104,0.004f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_STONEROOM \ - -1000, -300, 2.31f,0.64f, -711,0.012f, 83,0.017f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_AUDITORIUM \ - -1000, -476, 4.32f,0.59f, -789,0.020f, -289,0.030f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_CONCERTHALL \ - -1000, -500, 3.92f,0.70f, -1230,0.020f, -2,0.029f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_CAVE \ - -1000, 0, 2.91f,1.30f, -602,0.015f, -302,0.022f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_ARENA \ - -1000, -698, 7.24f,0.33f, -1166,0.020f, 16,0.030f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_HANGAR \ - -1000,-1000,10.05f,0.23f, -602,0.020f, 198,0.030f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_CARPETEDHALLWAY \ - -1000,-4000, 0.30f,0.10f, -1831,0.002f, -1630,0.030f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_HALLWAY \ - -1000, -300, 1.49f,0.59f, -1219,0.007f, 441,0.011f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_STONECORRIDOR \ - -1000, -237, 2.70f,0.79f, -1214,0.013f, 395,0.020f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_ALLEY \ - -1000, -270, 1.49f,0.86f, -1204,0.007f, -4,0.011f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_FOREST \ - -1000,-3300, 1.49f,0.54f, -2560,0.162f, -613,0.088f, 79.0f,100.0f -#define SNDMIX_REVERB_PRESET_CITY \ - -1000, -800, 1.49f,0.67f, -2273,0.007f, -2217,0.011f, 50.0f,100.0f -#define SNDMIX_REVERB_PRESET_MOUNTAINS \ - -1000,-2500, 1.49f,0.21f, -2780,0.300f, -2014,0.100f, 27.0f,100.0f -#define SNDMIX_REVERB_PRESET_QUARRY \ - -1000,-1000, 1.49f,0.83f,-10000,0.061f, 500,0.025f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_PLAIN \ - -1000,-2000, 1.49f,0.50f, -2466,0.179f, -2514,0.100f, 21.0f,100.0f -#define SNDMIX_REVERB_PRESET_PARKINGLOT \ - -1000, 0, 1.65f,1.50f, -1363,0.008f, -1153,0.012f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_SEWERPIPE \ - -1000,-1000, 2.81f,0.14f, 429,0.014f, 648,0.021f, 80.0f, 60.0f -#define SNDMIX_REVERB_PRESET_UNDERWATER \ - -1000,-4000, 1.49f,0.10f, -449,0.007f, 1700,0.011f,100.0f,100.0f - -// Examples simulating General MIDI 2'musical' reverb presets -// -// Name (Decay time) Description -// -// Small Room (1.1s) A small size room with a length of 5m or so. -// Medium Room (1.3s) A medium size room with a length of 10m or so. -// Large Room (1.5s) A large size room suitable for live performances. -// Medium Hall (1.8s) A medium size concert hall. -// Large Hall (1.8s) A large size concert hall suitable for a full orchestra. -// Plate (1.3s) A plate reverb simulation. - -#define SNDMIX_REVERB_PRESET_SMALLROOM \ - -1000, -600, 1.10f,0.83f, -400,0.005f, 500,0.010f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_MEDIUMROOM \ - -1000, -600, 1.30f,0.83f, -1000,0.010f, -200,0.020f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_LARGEROOM \ - -1000, -600, 1.50f,0.83f, -1600,0.020f, -1000,0.040f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_MEDIUMHALL \ - -1000, -600, 1.80f,0.70f, -1300,0.015f, -800,0.030f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_LARGEHALL \ - -1000, -600, 1.80f,0.70f, -2000,0.030f, -1400,0.060f,100.0f,100.0f -#define SNDMIX_REVERB_PRESET_PLATE \ - -1000, -200, 1.30f,0.90f, 0,0.002f, 0,0.010f,100.0f, 75.0f +enum : uint32 +{ + NUM_REVERBTYPES = 29 +}; +mpt::ustring GetReverbPresetName(uint32 preset); +const SNDMIX_REVERB_PROPERTIES *GetReverbPreset(uint32 preset); OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.cpp index c1c10df2d..29ff9eac7 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.cpp @@ -26,7 +26,7 @@ CriticalSection::CriticalSection() Enter(); } -CriticalSection::CriticalSection(CriticalSection &&other) +CriticalSection::CriticalSection(CriticalSection &&other) noexcept : m_refGlobalMutex(other.m_refGlobalMutex) , inSection(other.inSection) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.h b/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.h index 8735e46a1..7a1b901ac 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/AudioCriticalSection.h @@ -42,7 +42,7 @@ public: }; public: CriticalSection(); - CriticalSection(CriticalSection &&other); + CriticalSection(CriticalSection &&other) noexcept; explicit CriticalSection(InitialState state); void Enter(); void Leave(); @@ -61,7 +61,8 @@ public: }; public: CriticalSection() {} - explicit CriticalSection(InitialState state) { MPT_UNREFERENCED_PARAMETER(state); } + CriticalSection(CriticalSection &&) noexcept {} + explicit CriticalSection(InitialState) {} void Enter() {} void Leave() {} ~CriticalSection() {} diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/AudioReadTarget.h b/Frameworks/OpenMPT/OpenMPT/soundlib/AudioReadTarget.h index 3bb6f5d82..21bc93d58 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/AudioReadTarget.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/AudioReadTarget.h @@ -12,10 +12,11 @@ #include "BuildSettings.h" #include "Sndfile.h" -#include "Dither.h" #include "../soundbase/SampleFormat.h" #include "../soundbase/SampleFormatConverters.h" #include "../soundbase/SampleFormatCopy.h" +#include "../soundbase/SampleBuffer.h" +#include "../soundbase/Dither.h" #include "MixerLoops.h" #include "Mixer.h" @@ -23,7 +24,7 @@ OPENMPT_NAMESPACE_BEGIN -template +template class AudioReadTargetBuffer : public IAudioReadTarget { @@ -31,251 +32,116 @@ private: std::size_t countRendered; Dither &dither; protected: - Tsample *outputBuffer; - Tsample * const *outputBuffers; + Tbuffer outputBuffer; public: - AudioReadTargetBuffer(Dither &dither_, Tsample *buffer, Tsample * const *buffers) + AudioReadTargetBuffer(Tbuffer buf, Dither &dither_) : countRendered(0) , dither(dither_) - , outputBuffer(buffer) - , outputBuffers(buffers) + , outputBuffer(buf) { - MPT_ASSERT(SampleFormat(SampleFormatTraits::sampleFormat()).IsValid()); + MPT_ASSERT(SampleFormat(SampleFormatTraits::sampleFormat()).IsValid()); } std::size_t GetRenderedCount() const { return countRendered; } public: - void DataCallback(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override + void DataCallback(MixSampleInt *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override { - // Convert to output sample format and optionally perform dithering and clipping if needed - - const SampleFormat sampleFormat = SampleFormatTraits::sampleFormat(); - - if(sampleFormat.IsInt()) - { - dither.Process(MixSoundBuffer, countChunk, channels, sampleFormat.GetBitsPerSample()); - } - - if(outputBuffer) - { - ConvertInterleavedFixedPointToInterleaved(outputBuffer + (channels * countRendered), MixSoundBuffer, channels, countChunk); - } - if(outputBuffers) - { - Tsample *buffers[4] = { nullptr, nullptr, nullptr, nullptr }; - for(std::size_t channel = 0; channel < channels; ++channel) + dither.WithDither( + [&](auto &ditherInstance) { - buffers[channel] = outputBuffers[channel] + countRendered; + ConvertBufferMixFixedToBuffer(advance_audio_buffer(outputBuffer, countRendered), audio_buffer_interleaved(MixSoundBuffer, channels, countChunk), ditherInstance, channels, countChunk); } - ConvertInterleavedFixedPointToNonInterleaved(buffers, MixSoundBuffer, channels, countChunk); - } - + ); + countRendered += countChunk; + } + void DataCallback(MixSampleFloat *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override + { + dither.WithDither( + [&](auto &ditherInstance) + { + ConvertBufferMixFloatToBuffer(advance_audio_buffer(outputBuffer, countRendered), audio_buffer_interleaved(MixSoundBuffer, channels, countChunk), ditherInstance, channels, countChunk); + } + ); countRendered += countChunk; } }; -#if defined(MODPLUG_TRACKER) - - -class AudioReadTargetBufferInterleavedDynamic - : public IAudioReadTarget -{ -private: - const SampleFormat sampleFormat; - bool clipFloat; - Dither &dither; - void *buffer; -public: - AudioReadTargetBufferInterleavedDynamic(SampleFormat sampleFormat_, bool clipFloat_, Dither &dither_, void *buffer_) - : sampleFormat(sampleFormat_) - , clipFloat(clipFloat_) - , dither(dither_) - , buffer(buffer_) - { - MPT_ASSERT_ALWAYS(sampleFormat.IsValid()); - } - void DataCallback(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override - { - switch(sampleFormat.value) - { - case SampleFormatUnsigned8: - { - typedef SampleFormatToType::type Tsample; - AudioReadTargetBuffer target(dither, reinterpret_cast(buffer), nullptr); - target.DataCallback(MixSoundBuffer, channels, countChunk); - } - break; - case SampleFormatInt16: - { - typedef SampleFormatToType::type Tsample; - AudioReadTargetBuffer target(dither, reinterpret_cast(buffer), nullptr); - target.DataCallback(MixSoundBuffer, channels, countChunk); - } - break; - case SampleFormatInt24: - { - typedef SampleFormatToType::type Tsample; - AudioReadTargetBuffer target(dither, reinterpret_cast(buffer), nullptr); - target.DataCallback(MixSoundBuffer, channels, countChunk); - } - break; - case SampleFormatInt32: - { - typedef SampleFormatToType::type Tsample; - AudioReadTargetBuffer target(dither, reinterpret_cast(buffer), nullptr); - target.DataCallback(MixSoundBuffer, channels, countChunk); - } - break; - case SampleFormatFloat32: - if(clipFloat) - { - typedef SampleFormatToType::type Tsample; - AudioReadTargetBuffer target(dither, reinterpret_cast(buffer), nullptr); - target.DataCallback(MixSoundBuffer, channels, countChunk); - } else - { - typedef SampleFormatToType::type Tsample; - AudioReadTargetBuffer target(dither, reinterpret_cast(buffer), nullptr); - target.DataCallback(MixSoundBuffer, channels, countChunk); - } - break; - } - // increment output buffer for potentially next callback - buffer = reinterpret_cast(buffer) + (sampleFormat.GetBitsPerSample()/8) * channels * countChunk; - } -}; - - -class AudioSourceBuffer - : public IAudioSource -{ -private: - std::size_t countRendered; -protected: - SampleFormat sampleFormat; - const void *inputBuffer; -public: - AudioSourceBuffer(SampleFormat sampleFormat, const void *buffer) - : countRendered(0) - , sampleFormat(sampleFormat) - , inputBuffer(buffer) - { - MPT_ASSERT(sampleFormat.IsValid()); - } - virtual ~AudioSourceBuffer() { } - std::size_t GetRenderedCount() const { return countRendered; } -private: - template - void Fill(const Tsample *inputBuffer, int32 * const *MixInputBuffers, std::size_t channels, std::size_t countChunk) - { - for(std::size_t channel = 0; channel < channels; ++channel) - { - SC::ConvertToFixedPoint conv; - for(std::size_t frame = 0; frame < countChunk; ++frame) - { - MixInputBuffers[channel][frame] = conv(inputBuffer[channel + ((countRendered + frame) * channels)]); - } - } - countRendered += countChunk; - } -public: - virtual void FillCallback(int32 * const *MixInputBuffers, std::size_t channels, std::size_t countChunk) - { - switch(sampleFormat.value) - { - case SampleFormatUnsigned8: - { - typedef SampleFormatToType::type Tsample; - Fill(reinterpret_cast(inputBuffer), MixInputBuffers, channels, countChunk); - } - break; - case SampleFormatInt16: - { - typedef SampleFormatToType::type Tsample; - Fill(reinterpret_cast(inputBuffer), MixInputBuffers, channels, countChunk); - } - break; - case SampleFormatInt24: - { - typedef SampleFormatToType::type Tsample; - Fill(reinterpret_cast(inputBuffer), MixInputBuffers, channels, countChunk); - } - break; - case SampleFormatInt32: - { - typedef SampleFormatToType::type Tsample; - Fill(reinterpret_cast(inputBuffer), MixInputBuffers, channels, countChunk); - } - break; - case SampleFormatFloat32: - { - typedef SampleFormatToType::type Tsample; - Fill(reinterpret_cast(inputBuffer), MixInputBuffers, channels, countChunk); - } - break; - } - } -}; - - -#else // !MODPLUG_TRACKER +#if defined(LIBOPENMPT_BUILD) template -void ApplyGainBeforeConversionIfAppropriate(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk, float gainFactor) +void ApplyGainBeforeConversionIfAppropriateFixed(MixSampleInt *MixSoundBuffer, std::size_t channels, std::size_t countChunk, float gainFactor) { // Apply final output gain for non floating point output ApplyGain(MixSoundBuffer, channels, countChunk, mpt::saturate_round(gainFactor * (1<<16))); } template<> -void ApplyGainBeforeConversionIfAppropriate(int32 * /*MixSoundBuffer*/, std::size_t /*channels*/, std::size_t /*countChunk*/, float /*gainFactor*/) +void ApplyGainBeforeConversionIfAppropriateFixed(MixSampleInt * /*MixSoundBuffer*/, std::size_t /*channels*/, std::size_t /*countChunk*/, float /*gainFactor*/) { // nothing } template -void ApplyGainAfterConversionIfAppropriate(Tsample * /*buffer*/, Tsample * const * /*buffers*/, std::size_t /*countRendered*/, std::size_t /*channels*/, std::size_t /*countChunk*/, float /*gainFactor*/) +void ApplyGainAfterConversionIfAppropriateFixed(audio_buffer_interleaved /*buffer*/, std::size_t /*countRendered*/, std::size_t /*channels*/, std::size_t /*countChunk*/, float /*gainFactor*/) +{ + // nothing +} +template +void ApplyGainAfterConversionIfAppropriateFixed(audio_buffer_planar /*buffer*/, std::size_t /*countRendered*/, std::size_t /*channels*/, std::size_t /*countChunk*/, float /*gainFactor*/) { // nothing } template<> -void ApplyGainAfterConversionIfAppropriate(float *buffer, float * const *buffers, std::size_t countRendered, std::size_t channels, std::size_t countChunk, float gainFactor) +void ApplyGainAfterConversionIfAppropriateFixed(audio_buffer_interleaved buffer, std::size_t countRendered, std::size_t channels, std::size_t countChunk, float gainFactor) { // Apply final output gain for floating point output after conversion so we do not suffer underflow or clipping - ApplyGain(buffer, buffers, countRendered, channels, countChunk, gainFactor); + ApplyGain(buffer, countRendered, channels, countChunk, gainFactor); +} +template<> +void ApplyGainAfterConversionIfAppropriateFixed(audio_buffer_planar buffer, std::size_t countRendered, std::size_t channels, std::size_t countChunk, float gainFactor) +{ + // Apply final output gain for floating point output after conversion so we do not suffer underflow or clipping + ApplyGain(buffer, countRendered, channels, countChunk, gainFactor); } -template +inline void ApplyGainBeforeConversionIfAppropriateFloat(MixSampleFloat *MixSoundBuffer, std::size_t channels, std::size_t countChunk, float gainFactor) +{ + // Apply final output gain for non floating point output + ApplyGain(MixSoundBuffer, channels, countChunk, gainFactor); +} + +template class AudioReadTargetGainBuffer - : public AudioReadTargetBuffer + : public AudioReadTargetBuffer { private: - typedef AudioReadTargetBuffer Tbase; + typedef AudioReadTargetBuffer Tbase; private: const float gainFactor; public: - AudioReadTargetGainBuffer(Dither &dither, Tsample *buffer, Tsample * const *buffers, float gainFactor_) - : Tbase(dither, buffer, buffers) + AudioReadTargetGainBuffer(Tbuffer buf, Dither &dither, float gainFactor_) + : Tbase(buf, dither) , gainFactor(gainFactor_) { return; } public: - void DataCallback(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override + void DataCallback(MixSampleInt *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override { const std::size_t countRendered_ = Tbase::GetRenderedCount(); - - ApplyGainBeforeConversionIfAppropriate(MixSoundBuffer, channels, countChunk, gainFactor); - + ApplyGainBeforeConversionIfAppropriateFixed(MixSoundBuffer, channels, countChunk, gainFactor); + Tbase::DataCallback(MixSoundBuffer, channels, countChunk); + ApplyGainAfterConversionIfAppropriateFixed(Tbase::outputBuffer, countRendered_, channels, countChunk, gainFactor); + } + void DataCallback(MixSampleFloat *MixSoundBuffer, std::size_t channels, std::size_t countChunk) override + { + ApplyGainBeforeConversionIfAppropriateFloat(MixSoundBuffer, channels, countChunk, gainFactor); Tbase::DataCallback(MixSoundBuffer, channels, countChunk); - - ApplyGainAfterConversionIfAppropriate(Tbase::outputBuffer, Tbase::outputBuffers, countRendered_, channels, countChunk, gainFactor); - } }; -#endif // MODPLUG_TRACKER +#endif // LIBOPENMPT_BUILD OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/BitReader.h b/Frameworks/OpenMPT/OpenMPT/soundlib/BitReader.h index e4f52d3b3..32e6a2ed2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/BitReader.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/BitReader.h @@ -29,7 +29,7 @@ protected: off_t m_bufPos = 0, m_bufSize = 0; uint32 bitBuf = 0; // Current bit buffer int m_bitNum = 0; // Currently available number of bits - mpt::byte buffer[mpt::IO::BUFFERSIZE_TINY]; + std::byte buffer[mpt::IO::BUFFERSIZE_TINY]{}; public: @@ -39,7 +39,7 @@ public: eof() : std::range_error("Truncated bit buffer") { } }; - BitReader(mpt::span bytedata) : FileReader(bytedata) { } + BitReader(mpt::span bytedata) : FileReader(bytedata) { } BitReader(const FileReader &other = FileReader()) : FileReader(other) { } off_t GetLength() const diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerMMCMP.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerMMCMP.cpp index 92450b4c3..88cf18664 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerMMCMP.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerMMCMP.cpp @@ -20,7 +20,9 @@ OPENMPT_NAMESPACE_BEGIN -//#define MMCMP_LOG +#ifdef MPT_ALL_LOGGING +#define MMCMP_LOG +#endif struct MMCMPFILEHEADER @@ -72,23 +74,23 @@ MPT_BINARY_STRUCT(MMCMPSUBBLOCK, 8) #define MMCMP_ABS16 0x0200 #define MMCMP_ENDIAN 0x0400 -static const uint8 MMCMP8BitCommands[8] = +static constexpr uint8 MMCMP8BitCommands[8] = { 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF8 }; -static const uint8 MMCMP8BitFetch[8] = +static constexpr uint8 MMCMP8BitFetch[8] = { 3, 3, 3, 3, 2, 1, 0, 0 }; -static const uint16 MMCMP16BitCommands[16] = +static constexpr uint16 MMCMP16BitCommands[16] = { 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF0, 0x1F0, 0x3F0, 0x7F0, 0xFF0, 0x1FF0, 0x3FF0, 0x7FF0, 0xFFF0 }; -static const uint8 MMCMP16BitFetch[16] = +static constexpr uint8 MMCMP16BitFetch[16] = { 4, 4, 4, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 @@ -198,7 +200,7 @@ bool UnpackMMCMP(std::vector &containerItems, FileReader &file, C if(mmh.blktable + 4 * mmh.nblocks > file.GetLength()) return false; containerItems.emplace_back(); - containerItems.back().data_cache = mpt::make_unique >(); + containerItems.back().data_cache = std::make_unique >(); std::vector & unpackedData = *(containerItems.back().data_cache); unpackedData.resize(mmh.filesize); @@ -224,9 +226,9 @@ bool UnpackMMCMP(std::vector &containerItems, FileReader &file, C uint32 memPos = blkPos + sizeof(MMCMPBLOCK) + blk.sub_blk * sizeof(MMCMPSUBBLOCK); #ifdef MMCMP_LOG - Log("block %d: flags=%04X sub_blocks=%d", nBlock, (uint32)pblk->flags, (uint32)pblk->sub_blk); - Log(" pksize=%d unpksize=%d", pblk->pk_size, pblk->unpk_size); - Log(" tt_entries=%d num_bits=%d\n", pblk->tt_entries, pblk->num_bits); + MPT_LOG(LogDebug, "MMCMP", mpt::format(U_("block %1: flags=%2 sub_blocks=%3"))(nBlock, mpt::ufmt::HEX0<4>(static_cast(blk.flags)), static_cast(blk.sub_blk))); + MPT_LOG(LogDebug, "MMCMP", mpt::format(U_(" pksize=%1 unpksize=%2"))(static_cast(blk.pk_size), static_cast(blk.unpk_size))); + MPT_LOG(LogDebug, "MMCMP", mpt::format(U_(" tt_entries=%1 num_bits=%2"))(static_cast(blk.tt_entries), static_cast(blk.num_bits))); #endif // Data is not packed if (!(blk.flags & MMCMP_COMP)) @@ -236,7 +238,7 @@ bool UnpackMMCMP(std::vector &containerItems, FileReader &file, C if(!psubblk) return false; if(!MMCMP_IsDstBlockValid(unpackedData, *psubblk)) return false; #ifdef MMCMP_LOG - Log(" Unpacked sub-block %d: offset %d, size=%d\n", i, psubblk->unpk_pos, psubblk->unpk_size); + MPT_LOG(LogDebug, "MMCMP", mpt::format(U_(" Unpacked sub-block %1: offset %2, size=%3"))(i, static_cast(psubblk->unpk_pos), static_cast(psubblk->unpk_size))); #endif if(!file.Seek(memPos)) return false; if(file.ReadRaw(&(unpackedData[psubblk->unpk_pos]), psubblk->unpk_size) != psubblk->unpk_size) return false; @@ -256,10 +258,7 @@ bool UnpackMMCMP(std::vector &containerItems, FileReader &file, C uint32 oldval = 0; #ifdef MMCMP_LOG - Log(" 16-bit block: pos=%d size=%d ", psubblk->unpk_pos, psubblk->unpk_size); - if (pblk->flags & MMCMP_DELTA) Log("DELTA "); - if (pblk->flags & MMCMP_ABS16) Log("ABS16 "); - Log("\n"); + MPT_LOG(LogDebug, "MMCMP", mpt::format(U_(" 16-bit block: pos=%1 size=%2 %3 %4"))(psubblk->unpk_pos, psubblk->unpk_size, (blk.flags & MMCMP_DELTA) ? U_("DELTA ") : U_(""), (blk.flags & MMCMP_ABS16) ? U_("ABS16 ") : U_(""))); #endif if(!file.Seek(memPos + blk.tt_entries)) return false; if(!file.CanRead(blk.pk_size - blk.tt_entries)) return false; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerPP20.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerPP20.cpp index 286d68411..90c59ac1e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerPP20.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerPP20.cpp @@ -23,10 +23,10 @@ OPENMPT_NAMESPACE_BEGIN struct PPBITBUFFER { - uint32 bitcount; - uint32 bitbuffer; - const uint8 *pStart; - const uint8 *pSrc; + uint32 bitcount = 0; + uint32 bitbuffer = 0; + const uint8 *pStart = nullptr; + const uint8 *pSrc = nullptr; uint32 GetBits(uint32 n); }; @@ -36,15 +36,16 @@ uint32 PPBITBUFFER::GetBits(uint32 n) { uint32 result = 0; - for (uint32 i=0; i>= 1; bitcount--; } @@ -52,58 +53,54 @@ uint32 PPBITBUFFER::GetBits(uint32 n) } -static bool PP20_DoUnpack(const uint8 *pSrc, uint32 nSrcLen, uint8 *pDst, uint32 nDstLen) +static bool PP20_DoUnpack(const uint8 *pSrc, uint32 srcLen, uint8 *pDst, uint32 dstLen) { + const std::array modeTable{pSrc[0], pSrc[1], pSrc[2], pSrc[3]}; PPBITBUFFER BitBuffer; - uint32 nBytesLeft; - BitBuffer.pStart = pSrc; - BitBuffer.pSrc = pSrc + nSrcLen - 4; - BitBuffer.bitbuffer = 0; - BitBuffer.bitcount = 0; - BitBuffer.GetBits(pSrc[nSrcLen-1]); - nBytesLeft = nDstLen; - while (nBytesLeft > 0) + BitBuffer.pSrc = pSrc + srcLen - 4; + BitBuffer.GetBits(pSrc[srcLen - 1]); + uint32 bytesLeft = dstLen; + while(bytesLeft > 0) { - if (!BitBuffer.GetBits(1)) + if(!BitBuffer.GetBits(1)) { - uint32 n = 1; - while (n < nBytesLeft) + uint32 count = 1, countAdd; + do { - uint32 code = BitBuffer.GetBits(2); - n += code; - if (code != 3) break; - } - LimitMax(n, nBytesLeft); - for (uint32 i=0; i= nSrcLen) return false; - uint32 nbits = pSrc[n-1]; - uint32 nofs; - if (n==4) + uint32 modeIndex = BitBuffer.GetBits(2); + MPT_CHECKER_ASSUME(modeIndex < 4); + uint32 count = modeIndex + 2, offset; + if(modeIndex == 3) { - nofs = BitBuffer.GetBits( (BitBuffer.GetBits(1)) ? nbits : 7 ); - while (n < nBytesLeft) + offset = BitBuffer.GetBits((BitBuffer.GetBits(1)) ? modeTable[modeIndex] : 7); + uint32 countAdd = 7; + do { - uint32 code = BitBuffer.GetBits(3); - n += code; - if (code != 7) break; - } + countAdd = BitBuffer.GetBits(3); + count += countAdd; + } while(countAdd == 7); } else { - nofs = BitBuffer.GetBits(nbits); + offset = BitBuffer.GetBits(modeTable[modeIndex]); } - LimitMax(n, nBytesLeft); - for (uint32 i=0; i<=n; i++) + LimitMax(count, bytesLeft); + for(uint32 i = 0; i < count; i++) { - pDst[nBytesLeft-1] = (nBytesLeft+nofs < nDstLen) ? pDst[nBytesLeft+nofs] : 0; - if (!--nBytesLeft) break; + pDst[bytesLeft - 1] = (bytesLeft + offset < dstLen) ? pDst[bytesLeft + offset] : 0; + --bytesLeft; } } } @@ -113,8 +110,8 @@ static bool PP20_DoUnpack(const uint8 *pSrc, uint32 nSrcLen, uint8 *pDst, uint32 struct PP20header { - char magic[4]; // "PP20" - uint8be efficiency[4]; + char magic[4]; // "PP20" + uint8 efficiency[4]; }; MPT_BINARY_STRUCT(PP20header, 8) @@ -178,7 +175,7 @@ bool UnpackPP20(std::vector &containerItems, FileReader &file, Co } containerItems.emplace_back(); - containerItems.back().data_cache = mpt::make_unique >(); + containerItems.back().data_cache = std::make_unique >(); std::vector & unpackedData = *(containerItems.back().data_cache); FileReader::off_t length = file.GetLength(); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerUMX.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerUMX.cpp index 9ec76941e..53671c787 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerUMX.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerUMX.cpp @@ -174,7 +174,7 @@ bool UnpackUMX(std::vector &containerItems, FileReader &file, Con if(objName >= 0 && static_cast(objName) < names.size()) { - item.name = mpt::ToUnicode(mpt::CharsetISO8859_1, names[objName]); + item.name = mpt::ToUnicode(mpt::Charset::ISO8859_1, names[objName]); } item.file = chunk.ReadChunk(size); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerXPK.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerXPK.cpp index 3ade69d22..5eb301fc2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerXPK.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ContainerXPK.cpp @@ -21,7 +21,9 @@ OPENMPT_NAMESPACE_BEGIN -//#define MMCMP_LOG +#ifdef MPT_ALL_LOGGING +#define MMCMP_LOG +#endif struct XPKFILEHEADER @@ -102,11 +104,11 @@ static int32 bfexts(std::size_t p, int32 bo, int32 bc, XPK_BufferBounds &bufs) static uint8 XPK_ReadTable(int32 index) { - static const uint8 xpk_table[] = { + static constexpr uint8 xpk_table[] = { 2,3,4,5,6,7,8,0,3,2,4,5,6,7,8,0,4,3,5,2,6,7,8,0,5,4,6,2,3,7,8,0,6,5,7,2,3,4,8,0,7,6,8,2,3,4,5,0,8,7,6,2,3,4,5,0 }; if(index < 0) throw XPK_error(); - if(static_cast(index) >= mpt::size(xpk_table)) throw XPK_error(); + if(static_cast(index) >= std::size(xpk_table)) throw XPK_error(); return xpk_table[index]; } @@ -157,9 +159,9 @@ static bool XPK_DoUnpack(const uint8 *src_, uint32 srcLen, std::vector &un if (type != 1) { - #ifdef MMCMP_LOG - Log("Invalid XPK type! (%d bytes left)\n", len); - #endif + #ifdef MMCMP_LOG + MPT_LOG(LogDebug, "XPK", mpt::format(U_("Invalid XPK type! (%1 bytes left)"))(len)); + #endif break; } len -= cup1; @@ -344,7 +346,7 @@ static bool ValidateHeader(const XPKFILEHEADER &header) { return false; } - MPT_STATIC_ASSERT(sizeof(XPKFILEHEADER) >= 8); + static_assert(sizeof(XPKFILEHEADER) >= 8); if(header.SrcLen < (sizeof(XPKFILEHEADER) - 8)) { return false; @@ -410,12 +412,12 @@ bool UnpackXPK(std::vector &containerItems, FileReader &file, Con } containerItems.emplace_back(); - containerItems.back().data_cache = mpt::make_unique >(); + containerItems.back().data_cache = std::make_unique >(); std::vector & unpackedData = *(containerItems.back().data_cache); -#ifdef MMCMP_LOG - Log("XPK detected (SrcLen=%d DstLen=%d) filesize=%d\n", header.SrcLen, header.DstLen, file.GetLength()); -#endif + #ifdef MMCMP_LOG + MPT_LOG(LogDebug, "XPK", mpt::uformat(U_("XPK detected (SrcLen=%1 DstLen=%2) filesize=%3"))(static_cast(header.SrcLen), static_cast(header.DstLen), file.GetLength())); + #endif bool result = false; try { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.cpp deleted file mode 100644 index 697065d76..000000000 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.cpp +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Dither.cpp - * ---------- - * Purpose: Dithering when converting to lower resolution sample formats. - * Notes : (currently none) - * Authors: Olivier Lapicque - * OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - -#include "stdafx.h" - -#include "Dither.h" -#include "Mixer.h" - -#include "../common/misc_util.h" - - -OPENMPT_NAMESPACE_BEGIN - - -////////////////////////////////////////////////////////////////////////// -// Noise Shaping (Dithering) - - -mpt::ustring Dither::GetModeName(DitherMode mode) -{ - switch(mode) - { - case DitherNone : return U_("no" ); break; - case DitherDefault: return U_("default"); break; - case DitherModPlug: return U_("0.5 bit"); break; - case DitherSimple : return U_("1 bit" ); break; - default : return U_("" ); break; - } -} - - -#if MPT_COMPILER_MSVC -#pragma warning(disable:4731) // ebp modified -#endif - - -#ifdef ENABLE_X86 - -void X86_Dither(int32 *pBuffer, uint32 nSamples, uint32 nBits, DitherModPlugState *state) -{ - if(nBits + MIXING_ATTENUATION + 1 >= 32) //if(nBits>16) - { - return; - } - - static int gDitherA_global, gDitherB_global; - - int gDitherA = state ? state->rng_a : gDitherA_global; - int gDitherB = state ? state->rng_b : gDitherB_global; - - _asm { - mov esi, pBuffer // esi = pBuffer+i - mov eax, nSamples // ebp = i - mov ecx, nBits // ecx = number of bits of noise - mov edi, gDitherA // Noise generation - mov ebx, gDitherB - add ecx, MIXING_ATTENUATION - add ecx, 1 - push ebp - mov ebp, eax -noiseloop: - rol edi, 1 - mov eax, dword ptr [esi] - xor edi, 0x10204080 - add esi, 4 - lea edi, [ebx*4+edi+0x78649E7D] - mov edx, edi - rol edx, 16 - lea edx, [edx*4+edx] - add ebx, edx - mov edx, ebx - sar edx, cl - add eax, edx - dec ebp - mov dword ptr [esi-4], eax - jnz noiseloop - pop ebp - mov gDitherA, edi - mov gDitherB, ebx - } - - if(state) state->rng_a = gDitherA; else gDitherA_global = gDitherA; - if(state) state->rng_b = gDitherB; else gDitherB_global = gDitherB; - -} - -#endif // ENABLE_X86 - - -static MPT_FORCEINLINE int32 dither_rand(uint32 &a, uint32 &b) -{ - a = (a << 1) | (a >> 31); - a ^= 0x10204080u; - a += 0x78649E7Du + (b * 4); - b += ((a << 16 ) | (a >> 16)) * 5; - return static_cast(b); -} - -static void C_Dither(int32 *pBuffer, std::size_t count, uint32 nBits, DitherModPlugState *state) -{ - if(nBits + MIXING_ATTENUATION + 1 >= 32) //if(nBits>16) - { - return; - } - - static uint32 global_a = 0; - static uint32 global_b = 0; - - uint32 a = state ? state->rng_a : global_a; - uint32 b = state ? state->rng_b : global_b; - - while(count--) - { - *pBuffer += mpt::rshift_signed(dither_rand(a, b), (nBits + MIXING_ATTENUATION + 1)); - pBuffer++; - } - - if(state) state->rng_a = a; else global_a = a; - if(state) state->rng_b = b; else global_b = b; - -} - -static void Dither_ModPlug(int32 *pBuffer, std::size_t count, std::size_t channels, uint32 nBits, DitherModPlugState &state) -{ - #ifdef ENABLE_X86 - X86_Dither(pBuffer, count * channels, nBits, &state); - #else // !ENABLE_X86 - C_Dither(pBuffer, count * channels, nBits, &state); - #endif // ENABLE_X86 -} - - -template -struct Dither_SimpleTemplate -{ -MPT_NOINLINE void operator () (int32 *mixbuffer, std::size_t count, DitherSimpleState &state, mpt::fast_prng &prng) -{ - STATIC_ASSERT(sizeof(int) == 4); - const int rshift = (32-targetbits) - MIXING_ATTENUATION; - MPT_CONSTANT_IF(rshift <= 0) - { - // nothing to dither - return; - } - const int round_mask = ~((1<(prng, noise_bits) + mpt::random(prng, noise_bits)) >> 1; - } else - { - unoise = mpt::random(prng, noise_bits); - } - int noise = static_cast(unoise) - noise_bias; // un-bias - int val = *mixbuffer; - MPT_CONSTANT_IF(shaped) - { - val += (s.error[channel] >> 1); - } - int rounded = (val + noise + round_offset) & round_mask;; - s.error[channel] = val - rounded; - *mixbuffer = rounded; - mixbuffer++; - } - } - state = s; -} -}; - -static void Dither_Simple(int32 *mixbuffer, std::size_t count, std::size_t channels, int bits, DitherSimpleState &state, mpt::fast_prng &prng) -{ - switch(bits) - { - case 8: - switch(channels) - { - case 1: - Dither_SimpleTemplate<8,1>()(mixbuffer, count, state, prng); - break; - case 2: - Dither_SimpleTemplate<8,2>()(mixbuffer, count, state, prng); - break; - case 4: - Dither_SimpleTemplate<8,4>()(mixbuffer, count, state, prng); - break; - } - break; - case 16: - switch(channels) - { - case 1: - Dither_SimpleTemplate<16,1>()(mixbuffer, count, state, prng); - break; - case 2: - Dither_SimpleTemplate<16,2>()(mixbuffer, count, state, prng); - break; - case 4: - Dither_SimpleTemplate<16,4>()(mixbuffer, count, state, prng); - break; - } - break; - case 24: - switch(channels) - { - case 1: - Dither_SimpleTemplate<24,1>()(mixbuffer, count, state, prng); - break; - case 2: - Dither_SimpleTemplate<24,2>()(mixbuffer, count, state, prng); - break; - case 4: - Dither_SimpleTemplate<24,4>()(mixbuffer, count, state, prng); - break; - } - break; - } -} - - -void Dither::Reset() -{ - state.Reset(); - } - - -void Dither::SetMode(DitherMode mode_) -{ - mode = mode_; -} - - -DitherMode Dither::GetMode() const -{ - return mode; -} - - -void Dither::Process(int32 *mixbuffer, std::size_t count, std::size_t channels, int bits) -{ - switch(mode) - { - case DitherNone: - // nothing - break; - case DitherModPlug: - Dither_ModPlug(mixbuffer, count, channels, bits, state.modplug); - break; - case DitherSimple: - Dither_Simple(mixbuffer, count, channels, bits, state.simple, state.prng); - break; - case DitherDefault: - default: - Dither_ModPlug(mixbuffer, count, channels, bits, state.modplug); - break; - } -} - - -OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.h deleted file mode 100644 index db83780d8..000000000 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Dither.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Dither.h - * -------- - * Purpose: Dithering when converting to lower resolution sample formats. - * Notes : (currently none) - * Authors: Olivier Lapicque - * OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - - -#pragma once - -#include "BuildSettings.h" - - -#include "../common/mptRandom.h" - - -OPENMPT_NAMESPACE_BEGIN - - -struct DitherModPlugState -{ - uint32 rng_a; - uint32 rng_b; - DitherModPlugState() - { - rng_a = 0; - rng_b = 0; - } -}; - -struct DitherSimpleState -{ - int32 error[4]; - DitherSimpleState() { - error[0] = 0; - error[1] = 0; - error[2] = 0; - error[3] = 0; - } -}; - -struct DitherState -{ - DitherModPlugState modplug; - DitherSimpleState simple; - mpt::fast_prng prng; - void Reset() - { - modplug = DitherModPlugState(); - simple = DitherSimpleState(); - } - template - DitherState(Trd & rd) - : prng(mpt::make_prng(rd)) - { - return; - } -}; - -enum DitherMode -{ - DitherNone = 0, - DitherDefault = 1, // chosen by OpenMPT code, might change - DitherModPlug = 2, // rectangular, 0.5 bit depth, no noise shaping (original ModPlug Tracker) - DitherSimple = 3, // rectangular, 1 bit depth, simple 1st order noise shaping - NumDitherModes -}; - -class Dither -{ -private: - DitherState state; - DitherMode mode; -public: - template - Dither(Trd & rd) - : state(rd) - { - mode = DitherDefault; - } - void SetMode(DitherMode mode_); - DitherMode GetMode() const; - void Reset(); - void Process(int32 *mixbuffer, std::size_t count, std::size_t channels, int bits); - static mpt::ustring GetModeName(DitherMode mode); -}; - - -OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp index 39d10a012..4f849747a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.cpp @@ -28,149 +28,179 @@ OPENMPT_NAMESPACE_BEGIN #ifdef MODPLUG_TRACKER -//#define DLSBANK_LOG -//#define DLSINSTR_LOG +#ifdef MPT_ALL_LOGGING +#define DLSBANK_LOG +#define DLSINSTR_LOG +#endif #define F_RGN_OPTION_SELFNONEXCLUSIVE 0x0001 +// Region Flags +enum RegionFlags +{ + DLSREGION_KEYGROUPMASK = 0x0F, + DLSREGION_OVERRIDEWSMP = 0x10, + DLSREGION_PINGPONGLOOP = 0x20, + DLSREGION_SAMPLELOOP = 0x40, + DLSREGION_SELFNONEXCLUSIVE = 0x80, + DLSREGION_SUSTAINLOOP = 0x100, + DLSREGION_ISGLOBAL = 0x200, +}; + /////////////////////////////////////////////////////////////////////////// // Articulation connection graph definitions -// Generic Sources -#define CONN_SRC_NONE 0x0000 -#define CONN_SRC_LFO 0x0001 -#define CONN_SRC_KEYONVELOCITY 0x0002 -#define CONN_SRC_KEYNUMBER 0x0003 -#define CONN_SRC_EG1 0x0004 -#define CONN_SRC_EG2 0x0005 -#define CONN_SRC_PITCHWHEEL 0x0006 +enum ConnectionSource : uint16 +{ + // Generic Sources + CONN_SRC_NONE = 0x0000, + CONN_SRC_LFO = 0x0001, + CONN_SRC_KEYONVELOCITY = 0x0002, + CONN_SRC_KEYNUMBER = 0x0003, + CONN_SRC_EG1 = 0x0004, + CONN_SRC_EG2 = 0x0005, + CONN_SRC_PITCHWHEEL = 0x0006, -#define CONN_SRC_POLYPRESSURE 0x0007 -#define CONN_SRC_CHANNELPRESSURE 0x0008 -#define CONN_SRC_VIBRATO 0x0009 + CONN_SRC_POLYPRESSURE = 0x0007, + CONN_SRC_CHANNELPRESSURE = 0x0008, + CONN_SRC_VIBRATO = 0x0009, -// Midi Controllers 0-127 -#define CONN_SRC_CC1 0x0081 -#define CONN_SRC_CC7 0x0087 -#define CONN_SRC_CC10 0x008a -#define CONN_SRC_CC11 0x008b + // Midi Controllers 0-127 + CONN_SRC_CC1 = 0x0081, + CONN_SRC_CC7 = 0x0087, + CONN_SRC_CC10 = 0x008a, + CONN_SRC_CC11 = 0x008b, -#define CONN_SRC_CC91 0x00db -#define CONN_SRC_CC93 0x00dd + CONN_SRC_CC91 = 0x00db, + CONN_SRC_CC93 = 0x00dd, -#define CONN_SRC_RPN0 0x0100 -#define CONN_SRC_RPN1 0x0101 -#define CONN_SRC_RPN2 0x0102 + CONN_SRC_RPN0 = 0x0100, + CONN_SRC_RPN1 = 0x0101, + CONN_SRC_RPN2 = 0x0102, +}; -// Generic Destinations -#define CONN_DST_NONE 0x0000 -#define CONN_DST_ATTENUATION 0x0001 -#define CONN_DST_RESERVED 0x0002 -#define CONN_DST_PITCH 0x0003 -#define CONN_DST_PAN 0x0004 +enum ConnectionDestination : uint16 +{ + // Generic Destinations + CONN_DST_NONE = 0x0000, + CONN_DST_ATTENUATION = 0x0001, + CONN_DST_RESERVED = 0x0002, + CONN_DST_PITCH = 0x0003, + CONN_DST_PAN = 0x0004, -// LFO Destinations -#define CONN_DST_LFO_FREQUENCY 0x0104 -#define CONN_DST_LFO_STARTDELAY 0x0105 + // LFO Destinations + CONN_DST_LFO_FREQUENCY = 0x0104, + CONN_DST_LFO_STARTDELAY = 0x0105, -#define CONN_DST_KEYNUMBER 0x0005 + CONN_DST_KEYNUMBER = 0x0005, -// EG1 Destinations -#define CONN_DST_EG1_ATTACKTIME 0x0206 -#define CONN_DST_EG1_DECAYTIME 0x0207 -#define CONN_DST_EG1_RESERVED 0x0208 -#define CONN_DST_EG1_RELEASETIME 0x0209 -#define CONN_DST_EG1_SUSTAINLEVEL 0x020a + // EG1 Destinations + CONN_DST_EG1_ATTACKTIME = 0x0206, + CONN_DST_EG1_DECAYTIME = 0x0207, + CONN_DST_EG1_RESERVED = 0x0208, + CONN_DST_EG1_RELEASETIME = 0x0209, + CONN_DST_EG1_SUSTAINLEVEL = 0x020a, -#define CONN_DST_EG1_DELAYTIME 0x020b -#define CONN_DST_EG1_HOLDTIME 0x020c -#define CONN_DST_EG1_SHUTDOWNTIME 0x020d + CONN_DST_EG1_DELAYTIME = 0x020b, + CONN_DST_EG1_HOLDTIME = 0x020c, + CONN_DST_EG1_SHUTDOWNTIME = 0x020d, -// EG2 Destinations -#define CONN_DST_EG2_ATTACKTIME 0x030a -#define CONN_DST_EG2_DECAYTIME 0x030b -#define CONN_DST_EG2_RESERVED 0x030c -#define CONN_DST_EG2_RELEASETIME 0x030d -#define CONN_DST_EG2_SUSTAINLEVEL 0x030e + // EG2 Destinations + CONN_DST_EG2_ATTACKTIME = 0x030a, + CONN_DST_EG2_DECAYTIME = 0x030b, + CONN_DST_EG2_RESERVED = 0x030c, + CONN_DST_EG2_RELEASETIME = 0x030d, + CONN_DST_EG2_SUSTAINLEVEL = 0x030e, -#define CONN_DST_EG2_DELAYTIME 0x030f -#define CONN_DST_EG2_HOLDTIME 0x0310 + CONN_DST_EG2_DELAYTIME = 0x030f, + CONN_DST_EG2_HOLDTIME = 0x0310, -#define CONN_TRN_NONE 0x0000 -#define CONN_TRN_CONCAVE 0x0001 + CONN_TRN_NONE = 0x0000, + CONN_TRN_CONCAVE = 0x0001, +}; ////////////////////////////////////////////////////////// // Supported DLS1 Articulations -#define MAKE_ART(src, ctl, dst) ( ((dst)<<16) | ((ctl)<<8) | (src) ) +// [4-bit transform][12-bit dest][8-bit control][8-bit source] = 32-bit ID +constexpr uint32 DLSArt(uint8 src, uint8 ctl, uint16 dst) +{ + return (dst << 16u) | (ctl << 8u) | src; +} -// Vibrato / Tremolo -#define ART_LFO_FREQUENCY MAKE_ART (CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_LFO_FREQUENCY) -#define ART_LFO_STARTDELAY MAKE_ART (CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_LFO_STARTDELAY) -#define ART_LFO_ATTENUATION MAKE_ART (CONN_SRC_LFO, CONN_SRC_NONE, CONN_DST_ATTENUATION) -#define ART_LFO_PITCH MAKE_ART (CONN_SRC_LFO, CONN_SRC_NONE, CONN_DST_PITCH) -#define ART_LFO_MODWTOATTN MAKE_ART (CONN_SRC_LFO, CONN_SRC_CC1, CONN_DST_ATTENUATION) -#define ART_LFO_MODWTOPITCH MAKE_ART (CONN_SRC_LFO, CONN_SRC_CC1, CONN_DST_PITCH) +enum DLSArt : uint32 +{ + // Vibrato / Tremolo + ART_LFO_FREQUENCY = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_LFO_FREQUENCY), + ART_LFO_STARTDELAY = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_LFO_STARTDELAY), + ART_LFO_ATTENUATION = DLSArt(CONN_SRC_LFO, CONN_SRC_NONE, CONN_DST_ATTENUATION), + ART_LFO_PITCH = DLSArt(CONN_SRC_LFO, CONN_SRC_NONE, CONN_DST_PITCH), + ART_LFO_MODWTOATTN = DLSArt(CONN_SRC_LFO, CONN_SRC_CC1, CONN_DST_ATTENUATION), + ART_LFO_MODWTOPITCH = DLSArt(CONN_SRC_LFO, CONN_SRC_CC1, CONN_DST_PITCH), -// Volume Envelope -#define ART_VOL_EG_ATTACKTIME MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_ATTACKTIME) -#define ART_VOL_EG_DECAYTIME MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_DECAYTIME) -#define ART_VOL_EG_SUSTAINLEVEL MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_SUSTAINLEVEL) -#define ART_VOL_EG_RELEASETIME MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_RELEASETIME) -#define ART_VOL_EG_DELAYTIME MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_DELAYTIME) -#define ART_VOL_EG_HOLDTIME MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_HOLDTIME) -#define ART_VOL_EG_SHUTDOWNTIME MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_SHUTDOWNTIME) -#define ART_VOL_EG_VELTOATTACK MAKE_ART(CONN_SRC_KEYONVELOCITY, CONN_SRC_NONE, CONN_DST_EG1_ATTACKTIME) -#define ART_VOL_EG_KEYTODECAY MAKE_ART(CONN_SRC_KEYNUMBER, CONN_SRC_NONE, CONN_DST_EG1_DECAYTIME) + // Volume Envelope + ART_VOL_EG_ATTACKTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_ATTACKTIME), + ART_VOL_EG_DECAYTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_DECAYTIME), + ART_VOL_EG_SUSTAINLEVEL = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_SUSTAINLEVEL), + ART_VOL_EG_RELEASETIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_RELEASETIME), + ART_VOL_EG_DELAYTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_DELAYTIME), + ART_VOL_EG_HOLDTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_HOLDTIME), + ART_VOL_EG_SHUTDOWNTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_SHUTDOWNTIME), + ART_VOL_EG_VELTOATTACK = DLSArt(CONN_SRC_KEYONVELOCITY, CONN_SRC_NONE, CONN_DST_EG1_ATTACKTIME), + ART_VOL_EG_KEYTODECAY = DLSArt(CONN_SRC_KEYNUMBER, CONN_SRC_NONE, CONN_DST_EG1_DECAYTIME), -// Pitch Envelope -#define ART_PITCH_EG_ATTACKTIME MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_ATTACKTIME) -#define ART_PITCH_EG_DECAYTIME MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_DECAYTIME) -#define ART_PITCH_EG_SUSTAINLEVEL MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_SUSTAINLEVEL) -#define ART_PITCH_EG_RELEASETIME MAKE_ART(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_RELEASETIME) -#define ART_PITCH_EG_VELTOATTACK MAKE_ART(CONN_SRC_KEYONVELOCITY, CONN_SRC_NONE, CONN_DST_EG2_ATTACKTIME) -#define ART_PITCH_EG_KEYTODECAY MAKE_ART(CONN_SRC_KEYNUMBER, CONN_SRC_NONE, CONN_DST_EG2_DECAYTIME) + // Pitch Envelope + ART_PITCH_EG_ATTACKTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_ATTACKTIME), + ART_PITCH_EG_DECAYTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_DECAYTIME), + ART_PITCH_EG_SUSTAINLEVEL = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_SUSTAINLEVEL), + ART_PITCH_EG_RELEASETIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_RELEASETIME), + ART_PITCH_EG_VELTOATTACK = DLSArt(CONN_SRC_KEYONVELOCITY, CONN_SRC_NONE, CONN_DST_EG2_ATTACKTIME), + ART_PITCH_EG_KEYTODECAY = DLSArt(CONN_SRC_KEYNUMBER, CONN_SRC_NONE, CONN_DST_EG2_DECAYTIME), -// Default Pan -#define ART_DEFAULTPAN MAKE_ART (CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_PAN) + // Default Pan + ART_DEFAULTPAN = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_PAN), +}; ////////////////////////////////////////////////////////// // DLS IFF Chunk IDs -// Standard IFF chunks IDs -#define IFFID_FORM 0x4d524f46 -#define IFFID_RIFF 0x46464952 -#define IFFID_LIST 0x5453494C -#define IFFID_INFO 0x4F464E49 +enum IFFChunkID : uint32 +{ + // Standard IFF chunks IDs + IFFID_FORM = 0x4d524f46, + IFFID_RIFF = 0x46464952, + IFFID_LIST = 0x5453494C, + IFFID_INFO = 0x4F464E49, -// IFF Info fields -#define IFFID_ICOP 0x504F4349 -#define IFFID_INAM 0x4D414E49 -#define IFFID_ICMT 0x544D4349 -#define IFFID_IENG 0x474E4549 -#define IFFID_ISFT 0x54465349 -#define IFFID_ISBJ 0x4A425349 + // IFF Info fields + IFFID_ICOP = 0x504F4349, + IFFID_INAM = 0x4D414E49, + IFFID_ICMT = 0x544D4349, + IFFID_IENG = 0x474E4549, + IFFID_ISFT = 0x54465349, + IFFID_ISBJ = 0x4A425349, -// Wave IFF chunks IDs -#define IFFID_wave 0x65766177 -#define IFFID_wsmp 0x706D7377 + // Wave IFF chunks IDs + IFFID_wave = 0x65766177, + IFFID_wsmp = 0x706D7377, -#define IFFID_XDLS 0x534c4458 -#define IFFID_DLS 0x20534C44 -#define IFFID_MLS 0x20534C4D -#define IFFID_RMID 0x44494D52 -#define IFFID_colh 0x686C6F63 -#define IFFID_ins 0x20736E69 -#define IFFID_insh 0x68736E69 -#define IFFID_ptbl 0x6C627470 -#define IFFID_wvpl 0x6C707677 -#define IFFID_rgn 0x206E6772 -#define IFFID_rgn2 0x326E6772 -#define IFFID_rgnh 0x686E6772 -#define IFFID_wlnk 0x6B6E6C77 -#define IFFID_art1 0x31747261 -#define IFFID_art2 0x32747261 + IFFID_XDLS = 0x534c4458, + IFFID_DLS = 0x20534C44, + IFFID_MLS = 0x20534C4D, + IFFID_RMID = 0x44494D52, + IFFID_colh = 0x686C6F63, + IFFID_ins = 0x20736E69, + IFFID_insh = 0x68736E69, + IFFID_ptbl = 0x6C627470, + IFFID_wvpl = 0x6C707677, + IFFID_rgn = 0x206E6772, + IFFID_rgn2 = 0x326E6772, + IFFID_rgnh = 0x686E6772, + IFFID_wlnk = 0x6B6E6C77, + IFFID_art1 = 0x31747261, + IFFID_art2 = 0x32747261, +}; ////////////////////////////////////////////////////////// // DLS Structures definitions @@ -186,24 +216,20 @@ MPT_BINARY_STRUCT(IFFCHUNK, 8) struct RIFFCHUNKID { uint32le id_RIFF; - union - { - uint32le riff_len; - uint32be riff_len_be; - }; + uint32le riff_len; uint32le id_DLS; }; MPT_BINARY_STRUCT(RIFFCHUNKID, 12) -struct LISTCHUNK +struct LISTChunk { uint32le id; uint32le len; uint32le listid; }; -MPT_BINARY_STRUCT(LISTCHUNK, 12) +MPT_BINARY_STRUCT(LISTChunk, 12) struct DLSRGNRANGE { @@ -230,50 +256,42 @@ struct PTBLCHUNK MPT_BINARY_STRUCT(PTBLCHUNK, 8) -struct INSHCHUNK +struct INSHChunk { - uint32le id; - uint32le len; uint32le cRegions; uint32le ulBank; uint32le ulInstrument; }; -MPT_BINARY_STRUCT(INSHCHUNK, 20) +MPT_BINARY_STRUCT(INSHChunk, 12) -struct RGNHCHUNK +struct RGNHChunk { - uint32le id; - uint32le len; DLSRGNRANGE RangeKey; DLSRGNRANGE RangeVelocity; uint16le fusOptions; uint16le usKeyGroup; }; -MPT_BINARY_STRUCT(RGNHCHUNK, 20) +MPT_BINARY_STRUCT(RGNHChunk, 12) -struct WLNKCHUNK +struct WLNKChunk { - uint32le id; - uint32le len; uint16le fusOptions; uint16le usPhaseGroup; uint32le ulChannel; uint32le ulTableIndex; }; -MPT_BINARY_STRUCT(WLNKCHUNK, 20) +MPT_BINARY_STRUCT(WLNKChunk, 12) -struct ART1CHUNK +struct ART1Chunk { - uint32le id; - uint32le len; uint32le cbSize; uint32le cConnectionBlocks; }; -MPT_BINARY_STRUCT(ART1CHUNK, 16) +MPT_BINARY_STRUCT(ART1Chunk, 8) struct CONNECTIONBLOCK { @@ -288,8 +306,6 @@ MPT_BINARY_STRUCT(CONNECTIONBLOCK, 12) struct WSMPCHUNK { - uint32le id; - uint32le len; uint32le cbSize; uint16le usUnityNote; int16le sFineTune; @@ -298,7 +314,7 @@ struct WSMPCHUNK uint32le cSampleLoops; }; -MPT_BINARY_STRUCT(WSMPCHUNK, 28) +MPT_BINARY_STRUCT(WSMPCHUNK, 20) struct WSMPSAMPLELOOP { @@ -315,22 +331,28 @@ MPT_BINARY_STRUCT(WSMPSAMPLELOOP, 16) ///////////////////////////////////////////////////////////////////// // SF2 IFF Chunk IDs -#define IFFID_sfbk 0x6b626673 -#define IFFID_sdta 0x61746473 -#define IFFID_pdta 0x61746470 -#define IFFID_phdr 0x72646870 -#define IFFID_pbag 0x67616270 -#define IFFID_pgen 0x6E656770 -#define IFFID_inst 0x74736E69 -#define IFFID_ibag 0x67616269 -#define IFFID_igen 0x6E656769 -#define IFFID_shdr 0x72646873 +enum SF2ChunkID : uint32 +{ + IFFID_sfbk = 0x6b626673, + IFFID_sfpk = 0x6b706673, + IFFID_sdta = 0x61746473, + IFFID_pdta = 0x61746470, + IFFID_phdr = 0x72646870, + IFFID_pbag = 0x67616270, + IFFID_pgen = 0x6E656770, + IFFID_inst = 0x74736E69, + IFFID_ibag = 0x67616269, + IFFID_igen = 0x6E656769, + IFFID_shdr = 0x72646873, +}; /////////////////////////////////////////// // SF2 Generators IDs -enum SF2Generators +enum SF2Generators : uint16 { + SF2_GEN_START_LOOP_FINE = 2, + SF2_GEN_END_LOOP_FINE = 3, SF2_GEN_MODENVTOFILTERFC = 11, SF2_GEN_PAN = 17, SF2_GEN_DECAYMODENV = 28, @@ -341,11 +363,14 @@ enum SF2Generators SF2_GEN_RELEASEVOLENV = 38, SF2_GEN_INSTRUMENT = 41, SF2_GEN_KEYRANGE = 43, + SF2_GEN_START_LOOP_COARSE = 45, SF2_GEN_ATTENUATION = 48, + SF2_GEN_END_LOOP_COARSE = 50, SF2_GEN_COARSETUNE = 51, SF2_GEN_FINETUNE = 52, SF2_GEN_SAMPLEID = 53, SF2_GEN_SAMPLEMODES = 54, + SF2_GEN_SCALE_TUNING = 56, SF2_GEN_KEYGROUP = 57, SF2_GEN_UNITYNOTE = 58, }; @@ -426,18 +451,13 @@ MPT_BINARY_STRUCT(SFSAMPLE, 46) ///////////////////////////////////////////////////////////////////// -struct SF2LOADERINFO +struct SF2LoaderInfo { - uint32 nPresetBags; - const SFPRESETBAG *pPresetBags; - uint32 nPresetGens; - const SFGENLIST *pPresetGens; - uint32 nInsts; - const SFINST *pInsts; - uint32 nInstBags; - const SFINSTBAG *pInstBags; - uint32 nInstGens; - const SFINSTGENLIST *pInstGens; + FileReader presetBags; + FileReader presetGens; + FileReader insts; + FileReader instBags; + FileReader instGens; }; @@ -470,7 +490,7 @@ int32 CDLSBank::DLS32BitTimeCentsToMilliseconds(int32 lTimeCents) // tc = log2(time[secs]) * 1200*65536 // time[secs] = 2^(tc/(1200*65536)) if ((uint32)lTimeCents == 0x80000000) return 0; - double fmsecs = 1000.0 * pow(2.0, ((double)lTimeCents)/(1200.0*65536.0)); + double fmsecs = 1000.0 * std::pow(2.0, ((double)lTimeCents)/(1200.0*65536.0)); if (fmsecs < -32767) return -32767; if (fmsecs > 32767) return 32767; return (int32)fmsecs; @@ -481,7 +501,7 @@ int32 CDLSBank::DLS32BitTimeCentsToMilliseconds(int32 lTimeCents) int32 CDLSBank::DLS32BitRelativeGainToLinear(int32 lCentibels) { // v = 10^(cb/(200*65536)) * V - return (int32)(65536.0 * pow(10.0, ((double)lCentibels)/(200*65536.0)) ); + return (int32)(65536.0 * std::pow(10.0, ((double)lCentibels)/(200*65536.0)) ); } @@ -489,7 +509,7 @@ int32 CDLSBank::DLS32BitRelativeLinearToGain(int32 lGain) { // cb = log10(v/V) * 200 * 65536 if (lGain <= 0) return -960 * 65536; - return (int32)( 200*65536.0 * log10( ((double)lGain)/65536.0 ) ); + return (int32)(200 * 65536.0 * std::log10(((double)lGain) / 65536.0)); } @@ -526,7 +546,7 @@ bool CDLSBank::IsDLSBank(const mpt::PathString &filename) // Miles Sound System do { - uint32 len = riff.riff_len_be; + uint32 len = mpt::bit_cast(riff.riff_len); if (len <= 4) break; if (riff.id_DLS == IFFID_XDLS) { @@ -621,9 +641,9 @@ bool CDLSBank::FindAndExtract(CSoundFile &sndFile, const INSTRUMENTINDEX ins, co if(ExtractInstrument(sndFile, ins, dlsIns, drumRgn)) { pIns = sndFile.Instruments[ins]; // Reset pointer because ExtractInstrument may delete the previous value. - if((key >= 24) && (key < 24 + mpt::size(szMidiPercussionNames))) + if((key >= 24) && (key < 24 + std::size(szMidiPercussionNames))) { - mpt::String::CopyN(pIns->name, szMidiPercussionNames[key - 24]); + pIns->name = szMidiPercussionNames[key - 24]; } return true; } @@ -635,28 +655,28 @@ bool CDLSBank::FindAndExtract(CSoundFile &sndFile, const INSTRUMENTINDEX ins, co /////////////////////////////////////////////////////////////// // Update DLS instrument definition from an IFF chunk -bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK *pchunk, uint32 dwMaxLen) +bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chunk) { - if ((!pchunk->len) || (pchunk->len+8 > dwMaxLen)) return false; - if (pchunk->id == IFFID_LIST) + IFFCHUNK header; + chunk.ReadStruct(header); + if(!header.len || !chunk.CanRead(header.len)) + return false; + if(header.id == IFFID_LIST) { - LISTCHUNK *plist = (LISTCHUNK *)pchunk; - uint32 dwPos = 12; - while (dwPos < plist->len) + uint32 listid = chunk.ReadUint32LE(); + while(chunk.CanRead(sizeof(IFFCHUNK))) { - const IFFCHUNK *p = (const IFFCHUNK *)(((uint8 *)plist) + dwPos); - if (!(p->id & 0xFF)) + IFFCHUNK subHeader; + chunk.ReadStruct(subHeader); + chunk.SkipBack(sizeof(IFFCHUNK)); + FileReader subData = chunk.ReadChunk(subHeader.len + sizeof(IFFCHUNK)); + if(subHeader.len & 1) { - p = (const IFFCHUNK *)( ((uint8 *)p)+1 ); - dwPos++; + chunk.Skip(1); } - if (dwPos + p->len + 8 <= plist->len + 12) - { - UpdateInstrumentDefinition(pDlsIns, p, p->len+8); - } - dwPos += p->len + 8; + UpdateInstrumentDefinition(pDlsIns, subData); } - switch(plist->listid) + switch(listid) { case IFFID_rgn: // Level 1 region case IFFID_rgn2: // Level 2 region @@ -665,64 +685,75 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK } } else { - switch(pchunk->id) + switch(header.id) { case IFFID_insh: - pDlsIns->ulBank = ((INSHCHUNK *)pchunk)->ulBank; - pDlsIns->ulInstrument = ((INSHCHUNK *)pchunk)->ulInstrument; - //Log("%3d regions, bank 0x%04X instrument %3d\n", ((INSHCHUNK *)pchunk)->cRegions, pDlsIns->ulBank, pDlsIns->ulInstrument); + { + INSHChunk insh; + chunk.ReadStruct(insh); + pDlsIns->ulBank = insh.ulBank; + pDlsIns->ulInstrument = insh.ulInstrument; + //Log("%3d regions, bank 0x%04X instrument %3d\n", insh.cRegions, pDlsIns->ulBank, pDlsIns->ulInstrument); break; + } case IFFID_rgnh: if (pDlsIns->nRegions < DLSMAXREGIONS) { - RGNHCHUNK *p = (RGNHCHUNK *)pchunk; - DLSREGION *pregion = &pDlsIns->Regions[pDlsIns->nRegions]; - pregion->uKeyMin = (uint8)p->RangeKey.usLow; - pregion->uKeyMax = (uint8)p->RangeKey.usHigh; - pregion->fuOptions = (uint8)(p->usKeyGroup & DLSREGION_KEYGROUPMASK); - if (p->fusOptions & F_RGN_OPTION_SELFNONEXCLUSIVE) pregion->fuOptions |= DLSREGION_SELFNONEXCLUSIVE; - //Log(" Region %d: fusOptions=0x%02X usKeyGroup=0x%04X ", pDlsIns->nRegions, p->fusOptions, p->usKeyGroup); - //Log("KeyRange[%3d,%3d] ", p->RangeKey.usLow, p->RangeKey.usHigh); + RGNHChunk rgnh; + chunk.ReadStruct(rgnh); + DLSREGION ®ion = pDlsIns->Regions[pDlsIns->nRegions]; + region.uKeyMin = (uint8)rgnh.RangeKey.usLow; + region.uKeyMax = (uint8)rgnh.RangeKey.usHigh; + region.fuOptions = (uint8)(rgnh.usKeyGroup & DLSREGION_KEYGROUPMASK); + if(rgnh.fusOptions & F_RGN_OPTION_SELFNONEXCLUSIVE) + region.fuOptions |= DLSREGION_SELFNONEXCLUSIVE; + //Log(" Region %d: fusOptions=0x%02X usKeyGroup=0x%04X ", pDlsIns->nRegions, rgnh.fusOptions, rgnh.usKeyGroup); + //Log("KeyRange[%3d,%3d] ", rgnh.RangeKey.usLow, rgnh.RangeKey.usHigh); } break; case IFFID_wlnk: if (pDlsIns->nRegions < DLSMAXREGIONS) { - DLSREGION *pregion = &pDlsIns->Regions[pDlsIns->nRegions]; - WLNKCHUNK *p = (WLNKCHUNK *)pchunk; - pregion->nWaveLink = (uint16)p->ulTableIndex; - if ((pregion->nWaveLink < uint16_max) && (pregion->nWaveLink >= m_nMaxWaveLink)) m_nMaxWaveLink = pregion->nWaveLink + 1; - //Log(" WaveLink %d: fusOptions=0x%02X usPhaseGroup=0x%04X ", pDlsIns->nRegions, p->fusOptions, p->usPhaseGroup); - //Log("ulChannel=%d ulTableIndex=%4d\n", p->ulChannel, p->ulTableIndex); + WLNKChunk wlnk; + chunk.ReadStruct(wlnk); + DLSREGION ®ion = pDlsIns->Regions[pDlsIns->nRegions]; + region.nWaveLink = (uint16)wlnk.ulTableIndex; + if((region.nWaveLink < Util::MaxValueOfType(region.nWaveLink)) && (region.nWaveLink >= m_nMaxWaveLink)) + m_nMaxWaveLink = region.nWaveLink + 1; + //Log(" WaveLink %d: fusOptions=0x%02X usPhaseGroup=0x%04X ", pDlsIns->nRegions, wlnk.fusOptions, wlnk.usPhaseGroup); + //Log("ulChannel=%d ulTableIndex=%4d\n", wlnk.ulChannel, wlnk.ulTableIndex); } break; case IFFID_wsmp: if (pDlsIns->nRegions < DLSMAXREGIONS) { - DLSREGION *pregion = &pDlsIns->Regions[pDlsIns->nRegions]; - WSMPCHUNK *p = (WSMPCHUNK *)pchunk; - pregion->fuOptions |= DLSREGION_OVERRIDEWSMP; - pregion->uUnityNote = (uint8)p->usUnityNote; - pregion->sFineTune = p->sFineTune; - int32 lVolume = DLS32BitRelativeGainToLinear(p->lAttenuation) / 256; + DLSREGION ®ion = pDlsIns->Regions[pDlsIns->nRegions]; + WSMPCHUNK wsmp; + chunk.ReadStruct(wsmp); + region.fuOptions |= DLSREGION_OVERRIDEWSMP; + region.uUnityNote = (uint8)wsmp.usUnityNote; + region.sFineTune = wsmp.sFineTune; + int32 lVolume = DLS32BitRelativeGainToLinear(wsmp.lAttenuation) / 256; if (lVolume > 256) lVolume = 256; if (lVolume < 4) lVolume = 4; - pregion->usVolume = (uint16)lVolume; + region.usVolume = (uint16)lVolume; //Log(" WaveSample %d: usUnityNote=%2d sFineTune=%3d ", pDlsEnv->nRegions, p->usUnityNote, p->sFineTune); //Log("fulOptions=0x%04X loops=%d\n", p->fulOptions, p->cSampleLoops); - if ((p->cSampleLoops) && (p->cbSize + sizeof(WSMPSAMPLELOOP) <= p->len)) + if((wsmp.cSampleLoops) && (wsmp.cbSize + sizeof(WSMPSAMPLELOOP) <= header.len)) { - WSMPSAMPLELOOP *ploop = (WSMPSAMPLELOOP *)(((uint8 *)p)+8+p->cbSize); + WSMPSAMPLELOOP loop; + chunk.Seek(sizeof(IFFCHUNK) + wsmp.cbSize); + chunk.ReadStruct(loop); //Log("looptype=%2d loopstart=%5d loopend=%5d\n", ploop->ulLoopType, ploop->ulLoopStart, ploop->ulLoopLength); - if (ploop->ulLoopLength > 3) + if(loop.ulLoopLength > 3) { - pregion->fuOptions |= DLSREGION_SAMPLELOOP; - //if (ploop->ulLoopType) pregion->fuOptions |= DLSREGION_PINGPONGLOOP; - pregion->ulLoopStart = ploop->ulLoopStart; - pregion->ulLoopEnd = ploop->ulLoopStart + ploop->ulLoopLength; + region.fuOptions |= DLSREGION_SAMPLELOOP; + //if(loop.ulLoopType) region.fuOptions |= DLSREGION_PINGPONGLOOP; + region.ulLoopStart = loop.ulLoopStart; + region.ulLoopEnd = loop.ulLoopStart + loop.ulLoopLength; } } } @@ -731,7 +762,8 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK case IFFID_art1: case IFFID_art2: { - ART1CHUNK *p = (ART1CHUNK *)pchunk; + ART1Chunk art1; + chunk.ReadStruct(art1); if (pDlsIns->ulBank & F_INSTRUMENT_DRUMS) { if (pDlsIns->nRegions >= DLSMAXREGIONS) break; @@ -739,25 +771,28 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK { pDlsIns->nMelodicEnv = static_cast(m_Envelopes.size() + 1); } - if (p->cbSize+p->cConnectionBlocks*sizeof(CONNECTIONBLOCK) > p->len) break; + if(art1.cbSize + art1.cConnectionBlocks * sizeof(CONNECTIONBLOCK) > header.len) + break; DLSENVELOPE dlsEnv; MemsetZero(dlsEnv); dlsEnv.nDefPan = 128; dlsEnv.nVolSustainLevel = 128; //Log(" art1 (%3d bytes): cbSize=%d cConnectionBlocks=%d\n", p->len, p->cbSize, p->cConnectionBlocks); - CONNECTIONBLOCK *pblk = (CONNECTIONBLOCK *)( ((uint8 *)p)+8+p->cbSize ); - for (uint32 iblk=0; iblkcConnectionBlocks; iblk++, pblk++) + chunk.Seek(sizeof(IFFCHUNK) + art1.cbSize); + for (uint32 iblk = 0; iblk < art1.cConnectionBlocks; iblk++) { + CONNECTIONBLOCK blk; + chunk.ReadStruct(blk); // [4-bit transform][12-bit dest][8-bit control][8-bit source] = 32-bit ID - uint32 dwArticulation = pblk->usTransform; - dwArticulation = (dwArticulation << 12) | (pblk->usDestination & 0x0FFF); - dwArticulation = (dwArticulation << 8) | (pblk->usControl & 0x00FF); - dwArticulation = (dwArticulation << 8) | (pblk->usSource & 0x00FF); + uint32 dwArticulation = blk.usTransform; + dwArticulation = (dwArticulation << 12) | (blk.usDestination & 0x0FFF); + dwArticulation = (dwArticulation << 8) | (blk.usControl & 0x00FF); + dwArticulation = (dwArticulation << 8) | (blk.usSource & 0x00FF); switch(dwArticulation) { case ART_DEFAULTPAN: { - int32 pan = 128 + pblk->lScale / (65536000/128); + int32 pan = 128 + blk.lScale / (65536000/128); if (pan < 0) pan = 0; if (pan > 255) pan = 255; dlsEnv.nDefPan = (uint8)pan; @@ -767,9 +802,9 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK case ART_VOL_EG_ATTACKTIME: // 32-bit time cents units. range = [0s, 20s] dlsEnv.wVolAttack = 0; - if (pblk->lScale > -0x40000000) + if(blk.lScale > -0x40000000) { - int32 l = pblk->lScale - 78743200; // maximum velocity + int32 l = blk.lScale - 78743200; // maximum velocity if (l > 0) l = 0; int32 attacktime = DLS32BitTimeCentsToMilliseconds(l); if (attacktime < 0) attacktime = 0; @@ -782,9 +817,9 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK case ART_VOL_EG_DECAYTIME: // 32-bit time cents units. range = [0s, 20s] dlsEnv.wVolDecay = 0; - if (pblk->lScale > -0x40000000) + if(blk.lScale > -0x40000000) { - int32 decaytime = DLS32BitTimeCentsToMilliseconds(pblk->lScale); + int32 decaytime = DLS32BitTimeCentsToMilliseconds(blk.lScale); if (decaytime > 20000) decaytime = 20000; if (decaytime >= 20) dlsEnv.wVolDecay = (uint16)(decaytime / 20); //Log("%3d: Envelope Decay Time set to %d (%d time cents)\n", (uint32)(dlsEnv.ulInstrument & 0x7F)|((dlsEnv.ulBank >> 16) & 0x8000), decaytime, pblk->lScale); @@ -794,9 +829,9 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK case ART_VOL_EG_RELEASETIME: // 32-bit time cents units. range = [0s, 20s] dlsEnv.wVolRelease = 0; - if (pblk->lScale > -0x40000000) + if(blk.lScale > -0x40000000) { - int32 releasetime = DLS32BitTimeCentsToMilliseconds(pblk->lScale); + int32 releasetime = DLS32BitTimeCentsToMilliseconds(blk.lScale); if (releasetime > 20000) releasetime = 20000; if (releasetime >= 20) dlsEnv.wVolRelease = (uint16)(releasetime / 20); //Log("%3d: Envelope Release Time set to %d (%d time cents)\n", (uint32)(dlsEnv.ulInstrument & 0x7F)|((dlsEnv.ulBank >> 16) & 0x8000), dlsEnv.wVolRelease, pblk->lScale); @@ -805,14 +840,14 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK case ART_VOL_EG_SUSTAINLEVEL: // 0.1% units - if (pblk->lScale >= 0) + if(blk.lScale >= 0) { - dlsEnv.nVolSustainLevel = DLSSustainLevelToLinear(pblk->lScale); + dlsEnv.nVolSustainLevel = DLSSustainLevelToLinear(blk.lScale); } break; //default: - // Log(" Articulation = 0x%08X value=%d\n", dwArticulation, pblk->lScale); + // Log(" Articulation = 0x%08X value=%d\n", dwArticulation, blk.lScale); } } m_Envelopes.push_back(dlsEnv); @@ -820,15 +855,15 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK break; case IFFID_INAM: - mpt::String::CopyN(pDlsIns->szName, ((const char *)pchunk) + 8, pchunk->len); + chunk.ReadString(pDlsIns->szName, header.len); break; #if 0 default: { char sid[5]; - memcpy(sid, &pchunk->id, 4); + memcpy(sid, &header.id, 4); sid[4] = 0; - Log(" \"%s\": %d bytes\n", (uint32)sid, pchunk->len.get()); + Log(" \"%s\": %d bytes\n", (uint32)sid, header.len.get()); } #endif } @@ -839,13 +874,13 @@ bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK /////////////////////////////////////////////////////////////// // Converts SF2 chunks to DLS -bool CDLSBank::UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &header, FileReader &chunk) +bool CDLSBank::UpdateSF2PresetData(SF2LoaderInfo &sf2info, const IFFCHUNK &header, FileReader &chunk) { if (!chunk.IsValid()) return false; switch(header.id) { case IFFID_phdr: - if (m_Instruments.empty()) + if(m_Instruments.empty()) { uint32 numIns = static_cast(chunk.GetLength() / sizeof(SFPRESETHEADER)); if(numIns <= 1) @@ -855,13 +890,13 @@ bool CDLSBank::UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &heade m_Instruments.resize(numIns); #ifdef DLSBANK_LOG - Log("phdr: %d instruments\n", m_Instruments.size()); + MPT_LOG(LogDebug, "DLSBank", mpt::format(U_("phdr: %1 instruments"))(m_Instruments.size())); #endif SFPRESETHEADER psfh; chunk.ReadStruct(psfh); for (auto &dlsIns : m_Instruments) { - mpt::String::Copy(dlsIns.szName, psfh.achPresetName); + mpt::String::WriteAutoBuf(dlsIns.szName) = mpt::String::ReadAutoBuf(psfh.achPresetName); dlsIns.ulInstrument = psfh.wPreset & 0x7F; dlsIns.ulBank = (psfh.wBank >= 128) ? F_INSTRUMENT_DRUMS : (psfh.wBank << 8); dlsIns.wPresetBagNdx = psfh.wPresetBagNdx; @@ -873,65 +908,43 @@ bool CDLSBank::UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &heade break; case IFFID_pbag: - if (!m_Instruments.empty()) + if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFPRESETBAG))) { - uint32 nBags = static_cast(chunk.GetLength() / sizeof(SFPRESETBAG)); - if (nBags) - { - sf2info.nPresetBags = nBags; - sf2info.pPresetBags = reinterpret_cast(chunk.GetRawData()); - } + sf2info.presetBags = chunk.GetChunk(chunk.BytesLeft()); } #ifdef DLSINSTR_LOG - else Log("pbag: no instruments!\n"); + else MPT_LOG(LogDebug, "DLSINSTR", U_("pbag: no instruments!")); #endif break; case IFFID_pgen: - if (!m_Instruments.empty()) + if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFGENLIST))) { - uint32 nGens = static_cast(chunk.GetLength() / sizeof(SFGENLIST)); - if (nGens) - { - sf2info.nPresetGens = nGens; - sf2info.pPresetGens = reinterpret_cast(chunk.GetRawData()); - } + sf2info.presetGens = chunk.GetChunk(chunk.BytesLeft()); } #ifdef DLSINSTR_LOG - else Log("pgen: no instruments!\n"); + else MPT_LOG(LogDebug, "DLSINSTR", U_("pgen: no instruments!")); #endif break; case IFFID_inst: - if (!m_Instruments.empty()) + if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFINST))) { - uint32 nIns = static_cast(chunk.GetLength() / sizeof(SFINST)); - sf2info.nInsts = nIns; - sf2info.pInsts = reinterpret_cast(chunk.GetRawData()); + sf2info.insts = chunk.GetChunk(chunk.BytesLeft()); } break; case IFFID_ibag: - if (!m_Instruments.empty()) + if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFINSTBAG))) { - uint32 nBags = static_cast(chunk.GetLength() / sizeof(SFINSTBAG)); - if (nBags) - { - sf2info.nInstBags = nBags; - sf2info.pInstBags = reinterpret_cast(chunk.GetRawData()); - } + sf2info.instBags = chunk.GetChunk(chunk.BytesLeft()); } break; case IFFID_igen: - if (!m_Instruments.empty()) + if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFINSTGENLIST))) { - uint32 nGens = static_cast(chunk.GetLength() / sizeof(SFINSTGENLIST)); - if (nGens) - { - sf2info.nInstGens = nGens; - sf2info.pInstGens = reinterpret_cast(chunk.GetRawData()); - } + sf2info.instGens = chunk.GetChunk(chunk.BytesLeft()); } break; @@ -942,16 +955,16 @@ bool CDLSBank::UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &heade if (numSmp < 1) break; m_SamplesEx.resize(numSmp); m_WaveForms.resize(numSmp); - #ifdef DLSINSTR_LOG - Log("shdr: %d samples\n", m_SamplesEx.size()); - #endif + #ifdef DLSINSTR_LOG + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_("shdr: %1 samples"))(m_SamplesEx.size())); + #endif for (uint32 i = 0; i < numSmp; i++) { SFSAMPLE p; chunk.ReadStruct(p); DLSSAMPLEEX &dlsSmp = m_SamplesEx[i]; - mpt::String::Copy(dlsSmp.szName, p.achSampleName); + mpt::String::WriteAutoBuf(dlsSmp.szName) = mpt::String::ReadAutoBuf(p.achSampleName); dlsSmp.dwLen = 0; dlsSmp.dwSampleRate = p.dwSampleRate; dlsSmp.byOriginalPitch = p.byOriginalPitch; @@ -977,7 +990,7 @@ bool CDLSBank::UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &heade char sdbg[5]; memcpy(sdbg, &header.id, 4); sdbg[4] = 0; - Log("Unsupported SF2 chunk: %s (%d bytes)\n", sdbg, header.len.get()); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_("Unsupported SF2 chunk: %1 (%2 bytes)"))(mpt::ToUnicode(mpt::Charset::ASCII, mpt::String::ReadAutoBuf(sdbg)), header.len.get())); } #endif } @@ -993,16 +1006,22 @@ static int16 SF2TimeToDLS(int16 amount) // Convert all instruments to the DLS format -bool CDLSBank::ConvertSF2ToDLS(SF2LOADERINFO &sf2info) +bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info) { if (m_Instruments.empty() || m_SamplesEx.empty()) return false; + const uint32 numPresetBags = static_cast(sf2info.presetBags.GetLength() / sizeof(SFPRESETBAG)); + const uint32 numPresetGens = static_cast(sf2info.presetGens.GetLength() / sizeof(SFGENLIST)); + const uint32 numInsts = static_cast(sf2info.insts.GetLength() / sizeof(SFINST)); + const uint32 numInstBags = static_cast(sf2info.instBags.GetLength() / sizeof(SFINSTBAG)); + const uint32 numInstGens = static_cast(sf2info.instGens.GetLength() / sizeof(SFINSTGENLIST)); + for (auto &dlsIns : m_Instruments) { DLSENVELOPE dlsEnv; - uint32 nInstrNdx = 0; - int32 lAttenuation = 0; + std::vector instruments; + int32 instrAttenuation = 0; // Default Envelope Values dlsEnv.wVolAttack = 0; dlsEnv.wVolDecay = 0; @@ -1010,44 +1029,50 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LOADERINFO &sf2info) dlsEnv.nVolSustainLevel = 128; dlsEnv.nDefPan = 128; // Load Preset Bags + sf2info.presetBags.Seek(dlsIns.wPresetBagNdx * sizeof(SFPRESETBAG)); for (uint32 ipbagcnt=0; ipbagcnt<(uint32)dlsIns.wPresetBagNum; ipbagcnt++) { - uint32 ipbagndx = dlsIns.wPresetBagNdx + ipbagcnt; - if ((ipbagndx+1 >= sf2info.nPresetBags) || (!sf2info.pPresetBags)) break; // Load generators for each preset bag - const SFPRESETBAG *pbag = sf2info.pPresetBags + ipbagndx; - for (uint32 ipgenndx=pbag[0].wGenNdx; ipgenndx= sf2info.nPresetGens)) break; - const SFGENLIST *pgen = sf2info.pPresetGens + ipgenndx; - switch(pgen->sfGenOper) + SFGENLIST gen; + if(!sf2info.presetGens.ReadStruct(gen)) + break; + switch(gen.sfGenOper) { case SF2_GEN_ATTACKVOLENV: - dlsEnv.wVolAttack = SF2TimeToDLS(pgen->genAmount); + dlsEnv.wVolAttack = SF2TimeToDLS(gen.genAmount); break; case SF2_GEN_DECAYVOLENV: - dlsEnv.wVolDecay = SF2TimeToDLS(pgen->genAmount); + dlsEnv.wVolDecay = SF2TimeToDLS(gen.genAmount); break; case SF2_GEN_SUSTAINVOLENV: // 0.1% units - if(pgen->genAmount >= 0) + if(gen.genAmount >= 0) { - dlsEnv.nVolSustainLevel = SF2SustainLevelToLinear(pgen->genAmount); + dlsEnv.nVolSustainLevel = SF2SustainLevelToLinear(gen.genAmount); } break; case SF2_GEN_RELEASEVOLENV: - dlsEnv.wVolRelease = SF2TimeToDLS(pgen->genAmount); + dlsEnv.wVolRelease = SF2TimeToDLS(gen.genAmount); break; case SF2_GEN_INSTRUMENT: - nInstrNdx = pgen->genAmount + 1; + if(std::find(instruments.begin(), instruments.end(), gen.genAmount) == instruments.end()) + instruments.push_back(gen.genAmount); break; case SF2_GEN_ATTENUATION: - lAttenuation = - (int)(uint16)(pgen->genAmount); + instrAttenuation = -static_cast(gen.genAmount); break; #if 0 default: Log("Ins %3d: bag %3d gen %3d: ", nIns, ipbagndx, ipgenndx); - Log("genoper=%d amount=0x%04X ", pgen->sfGenOper, pgen->genAmount); + Log("genoper=%d amount=0x%04X ", gen.sfGenOper, gen.genAmount); Log((pSmp->ulBank & F_INSTRUMENT_DRUMS) ? "(drum)\n" : "\n"); #endif } @@ -1060,110 +1085,151 @@ bool CDLSBank::ConvertSF2ToDLS(SF2LOADERINFO &sf2info) dlsIns.nMelodicEnv = static_cast(m_Envelopes.size()); } // Load Instrument Bags - if ((!nInstrNdx) || (nInstrNdx >= sf2info.nInsts) || (!sf2info.pInsts)) continue; - nInstrNdx--; - dlsIns.nRegions = sf2info.pInsts[nInstrNdx+1].wInstBagNdx - sf2info.pInsts[nInstrNdx].wInstBagNdx; - //Log("\nIns %3d, %2d regions:\n", nIns, pSmp->nRegions); - if (dlsIns.nRegions > DLSMAXREGIONS) dlsIns.nRegions = DLSMAXREGIONS; - DLSREGION *pRgn = dlsIns.Regions; - for (uint32 nRgn = 0; nRgn < dlsIns.nRegions; nRgn++, pRgn++) + dlsIns.nRegions = 0; + for(const auto nInstrNdx : instruments) { - uint32 ibagcnt = sf2info.pInsts[nInstrNdx].wInstBagNdx + nRgn; - if ((ibagcnt >= sf2info.nInstBags) || (!sf2info.pInstBags)) break; - // Create a new envelope for drums - DLSENVELOPE *pDlsEnv = &dlsEnv; - if (!(dlsIns.ulBank & F_INSTRUMENT_DRUMS) && dlsIns.nMelodicEnv > 0 && dlsIns.nMelodicEnv <= m_Envelopes.size()) + if(nInstrNdx >= numInsts) + continue; + sf2info.insts.Seek(nInstrNdx * sizeof(SFINST)); + SFINST insts[2]; + sf2info.insts.ReadArray(insts); + const auto startRegion = dlsIns.nRegions; + dlsIns.nRegions += insts[1].wInstBagNdx - insts[0].wInstBagNdx; + //Log("\nIns %3d, %2d regions:\n", nIns, pSmp->nRegions); + if (dlsIns.nRegions > DLSMAXREGIONS) dlsIns.nRegions = DLSMAXREGIONS; + DLSREGION *pRgn = &dlsIns.Regions[startRegion]; + bool hasGlobalZone = false; + for(uint32 nRgn = startRegion; nRgn < dlsIns.nRegions; nRgn++, pRgn++) { - pDlsEnv = &m_Envelopes[dlsIns.nMelodicEnv - 1]; - } - // Region Default Values - int32 lAttn = lAttenuation; - pRgn->uUnityNote = 0xFF; // 0xFF means undefined -> use sample - pRgn->sFineTune = 0; - pRgn->nWaveLink = Util::MaxValueOfType(pRgn->nWaveLink); - // Load Generators - const SFINSTBAG *pbag = sf2info.pInstBags + ibagcnt; - for (uint32 igenndx=pbag[0].wGenNdx; igenndx= sf2info.nInstGens) || (!sf2info.pInstGens)) break; - const SFINSTGENLIST *pgen = sf2info.pInstGens + igenndx; - uint16 value = pgen->genAmount; - switch(pgen->sfGenOper) + uint32 ibagcnt = insts[0].wInstBagNdx + nRgn - startRegion; + if(ibagcnt >= numInstBags) + break; + // Create a new envelope for drums + DLSENVELOPE *pDlsEnv = &dlsEnv; + if(!(dlsIns.ulBank & F_INSTRUMENT_DRUMS) && dlsIns.nMelodicEnv > 0 && dlsIns.nMelodicEnv <= m_Envelopes.size()) { - case SF2_GEN_KEYRANGE: - pRgn->uKeyMin = (uint8)(value & 0xFF); - pRgn->uKeyMax = (uint8)(value >> 8); - if (pRgn->uKeyMin > pRgn->uKeyMax) - { - std::swap(pRgn->uKeyMin, pRgn->uKeyMax); - } - //if (nIns == 9) Log(" keyrange: %d-%d\n", pRgn->uKeyMin, pRgn->uKeyMax); - break; - case SF2_GEN_UNITYNOTE: - if (value < 128) pRgn->uUnityNote = (uint8)value; - break; - case SF2_GEN_ATTACKVOLENV: - pDlsEnv->wVolAttack = SF2TimeToDLS(pgen->genAmount); - break; - case SF2_GEN_DECAYVOLENV: - pDlsEnv->wVolDecay = SF2TimeToDLS(pgen->genAmount); - break; - case SF2_GEN_SUSTAINVOLENV: - // 0.1% units - if(pgen->genAmount >= 0) - { - pDlsEnv->nVolSustainLevel = SF2SustainLevelToLinear(pgen->genAmount); - } - break; - case SF2_GEN_RELEASEVOLENV: - pDlsEnv->wVolRelease = SF2TimeToDLS(pgen->genAmount); - break; - case SF2_GEN_PAN: - { - int pan = (short int)value; - pan = (((pan + 500) * 127) / 500) + 128; - if (pan < 0) pan = 0; - if (pan > 255) pan = 255; - pDlsEnv->nDefPan = (uint8)pan; - } - break; - case SF2_GEN_ATTENUATION: - lAttn = -(int)value; - break; - case SF2_GEN_SAMPLEID: - if (value < m_SamplesEx.size()) - { - pRgn->nWaveLink = value; - pRgn->ulLoopStart = m_SamplesEx[value].dwStartloop; - pRgn->ulLoopEnd = m_SamplesEx[value].dwEndloop; - } - break; - case SF2_GEN_SAMPLEMODES: - value &= 3; - pRgn->fuOptions &= uint16(~(DLSREGION_SAMPLELOOP|DLSREGION_PINGPONGLOOP|DLSREGION_SUSTAINLOOP)); - if (value == 1) pRgn->fuOptions |= DLSREGION_SAMPLELOOP; else - if (value == 2) pRgn->fuOptions |= DLSREGION_SAMPLELOOP|DLSREGION_PINGPONGLOOP; else - if (value == 3) pRgn->fuOptions |= DLSREGION_SAMPLELOOP|DLSREGION_SUSTAINLOOP; - pRgn->fuOptions |= DLSREGION_OVERRIDEWSMP; - break; - case SF2_GEN_KEYGROUP: - pRgn->fuOptions |= (uint8)(value & DLSREGION_KEYGROUPMASK); - break; - case SF2_GEN_COARSETUNE: - pRgn->sFineTune += static_cast(value) * 128; - break; - case SF2_GEN_FINETUNE: - pRgn->sFineTune += static_cast(Util::muldiv(static_cast(value), 128, 100)); - break; - //default: - // Log(" gen=%d value=%04X\n", pgen->sfGenOper, pgen->genAmount); + pDlsEnv = &m_Envelopes[dlsIns.nMelodicEnv - 1]; } + // Region Default Values + int32 regionAttn = 0; + pRgn->uKeyMin = 0; + pRgn->uKeyMax = 127; + pRgn->uUnityNote = 0xFF; // 0xFF means undefined -> use sample root note + pRgn->tuning = 100; + pRgn->sFineTune = 0; + pRgn->nWaveLink = Util::MaxValueOfType(pRgn->nWaveLink); + if(hasGlobalZone) + *pRgn = dlsIns.Regions[startRegion]; + // Load Generators + sf2info.instBags.Seek(ibagcnt * sizeof(SFINSTBAG)); + SFINSTBAG bags[2]; + sf2info.instBags.ReadArray(bags); + sf2info.instGens.Seek(bags[0].wGenNdx * sizeof(SFINSTGENLIST)); + uint16 lastOp = SF2_GEN_SAMPLEID; + int32 loopStart = 0, loopEnd = 0; + for(uint32 igenndx = bags[0].wGenNdx; igenndx < bags[1].wGenNdx; igenndx++) + { + if(igenndx >= numInstGens) + break; + SFINSTGENLIST gen; + sf2info.instGens.ReadStruct(gen); + uint16 value = gen.genAmount; + lastOp = gen.sfGenOper; + + switch(gen.sfGenOper) + { + case SF2_GEN_KEYRANGE: + pRgn->uKeyMin = (uint8)(value & 0xFF); + pRgn->uKeyMax = (uint8)(value >> 8); + if(pRgn->uKeyMin > pRgn->uKeyMax) + std::swap(pRgn->uKeyMin, pRgn->uKeyMax); + break; + case SF2_GEN_UNITYNOTE: + if (value < 128) pRgn->uUnityNote = (uint8)value; + break; + case SF2_GEN_ATTACKVOLENV: + pDlsEnv->wVolAttack = SF2TimeToDLS(gen.genAmount); + break; + case SF2_GEN_DECAYVOLENV: + pDlsEnv->wVolDecay = SF2TimeToDLS(gen.genAmount); + break; + case SF2_GEN_SUSTAINVOLENV: + // 0.1% units + if(gen.genAmount >= 0) + { + pDlsEnv->nVolSustainLevel = SF2SustainLevelToLinear(gen.genAmount); + } + break; + case SF2_GEN_RELEASEVOLENV: + pDlsEnv->wVolRelease = SF2TimeToDLS(gen.genAmount); + break; + case SF2_GEN_PAN: + { + int32 pan = static_cast(value); + pan = (((pan + 500) * 127) / 500) + 128; + pDlsEnv->nDefPan = mpt::saturate_cast(pan); + } + break; + case SF2_GEN_ATTENUATION: + regionAttn = -static_cast(value); + break; + case SF2_GEN_SAMPLEID: + if (value < m_SamplesEx.size()) + { + pRgn->nWaveLink = value; + pRgn->ulLoopStart = mpt::saturate_cast(m_SamplesEx[value].dwStartloop + loopStart); + pRgn->ulLoopEnd = mpt::saturate_cast(m_SamplesEx[value].dwEndloop + loopEnd); + } + break; + case SF2_GEN_SAMPLEMODES: + value &= 3; + pRgn->fuOptions &= uint16(~(DLSREGION_SAMPLELOOP|DLSREGION_PINGPONGLOOP|DLSREGION_SUSTAINLOOP)); + if(value == 1) + pRgn->fuOptions |= DLSREGION_SAMPLELOOP; + else if(value == 2) + pRgn->fuOptions |= DLSREGION_SAMPLELOOP | DLSREGION_PINGPONGLOOP; + else if(value == 3) + pRgn->fuOptions |= DLSREGION_SAMPLELOOP | DLSREGION_SUSTAINLOOP; + pRgn->fuOptions |= DLSREGION_OVERRIDEWSMP; + break; + case SF2_GEN_KEYGROUP: + pRgn->fuOptions |= (value & DLSREGION_KEYGROUPMASK); + break; + case SF2_GEN_COARSETUNE: + pRgn->sFineTune += static_cast(value) * 128; + break; + case SF2_GEN_FINETUNE: + pRgn->sFineTune += static_cast(Util::muldiv(static_cast(value), 128, 100)); + break; + case SF2_GEN_SCALE_TUNING: + pRgn->tuning = mpt::saturate_cast(value); + break; + case SF2_GEN_START_LOOP_FINE: + loopStart += static_cast(value); + break; + case SF2_GEN_END_LOOP_FINE: + loopEnd += static_cast(value); + break; + case SF2_GEN_START_LOOP_COARSE: + loopStart += static_cast(value) * 32768; + break; + case SF2_GEN_END_LOOP_COARSE: + loopEnd += static_cast(value) * 32768; + break; + //default: + // Log(" gen=%d value=%04X\n", pgen->sfGenOper, pgen->genAmount); + } + } + if(lastOp != SF2_GEN_SAMPLEID && nRgn == startRegion) + { + hasGlobalZone = true; + pRgn->fuOptions |= DLSREGION_ISGLOBAL; + } + int32 linearVol = DLS32BitRelativeGainToLinear(((instrAttenuation + regionAttn) * 65536) / 10) / 256; + Limit(linearVol, 16, 256); + pRgn->usVolume = static_cast(linearVol); + //Log("\n"); } - int32 lVolume = DLS32BitRelativeGainToLinear((lAttn/10) << 16) / 256; - if (lVolume < 16) lVolume = 16; - if (lVolume > 256) lVolume = 256; - pRgn->usVolume = (uint16)lVolume; - //Log("\n"); } } return true; @@ -1177,7 +1243,7 @@ bool CDLSBank::Open(const mpt::PathString &filename) { if(filename.empty()) return false; m_szFileName = filename; - InputFile f(filename); + InputFile f(filename, SettingCacheCompleteFileBeforeLoading()); if(!f.IsValid()) return false; return Open(GetFileReader(f)); } @@ -1185,7 +1251,6 @@ bool CDLSBank::Open(const mpt::PathString &filename) bool CDLSBank::Open(FileReader file) { - SF2LOADERINFO sf2info; uint32 nInsDef; if(!file.GetFileName().empty()) @@ -1229,7 +1294,7 @@ bool CDLSBank::Open(FileReader file) file.ReadStruct(riff); break; } - uint32 len = riff.riff_len_be; + uint32 len = mpt::bit_cast(riff.riff_len); if((len % 2u) != 0) len++; file.SkipBack(4); @@ -1241,11 +1306,11 @@ bool CDLSBank::Open(FileReader file) || !file.CanRead(riff.riff_len - 4)) { #ifdef DLSBANK_LOG - Log("Invalid DLS bank!\n"); + MPT_LOG(LogDebug, "DLSBANK", U_("Invalid DLS bank!")); #endif return false; } - MemsetZero(sf2info); + SF2LoaderInfo sf2info; m_nType = (riff.id_DLS == IFFID_sfbk) ? SOUNDBANK_TYPE_SF2 : SOUNDBANK_TYPE_DLS; m_dwWavePoolOffset = 0; m_Instruments.clear(); @@ -1270,13 +1335,13 @@ bool CDLSBank::Open(FileReader file) // DLS 1.0: Instruments Collection Header case IFFID_colh: #ifdef DLSBANK_LOG - Log("colh (%d bytes)\n", chunkHeader.len); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("colh (%1 bytes)"))(chunkHeader.len.get())); #endif if (m_Instruments.empty()) { m_Instruments.resize(chunk.ReadUint32LE()); #ifdef DLSBANK_LOG - Log(" %d instruments\n", m_Instruments.size()); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_(" %1 instruments"))(m_Instruments.size())); #endif } break; @@ -1284,7 +1349,7 @@ bool CDLSBank::Open(FileReader file) // DLS 1.0: Instruments Pointers Table case IFFID_ptbl: #ifdef DLSBANK_LOG - Log("ptbl (%d bytes)\n", chunkHeader.len); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("ptbl (%1 bytes)"))(chunkHeader.len.get())); #endif if (m_WaveForms.empty()) { @@ -1298,7 +1363,7 @@ bool CDLSBank::Open(FileReader file) m_WaveForms.push_back(chunk.ReadUint32LE()); } #ifdef DLSBANK_LOG - Log(" %d waveforms\n", m_WaveForms.size()); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_(" %1 waveforms"))(m_WaveForms.size())); #endif } break; @@ -1306,7 +1371,7 @@ bool CDLSBank::Open(FileReader file) // DLS 1.0: LIST section case IFFID_LIST: #ifdef DLSBANK_LOG - Log("LIST\n"); + MPT_LOG(LogDebug, "DLSBANK", U_("LIST")); #endif { uint32 listid = chunk.ReadUint32LE(); @@ -1315,7 +1380,7 @@ bool CDLSBank::Open(FileReader file) { m_dwWavePoolOffset = dwMemPos + 4; #ifdef DLSBANK_LOG - Log("Wave Pool offset: %d\n", m_dwWavePoolOffset); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("Wave Pool offset: %1"))(m_dwWavePoolOffset)); #endif break; } @@ -1323,12 +1388,12 @@ bool CDLSBank::Open(FileReader file) while (chunk.CanRead(12)) { IFFCHUNK listHeader; - const void *subData = chunk.GetRawData(); chunk.ReadStruct(listHeader); if(!chunk.CanRead(listHeader.len)) break; + FileReader subData = chunk.GetChunkAt(chunk.GetPosition() - sizeof(IFFCHUNK), listHeader.len + 8); FileReader listChunk = chunk.ReadChunk(listHeader.len); if(listHeader.len % 2u) chunk.Skip(1); @@ -1340,7 +1405,7 @@ bool CDLSBank::Open(FileReader file) { DLSINSTRUMENT *pDlsIns = &m_Instruments[nInsDef]; //Log("Instrument %d:\n", nInsDef); - UpdateInstrumentDefinition(pDlsIns, static_cast(subData), listHeader.len + 8); + UpdateInstrumentDefinition(pDlsIns, subData); nInsDef++; } } else @@ -1383,7 +1448,7 @@ bool CDLSBank::Open(FileReader file) char sdbg[5]; memcpy(sdbg, &chunkHeader.id, 4); sdbg[4] = 0; - Log("Unsupported chunk: %s (%d bytes)\n", sdbg, chunkHeader.len); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("Unsupported chunk: %1 (%2 bytes)"))(mpt::ToUnicode(mpt::Charset::ASCII, mpt::String::ReadAutoBuf(sdbg)), chunkHeader.len.get())); } break; #endif @@ -1393,7 +1458,7 @@ bool CDLSBank::Open(FileReader file) if ((m_WaveForms.empty()) && (m_dwWavePoolOffset) && (m_nType & SOUNDBANK_TYPE_DLS) && (m_nMaxWaveLink > 0)) { #ifdef DLSBANK_LOG - Log("ptbl not present: building table (%d wavelinks)...\n", m_nMaxWaveLink); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("ptbl not present: building table (%1 wavelinks)..."))(m_nMaxWaveLink)); #endif m_WaveForms.reserve(m_nMaxWaveLink); file.Seek(m_dwWavePoolOffset); @@ -1406,7 +1471,7 @@ bool CDLSBank::Open(FileReader file) file.Skip(chunk.len); } #ifdef DLSBANK_LOG - Log("Found %d waveforms\n", m_WaveForms.size()); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("Found %1 waveforms"))(m_WaveForms.size())); #endif } // Convert the SF2 data to DLS @@ -1415,7 +1480,7 @@ bool CDLSBank::Open(FileReader file) ConvertSF2ToDLS(sf2info); } #ifdef DLSBANK_LOG - Log("DLS bank closed\n"); + MPT_LOG(LogDebug, "DLSBANK", U_("DLS bank closed")); #endif return true; } @@ -1446,7 +1511,7 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &wav if (nIns >= m_Instruments.size() || !m_dwWavePoolOffset) { #ifdef DLSBANK_LOG - Log("ExtractWaveForm(%d) failed: m_Instruments.size()=%d m_dwWavePoolOffset=%d m_WaveForms.size()=%d\n", nIns, m_Instruments.size(), m_dwWavePoolOffset, m_WaveForms.size()); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("ExtractWaveForm(%1) failed: m_Instruments.size()=%2 m_dwWavePoolOffset=%3 m_WaveForms.size()=%4"))(nIns, m_Instruments.size(), m_dwWavePoolOffset, m_WaveForms.size())); #endif return false; } @@ -1454,7 +1519,7 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &wav if (nRgn >= dlsIns.nRegions) { #ifdef DLSBANK_LOG - Log("invalid waveform region: nIns=%d nRgn=%d pSmp->nRegions=%d\n", nIns, nRgn, pSmp->nRegions); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("invalid waveform region: nIns=%1 nRgn=%2 pSmp->nRegions=%3"))(nIns, nRgn, dlsIns.nRegions)); #endif return false; } @@ -1462,7 +1527,7 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &wav if(nWaveLink >= m_WaveForms.size()) { #ifdef DLSBANK_LOG - Log("Invalid wavelink id: nWaveLink=%d nWaveForms=%d\n", nWaveLink, m_WaveForms.size()); + MPT_LOG(LogDebug, "DLSBANK", mpt::format(U_("Invalid wavelink id: nWaveLink=%1 nWaveForms=%2"))(nWaveLink, m_WaveForms.size())); #endif return false; } @@ -1494,17 +1559,17 @@ bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector &wav } } else { - LISTCHUNK chunk; - if (mpt::IO::Read(f, chunk)) + LISTChunk chunk; + if(mpt::IO::Read(f, chunk)) { - if ((chunk.id == IFFID_LIST) && (chunk.listid == IFFID_wave) && (chunk.len > 4)) + if((chunk.id == IFFID_LIST) && (chunk.listid == IFFID_wave) && (chunk.len > 4)) { length = chunk.len + 8; try { - waveData.assign(chunk.len + 8, 0); - memcpy(waveData.data(), &chunk, 12); - mpt::IO::ReadRaw(f, &waveData[12], length - 12); + waveData.assign(chunk.len + sizeof(IFFCHUNK), 0); + memcpy(waveData.data(), &chunk, sizeof(chunk)); + mpt::IO::ReadRaw(f, &waveData[sizeof(chunk)], length - sizeof(chunk)); } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) { MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); @@ -1521,14 +1586,14 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI { std::vector pWaveForm; uint32 dwLen = 0; - bool bOk, bWaveForm; + bool ok, hasWaveform; if (nIns >= m_Instruments.size()) return false; const DLSINSTRUMENT *pDlsIns = &m_Instruments[nIns]; if (nRgn >= pDlsIns->nRegions) return false; if (!ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen)) return false; if (dwLen < 16) return false; - bOk = false; + ok = false; FileReader wsmpChunk; if (m_nType & SOUNDBANK_TYPE_SF2) @@ -1541,7 +1606,7 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI { const DLSSAMPLEEX &p = m_SamplesEx[nWaveLink]; #ifdef DLSINSTR_LOG - Log(" SF2 WaveLink #%3d: %5dHz\n", nWaveLink, p->dwSampleRate); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" SF2 WaveLink #%1: %2Hz"))(nWaveLink, p.dwSampleRate)); #endif sample.Initialize(); sample.nLength = dwLen / 2; @@ -1551,9 +1616,9 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI sample.RelativeTone = p.byOriginalPitch; sample.nFineTune = p.chPitchCorrection; if (p.szName[0]) - mpt::String::Copy(sndFile.m_szNames[nSample], p.szName); + sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(p.szName); else if(pDlsIns->szName[0]) - mpt::String::Copy(sndFile.m_szNames[nSample], pDlsIns->szName); + sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(pDlsIns->szName); FileReader chunk(mpt::as_span(pWaveForm.data(), dwLen)); SampleIO( @@ -1563,15 +1628,15 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI SampleIO::signedPCM) .ReadSample(sample, chunk); } - bWaveForm = sample.HasSampleData(); + hasWaveform = sample.HasSampleData(); } else { FileReader file(mpt::as_span(pWaveForm.data(), dwLen)); - bWaveForm = sndFile.ReadWAVSample(nSample, file, false, &wsmpChunk); + hasWaveform = sndFile.ReadWAVSample(nSample, file, false, &wsmpChunk); if(pDlsIns->szName[0]) - mpt::String::Copy(sndFile.m_szNames[nSample], pDlsIns->szName); + sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(pDlsIns->szName); } - if (bWaveForm) + if (hasWaveform) { ModSample &sample = sndFile.GetSample(nSample); const DLSREGION &rgn = pDlsIns->Regions[nRgn]; @@ -1604,7 +1669,7 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI int lVolume = rgn.usVolume; WSMPCHUNK wsmp; - if(!(rgn.fuOptions & DLSREGION_OVERRIDEWSMP) && wsmpChunk.IsValid() && wsmpChunk.ReadStructPartial(wsmp)) + if(!(rgn.fuOptions & DLSREGION_OVERRIDEWSMP) && wsmpChunk.IsValid() && wsmpChunk.Skip(sizeof(IFFCHUNK)) && wsmpChunk.ReadStructPartial(wsmp)) { usUnityNote = wsmp.usUnityNote; sFineTune = wsmp.sFineTune; @@ -1612,7 +1677,7 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI if(wsmp.cSampleLoops) { WSMPSAMPLELOOP loop; - wsmpChunk.Skip(8 + wsmp.cbSize); + wsmpChunk.Seek(sizeof(IFFCHUNK) + wsmp.cbSize); wsmpChunk.ReadStruct(loop); if(loop.ulLoopLength > 3) { @@ -1628,7 +1693,7 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI sFineTune += sample.nFineTune; } #ifdef DLSINSTR_LOG - Log("WSMP: usUnityNote=%d.%d, %dHz (transp=%d)\n", usUnityNote, sFineTune, sample.nC5Speed, transpose); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_("WSMP: usUnityNote=%1.%2, %3Hz (transp=%4)"))(usUnityNote, sFineTune, sample.nC5Speed, transpose)); #endif if (usUnityNote > 0x7F) usUnityNote = 60; int steps = (60 + transpose - usUnityNote) * 128 + sFineTune; @@ -1641,15 +1706,15 @@ bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nI sample.Convert(MOD_TYPE_IT, sndFile.GetType()); sample.PrecomputeLoops(sndFile, false); - bOk = true; + ok = true; } - return bOk; + return ok; } static uint16 ScaleEnvelope(uint32 time, float tempoScale) { - return std::max(mpt::saturate_round(time * tempoScale), 1); + return std::max(mpt::saturate_round(time * tempoScale), uint16(1)); } @@ -1673,18 +1738,20 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui nRgnMax = pDlsIns->nRegions; nEnv = pDlsIns->nMelodicEnv; } + if(nRgnMin == 0 && (pDlsIns->Regions[0].fuOptions & DLSREGION_ISGLOBAL)) + nRgnMin++; #ifdef DLSINSTR_LOG - Log("DLS Instrument #%d: %s\n", nIns, pDlsIns->szName); - Log(" Bank=0x%04X Instrument=0x%04X\n", pDlsIns->ulBank, pDlsIns->ulInstrument); - Log(" %2d regions, nMelodicEnv=%d\n", pDlsIns->nRegions, pDlsIns->nMelodicEnv); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_("DLS Instrument #%1: %2"))(nIns, mpt::ToUnicode(mpt::Charset::ASCII, mpt::String::ReadAutoBuf(pDlsIns->szName)))); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" Bank=0x%1 Instrument=0x%2"))(mpt::ufmt::HEX0<4>(pDlsIns->ulBank), mpt::ufmt::HEX0<4>(pDlsIns->ulInstrument))); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" %1 regions, nMelodicEnv=%2"))(pDlsIns->nRegions, pDlsIns->nMelodicEnv)); for (uint32 iDbg=0; iDbgnRegions; iDbg++) { const DLSREGION *prgn = &pDlsIns->Regions[iDbg]; - Log(" Region %d:\n", iDbg); - Log(" WaveLink = %d (loop [%5d, %5d])\n", prgn->nWaveLink, prgn->ulLoopStart, prgn->ulLoopEnd); - Log(" Key Range: [%2d, %2d]\n", prgn->uKeyMin, prgn->uKeyMax); - Log(" fuOptions = 0x%04X\n", prgn->fuOptions); - Log(" usVolume = %3d, Unity Note = %d\n", prgn->usVolume, prgn->uUnityNote); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" Region %1:"))(iDbg)); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" WaveLink = %1 (loop [%2, %3])"))(prgn->nWaveLink, prgn->ulLoopStart, prgn->ulLoopEnd)); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" Key Range: [%1, %2]"))(prgn->uKeyMin, prgn->uKeyMax)); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" fuOptions = 0x%1"))(mpt::ufmt::HEX0<4>(prgn->fuOptions))); + MPT_LOG(LogDebug, "DLSINSTR", mpt::format(U_(" usVolume = %1, Unity Note = %2"))(prgn->usVolume, prgn->uUnityNote)); } #endif @@ -1701,38 +1768,40 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui // Initializes Instrument if (pDlsIns->ulBank & F_INSTRUMENT_DRUMS) { - char s[64] = ""; uint32 key = pDlsIns->Regions[nDrumRgn].uKeyMin; - if ((key >= 24) && (key <= 84)) lstrcpyA(s, szMidiPercussionNames[key-24]); - if (pDlsIns->szName[0]) + if((key >= 24) && (key <= 84)) { - sprintf(&s[strlen(s)], " (%s", pDlsIns->szName); - size_t n = strlen(s); - while ((n) && (s[n-1] == ' ')) + std::string s = szMidiPercussionNames[key-24]; + if(!mpt::String::ReadAutoBuf(pDlsIns->szName).empty()) { - n--; - s[n] = 0; + s += mpt::format(" (%1)")(mpt::String::RTrim(mpt::String::ReadAutoBuf(pDlsIns->szName))); } - lstrcatA(s, ")"); + pIns->name = s; + } else + { + pIns->name = mpt::String::ReadAutoBuf(pDlsIns->szName); } - mpt::String::Copy(pIns->name, s); } else { - mpt::String::Copy(pIns->name, pDlsIns->szName); + pIns->name = mpt::String::ReadAutoBuf(pDlsIns->szName); } int nTranspose = 0; - if (pDlsIns->ulBank & F_INSTRUMENT_DRUMS) + if(pDlsIns->ulBank & F_INSTRUMENT_DRUMS) { - for (uint32 iNoteMap=0; iNoteMapRegions[nDrumRgn].uKeyMin) pIns->NoteMap[iNoteMap] = (uint8)(pDlsIns->Regions[nDrumRgn].uKeyMin + 1); - if (iNoteMap > pDlsIns->Regions[nDrumRgn].uKeyMax) pIns->NoteMap[iNoteMap] = (uint8)(pDlsIns->Regions[nDrumRgn].uKeyMax + 1); + // Format has instrument note mapping + if(pDlsIns->Regions[nDrumRgn].tuning == 0) + pIns->NoteMap[iNoteMap] = NOTE_MIDDLEC; + else if(iNoteMap < pDlsIns->Regions[nDrumRgn].uKeyMin) + pIns->NoteMap[iNoteMap] = (uint8)(pDlsIns->Regions[nDrumRgn].uKeyMin + NOTE_MIN); + else if(iNoteMap > pDlsIns->Regions[nDrumRgn].uKeyMax) + pIns->NoteMap[iNoteMap] = (uint8)(pDlsIns->Regions[nDrumRgn].uKeyMax + NOTE_MIN); } else { - if (iNoteMap == pDlsIns->Regions[nDrumRgn].uKeyMin) + if(iNoteMap == pDlsIns->Regions[nDrumRgn].uKeyMin) { nTranspose = (pDlsIns->Regions[nDrumRgn].uKeyMin + (pDlsIns->Regions[nDrumRgn].uKeyMax - pDlsIns->Regions[nDrumRgn].uKeyMin) / 2) - 60; } @@ -1752,7 +1821,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui // Extract Samples for (uint32 nRgn=nRgnMin; nRgnRegions[nRgn]; // Elimitate Duplicate Regions @@ -1766,13 +1835,13 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui || ((pRgn2->uKeyMin == pRgn->uKeyMin) && (pRgn2->uKeyMax == pRgn->uKeyMax))) { - bDupRgn = true; + duplicateRegion = true; nSmp = RgnToSmp[iDup]; break; } } // Create a new sample - if (!bDupRgn) + if (!duplicateRegion) { uint32 nmaxsmp = (m_nType & MOD_TYPE_XM) ? 16 : 32; if (nLoadedSmp >= nmaxsmp) @@ -1800,7 +1869,7 @@ bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, ui } } // Load the sample - if(!bDupRgn || !sndFile.GetSample(nSmp).HasSampleData()) + if(!duplicateRegion || !sndFile.GetSample(nSmp).HasSampleData()) { ExtractSample(sndFile, nSmp, nIns, nRgn, nTranspose); } else if(sndFile.GetSample(nSmp).GetNumChannels() == 1) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h index 8b85ef8fc..bf49ad564 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Dlsbank.h @@ -25,14 +25,6 @@ OPENMPT_NAMESPACE_BEGIN #define DLSMAXREGIONS 128 -// Region Flags -#define DLSREGION_KEYGROUPMASK 0x0F -#define DLSREGION_OVERRIDEWSMP 0x10 -#define DLSREGION_PINGPONGLOOP 0x20 -#define DLSREGION_SAMPLELOOP 0x40 -#define DLSREGION_SELFNONEXCLUSIVE 0x80 -#define DLSREGION_SUSTAINLOOP 0x100 - struct DLSREGION { uint32 ulLoopStart; @@ -45,6 +37,7 @@ struct DLSREGION uint8 uKeyMin; uint8 uKeyMax; uint8 uUnityNote; + uint8 tuning = 100; }; struct DLSENVELOPE @@ -97,7 +90,7 @@ struct SOUNDBANKINFO }; struct IFFCHUNK; -struct SF2LOADERINFO; +struct SF2LoaderInfo; class CDLSBank { @@ -141,9 +134,9 @@ public: // Internal Loader Functions protected: - bool UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, const IFFCHUNK *pchunk, uint32 dwMaxLen); - bool UpdateSF2PresetData(SF2LOADERINFO &sf2info, const IFFCHUNK &header, FileReader &chunk); - bool ConvertSF2ToDLS(SF2LOADERINFO &sf2info); + bool UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chunk); + bool UpdateSF2PresetData(SF2LoaderInfo &sf2info, const IFFCHUNK &header, FileReader &chunk); + bool ConvertSF2ToDLS(SF2LoaderInfo &sf2info); public: // DLS Unit conversion diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp index 1266d64a7..4674339a5 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Fastmix.cpp @@ -20,8 +20,8 @@ #include "Sndfile.h" #include "MixerLoops.h" #include "MixFuncTable.h" -#include // For FLT_EPSILON #include "plugins/PlugInterface.h" +#include // For FLT_EPSILON #include @@ -32,13 +32,16 @@ OPENMPT_NAMESPACE_BEGIN struct MixLoopState { - const int8 * samplePointer; - const int8 * lookaheadPointer; - SmpLength lookaheadStart; - uint32 maxSamples; + const int8 * samplePointer = nullptr; + const int8 * lookaheadPointer = nullptr; + SmpLength lookaheadStart = 0; + uint32 maxSamples = 0; MixLoopState(const ModChannel &chn) { + if(chn.pCurrentSample == nullptr) + return; + UpdateLookaheadPointers(chn); // For platforms that have no fast 64-bit division, precompute this constant @@ -47,7 +50,8 @@ struct MixLoopState if(increment.IsNegative()) increment.Negate(); maxSamples = 16384u / (increment.GetUInt() + 1u); - if(maxSamples < 2) maxSamples = 2; + if(maxSamples < 2) + maxSamples = 2; } // Calculate offset of loop wrap-around buffer for this sample. @@ -147,7 +151,7 @@ struct MixLoopState if ((chn.position.GetUInt() <= chn.nLoopStart) || (chn.position.GetUInt() >= chn.nLength)) { // Impulse Tracker's software mixer would put a -2 (instead of -1) in the following line (doesn't happen on a GUS) - chn.position.SetInt(chn.nLength - std::min(chn.nLength, ITPingPongMode ? 2 : 1)); + chn.position.SetInt(chn.nLength - std::min(chn.nLength, ITPingPongMode ? SmpLength(2) : SmpLength(1))); } } else { @@ -276,11 +280,13 @@ void CSoundFile::CreateStereoMix(int count) { mixsample_t *pOfsL, *pOfsR; - if (!count) return; + if(!count) + return; // Resetting sound buffer - StereoFill(MixSoundBuffer, count, gnDryROfsVol, gnDryLOfsVol); - if(m_MixerSettings.gnChannels > 2) InitMixBuffer(MixRearBuffer, count*2); + StereoFill(MixSoundBuffer, count, m_dryROfsVol, m_dryLOfsVol); + if(m_MixerSettings.gnChannels > 2) + StereoFill(MixRearBuffer, count, m_surroundROfsVol, m_surroundLOfsVol); CHANNELINDEX nchmixed = 0; @@ -290,9 +296,11 @@ void CSoundFile::CreateStereoMix(int count) { ModChannel &chn = m_PlayState.Chn[m_PlayState.ChnMix[nChn]]; - if(!chn.pCurrentSample) continue; - pOfsR = &gnDryROfsVol; - pOfsL = &gnDryLOfsVol; + if(!chn.pCurrentSample && !chn.nLOfs && !chn.nROfs) + continue; + + pOfsR = &m_dryROfsVol; + pOfsL = &m_dryLOfsVol; uint32 functionNdx = MixFuncTable::ResamplingModeToMixFlags(static_cast(chn.resamplingMode)); if(chn.dwFlags[CHN_16BIT]) functionNdx |= MixFuncTable::ndx16Bit; @@ -311,7 +319,11 @@ void CSoundFile::CreateStereoMix(int count) } #endif if(chn.dwFlags[CHN_SURROUND] && m_MixerSettings.gnChannels > 2) + { pbuffer = MixRearBuffer; + pOfsR = &m_surroundROfsVol; + pOfsL = &m_surroundLOfsVol; + } //Look for plugins associated with this implicit tracker channel. #ifndef NO_PLUGINS @@ -379,7 +391,7 @@ void CSoundFile::CreateStereoMix(int count) { // Detecting the longest play time for each sample for optimization chn.position += chn.increment * nSmpCount; - size_t smp = std::distance(Samples, chn.pModSample); + size_t smp = std::distance(static_cast(static_cast::type>(Samples)), chn.pModSample); if(smp < m_SamplePlayLengths->size()) { m_SamplePlayLengths->at(smp) = std::max(m_SamplePlayLengths->at(smp), chn.position.GetUInt()); @@ -428,31 +440,31 @@ void CSoundFile::CreateStereoMix(int count) } } - if(chn.position.GetUInt() >= chn.nLoopEnd && chn.dwFlags[CHN_LOOP]) + const bool pastLoopEnd = chn.position.GetUInt() >= chn.nLoopEnd && chn.dwFlags[CHN_LOOP]; + const bool pastSampleEnd = chn.position.GetUInt() >= chn.nLength && !chn.dwFlags[CHN_LOOP] && chn.nLength && !chn.nMasterChn; + const bool doSampleSwap = m_playBehaviour[kMODSampleSwap] && chn.nNewIns && chn.nNewIns <= GetNumSamples() && chn.pModSample != &Samples[chn.nNewIns]; + if((pastLoopEnd || pastSampleEnd) && doSampleSwap) { - if(m_playBehaviour[kMODSampleSwap] && chn.nNewIns && chn.nNewIns <= GetNumSamples() && chn.pModSample != &Samples[chn.nNewIns]) + // ProTracker compatibility: Instrument changes without a note do not happen instantly, but rather when the sample loop has finished playing. + // Test case: PTInstrSwap.mod, PTSwapNoLoop.mod + const ModSample &smp = Samples[chn.nNewIns]; + chn.pModSample = &smp; + chn.pCurrentSample = smp.samplev(); + chn.dwFlags = (chn.dwFlags & CHN_CHANNELFLAGS) | smp.uFlags; + chn.nLength = smp.uFlags[CHN_LOOP] ? smp.nLoopEnd : 0; // non-looping sample continue in oneshot mode (i.e. they will most probably just play silence) + chn.nLoopStart = smp.nLoopStart; + chn.nLoopEnd = smp.nLoopEnd; + chn.position.SetInt(chn.nLoopStart); + mixLoopState.UpdateLookaheadPointers(chn); + if(!chn.pCurrentSample) { - // ProTracker compatibility: Instrument changes without a note do not happen instantly, but rather when the sample loop has finished playing. - // Test case: PTInstrSwap.mod - const ModSample &smp = Samples[chn.nNewIns]; - chn.pModSample = &smp; - chn.pCurrentSample = smp.samplev(); - chn.dwFlags = (chn.dwFlags & CHN_CHANNELFLAGS) | smp.uFlags; - chn.nLength = smp.uFlags[CHN_LOOP] ? smp.nLoopEnd : smp.nLength; - chn.nLoopStart = smp.nLoopStart; - chn.nLoopEnd = smp.nLoopEnd; - chn.position.SetInt(chn.nLoopStart); - mixLoopState.UpdateLookaheadPointers(chn); - if(!chn.pCurrentSample) - { - break; - } - } else if(m_playBehaviour[kMODOneShotLoops] && chn.nLoopStart == 0) - { - // ProTracker "oneshot" loops (if loop start is 0, play the whole sample once and then repeat until loop end) - chn.position.SetInt(0); - chn.nLoopEnd = chn.nLength = chn.pModSample->nLoopEnd; + break; } + } else if(pastLoopEnd && !doSampleSwap && m_playBehaviour[kMODOneShotLoops] && chn.nLoopStart == 0) + { + // ProTracker "oneshot" loops (if loop start is 0, play the whole sample once and then repeat until loop end) + chn.position.SetInt(0); + chn.nLoopEnd = chn.nLength = chn.pModSample->nLoopEnd; } } while(nsamples > 0); @@ -467,7 +479,7 @@ void CSoundFile::CreateStereoMix(int count) } #endif // NO_PLUGINS } - m_nMixStat = std::max(m_nMixStat, nchmixed); + m_nMixStat = std::max(m_nMixStat, nchmixed); } @@ -511,7 +523,7 @@ void CSoundFile::ProcessPlugins(uint32 nCount) #ifdef MPT_INTMIXER StereoMixToFloat(state.pMixBuffer, plugInputL, plugInputR, nCount, IntToFloat); #else - DeinterleaveStereo(pState->pMixBuffer, plugInputL, plugInputR, nCount); + DeinterleaveStereo(state.pMixBuffer, plugInputL, plugInputR, nCount); #endif // MPT_INTMIXER } else if (state.nVolDecayR || state.nVolDecayL) { @@ -519,7 +531,7 @@ void CSoundFile::ProcessPlugins(uint32 nCount) #ifdef MPT_INTMIXER StereoMixToFloat(state.pMixBuffer, plugInputL, plugInputR, nCount, IntToFloat); #else - DeinterleaveStereo(pState->pMixBuffer, plugInputL, plugInputR, nCount); + DeinterleaveStereo(state.pMixBuffer, plugInputL, plugInputR, nCount); #endif // MPT_INTMIXER } else { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.cpp index ddf509b0a..324a4dcec 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.cpp @@ -23,64 +23,53 @@ OPENMPT_NAMESPACE_BEGIN // Algorithm parameters for 16-Bit samples struct IT16BitParams { - typedef int16 sample_t; - static const int16 lowerTab[]; - static const int16 upperTab[]; - static const int8 fetchA; - static const int8 lowerB; - static const int8 upperB; - static const int8 defWidth; - static const int mask; + using sample_t = int16; + static constexpr int16 lowerTab[] = {0, -1, -3, -7, -15, -31, -56, -120, -248, -504, -1016, -2040, -4088, -8184, -16376, -32760, -32768}; + static constexpr int16 upperTab[] = {0, 1, 3, 7, 15, 31, 55, 119, 247, 503, 1015, 2039, 4087, 8183, 16375, 32759, 32767}; + static constexpr int8 fetchA = 4; + static constexpr int8 lowerB = -8; + static constexpr int8 upperB = 7; + static constexpr int8 defWidth = 17; + static constexpr int mask = 0xFFFF; }; -const int16 IT16BitParams::lowerTab[] = { 0, -1, -3, -7, -15, -31, -56, -120, -248, -504, -1016, -2040, -4088, -8184, -16376, -32760, -32768 }; -const int16 IT16BitParams::upperTab[] = { 0, 1, 3, 7, 15, 31, 55, 119, 247, 503, 1015, 2039, 4087, 8183, 16375, 32759, 32767 }; -const int8 IT16BitParams::fetchA = 4; -const int8 IT16BitParams::lowerB = -8; -const int8 IT16BitParams::upperB = 7; -const int8 IT16BitParams::defWidth = 17; -const int IT16BitParams::mask = 0xFFFF; - // Algorithm parameters for 8-Bit samples struct IT8BitParams { - typedef int8 sample_t; - static const int8 lowerTab[]; - static const int8 upperTab[]; - static const int8 fetchA; - static const int8 lowerB; - static const int8 upperB; - static const int8 defWidth; - static const int mask; + using sample_t = int8; + static constexpr int8 lowerTab[] = {0, -1, -3, -7, -15, -31, -60, -124, -128}; + static constexpr int8 upperTab[] = {0, 1, 3, 7, 15, 31, 59, 123, 127}; + static constexpr int8 fetchA = 3; + static constexpr int8 lowerB = -4; + static constexpr int8 upperB = 3; + static constexpr int8 defWidth = 9; + static constexpr int mask = 0xFF; }; -const int8 IT8BitParams::lowerTab[] = { 0, -1, -3, -7, -15, -31, -60, -124, -128 }; -const int8 IT8BitParams::upperTab[] = { 0, 1, 3, 7, 15, 31, 59, 123, 127 }; -const int8 IT8BitParams::fetchA = 3; -const int8 IT8BitParams::lowerB = -4; -const int8 IT8BitParams::upperB = 3; -const int8 IT8BitParams::defWidth = 9; -const int IT8BitParams::mask = 0xFF; - -static const int8 ITWidthChangeSize[] = { 4, 5, 6, 7, 8, 9, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }; +static constexpr int8 ITWidthChangeSize[] = { 4, 5, 6, 7, 8, 9, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }; ////////////////////////////////////////////////////////////////////////////// // IT 2.14 compression ITCompression::ITCompression(const ModSample &sample, bool it215, std::ostream *f, SmpLength maxLength) - : file(f) - , mptSample(sample) - , is215(it215) + : file(f) + , mptSample(sample) + , is215(it215) { - packedData = new (std::nothrow) uint8[bufferSize]; - sampleData = new (std::nothrow) uint8[blockSize]; - packedTotalLength = 0; - if(packedData == nullptr || sampleData == nullptr) - { - return; - } + if(mptSample.GetElementarySampleSize() > 1) + Compress(mptSample.sample16(), maxLength); + else + Compress(mptSample.sample8(), maxLength); +} + +template +void ITCompression::Compress(const typename Properties::sample_t *mptSampleData, SmpLength maxLength) +{ + packedData.resize(bufferSize); + std::vector sampleData; + sampleData.resize(blockSize / sizeof(typename Properties::sample_t)); if(maxLength == 0 || maxLength > mptSample.nLength) maxLength = mptSample.nLength; for(uint8 chn = 0; chn < mptSample.GetNumChannels(); chn++) @@ -95,29 +84,25 @@ ITCompression::ITCompression(const ModSample &sample, bool it215, std::ostream * remBits = 8; byteVal = 0; - if(mptSample.GetElementarySampleSize() > 1) - Compress(sample.sample16() + chn, offset, remain); - else - Compress(sample.sample8() + chn, offset, remain); + CompressBlock(mptSampleData + chn, offset, remain, sampleData.data()); - if(file) mpt::IO::WriteRaw(*file, packedData, packedLength); + if(file) mpt::IO::WriteRaw(*file, packedData.data(), packedLength); packedTotalLength += packedLength; offset += baseLength; remain -= baseLength; } } - - delete[] packedData; - delete[] reinterpret_cast(sampleData); + packedData.resize(0); + packedData.shrink_to_fit(); } template -void ITCompression::CopySample(void *target, const void *source, SmpLength offset, SmpLength length, SmpLength skip) +void ITCompression::CopySample(T *target, const T *source, SmpLength offset, SmpLength length, SmpLength skip) { - T *out = static_cast(target); - const T *in = static_cast(source) + offset * skip; + T *out = target; + const T *in = source + offset * skip; for(SmpLength i = 0, j = 0; j < length; i += skip, j++) { out[j] = in[i]; @@ -127,9 +112,9 @@ void ITCompression::CopySample(void *target, const void *source, SmpLength offse // Convert sample to delta values. template -void ITCompression::Deltafy() +void ITCompression::Deltafy(T *sampleData) { - T *p = static_cast(sampleData); + T *p = sampleData; int oldVal = 0; for(SmpLength i = 0; i < baseLength; i++) { @@ -141,26 +126,26 @@ void ITCompression::Deltafy() template -void ITCompression::Compress(const void *data, SmpLength offset, SmpLength actualLength) +void ITCompression::CompressBlock(const typename Properties::sample_t *data, SmpLength offset, SmpLength actualLength, typename Properties::sample_t *sampleData) { baseLength = std::min(actualLength, SmpLength(blockSize / sizeof(typename Properties::sample_t))); CopySample(sampleData, data, offset, baseLength, mptSample.GetNumChannels()); - Deltafy(); + Deltafy(sampleData); if(is215) { - Deltafy(); + Deltafy(sampleData); } // Initialise bit width table with initial values bwt.assign(baseLength, Properties::defWidth); // Recurse! - SquishRecurse(Properties::defWidth, Properties::defWidth, Properties::defWidth, Properties::defWidth - 2, 0, baseLength); + SquishRecurse(Properties::defWidth, Properties::defWidth, Properties::defWidth, Properties::defWidth - 2, 0, baseLength, sampleData); // Write those bits! - const typename Properties::sample_t *p = static_cast(sampleData); + const typename Properties::sample_t *p = sampleData; int8 width = Properties::defWidth; for(size_t i = 0; i < baseLength; i++) { @@ -207,7 +192,7 @@ int8 ITCompression::GetWidthChangeSize(int8 w, bool is16) template -void ITCompression::SquishRecurse(int8 sWidth, int8 lWidth, int8 rWidth, int8 width, SmpLength offset, SmpLength length) +void ITCompression::SquishRecurse(int8 sWidth, int8 lWidth, int8 rWidth, int8 width, SmpLength offset, SmpLength length, const typename Properties::sample_t *sampleData) { if(width + 1 < 1) { @@ -220,7 +205,7 @@ void ITCompression::SquishRecurse(int8 sWidth, int8 lWidth, int8 rWidth, int8 wi SmpLength i = offset; SmpLength end = offset + length; - const typename Properties::sample_t *p = static_cast(sampleData); + const typename Properties::sample_t *p = sampleData; while(i < end) { @@ -264,7 +249,7 @@ void ITCompression::SquishRecurse(int8 sWidth, int8 lWidth, int8 rWidth, int8 wi comparison = (keepDown <= levelLeft); } - SquishRecurse(comparison ? (width + 1) : sWidth, xlwidth, xrwidth, width - 1, start, blockLength); + SquishRecurse(comparison ? (width + 1) : sWidth, xlwidth, xrwidth, width - 1, start, blockLength, sampleData); } else { bwt[i] = sWidth; @@ -325,8 +310,8 @@ void ITCompression::WriteByte(uint8 v) ITDecompression::ITDecompression(FileReader &file, ModSample &sample, bool it215) - : mptSample(sample) - , is215(it215) + : mptSample(sample) + , is215(it215) { for(uint8 chn = 0; chn < mptSample.GetNumChannels(); chn++) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.h index ca0f9b4f2..d9f60b78c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ITCompression.h @@ -28,39 +28,43 @@ public: ITCompression(const ModSample &sample, bool it215, std::ostream *f, SmpLength maxLength = 0); size_t GetCompressedSize() const { return packedTotalLength; } - enum : size_t { bufferSize = 2 + 0xFFFF }; // Our output buffer can't be longer than this. - enum : size_t { blockSize = 0x8000 }; // Block size (in bytes) in which samples are being processed + static constexpr size_t bufferSize = 2 + 0xFFFF; // Our output buffer can't be longer than this. + static constexpr size_t blockSize = 0x8000; // Block size (in bytes) in which samples are being processed protected: - std::vector bwt; // Bit width table for each sampling point - uint8 *packedData; // Compressed data for current sample block - std::ostream *file; // File to which compressed data will be written (can be nullptr if you only want to find out the sample size) - void *sampleData; // Pre-processed sample data for currently compressed sample block - const ModSample &mptSample; // Sample that is being processed - size_t packedLength; // Size of currently compressed sample block - size_t packedTotalLength; // Size of all compressed data so far - SmpLength baseLength; // Length of the currently compressed sample block (in samples) + std::vector bwt; // Bit width table for each sampling point + std::vector packedData; // Compressed data for current sample block + std::ostream *file = nullptr; // File to which compressed data will be written (can be nullptr if you only want to find out the sample size) + std::vector sampleData8; // Pre-processed sample data for currently compressed sample block + std::vector sampleData16; // Pre-processed sample data for currently compressed sample block + const ModSample &mptSample; // Sample that is being processed + size_t packedLength = 0; // Size of currently compressed sample block + size_t packedTotalLength = 0; // Size of all compressed data so far + SmpLength baseLength = 0; // Length of the currently compressed sample block (in samples) // Bit writer - int8 bitPos; // Current bit position in this byte - int8 remBits; // Remaining bits in this byte - uint8 byteVal; // Current byte value to be written + int8 bitPos = 0; // Current bit position in this byte + int8 remBits = 0; // Remaining bits in this byte + uint8 byteVal = 0; // Current byte value to be written - bool is215; // Use IT2.15 compression (double deltas) - - template - static void CopySample(void *target, const void *source, SmpLength offset, SmpLength length, SmpLength skip); - - template - void Deltafy(); + const bool is215; // Use IT2.15 compression (double deltas) template - void Compress(const void *data, SmpLength offset, SmpLength actualLength); + void Compress(const typename Properties::sample_t *mptSampleData, SmpLength maxLength); + + template + static void CopySample(T *target, const T *source, SmpLength offset, SmpLength length, SmpLength skip); + + template + void Deltafy(T *sampleData); + + template + void CompressBlock(const typename Properties::sample_t *data, SmpLength offset, SmpLength actualLength, typename Properties::sample_t *sampleData); static int8 GetWidthChangeSize(int8 w, bool is16); template - void SquishRecurse(int8 sWidth, int8 lWidth, int8 rWidth, int8 width, SmpLength offset, SmpLength length); + void SquishRecurse(int8 sWidth, int8 lWidth, int8 rWidth, int8 width, SmpLength offset, SmpLength length, const typename Properties::sample_t *sampleData); static int8 ConvertWidth(int8 curWidth, int8 newWidth); void WriteBits(int8 width, int v); @@ -76,13 +80,14 @@ public: protected: BitReader bitFile; - ModSample &mptSample; // Sample that is being processed + ModSample &mptSample; // Sample that is being processed - SmpLength writtenSamples; // Number of samples so far written on this channel - SmpLength writePos; // Absolut write position in sample (for stereo samples) - SmpLength curLength; // Length of currently processed block - unsigned int mem1, mem2; // Integrator memory - bool is215; // Use IT2.15 compression (double deltas) + SmpLength writtenSamples = 0; // Number of samples so far written on this channel + SmpLength writePos = 0; // Absolut write position in sample (for stereo samples) + SmpLength curLength = 0; // Length of currently processed block + unsigned int mem1 = 0, mem2 = 0; // Integrator memory + + const bool is215; // Use IT2.15 compression (double deltas) template void Uncompress(typename Properties::sample_t *target); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.cpp index 13a3923cc..1ea975a69 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.cpp @@ -73,22 +73,19 @@ void ITEnvelope::ConvertToMPT(InstrumentEnvelope &mptEnv, uint8 envOffset, uint8 // Envelope Data // Attention: Full MPTM envelope is stored in extended instrument properties - for(uint32 ev = 0; ev < std::min(25, num); ev++) + for(uint32 ev = 0; ev < std::min(uint8(25), num); ev++) { mptEnv[ev].value = Clamp(data[ev].value + envOffset, 0, 64); mptEnv[ev].tick = data[ev].tick; - if(ev > 0 && ev < num && mptEnv[ev].tick < mptEnv[ev - 1].tick) + if(ev > 0 && mptEnv[ev].tick < mptEnv[ev - 1].tick && !(mptEnv[ev].tick & 0xFF00)) { // Fix broken envelopes... Instruments 2 and 3 in NoGap.it by Werewolf have envelope points where the high byte of envelope nodes is missing. - // NoGap.it was saved with MPT 1.07 or MPT 1.09, which *normally* doesn't do this in IT files. + // NoGap.it was saved with MPT 1.07 - 1.09, which *normally* doesn't do this in IT files. // However... It turns out that MPT 1.07 omitted the high byte of envelope nodes when saving an XI instrument file, and it looks like // Instrument 2 and 3 in NoGap.it were loaded from XI files. - mptEnv[ev].tick &= 0xFF; - mptEnv[ev].tick |= (mptEnv[ev].tick & ~0xFF); + mptEnv[ev].tick |= mptEnv[ev - 1].tick & 0xFF00; if(mptEnv[ev].tick < mptEnv[ev - 1].tick) - { mptEnv[ev].tick += 0x100; - } } } } @@ -103,8 +100,8 @@ void ITOldInstrument::ConvertToMPT(ModInstrument &mptIns) const return; } - mpt::String::Read(mptIns.name, name); - mpt::String::Read(mptIns.filename, filename); + mptIns.name = mpt::String::ReadBuf(mpt::String::spacePadded, name); + mptIns.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); // Volume / Panning mptIns.nFadeOut = fadeout << 6; @@ -170,13 +167,13 @@ uint32 ITInstrument::ConvertToIT(const ModInstrument &mptIns, bool compatExport, memcpy(id, "IMPI", 4); trkvers = 0x5000 | static_cast(Version::Current().GetRawVersion() >> 16); - mpt::String::Write(filename, mptIns.filename); - mpt::String::Write(name, mptIns.name); + mpt::String::WriteBuf(mpt::String::nullTerminated, filename) = mptIns.filename; + mpt::String::WriteBuf(mpt::String::nullTerminated, name) = mptIns.name; // Volume / Panning - fadeout = static_cast(std::min(mptIns.nFadeOut >> 5, 256u)); - gbv = static_cast(std::min(mptIns.nGlobalVol * 2u, 128u)); - dfp = static_cast(std::min(mptIns.nPan / 4u, 64u)); + fadeout = static_cast(std::min(mptIns.nFadeOut >> 5, uint32(256))); + gbv = static_cast(std::min(mptIns.nGlobalVol * 2u, uint32(128))); + dfp = static_cast(std::min(mptIns.nPan / 4u, uint32(64))); if(!mptIns.dwFlags[INS_SETPANNING]) dfp |= ITInstrument::ignorePanning; // Random Variation @@ -261,8 +258,8 @@ uint32 ITInstrument::ConvertToMPT(ModInstrument &mptIns, MODTYPE modFormat) cons return 0; } - mpt::String::Read(mptIns.name, name); - mpt::String::Read(mptIns.filename, filename); + mptIns.name = mpt::String::ReadBuf(mpt::String::spacePadded, name); + mptIns.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); // Volume / Panning mptIns.nFadeOut = fadeout << 5; @@ -273,8 +270,8 @@ uint32 ITInstrument::ConvertToMPT(ModInstrument &mptIns, MODTYPE modFormat) cons mptIns.dwFlags.set(INS_SETPANNING, !(dfp & ITInstrument::ignorePanning)); // Random Variation - mptIns.nVolSwing = std::min(rv, 100); - mptIns.nPanSwing = std::min(rp, 64); + mptIns.nVolSwing = std::min(static_cast(rv), uint8(100)); + mptIns.nPanSwing = std::min(static_cast(rp), uint8(64)); // NNA Stuff mptIns.nNNA = static_cast(nna.get()); @@ -441,8 +438,8 @@ void ITSample::ConvertToIT(const ModSample &mptSmp, MODTYPE fromType, bool compr // Header memcpy(id, "IMPS", 4); - mpt::String::Write(filename, mptSmp.filename); - //mpt::String::Write(name, m_szNames[nsmp]); + mpt::String::WriteBuf(mpt::String::nullTerminated, filename) = mptSmp.filename; + //mpt::String::WriteBuf(mpt::String::nullTerminated, name) = m_szNames[nsmp]; // Volume / Panning gvl = static_cast(mptSmp.nGlobalVol); @@ -511,14 +508,13 @@ void ITSample::ConvertToIT(const ModSample &mptSmp, MODTYPE fromType, bool compr } else if(mptSmp.uFlags[SMP_KEEPONDISK]) { #ifndef MPT_EXTERNAL_SAMPLES - MPT_UNREFERENCED_PARAMETER(allowExternal); -#else + allowExternal = false; +#endif // MPT_EXTERNAL_SAMPLES // Save external sample (filename at sample pointer) if(allowExternal && mptSmp.HasSampleData()) { cvt = ITSample::cvtExternalSample; } else -#endif // MPT_EXTERNAL_SAMPLES { length = loopbegin = loopend = susloopbegin = susloopend = 0; } @@ -535,7 +531,7 @@ uint32 ITSample::ConvertToMPT(ModSample &mptSmp) const } mptSmp.Initialize(MOD_TYPE_IT); - mpt::String::Read(mptSmp.filename, filename); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); // Volume / Panning mptSmp.nVolume = vol * 4; @@ -673,7 +669,7 @@ uint32 DecodeITEditTimer(uint16 cwtv, uint32 editTime) { editTime ^= 0x4954524B; // 'ITRK' editTime = (editTime >> 7) | (editTime << (32 - 7)); - editTime = -(int32)editTime; + editTime = ~editTime + 1; editTime = (editTime << 4) | (editTime >> (32 - 4)); editTime ^= 0x4A54484C; // 'JTHL' } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h index 66759f246..64751f8e2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ITTools.h @@ -275,9 +275,9 @@ struct FileHistory; // IT Header extension: Save history struct ITHistoryStruct { - uint16le fatdate; // DOS / FAT date when the file was opened / created in the editor. For details, read http://msdn.microsoft.com/en-us/library/ms724247(VS.85).aspx - uint16le fattime; // DOS / FAT time when the file was opened / created in the editor. - uint32le runtime; // The time how long the file was open in the editor, in 1/18.2th seconds. (= ticks of the DOS timer) + uint16le fatdate; // DOS / FAT date when the file was opened / created in the editor. For details, read https://docs.microsoft.com/de-de/windows/win32/api/winbase/nf-winbase-dosdatetimetofiletime + uint16le fattime; // DOS / FAT time when the file was opened / created in the editor. + uint32le runtime; // The time how long the file was open in the editor, in 1/18.2th seconds. (= ticks of the DOS timer) // Convert an ITHistoryStruct to OpenMPT's internal edit history representation void ConvertToMPT(FileHistory &mptHistory) const; @@ -307,9 +307,9 @@ enum IT_ReaderBitMasks template struct SchismVersionFromDate { - enum : int32 { mm = (m + 9) % 12 }; - enum : int32 { yy = y - mm / 10 }; - enum : int32 { date = yy * 365 + yy / 4 - yy / 100 + yy / 400 + (mm * 306 + 5) / 10 + (d - 1) }; + static constexpr int32 mm = (m + 9) % 12; + static constexpr int32 yy = y - mm / 10; + static constexpr int32 date = yy * 365 + yy / 4 - yy / 100 + yy / 400 + (mm * 306 + 5) / 10 + (d - 1); static constexpr int32 Version(const int32 trackerID = 0x1000) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/InstrumentExtensions.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/InstrumentExtensions.cpp index 87bb171f9..c985ef303 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/InstrumentExtensions.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/InstrumentExtensions.cpp @@ -79,7 +79,7 @@ DT.. [EXT] nDefaultTempo; DTFR [EXT] Fractional part of default tempo DNA. nDNA; EBIH [EXT] embeded instrument header tag (ITP file format) -FM.. nFilterMode; +FM.. filterMode; fn[. filename[12]; FO.. nFadeOut; GV.. nGlobalVol; @@ -180,7 +180,7 @@ bool IsNegative(const T &val) } \ if(only_this_code == fcode || only_this_code == Util::MaxValueOfType(only_this_code)) \ { \ - type tmp = input-> name; \ + type tmp = (type)(input-> name ); \ mpt::IO::WriteIntLE(file, tmp); \ } \ /**/ @@ -221,7 +221,7 @@ bool IsNegative(const T &val) // Convenient macro to help WRITE_HEADER declaration for array members ONLY // ------------------------------------------------------------------------ #define WRITE_MPTHEADER_array_member(name,type,code,arraysize) \ - STATIC_ASSERT(sizeof(type) == sizeof(input-> name [0])); \ + static_assert(sizeof(type) == sizeof(input-> name [0])); \ MPT_ASSERT(sizeof(input->name) >= sizeof(type) * arraysize);\ fcode = code;\ fsize = sizeof( type ) * arraysize;\ @@ -251,7 +251,7 @@ bool IsNegative(const T &val) #define WRITE_MPTHEADER_envelope_member(envType,envField,type,code) \ {\ const InstrumentEnvelope &env = input->GetEnvelope(envType); \ - STATIC_ASSERT(sizeof(type) == sizeof(env[0]. envField)); \ + static_assert(sizeof(type) == sizeof(env[0]. envField)); \ fcode = code;\ fsize = mpt::saturate_cast(sizeof( type ) * env.size());\ MPT_ASSERT(size_t(fsize) == sizeof( type ) * env.size()); \ @@ -266,7 +266,7 @@ bool IsNegative(const T &val) } \ if(only_this_code == fcode || only_this_code == Util::MaxValueOfType(only_this_code)) \ { \ - uint32 maxNodes = std::min(fsize/sizeof(type), env.size()); \ + uint32 maxNodes = std::min(static_cast(fsize/sizeof(type)), static_cast(env.size())); \ for(uint32 i = 0; i < maxNodes; ++i) \ { \ type tmp; \ @@ -287,16 +287,16 @@ bool IsNegative(const T &val) // Write (in 'file') 'input' ModInstrument with 'code' & 'size' extra field infos for each member void WriteInstrumentHeaderStructOrField(ModInstrument * input, std::ostream &file, uint32 only_this_code, uint16 fixedsize) { -uint32 fcode; -uint16 fsize; -// If true, all extension are written to the file; otherwise only the specified extension is written. -// writeAll is true iff we are saving an instrument (or, hypothetically, the legacy ITP format) -const bool writeAll = only_this_code == Util::MaxValueOfType(only_this_code); + uint32 fcode; + uint16 fsize; + // If true, all extension are written to the file; otherwise only the specified extension is written. + // writeAll is true iff we are saving an instrument (or, hypothetically, the legacy ITP format) + const bool writeAll = only_this_code == Util::MaxValueOfType(only_this_code); -if(!writeAll) -{ - MPT_ASSERT(fixedsize > 0); -} + if(!writeAll) + { + MPT_ASSERT(fixedsize > 0); + } WRITE_MPTHEADER_sized_member( nFadeOut , uint32 , MagicBE("FO..") ) WRITE_MPTHEADER_sized_member( nPan , uint32 , MagicBE("P...") ) @@ -317,7 +317,7 @@ if(!writeAll) WRITE_MPTHEADER_sized_member( resampling , uint8 , MagicBE("R...") ) WRITE_MPTHEADER_sized_member( nCutSwing , uint8 , MagicBE("CS..") ) WRITE_MPTHEADER_sized_member( nResSwing , uint8 , MagicBE("RS..") ) - WRITE_MPTHEADER_sized_member( nFilterMode , uint8 , MagicBE("FM..") ) + WRITE_MPTHEADER_sized_member( filterMode , uint8 , MagicBE("FM..") ) WRITE_MPTHEADER_sized_member( pluginVelocityHandling , uint8 , MagicBE("PVEH") ) WRITE_MPTHEADER_sized_member( pluginVolumeHandling , uint8 , MagicBE("PVOH") ) WRITE_MPTHEADER_trunc_member( pitchToTempoLock.GetInt() , uint16 , MagicBE("PTTL") ) @@ -387,7 +387,7 @@ void CSoundFile::SaveExtendedInstrumentProperties(INSTRUMENTINDEX numInstruments WritePropertyIfNeeded(*this, &ModInstrument::nPan, MagicBE("P..."), sizeof(ModInstrument::nPan), f, numInstruments); WritePropertyIfNeeded(*this, &ModInstrument::nCutSwing, MagicBE("CS.."), sizeof(ModInstrument::nCutSwing), f, numInstruments); WritePropertyIfNeeded(*this, &ModInstrument::nResSwing, MagicBE("RS.."), sizeof(ModInstrument::nResSwing), f, numInstruments); - WritePropertyIfNeeded(*this, &ModInstrument::nFilterMode, MagicBE("FM.."), sizeof(ModInstrument::nFilterMode), f, numInstruments); + WritePropertyIfNeeded(*this, &ModInstrument::filterMode, MagicBE("FM.."), sizeof(ModInstrument::filterMode), f, numInstruments); if(IsPropertyNeeded(Instruments, &ModInstrument::pitchToTempoLock)) { WriteInstrumentPropertyForAllInstruments(MagicBE("PTTL"), sizeof(uint16), f, numInstruments); @@ -472,7 +472,7 @@ void CSoundFile::WriteInstrumentPropertyForAllInstruments(uint32 code, uint16 si { \ type tmp; \ tmp = file.ReadTruncatedIntLE(fsize); \ - STATIC_ASSERT(sizeof(tmp) == sizeof(input-> name )); \ + static_assert(sizeof(tmp) == sizeof(input-> name )); \ input-> name = decltype(input-> name )(tmp); \ result = true; \ } \ @@ -485,10 +485,10 @@ void CSoundFile::WriteInstrumentPropertyForAllInstruments(uint32 code, uint16 si #define GET_MPTHEADER_array_member(name,type,code) \ case code: \ {\ - if( fsize <= sizeof( type ) * CountOf(input-> name) ) \ + if( fsize <= sizeof( type ) * std::size(input-> name) ) \ { \ FileReader arrayChunk = file.ReadChunk(fsize); \ - for(std::size_t i = 0; i < CountOf(input-> name); ++i) \ + for(std::size_t i = 0; i < std::size(input-> name); ++i) \ { \ input-> name [i] = arrayChunk.ReadIntLE(); \ } \ @@ -497,18 +497,20 @@ void CSoundFile::WriteInstrumentPropertyForAllInstruments(uint32 code, uint16 si } break; // -------------------------------------------------------------------------------------------- -// Convenient macro to help GET_HEADER declaration for character array members ONLY +// Convenient macro to help GET_HEADER declaration for character buffer members ONLY // -------------------------------------------------------------------------------------------- -#define GET_MPTHEADER_chararray_member(name,type,code) \ +#define GET_MPTHEADER_charbuf_member(name,type,code) \ case code: \ {\ - if( fsize <= sizeof( type ) * CountOf(input-> name) ) \ + if( fsize <= sizeof( type ) * input-> name .static_length() ) \ { \ FileReader arrayChunk = file.ReadChunk(fsize); \ - for(std::size_t i = 0; i < CountOf(input-> name); ++i) \ + std::string tmp; \ + for(std::size_t i = 0; i < fsize; ++i) \ { \ - input-> name [i] = arrayChunk.ReadChar(); \ + tmp += arrayChunk.ReadChar(); \ } \ + input-> name = tmp; \ result = true; \ } \ } break; @@ -575,13 +577,13 @@ bool ReadInstrumentHeaderField(ModInstrument *input, uint32 fcode, uint16 fsize, GET_MPTHEADER_envelope_member(ENV_PITCH , value , uint8 , MagicBE("PiE[") ) GET_MPTHEADER_array_member( NoteMap , uint8 , MagicBE("NM[.") ) GET_MPTHEADER_array_member( Keyboard , uint16 , MagicBE("K[..") ) - GET_MPTHEADER_chararray_member( name , char , MagicBE("n[..") ) - GET_MPTHEADER_chararray_member( filename , char , MagicBE("fn[.") ) + GET_MPTHEADER_charbuf_member( name , char , MagicBE("n[..") ) + GET_MPTHEADER_charbuf_member( filename , char , MagicBE("fn[.") ) GET_MPTHEADER_sized_member( nMixPlug , uint8 , MagicBE("MiP.") ) GET_MPTHEADER_sized_member( nVolRampUp , uint16 , MagicBE("VR..") ) GET_MPTHEADER_sized_member( nCutSwing , uint8 , MagicBE("CS..") ) GET_MPTHEADER_sized_member( nResSwing , uint8 , MagicBE("RS..") ) - GET_MPTHEADER_sized_member( nFilterMode , uint8 , MagicBE("FM..") ) + GET_MPTHEADER_sized_member( filterMode , uint8 , MagicBE("FM..") ) GET_MPTHEADER_sized_member( pluginVelocityHandling , uint8 , MagicBE("PVEH") ) GET_MPTHEADER_sized_member( pluginVolumeHandling , uint8 , MagicBE("PVOH") ) GET_MPTHEADER_sized_member( PitchEnv.nReleaseNode , uint8 , MagicBE("PERN") ) @@ -614,15 +616,15 @@ bool ReadInstrumentHeaderField(ModInstrument *input, uint32 fcode, uint16 fsize, result = true; } break; case MagicBE("VE.."): - input->VolEnv.resize(std::min(MAX_ENVPOINTS, file.ReadTruncatedIntLE(fsize))); + input->VolEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadTruncatedIntLE(fsize))); result = true; break; case MagicBE("PE.."): - input->PanEnv.resize(std::min(MAX_ENVPOINTS, file.ReadTruncatedIntLE(fsize))); + input->PanEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadTruncatedIntLE(fsize))); result = true; break; case MagicBE("PiE."): - input->PitchEnv.resize(std::min(MAX_ENVPOINTS, file.ReadTruncatedIntLE(fsize))); + input->PitchEnv.resize(std::min(uint32(MAX_ENVPOINTS), file.ReadTruncatedIntLE(fsize))); result = true; break; } @@ -695,11 +697,6 @@ void ReadInstrumentExtensionField(ModInstrument* pIns, const uint32 code, const return; } - if(code == MagicBE("n[..")) - mpt::String::SetNullTerminator(pIns->name); - if(code == MagicBE("fn[.")) - mpt::String::SetNullTerminator(pIns->filename); - if(code == MagicBE("dF..")) // 'dF..' field requires additional processing. ConvertReadExtendedFlags(pIns); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/IntMixer.h b/Frameworks/OpenMPT/OpenMPT/soundlib/IntMixer.h index bd7becb21..90810bd11 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/IntMixer.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/IntMixer.h @@ -51,16 +51,16 @@ struct AmigaBlepInterpolation { SamplePosition subIncrement; Paula::State *paula; + const Paula::BlepArray *WinSincIntegral; int numSteps; - bool filter; - MPT_FORCEINLINE void Start(ModChannel &chn, const CResampler &) + MPT_FORCEINLINE void Start(ModChannel &chn, const CResampler &resampler) { paula = &chn.paulaState; numSteps = paula->numSteps; - filter = chn.dwFlags[CHN_AMIGAFILTER]; + WinSincIntegral = &resampler.blepTables.GetAmigaTable(resampler.m_Settings.emulateAmiga, chn.dwFlags[CHN_AMIGAFILTER]); if(numSteps) - subIncrement = chn.increment / paula->numSteps; + subIncrement = chn.increment / numSteps; } MPT_FORCEINLINE void End(const ModChannel &) { } @@ -94,8 +94,8 @@ struct AmigaBlepInterpolation paula->remainder.RemoveInt(); } - auto out = paula->OutputSample(filter); - for(unsigned int i = 0; i < Traits::numChannelsOut; i++) + auto out = paula->OutputSample(*WinSincIntegral); + for(int i = 0; i < Traits::numChannelsOut; i++) outSample[i] = out; } }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_669.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_669.cpp index 007d4463c..f09c1545c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_669.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_669.cpp @@ -148,7 +148,7 @@ bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Composer 669"); m_modFormat.type = U_("669"); m_modFormat.madeWithTracker = !memcmp(fileHeader.magic, "if", 2) ? UL_("Composer 669") : UL_("UNIS 669"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; m_nSamples = fileHeader.samples; for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++) @@ -160,13 +160,13 @@ bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags) if(sampleHeader.length >= 0x4000000) return false; sampleHeader.ConvertToMPT(Samples[smp]); - mpt::String::Read(m_szNames[smp], sampleHeader.filename); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.filename); } // Copy first song message line into song title - mpt::String::Read(m_songName, fileHeader.songMessage, 36); + m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songMessage, 36); // Song Message - m_songMessage.ReadFixedLineLength(mpt::byte_cast(fileHeader.songMessage), 108, 36, 0); + m_songMessage.ReadFixedLineLength(mpt::byte_cast(fileHeader.songMessage), 108, 36, 0); // Reading Orders ReadOrderFromArray(Order(), fileHeader.orders, MPT_ARRAY_COUNT(fileHeader.orders), 0xFF, 0xFE); @@ -190,7 +190,7 @@ bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags) continue; } - const ModCommand::COMMAND effTrans[] = + static constexpr ModCommand::COMMAND effTrans[] = { CMD_PORTAMENTOUP, // Slide up (param * 80) Hz on every tick CMD_PORTAMENTODOWN, // Slide down (param * 80) Hz on every tick @@ -209,29 +209,28 @@ bool CSoundFile::Read669(FileReader &file, ModLoadingFlags loadFlags) for(CHANNELINDEX chn = 0; chn < 8; chn++, m++) { - uint8 data[3]; - file.ReadArray(data); + const auto [noteInstr, instrVol, effParam] = file.ReadArray(); - uint8 note = data[0] >> 2; - uint8 instr = ((data[0] & 0x03) << 4) | (data[1] >> 4); - uint8 vol = data[1] & 0x0F; - if(data[0] < 0xFE) + uint8 note = noteInstr >> 2; + uint8 instr = ((noteInstr & 0x03) << 4) | (instrVol >> 4); + uint8 vol = instrVol & 0x0F; + if(noteInstr < 0xFE) { m->note = note + 36 + NOTE_MIN; m->instr = instr + 1; effect[chn] = 0xFF; } - if(data[0] <= 0xFE) + if(noteInstr <= 0xFE) { m->volcmd = VOLCMD_VOLUME; m->vol = ((vol * 64 + 8) / 15); } - if(data[2] != 0xFF) + if(effParam != 0xFF) { - effect[chn] = data[2]; + effect[chn] = effParam; } - if((data[2] & 0x0F) == 0 && data[2] != 0x30) + if((effParam & 0x0F) == 0 && effParam != 0x30) { // A param value of 0 resets the effect. effect[chn] = 0xFF; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_amf.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_amf.cpp index df683325a..f986ca2b8 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_amf.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_amf.cpp @@ -49,7 +49,7 @@ struct AsylumSampleHeader { mptSmp.Initialize(); mptSmp.nFineTune = MOD2XMFineTune(finetune); - mptSmp.nVolume = std::min(defaultVolume, 64) * 4u; + mptSmp.nVolume = std::min(defaultVolume.get(), uint8(64)) * 4u; mptSmp.RelativeTone = transpose; mptSmp.nLength = length; @@ -149,7 +149,7 @@ bool CSoundFile::ReadAMF_Asylum(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("ASYLUM Music Format"); m_modFormat.type = U_("amf"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; uint8 orders[256]; file.ReadArray(orders); @@ -161,7 +161,7 @@ bool CSoundFile::ReadAMF_Asylum(FileReader &file, ModLoadingFlags loadFlags) AsylumSampleHeader sampleHeader; file.ReadStruct(sampleHeader); sampleHeader.ConvertToMPT(Samples[smp]); - mpt::String::Read(m_szNames[smp], sampleHeader.name); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); } file.Skip((64 - fileHeader.numSamples) * sizeof(AsylumSampleHeader)); @@ -178,16 +178,14 @@ bool CSoundFile::ReadAMF_Asylum(FileReader &file, ModLoadingFlags loadFlags) for(auto &m : Patterns[pat]) { - uint8 data[4]; - file.ReadArray(data); - - if(data[0] && data[0] + 12 + NOTE_MIN <= NOTE_MAX) + const auto [note, instr, command, param] = file.ReadArray(); + if(note && note + 12 + NOTE_MIN <= NOTE_MAX) { - m.note = data[0] + 12 + NOTE_MIN; + m.note = note + 12 + NOTE_MIN; } - m.instr = data[1]; - m.command = data[2]; - m.param = data[3]; + m.instr = instr; + m.command = command; + m.param = param; ConvertModCommand(m); #ifdef MODPLUG_TRACKER if(m.command == CMD_PANNING8) @@ -225,9 +223,7 @@ static void AMFReadPattern(CPattern &pattern, CHANNELINDEX chn, FileReader &file ModCommand::INSTR lastInstr = 0; while(fileChunk.CanRead(3)) { - const uint8 row = fileChunk.ReadUint8(); - const uint8 command = fileChunk.ReadUint8(); - const uint8 value = fileChunk.ReadUint8(); + const auto [row, command, value] = fileChunk.ReadArray(); if(row >= pattern.GetNumRows()) { break; @@ -267,7 +263,7 @@ static void AMFReadPattern(CPattern &pattern, CHANNELINDEX chn, FileReader &file } else { // Effect - static const ModCommand::COMMAND effTrans[] = + static constexpr ModCommand::COMMAND effTrans[] = { CMD_NONE, CMD_SPEED, CMD_VOLUMESLIDE, CMD_VOLUME, CMD_PORTAMENTOUP, CMD_NONE, CMD_TONEPORTAMENTO, CMD_TREMOR, @@ -441,12 +437,12 @@ bool CSoundFile::ReadAMF_DSMI(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = mpt::format(U_("DSMI v%1"))(fileHeader.version); m_modFormat.type = U_("amf"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; m_nChannels = fileHeader.numChannels; m_nSamples = fileHeader.numSamples; - mpt::String::Read(m_songName, fileHeader.title); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.title); if(fileHeader.version < 10) { @@ -486,10 +482,10 @@ bool CSoundFile::ReadAMF_DSMI(FileReader &file, ModLoadingFlags loadFlags) // Get Tempo/Speed if(fileHeader.version >= 13) { - uint8 tempo = file.ReadUint8(); + auto [tempo, speed] = file.ReadArray(); if(tempo < 32) tempo = 125; m_nDefaultTempo.Set(tempo); - m_nDefaultSpeed = file.ReadUint8(); + m_nDefaultSpeed = speed; } else { m_nDefaultTempo.Set(125); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ams.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ams.cpp index 47aa9795c..c9b31a54a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ams.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ams.cpp @@ -42,7 +42,7 @@ static void ReadAMSPattern(CPattern &pattern, bool newVersion, FileReader &patte }; // Effect translation table for extended (non-Protracker) effects - static const ModCommand::COMMAND effTrans[] = + static constexpr ModCommand::COMMAND effTrans[] = { CMD_S3MCMDEX, // Forward / Backward CMD_PORTAMENTOUP, // Extra fine slide up @@ -157,7 +157,7 @@ static void ReadAMSPattern(CPattern &pattern, bool newVersion, FileReader &patte } break; } - } else if(effect - 0x10 < (int)CountOf(effTrans)) + } else if(effect < 0x10 + CountOf(effTrans)) { // Extended commands m.command = effTrans[effect - 0x10]; @@ -299,7 +299,7 @@ struct AMSSampleHeader mptSmp.nLoopStart = std::min(loopStart, length); mptSmp.nLoopEnd = std::min(loopEnd, length); - mptSmp.nVolume = (std::min(127, volume) * 256 + 64) / 127; + mptSmp.nVolume = (std::min(uint8(127), volume.get()) * 256 + 64) / 127; if(panFinetune & 0xF0) { mptSmp.nPan = (panFinetune & 0xF0); @@ -409,11 +409,11 @@ bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Extreme's Tracker"); m_modFormat.type = U_("ams"); m_modFormat.madeWithTracker = mpt::format(U_("Extreme's Tracker %1.%2"))(fileHeader.versionHigh, fileHeader.versionLow); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; std::vector packSample(fileHeader.numSamps); - STATIC_ASSERT(MAX_SAMPLES > 255); + static_assert(MAX_SAMPLES > 255); for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) { AMSSampleHeader sampleHeader; @@ -471,10 +471,10 @@ bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags) } } - textOut = mpt::ToCharset(mpt::CharsetCP437, mpt::CharsetCP437AMS, textOut); + textOut = mpt::ToCharset(mpt::Charset::CP437, mpt::Charset::CP437AMS, textOut); // Packed text doesn't include any line breaks! - m_songMessage.ReadFixedLineLength(mpt::byte_cast(textOut.c_str()), textOut.length(), 76, 0); + m_songMessage.ReadFixedLineLength(mpt::byte_cast(textOut.c_str()), textOut.length(), 76, 0); } // Read Order List @@ -556,7 +556,7 @@ struct AMS2Envelope return; } - STATIC_ASSERT(MAX_ENVPOINTS >= CountOf(data)); + static_assert(MAX_ENVPOINTS >= CountOf(data)); mptEnv.resize(std::min(numPoints, uint8(CountOf(data)))); mptEnv.nLoopStart = loopStart; mptEnv.nLoopEnd = loopEnd; @@ -661,7 +661,7 @@ struct AMS2SampleHeader uint32 newC4speed = ModSample::TransposeToFrequency(relativeTone, MOD2XMFineTune(panFinetune & 0x0F)); mptSmp.nC5Speed = (mptSmp.nC5Speed * newC4speed) / 8363; - mptSmp.nVolume = (std::min(volume, 127) * 256 + 64) / 127; + mptSmp.nVolume = (std::min(volume.get(), uint8(127)) * 256 + 64) / 127; if(panFinetune & 0xF0) { mptSmp.nPan = (panFinetune & 0xF0); @@ -784,7 +784,7 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Velvet Studio"); m_modFormat.type = U_("ams"); m_modFormat.madeWithTracker = mpt::format(U_("Velvet Studio %1.%2"))(fileHeader.versionHigh.get(), mpt::ufmt::dec0<2>(fileHeader.versionLow.get())); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; uint16 headerFlags; if(fileHeader.versionLow >= 2) @@ -814,7 +814,7 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) packStatusMask = 0x8000, // If bit is set, sample is packed }; - STATIC_ASSERT(MAX_INSTRUMENTS > 255); + static_assert(MAX_INSTRUMENTS > 255); for(INSTRUMENTINDEX ins = 1; ins <= m_nInstruments; ins++) { ModInstrument *instrument = AllocateInstrument(ins); @@ -835,7 +835,7 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) continue; } - STATIC_ASSERT(CountOf(instrument->Keyboard) >= CountOf(sampleAssignment)); + static_assert(mpt::array_sizeKeyboard)>::size >= std::size(sampleAssignment)); for(size_t i = 0; i < 120; i++) { instrument->Keyboard[i] = sampleAssignment[i] + GetNumSamples() + 1; @@ -908,7 +908,7 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) { std::string str; file.ReadString(str, composerLength); - m_songArtist = mpt::ToUnicode(mpt::CharsetCP437AMS2, str); + m_songArtist = mpt::ToUnicode(mpt::Charset::CP437AMS2, str); } // Channel names @@ -946,9 +946,9 @@ bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags) textOut.push_back(c); } } - textOut = mpt::ToCharset(mpt::CharsetCP437, mpt::CharsetCP437AMS2, textOut); + textOut = mpt::ToCharset(mpt::Charset::CP437, mpt::Charset::CP437AMS2, textOut); // Packed text doesn't include any line breaks! - m_songMessage.ReadFixedLineLength(mpt::byte_cast(textOut.c_str()), textOut.length(), 74, 0); + m_songMessage.ReadFixedLineLength(mpt::byte_cast(textOut.c_str()), textOut.length(), 74, 0); } // Read Order List diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_c67.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_c67.cpp index 495812d76..9d5d70b82 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_c67.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_c67.cpp @@ -104,7 +104,7 @@ static void TranslateVolume(ModCommand &m, uint8 volume, bool isFM) // CDFM uses a linear volume scale for FM instruments. // ScreamTracker, on the other hand, directly uses the OPL chip's logarithmic volume scale. // Neither FM nor PCM instruments can be fully muted in CDFM. - static const uint8 fmVolume[16] = + static constexpr uint8 fmVolume[16] = { 0x08, 0x10, 0x18, 0x20, 0x28, 0x2C, 0x30, 0x34, 0x36, 0x38, 0x3A, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, @@ -160,7 +160,7 @@ bool CSoundFile::ReadC67(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("CDFM"); m_modFormat.type = U_("c67"); m_modFormat.madeWithTracker = U_("Composer 670"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; m_nDefaultSpeed = fileHeader.speed; m_nDefaultTempo.Set(143); @@ -180,7 +180,7 @@ bool CSoundFile::ReadC67(FileReader &file, ModLoadingFlags loadFlags) { ModSample &mptSmp = Samples[smp + 1]; mptSmp.Initialize(MOD_TYPE_S3M); - mpt::String::Read(m_szNames[smp + 1], fileHeader.sampleNames[smp]); + m_szNames[smp + 1] = mpt::String::ReadBuf(mpt::String::nullTerminated, fileHeader.sampleNames[smp]); mptSmp.nLength = fileHeader.samples[smp].length; if(fileHeader.samples[smp].loopEnd <= fileHeader.samples[smp].length) { @@ -195,7 +195,7 @@ bool CSoundFile::ReadC67(FileReader &file, ModLoadingFlags loadFlags) { ModSample &mptSmp = Samples[smp + 33]; mptSmp.Initialize(MOD_TYPE_S3M); - mpt::String::Read(m_szNames[smp + 33], fileHeader.fmInstrNames[smp]); + m_szNames[smp + 33] = mpt::String::ReadBuf(mpt::String::nullTerminated, fileHeader.fmInstrNames[smp]); // Reorder OPL patch bytes (interleave modulator and carrier) const auto &fm = fileHeader.fmInstr[smp]; OPLPatch patch{{}}; @@ -227,9 +227,7 @@ bool CSoundFile::ReadC67(FileReader &file, ModLoadingFlags loadFlags) { // Note, instrument, volume ModCommand &m = *pattern.GetpModCommand(row, cmd); - uint8 data[2]; - patChunk.ReadArray(data); - uint8 note = data[0], instrVol = data[1]; + const auto [note, instrVol] = patChunk.ReadArray(); bool fmChn = (cmd >= 4); m.note = NOTE_MIN + (fmChn ? 12 : 36) + (note & 0x0F) + ((note >> 4) & 0x07) * 12; m.instr = (fmChn ? 33 : 1) + (instrVol >> 4) + ((note & 0x80) >> 3); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp index d7b7bb25c..74dc4ba08 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dbm.cpp @@ -127,7 +127,7 @@ MPT_BINARY_STRUCT(DBMEnvelope, 136) // Note: Unlike in MOD, 1Fx, 2Fx, 5Fx / 5xF, 6Fx / 6xF and AFx / AxF are fine slides. -static const ModCommand::COMMAND dbmEffects[] = +static constexpr ModCommand::COMMAND dbmEffects[] = { CMD_ARPEGGIO, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO, CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_TREMOLO, @@ -262,7 +262,7 @@ static void ReadDBMEnvelopeChunk(FileReader chunk, EnvelopeType envType, CSoundF if(dbmEnv.flags & DBMEnvelope::envLoop) mptEnv.dwFlags.set(ENV_LOOP); } - uint8 numPoints = std::min(dbmEnv.numSegments, 31) + 1; + uint8 numPoints = std::min(dbmEnv.numSegments.get(), uint8(31)) + 1; mptEnv.resize(numPoints); mptEnv.nLoopStart = dbmEnv.loopBegin; @@ -345,8 +345,8 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) InitializeChannels(); m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS; m_nChannels = Clamp(infoData.channels, 1, MAX_BASECHANNELS); // note: MAX_BASECHANNELS is currently 127, but DBPro 2 supports up to 128 channels, DBPro 3 apparently up to 254. - m_nInstruments = std::min(infoData.instruments, MAX_INSTRUMENTS - 1); - m_nSamples = std::min(infoData.samples, MAX_SAMPLES - 1); + m_nInstruments = std::min(static_cast(infoData.instruments), static_cast(MAX_INSTRUMENTS - 1)); + m_nSamples = std::min(static_cast(infoData.samples), static_cast(MAX_SAMPLES - 1)); m_playBehaviour.set(kSlidesAtSpeed1); m_playBehaviour.reset(kITVibratoTremoloPanbrello); m_playBehaviour.reset(kITArpeggio); @@ -354,7 +354,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("DigiBooster Pro"); m_modFormat.type = U_("dbm"); m_modFormat.madeWithTracker = mpt::format(U_("DigiBooster Pro %1.%2"))(mpt::ufmt::hex(fileHeader.trkVerHi), mpt::ufmt::hex(fileHeader.trkVerLo)); - m_modFormat.charset = mpt::CharsetISO8859_1; + m_modFormat.charset = mpt::Charset::ISO8859_1; // Name chunk FileReader nameChunk = chunks.GetChunk(DBMChunk::idNAME); @@ -378,10 +378,10 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) if(!Order().empty()) { // Add a new sequence for this song - if(Order.AddSequence(false) == SEQUENCEINDEX_INVALID) + if(Order.AddSequence() == SEQUENCEINDEX_INVALID) break; } - Order().SetName(name); + Order().SetName(mpt::ToUnicode(mpt::Charset::ISO8859_1, name)); ReadOrderFromFile(Order(), songChunk, numOrders); #else const ORDERINDEX startIndex = Order().GetLength(); @@ -413,10 +413,9 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) { continue; } - ModSample &mptSmp = Samples[instrHeader.sample]; - mpt::String::Read(mptIns->name, instrHeader.name); - mpt::String::Read(m_szNames[instrHeader.sample], instrHeader.name); + mptIns->name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name); + m_szNames[instrHeader.sample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name); mptIns->nFadeOut = 0; mptIns->nPan = static_cast(instrHeader.panning + 128); @@ -424,8 +423,9 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) mptIns->dwFlags.set(INS_SETPANNING); // Sample Info + ModSample &mptSmp = Samples[instrHeader.sample]; mptSmp.Initialize(); - mptSmp.nVolume = std::min(instrHeader.volume, 64) * 4u; + mptSmp.nVolume = std::min(static_cast(instrHeader.volume), uint16(64)) * 4u; mptSmp.nC5Speed = instrHeader.sampleRate; if(instrHeader.loopLength && (instrHeader.flags & (DBMInstrument::smpLoop | DBMInstrument::smpPingPongLoop))) @@ -433,7 +433,8 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) mptSmp.nLoopStart = instrHeader.loopStart; mptSmp.nLoopEnd = mptSmp.nLoopStart + instrHeader.loopLength; mptSmp.uFlags.set(CHN_LOOP); - if(instrHeader.flags & DBMInstrument::smpPingPongLoop) mptSmp.uFlags.set(CHN_PINGPONGLOOP); + if(instrHeader.flags & DBMInstrument::smpPingPongLoop) + mptSmp.uFlags.set(CHN_PINGPONGLOOP); } } @@ -462,6 +463,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) patternNameChunk.Skip(1); // Encoding, should be UTF-8 or ASCII Patterns.ResizeArray(infoData.patterns); + std::vector> lostGlobalCommands; for(PATTERNINDEX pat = 0; pat < infoData.patterns; pat++) { uint16 numRows = patternChunk.ReadUint16BE(); @@ -469,9 +471,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) FileReader chunk = patternChunk.ReadChunk(packedSize); if(!Patterns.Insert(pat, numRows)) - { continue; - } std::string patName; patternNameChunk.ReadSizedString(patName); @@ -479,6 +479,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) PatternRow patRow = Patterns[pat].GetRow(0); ROWINDEX row = 0; + lostGlobalCommands.clear(); while(chunk.CanRead(1)) { const uint8 ch = chunk.ReadUint8(); @@ -486,6 +487,12 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) if(!ch) { // End Of Row + for(const auto &cmd : lostGlobalCommands) + { + Patterns[pat].WriteEffect(EffectWriter(cmd.first, cmd.second).Row(row)); + } + lostGlobalCommands.clear(); + if(++row >= numRows) break; @@ -503,12 +510,9 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) uint8 note = chunk.ReadUint8(); if(note == 0x1F) - note = NOTE_KEYOFF; + m.note = NOTE_KEYOFF; else if(note > 0 && note < 0xFE) - { - note = ((note >> 4) * 12) + (note & 0x0F) + 13; - } - m.note = note; + m.note = ((note >> 4) * 12) + (note & 0x0F) + 13; } if(b & 0x02) { @@ -516,8 +520,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) } if(b & 0x3C) { - uint8 cmd1 = CMD_NONE, cmd2 = CMD_NONE; - uint8 param1 = 0, param2 = 0; + uint8 cmd1 = 0, cmd2 = 0, param1 = 0, param2 = 0; if(b & 0x04) cmd2 = chunk.ReadUint8(); if(b & 0x08) param2 = chunk.ReadUint8(); if(b & 0x10) cmd1 = chunk.ReadUint8(); @@ -531,7 +534,9 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) std::swap(param1, param2); } - ModCommand::TwoRegularCommandsToMPT(cmd1, param1, cmd2, param2); + const auto lostCommand = ModCommand::TwoRegularCommandsToMPT(cmd1, param1, cmd2, param2); + if(ModCommand::IsGlobalCommand(lostCommand.first, lostCommand.second)) + lostGlobalCommands.insert(lostGlobalCommands.begin(), lostCommand); // Insert at front so that the last command of same type "wins" m.volcmd = cmd1; m.vol = param1; @@ -604,9 +609,9 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) plugin.Info.gain = 10; plugin.Info.reserved = 0; plugin.Info.dwOutputRouting = 0; - std::fill(plugin.Info.dwReserved, plugin.Info.dwReserved + mpt::size(plugin.Info.dwReserved), 0); - mpt::String::Write(plugin.Info.szName, "Echo"); - mpt::String::Write(plugin.Info.szLibraryName, "DigiBooster Pro Echo"); + std::fill(plugin.Info.dwReserved, plugin.Info.dwReserved + std::size(plugin.Info.dwReserved), 0); + plugin.Info.szName = "Echo"; + plugin.Info.szLibraryName = "DigiBooster Pro Echo"; plugin.pluginData.resize(sizeof(DigiBoosterEcho::PluginChunk)); DigiBoosterEcho::PluginChunk chunk = DigiBoosterEcho::PluginChunk::Create(settings[1], settings[3], settings[5], settings[7]); @@ -637,11 +642,11 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) uint32 sampleFlags = sampleChunk.ReadUint32BE(); uint32 sampleLength = sampleChunk.ReadUint32BE(); - ModSample &sample = Samples[smp]; - sample.nLength = sampleLength; - if(sampleFlags & 7) { + ModSample &sample = Samples[smp]; + sample.nLength = sampleLength; + SampleIO( (sampleFlags & 4) ? SampleIO::_32bit : ((sampleFlags & 2) ? SampleIO::_16bit : SampleIO::_8bit), SampleIO::mono, @@ -668,7 +673,7 @@ bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags) if(ReadMP3Sample(0, chunk, true)) { ModSample &srcSample = Samples[0]; - const mpt::byte *smpData = srcSample.sampleb(); + const std::byte *smpData = srcSample.sampleb(); SmpLength predelay = Util::muldiv_unsigned(20116, srcSample.nC5Speed, 100000); LimitMax(predelay, srcSample.nLength); smpData += predelay * srcSample.GetBytesPerSample(); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_digi.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_digi.cpp index 5029f67e8..71125c312 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_digi.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_digi.cpp @@ -130,7 +130,7 @@ bool CSoundFile::ReadDIGI(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("DigiBooster"); m_modFormat.type = U_("digi"); m_modFormat.madeWithTracker = mpt::format(U_("Digi Booster %1.%2"))(fileHeader.versionInt >> 4, fileHeader.versionInt & 0x0F); - m_modFormat.charset = mpt::CharsetISO8859_1; + m_modFormat.charset = mpt::Charset::ISO8859_1; ReadOrderFromArray(Order(), fileHeader.orders, fileHeader.lastOrdIndex + 1); @@ -148,7 +148,7 @@ bool CSoundFile::ReadDIGI(FileReader &file, ModLoadingFlags loadFlags) } sample.SanitizeLoops(); - sample.nVolume = std::min(fileHeader.smpVolume[smp], 64) * 4; + sample.nVolume = std::min(fileHeader.smpVolume[smp].get(), uint8(64)) * 4; sample.nFineTune = MOD2XMFineTune(fileHeader.smpFinetune[smp]); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp index b345243e0..a09d1bde1 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dmf.cpp @@ -195,17 +195,17 @@ static uint8 DMFporta2MPT(uint8 val, const uint8 internalTicks, const bool hasFi else if((val <= 0x0F && hasFine) || internalTicks < 2) return (val | 0xF0); else - return std::max(1, (val / (internalTicks - 1))); // no porta on first tick! + return std::max(uint8(1), static_cast((val / (internalTicks - 1)))); // no porta on first tick! } // Convert portamento / volume slide value (not very accurate due to X-Tracker's higher granularity, to say the least) static uint8 DMFslide2MPT(uint8 val, const uint8 internalTicks, const bool up) { - val = std::max(1, val / 4); + val = std::max(uint8(1), static_cast(val / 4)); const bool isFine = (val < 0x0F) || (internalTicks < 2); if(!isFine) - val = std::max(1, (val + internalTicks - 2) / (internalTicks - 1)); // no slides on first tick! "+ internalTicks - 2" for rounding precision + val = std::max(uint8(1), static_cast((val + internalTicks - 2) / (internalTicks - 1))); // no slides on first tick! "+ internalTicks - 2" for rounding precision if(up) return (isFine ? 0x0F : 0x00) | (val << 4); @@ -242,7 +242,7 @@ static uint8 DMFvibrato2MPT(uint8 val, const uint8 internalTicks) // X-Tracker: Period length specified in rows! const int periodInTicks = std::max(1, (val >> 4)) * internalTicks; const uint8 matchingPeriod = static_cast(Clamp((128 / periodInTicks), 1, 15)); - return (matchingPeriod << 4) | std::max(1, (val & 0x0F)); + return (matchingPeriod << 4) | std::max(uint8(1), static_cast(val & 0x0F)); } @@ -340,7 +340,7 @@ static PATTERNINDEX ConvertDMFPattern(FileReader &file, DMFPatternSettings &sett } PatternRow m = sndFile.Patterns[pat].GetRow(0); - const CHANNELINDEX numChannels = std::min(sndFile.GetNumChannels() - 1, patHead.numTracks); + const CHANNELINDEX numChannels = std::min(static_cast(sndFile.GetNumChannels() - 1), static_cast(patHead.numTracks)); // When breaking to a pattern with less channels that the previous pattern, // all voices in the now unused channels are killed: @@ -463,21 +463,19 @@ static PATTERNINDEX ConvertDMFPattern(FileReader &file, DMFPatternSettings &sett // => Tempo = 60 * Rows per Second * Speed / 24 // For some reason, using settings.tempoTicks + 1 gives more accurate results than just settings.tempoTicks... (same problem in the old libmodplug DMF loader) // Original unoptimized formula: - //const int tickspeed = (tempoRealBPMmode) ? MAX(1, (tempoData * beat * 4) / 60) : tempoData; + //const int tickspeed = (tempoRealBPMmode) ? std::max(1, (tempoData * beat * 4) / 60) : tempoData; const int tickspeed = (settings.realBPMmode) ? std::max(1, settings.tempoBPM * settings.beat * 2) : ((settings.tempoTicks + 1) * 30); // Try to find matching speed - try higher speeds first, so that effects like arpeggio and tremor work better. - for(speed = 255; speed > 2; speed--) + for(speed = 255; speed >= 1; speed--) { // Original unoptimized formula: // tempo = 30 * tickspeed * speed / 48; tempo = tickspeed * speed / 48; if(tempo >= 32 && tempo <= 255) - { break; - } } Limit(tempo, 32, 255); - settings.internalTicks = (uint8)speed; + settings.internalTicks = static_cast(std::max(1, speed)); } else { tempoChange = false; @@ -915,14 +913,10 @@ bool CSoundFile::ReadDMF(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = mpt::format(U_("X-Tracker v%1"))(fileHeader.version); m_modFormat.type = U_("dmf"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; - mpt::String::Read(m_songName, fileHeader.songname); - { - std::string artist; - mpt::String::Read(artist, fileHeader.composer); - m_songArtist = mpt::ToUnicode(mpt::CharsetCP437, artist); - } + m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songname); + m_songArtist = mpt::ToUnicode(mpt::Charset::CP437, mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.composer)); FileHistory mptHistory; mptHistory.loadDate.tm_mday = Clamp(fileHeader.creationDay, uint8(1), uint8(31)); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsm.cpp index 3f01635dd..5b7ee2e9f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dsm.cpp @@ -73,14 +73,14 @@ struct DSMSampleHeader void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); - mpt::String::Read(mptSmp.filename, filename); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); mptSmp.nC5Speed = sampleRate; mptSmp.uFlags.set(CHN_LOOP, (flags & 1) != 0); mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; - mptSmp.nVolume = std::min(volume, 64) * 4; + mptSmp.nVolume = std::min(volume.get(), uint8(64)) * 4; } // Retrieve the internal sample format flags for this sample. @@ -210,13 +210,13 @@ bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("DSIK Format"); m_modFormat.type = U_("dsm"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; - mpt::String::Read(m_songName, songHeader.songName); - m_nChannels = std::max(songHeader.numChannels, 1); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, songHeader.songName); + m_nChannels = std::max(songHeader.numChannels.get(), uint16(1)); m_nDefaultSpeed = songHeader.speed; m_nDefaultTempo.Set(songHeader.bpm); - m_nDefaultGlobalVolume = std::min(songHeader.globalVol, 64) * 4u; + m_nDefaultGlobalVolume = std::min(songHeader.globalVol.get(), uint8(64)) * 4u; if(!m_nDefaultGlobalVolume) m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; if(songHeader.mastervol == 0x80) { @@ -289,8 +289,7 @@ bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags) } if(flag & 0x10) { - uint8 command = chunk.ReadUint8(); - uint8 param = chunk.ReadUint8(); + auto [command, param] = chunk.ReadArray(); switch(command) { // Portamentos @@ -314,7 +313,7 @@ bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags) } } patNum++; - } else if(!memcmp(chunkHeader.magic, "INST", 4) && GetNumSamples() < SAMPLEINDEX(MAX_SAMPLES - 1)) + } else if(!memcmp(chunkHeader.magic, "INST", 4) && CanAddMoreSamples()) { // Read sample m_nSamples++; @@ -324,7 +323,7 @@ bool CSoundFile::ReadDSM(FileReader &file, ModLoadingFlags loadFlags) chunk.ReadStruct(sampleHeader); sampleHeader.ConvertToMPT(sample); - mpt::String::Read(m_szNames[m_nSamples], sampleHeader.sampleName); + m_szNames[m_nSamples] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.sampleName); if(loadFlags & loadSampleData) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dtm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dtm.cpp index 85de0e0cb..1b717fece 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dtm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_dtm.cpp @@ -107,7 +107,7 @@ struct DTMSample transposeAmount += (48 - transpose) * 128; } mptSmp.Transpose(transposeAmount * (1.0 / (12.0 * 128.0))); - mptSmp.nVolume = std::min(volume, 64u) * 4u; + mptSmp.nVolume = std::min(volume.get(), uint8(64)) * 4u; if(stereo & 1) { mptSmp.uFlags.set(CHN_STEREO); @@ -290,14 +290,14 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) for(CHANNELINDEX chn = 0; chn < 32 && chn < GetNumChannels(); chn++) { // Panning is in range 0...180, 90 = center - ChnSettings[chn].nPan = static_cast(128 + Util::muldivr(std::min(panning[chn], 180) - 90, 128, 90)); + ChnSettings[chn].nPan = static_cast(128 + Util::muldivr(std::min(static_cast(panning[chn]), int(180)) - 90, 128, 90)); } chunk.Skip(16); // Chunk ends here for old DTM modules if(chunk.CanRead(2)) { - m_nDefaultGlobalVolume = std::min(chunk.ReadUint16BE(), MAX_GLOBAL_VOLUME); + m_nDefaultGlobalVolume = std::min(chunk.ReadUint16BE(), static_cast(MAX_GLOBAL_VOLUME)); } chunk.Skip(128); uint16be volume[32]; @@ -306,7 +306,7 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) for(CHANNELINDEX chn = 0; chn < 32 && chn < GetNumChannels(); chn++) { // Volume is in range 0...128, 64 = normal - ChnSettings[chn].nVolume = static_cast(std::min(volume[chn], 128) / 2); + ChnSettings[chn].nVolume = static_cast(std::min(static_cast(volume[chn]), int(128)) / 2); } m_nSamplePreAmp *= 2; // Compensate for channel volume range } @@ -348,13 +348,13 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) m_nSamples = std::max(m_nSamples, realSample); ModSample &mptSmp = Samples[realSample]; dtmSample.ConvertToMPT(mptSmp, fileHeader.forcedSampleRate, patternFormat); - mpt::String::Read(m_szNames[realSample], dtmSample.name); + m_szNames[realSample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, dtmSample.name); } if(chunk.ReadUint16BE() == 0x0004) { // Digital Home Studio instruments - m_nInstruments = std::min(m_nSamples, MAX_INSTRUMENTS - 1); + m_nInstruments = std::min(static_cast(m_nSamples), static_cast(MAX_INSTRUMENTS - 1)); FileReader envChunk = chunks.GetChunk(DTMChunk::idIENV); while(chunk.CanRead(sizeof(DTMInstrument))) @@ -372,16 +372,16 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) if(mptIns != nullptr) { InstrumentEnvelope &mptEnv = mptIns->VolEnv; - mptIns->nFadeOut = std::min(instr.fadeout, 0xFFF); + mptIns->nFadeOut = std::min(static_cast(instr.fadeout), uint16(0xFFF)); if(instr.envelope != 0xFF && envChunk.Seek(2 + sizeof(DTMEnvelope) * instr.envelope)) { DTMEnvelope env; envChunk.ReadStruct(env); mptEnv.dwFlags.set(ENV_ENABLED); - mptEnv.resize(std::min({ env.numPoints, mpt::size(env.points), MAX_ENVPOINTS })); + mptEnv.resize(std::min({ static_cast(env.numPoints), std::size(env.points), static_cast(MAX_ENVPOINTS) })); for(size_t i = 0; i < mptEnv.size(); i++) { - mptEnv[i].value = std::min(64, env.points[i].value); + mptEnv[i].value = std::min(uint8(64), static_cast(env.points[i].value)); mptEnv[i].tick = env.points[i].tick; } @@ -432,17 +432,16 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) { ModCommand *m = Patterns[patNum].GetpModCommand(position.quot, chn); - uint8 data[6]; - rowChunk.ReadArray(data); - if(data[0] > 0 && data[0] <= 96) + const auto [note, volume, instr, command, param, delay] = rowChunk.ReadArray(); + if(note > 0 && note <= 96) { - m->note = data[0] + NOTE_MIN + 12; + m->note = note + NOTE_MIN + 12; if(position.rem) { m->command = CMD_MODCMDEX; m->param = 0xD0 | static_cast(std::min(position.rem, 15)); } - } else if(data[0] & 0x80) + } else if(note & 0x80) { // Lower 7 bits contain note, probably intended for MIDI-like note-on/note-off events if(position.rem) @@ -454,19 +453,19 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) m->note = NOTE_NOTECUT; } } - if(data[1]) + if(volume) { m->volcmd = VOLCMD_VOLUME; - m->vol = std::min(data[1], uint8(64)); // Volume can go up to 255, but we do not support over-amplification at the moment. + m->vol = std::min(volume, uint8(64)); // Volume can go up to 255, but we do not support over-amplification at the moment. } - if(data[2]) + if(instr) { - m->instr = data[2]; + m->instr = instr; } - if(data[3] || data[4]) + if(command || param) { - m->command = data[3]; - m->param = data[4]; + m->command = command; + m->param = param; ConvertModCommand(*m); #ifdef MODPLUG_TRACKER m->Convert(MOD_TYPE_MOD, MOD_TYPE_IT, *this); @@ -474,10 +473,10 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) // G is 8-bit volume // P is tremor (need to disable oldfx) } - if(data[5] & 0x80) - tick += (data[5] & 0x7F) * 0x100 + rowChunk.ReadUint8(); + if(delay & 0x80) + tick += (delay & 0x7F) * 0x100 + rowChunk.ReadUint8(); else - tick += data[5]; + tick += delay; position = std::div(tick, m_nDefaultSpeed); } } @@ -488,23 +487,23 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) { for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++, m++) { - uint8 data[4]; - chunk.ReadArray(data); + const auto data = chunk.ReadArray(); if(patternFormat == DTM_204_PATTERN_FORMAT) { - if(data[0] > 0 && data[0] < 0x80) + const auto [note, instrVol, instrCmd, param] = data; + if(note > 0 && note < 0x80) { - m->note = (data[0] >> 4) * 12 + (data[0] & 0x0F) + NOTE_MIN + 11; + m->note = (note >> 4) * 12 + (note & 0x0F) + NOTE_MIN + 11; } - uint8 vol = data[1] >> 2; + uint8 vol = instrVol >> 2; if(vol) { m->volcmd = VOLCMD_VOLUME; m->vol = vol - 1u; } - m->instr = ((data[1] & 0x03) << 4) | (data[2] >> 4); - m->command = data[2] & 0x0F; - m->param = data[3]; + m->instr = ((instrVol & 0x03) << 4) | (instrCmd >> 4); + m->command = instrCmd & 0x0F; + m->param = param; } else { ReadMODPatternEntry(data, *m); @@ -564,7 +563,7 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) while(chunk.CanRead(1) && chn < GetNumChannels()) { chunk.ReadNullString(name, 32); - mpt::String::Copy(ChnSettings[chn].szName, name); + ChnSettings[chn].szName = name; chn++; } } @@ -601,7 +600,7 @@ bool CSoundFile::ReadDTM(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Digital Tracker"); m_modFormat.type = U_("dtm"); m_modFormat.madeWithTracker = std::move(tracker); - m_modFormat.charset = mpt::CharsetISO8859_1; + m_modFormat.charset = mpt::Charset::ISO8859_1; return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_far.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_far.cpp index d902c74f1..da53d566a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_far.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_far.cpp @@ -171,9 +171,9 @@ bool CSoundFile::ReadFAR(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Farandole Composer"); m_modFormat.type = U_("far"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; - mpt::String::Read(m_songName, fileHeader.songName); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName); // Read channel settings for(CHANNELINDEX chn = 0; chn < 16; chn++) @@ -201,7 +201,7 @@ bool CSoundFile::ReadFAR(FileReader &file, ModLoadingFlags loadFlags) file.Seek(fileHeader.headerLength); // Pattern effect LUT - static const EffectCommand farEffects[] = + static constexpr EffectCommand farEffects[] = { CMD_NONE, CMD_PORTAMENTOUP, @@ -257,24 +257,23 @@ bool CSoundFile::ReadFAR(FileReader &file, ModLoadingFlags loadFlags) { ModCommand &m = rowBase[chn]; - uint8 data[4]; - patternChunk.ReadArray(data); + const auto [note, instr, volume, effect] = patternChunk.ReadArray(); - if(data[0] > 0 && data[0] < 85) + if(note > 0 && note <= 72) { - m.note = data[0] + 35 + NOTE_MIN; - m.instr = data[1] + 1; + m.note = note + 35 + NOTE_MIN; + m.instr = instr + 1; } - if(m.note != NOTE_NONE || data[2] > 0) + if(m.note != NOTE_NONE || volume > 0) { m.volcmd = VOLCMD_VOLUME; - m.vol = (Clamp(data[2], uint8(1), uint8(16)) - 1u) * 4u; + m.vol = (Clamp(volume, uint8(1), uint8(16)) - 1u) * 4u; } - m.param = data[3] & 0x0F; + m.param = effect & 0x0F; - switch(data[3] >> 4) + switch(effect >> 4) { case 0x03: // Porta to note m.param <<= 2; @@ -297,7 +296,7 @@ bool CSoundFile::ReadFAR(FileReader &file, ModLoadingFlags loadFlags) m.param = 6 / (1 + m.param) + 1; m.param |= 0x0D; } - m.command = farEffects[data[3] >> 4]; + m.command = farEffects[effect >> 4]; } } @@ -328,7 +327,7 @@ bool CSoundFile::ReadFAR(FileReader &file, ModLoadingFlags loadFlags) m_nSamples = smp + 1; ModSample &sample = Samples[m_nSamples]; - mpt::String::Read(m_szNames[m_nSamples], sampleHeader.name); + m_szNames[m_nSamples] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name); sampleHeader.ConvertToMPT(sample); sampleHeader.GetSampleFormat().ReadSample(sample, file); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_gdm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_gdm.cpp index 33492ce04..7171d8b06 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_gdm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_gdm.cpp @@ -90,15 +90,15 @@ struct GDMSampleHeader MPT_BINARY_STRUCT(GDMSampleHeader, 62) -static const MODTYPE gdmFormatOrigin[] = +static constexpr MODTYPE gdmFormatOrigin[] = { MOD_TYPE_NONE, MOD_TYPE_MOD, MOD_TYPE_MTM, MOD_TYPE_S3M, MOD_TYPE_669, MOD_TYPE_FAR, MOD_TYPE_ULT, MOD_TYPE_STM, MOD_TYPE_MED, MOD_TYPE_PSM }; -static const MPT_UCHAR_TYPE gdmFormatOriginType[][4] = +static constexpr mpt::uchar gdmFormatOriginType[][4] = { UL_(""), UL_("mod"), UL_("mtm"), UL_("s3m"), UL_("669"), UL_("far"), UL_("ult"), UL_("stm"), UL_("med"), UL_("psm") }; -static const MPT_UCHAR_TYPE * const gdmFormatOriginFormat[] = +static constexpr const mpt::uchar * gdmFormatOriginFormat[] = { UL_(""), UL_("Generic MOD"), @@ -119,7 +119,7 @@ static bool ValidateHeader(const GDMFileHeader &fileHeader) || fileHeader.dosEOF[0] != 13 || fileHeader.dosEOF[1] != 10 || fileHeader.dosEOF[2] != 26 || std::memcmp(fileHeader.magic2, "GMFS", 4) || fileHeader.formatMajorVer != 1 || fileHeader.formatMinorVer != 0 - || fileHeader.originalFormat >= mpt::size(gdmFormatOrigin) + || fileHeader.originalFormat >= std::size(gdmFormatOrigin) || fileHeader.originalFormat == 0) { return false; @@ -169,18 +169,17 @@ bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.madeWithTracker = mpt::format(U_("BWSB 2GDM %1.%2"))(fileHeader.trackerMajorVer, fileHeader.formatMinorVer); m_modFormat.originalType = gdmFormatOriginType[fileHeader.originalFormat]; m_modFormat.originalFormatName = gdmFormatOriginFormat[fileHeader.originalFormat]; - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; // Song name - mpt::String::Read(m_songName, fileHeader.songTitle); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songTitle); // Artist name { - std::string artist; - mpt::String::Read(artist, fileHeader.songMusician); + std::string artist = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songMusician); if(artist != "Unknown") { - m_songArtist = mpt::ToUnicode(mpt::CharsetCP437, artist); + m_songArtist = mpt::ToUnicode(mpt::Charset::CP437, artist); } } @@ -236,10 +235,25 @@ bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) ModSample &sample = Samples[smp]; sample.Initialize(); - mpt::String::Read(m_szNames[smp], gdmSample.name); - mpt::String::Read(sample.filename, gdmSample.fileName); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, gdmSample.name); + sample.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, gdmSample.fileName); sample.nC5Speed = gdmSample.c4Hertz; + if(UseFinetuneAndTranspose()) + { + // Use the same inaccurate table as 2GDM for translating back to finetune, as our own routines + // give slightly different results for the provided sample rates that may result in transpose != 0. + static constexpr uint16 rate2finetune[] = { 8363, 8424, 8485, 8547, 8608, 8671, 8734, 8797, 7894, 7951, 8009, 8067, 8125, 8184, 8244, 8303 }; + for(uint8 i = 0; i < 16; i++) + { + if(sample.nC5Speed == rate2finetune[i]) + { + sample.nFineTune = MOD2XMFineTune(i); + break; + } + } + } + sample.nGlobalVol = 64; // Not supported in this format sample.nLength = gdmSample.length; // in bytes @@ -254,26 +268,12 @@ bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) sample.nLoopStart = gdmSample.loopBegin; sample.nLoopEnd = gdmSample.loopEnd - 1; - if(UseFinetuneAndTranspose()) - { - // Use the same inaccurate table as 2GDM for translating back to finetune, as our own routines - // give slightly different results for the provided sample rates that may result in transpose != 0. - static const uint16 rate2finetune[] = { 8363, 8424, 8485, 8547, 8608, 8671, 8734, 8797, 7894, 7951, 8009, 8067, 8125, 8184, 8244, 8303 }; - for(uint8 i = 0; i < 16; i++) - { - if(sample.nC5Speed == rate2finetune[i]) - { - sample.nFineTune = MOD2XMFineTune(i); - } - } - } - if(gdmSample.flags & GDMSampleHeader::smpLoop) sample.uFlags.set(CHN_LOOP); // Loop sample if(gdmSample.flags & GDMSampleHeader::smpVolume) { // Default volume is used... 0...64, 255 = no default volume - sample.nVolume = std::min(gdmSample.volume, 64) * 4; + sample.nVolume = std::min(static_cast(gdmSample.volume), uint8(64)) * 4; } else { sample.uFlags.set(SMP_NODEFAULTVOLUME); @@ -357,20 +357,18 @@ bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) if(channelByte & noteFlag) { // Note and sample follows - uint8 noteByte = chunk.ReadUint8(); - uint8 noteSample = chunk.ReadUint8(); + auto [note, instr] = chunk.ReadArray(); - if(noteByte) + if(note) { - noteByte = (noteByte & 0x7F) - 1; // This format doesn't have note cuts - if(noteByte < 0xF0) noteByte = (noteByte & 0x0F) + 12 * (noteByte >> 4) + 12 + NOTE_MIN; - m.note = noteByte; + note = (note & 0x7F) - 1; // High bit = no-retrig flag (notes with portamento have this set) + m.note = (note & 0x0F) + 12 * (note >> 4) + 12 + NOTE_MIN; if(!m.IsAmigaNote()) { onlyAmigaNotes = false; } } - m.instr = noteSample; + m.instr = instr; } if(channelByte & effectFlag) @@ -384,11 +382,11 @@ bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) // We may want to restore the old command in some cases. const ModCommand oldCmd = m; - uint8 effByte = chunk.ReadUint8(); - m.param = chunk.ReadUint8(); + const auto [effByte, param] = chunk.ReadArray(); + m.param = param; // Effect translation LUT - static const EffectCommand gdmEffTrans[] = + static constexpr EffectCommand gdmEffTrans[] = { CMD_NONE, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO, CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_TREMOLO, @@ -431,7 +429,7 @@ bool CSoundFile::ReadGDM(FileReader &file, ModLoadingFlags loadFlags) break; case CMD_VOLUME: - m.param = MIN(m.param, 64); + m.param = std::min(m.param, uint8(64)); if(modSpecs.HasVolCommand(VOLCMD_VOLUME)) { m.volcmd = VOLCMD_VOLUME; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp index 72da4c02d..23c72a15d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_imf.cpp @@ -110,7 +110,7 @@ struct IMFInstrument uint16 minTick = 0; // minimum tick value for next node for(uint32 n = 0; n < mptEnv.size(); n++) { - minTick = mptEnv[n].tick = std::max(minTick, nodes[e][n].tick); + mptEnv[n].tick = minTick = std::max(minTick, nodes[e][n].tick.get()); minTick++; mptEnv[n].value = static_cast(std::min(nodes[e][n].value >> shift, ENVELOPE_MAX)); } @@ -119,12 +119,12 @@ struct IMFInstrument // Convert an IMFInstrument to OpenMPT's internal instrument representation. void ConvertToMPT(ModInstrument &mptIns, SAMPLEINDEX firstSample) const { - mpt::String::Read(mptIns.name, name); + mptIns.name = mpt::String::ReadBuf(mpt::String::nullTerminated, name); if(smpNum) { - STATIC_ASSERT(CountOf(mptIns.Keyboard) >= CountOf(map)); - for(size_t note = 0; note < CountOf(map); note++) + static_assert(mpt::array_size::size >= mpt::array_size::size); + for(size_t note = 0; note < std::size(map); note++) { mptIns.Keyboard[note] = firstSample + map[note]; } @@ -175,7 +175,7 @@ struct IMFSample void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); - mpt::String::Read(mptSmp.filename, filename); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; @@ -202,7 +202,7 @@ struct IMFSample MPT_BINARY_STRUCT(IMFSample, 64) -static const EffectCommand imfEffects[] = +static constexpr EffectCommand imfEffects[] = { CMD_NONE, CMD_SPEED, // 0x01 1xx Set Tempo @@ -273,13 +273,13 @@ static void ImportIMFEffect(ModCommand &m) break; case 0xF: // set finetune // we don't implement this, but let's at least import the value - m.param = 0x20 | MIN(m.param >> 4, 0x0F); + m.param = 0x20 | std::min(static_cast(m.param >> 4), uint8(0x0F)); break; case 0x14: // fine slide up case 0x15: // fine slide down // this is about as close as we can do... if(m.param >> 4) - m.param = 0xF0 | MIN(m.param >> 4, 0x0F); + m.param = 0xF0 | std::min(static_cast(m.param >> 4), uint8(0x0F)); else m.param |= 0xE0; break; @@ -287,7 +287,7 @@ static void ImportIMFEffect(ModCommand &m) m.param = (0xFF - m.param) / 2u; break; case 0x1F: // set global volume - m.param = MIN(m.param << 1, 0xFF); + m.param = mpt::saturate_cast(m.param * 2); break; case 0x21: n = 0; @@ -435,7 +435,7 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) ChnSettings[chn].Reset(); ChnSettings[chn].nPan = fileHeader.channels[chn].panning * 256 / 255; - mpt::String::Read(ChnSettings[chn].szName, fileHeader.channels[chn].name); + ChnSettings[chn].szName = mpt::String::ReadBuf(mpt::String::nullTerminated, fileHeader.channels[chn].name); // TODO: reverb/chorus? switch(fileHeader.channels[chn].status) @@ -461,7 +461,7 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Imago Orpheus"); m_modFormat.type = U_("imf"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; //From mikmod: work around an Orpheus bug if(fileHeader.channels[0].status == 0) @@ -476,7 +476,7 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) } // Song Name - mpt::String::Read(m_songName, fileHeader.title); + m_songName = mpt::String::ReadBuf(mpt::String::nullTerminated, fileHeader.title); m_SongFlags.set(SONG_LINEARSLIDES, fileHeader.flags & IMFFileHeader::linearSlides); m_nDefaultSpeed = fileHeader.tempo; @@ -521,8 +521,9 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) if(mask & 0x20) { // Read note/instrument - m.note = patternChunk.ReadUint8(); - m.instr = patternChunk.ReadUint8(); + const auto [note, instr] = patternChunk.ReadArray(); + m.note = note; + m.instr = instr; if(m.note == 160) { @@ -542,20 +543,17 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) if((mask & 0xC0) == 0xC0) { // Read both effects and figure out what to do with them - uint8 e1c = patternChunk.ReadUint8(); // Command 1 - uint8 e1d = patternChunk.ReadUint8(); // Data 1 - uint8 e2c = patternChunk.ReadUint8(); // Command 2 - uint8 e2d = patternChunk.ReadUint8(); // Data 2 + const auto [e1c, e1d, e2c, e2d] = patternChunk.ReadArray(); // Command 1, Data 1, Command 2, Data 2 if(e1c == 0x0C) { - m.vol = MIN(e1d, 0x40); + m.vol = std::min(e1d, uint8(0x40)); m.volcmd = VOLCMD_VOLUME; m.command = e2c; m.param = e2d; } else if(e2c == 0x0C) { - m.vol = MIN(e2d, 0x40); + m.vol = std::min(e2d, uint8(0x40)); m.volcmd = VOLCMD_VOLUME; m.command = e1c; m.param = e1d; @@ -582,8 +580,9 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) } else if(mask & 0xC0) { // There's one effect, just stick it in the effect column - m.command = patternChunk.ReadUint8(); - m.param = patternChunk.ReadUint8(); + const auto [command, param] = patternChunk.ReadArray(); + m.command = command; + m.param = param; } if(m.command) ImportIMFEffect(m); @@ -625,7 +624,7 @@ bool CSoundFile::ReadIMF(FileReader &file, ModLoadingFlags loadFlags) ModSample &sample = Samples[smpID]; sampleHeader.ConvertToMPT(sample); - mpt::String::Copy(m_szNames[smpID], sample.filename); + m_szNames[smpID] = sample.filename; if(sampleHeader.length) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp index e149e1d2e..89f281f73 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_it.cpp @@ -26,7 +26,7 @@ #include "../common/mptFileIO.h" #endif // MODPLUG_NO_FILESAVE #include "plugins/PlugInterface.h" -#include "../common/mptBufferIO.h" +#include #include "../common/version.h" #include "ITTools.h" @@ -59,12 +59,12 @@ MPTM version history for cwtv-field in "IT" header (only for MPTM files!): #ifndef MODPLUG_NO_FILESAVE -static bool AreNonDefaultTuningsUsed(CSoundFile& sf) +static bool AreNonDefaultTuningsUsed(const CSoundFile& sf) { - const INSTRUMENTINDEX iCount = sf.GetNumInstruments(); - for(INSTRUMENTINDEX i = 1; i <= iCount; i++) + const INSTRUMENTINDEX numIns = sf.GetNumInstruments(); + for(INSTRUMENTINDEX i = 1; i <= numIns; i++) { - if(sf.Instruments[i] != nullptr && sf.Instruments[i]->pTuning != 0) + if(sf.Instruments[i] != nullptr && sf.Instruments[i]->pTuning != nullptr) return true; } return false; @@ -72,7 +72,7 @@ static bool AreNonDefaultTuningsUsed(CSoundFile& sf) static void WriteTuningCollection(std::ostream& oStrm, const CTuningCollection& tc) { - tc.Serialize(oStrm, "Tune specific tunings"); + tc.Serialize(oStrm, U_("Tune specific tunings")); } static void WriteTuningMap(std::ostream& oStrm, const CSoundFile& sf) @@ -113,7 +113,7 @@ static void WriteTuningMap(std::ostream& oStrm, const CSoundFile& sf) for(auto &iter : tNameToShort_Map) { if(iter.first) - mpt::IO::WriteSizedStringLE(oStrm, iter.first->GetName()); + mpt::IO::WriteSizedStringLE(oStrm, mpt::ToCharset(mpt::Charset::UTF8, iter.first->GetName())); else //Case: Using original IT tuning. mpt::IO::WriteSizedStringLE(oStrm, "->MPT_ORIGINAL_IT<-"); @@ -142,15 +142,16 @@ static void WriteTuningMap(std::ostream& oStrm, const CSoundFile& sf) #endif // MODPLUG_NO_FILESAVE -static void ReadTuningCollection(std::istream& iStrm, CTuningCollection& tc, const size_t) +static void ReadTuningCollection(std::istream &iStrm, CTuningCollection &tc, const std::size_t dummy, mpt::Charset defaultCharset) { - std::string name; - tc.Deserialize(iStrm, name); + MPT_UNREFERENCED_PARAMETER(dummy); + mpt::ustring name; + tc.Deserialize(iStrm, name, defaultCharset); } template -static bool ReadTuningMapTemplate(std::istream& iStrm, std::map& shortToTNameMap, const size_t maxNum = 500) +static bool ReadTuningMapTemplate(std::istream& iStrm, std::map &shortToTNameMap, mpt::Charset charset, const size_t maxNum = 500) { TUNNUMTYPE numTuning = 0; mpt::IO::ReadIntLE(iStrm, numTuning); @@ -165,7 +166,7 @@ static bool ReadTuningMapTemplate(std::istream& iStrm, std::map(iStrm, ui); - shortToTNameMap[ui] = temp; + shortToTNameMap[ui] = mpt::ToUnicode(charset, temp); } if(iStrm.good()) return false; @@ -174,19 +175,19 @@ static bool ReadTuningMapTemplate(std::istream& iStrm, std::map shortToTNameMap; + std::map shortToTNameMap; if(old) { - ReadTuningMapTemplate(iStrm, shortToTNameMap); + ReadTuningMapTemplate(iStrm, shortToTNameMap, charset); } else { - ReadTuningMapTemplate(iStrm, shortToTNameMap); + ReadTuningMapTemplate(iStrm, shortToTNameMap, charset); } // Read & set tunings for instruments - std::vector notFoundTunings; + std::vector notFoundTunings; for(INSTRUMENTINDEX i = 1; i<=csf.GetNumInstruments(); i++) { uint16 ui = 0; @@ -194,9 +195,9 @@ static void ReadTuningMapImpl(std::istream& iStrm, CSoundFile& csf, const size_t auto iter = shortToTNameMap.find(ui); if(csf.Instruments[i] && iter != shortToTNameMap.end()) { - const std::string str = iter->second; + const mpt::ustring str = iter->second; - if(str == "->MPT_ORIGINAL_IT<-") + if(str == U_("->MPT_ORIGINAL_IT<-")) { csf.Instruments[i]->pTuning = nullptr; continue; @@ -210,11 +211,12 @@ static void ReadTuningMapImpl(std::istream& iStrm, CSoundFile& csf, const size_t CTuning *localTuning = TrackerSettings::Instance().oldLocalTunings->GetTuning(str); if(localTuning) { - CTuning* pNewTuning = new CTuning(*localTuning); - if(!csf.GetTuneSpecificTunings().AddTuning(pNewTuning)) + std::unique_ptr pNewTuning = std::unique_ptr(new CTuning(*localTuning)); + CTuning *pT = csf.GetTuneSpecificTunings().AddTuning(std::move(pNewTuning)); + if(pT) { - csf.AddToLog("Local tunings are deprecated and no longer supported. Tuning '" + str + "' found in Local tunings has been copied to Tune-specific tunings and will be saved in the module file."); - csf.Instruments[i]->pTuning = pNewTuning; + csf.AddToLog(U_("Local tunings are deprecated and no longer supported. Tuning '") + str + U_("' found in Local tunings has been copied to Tune-specific tunings and will be saved in the module file.")); + csf.Instruments[i]->pTuning = pT; if(csf.GetpModDoc() != nullptr) { csf.GetpModDoc()->SetModified(); @@ -222,20 +224,20 @@ static void ReadTuningMapImpl(std::istream& iStrm, CSoundFile& csf, const size_t continue; } else { - delete pNewTuning; - csf.AddToLog("Copying Local tuning '" + str + "' to Tune-specific tunings failed."); + csf.AddToLog(U_("Copying Local tuning '") + str + U_("' to Tune-specific tunings failed.")); } } #endif - if(str == "12TET [[fs15 1.17.02.49]]" || str == "12TET") + if(str == U_("12TET [[fs15 1.17.02.49]]") || str == U_("12TET")) { - CTuning* pNewTuning = csf.CreateTuning12TET(str); - if(!csf.GetTuneSpecificTunings().AddTuning(pNewTuning)) + std::unique_ptr pNewTuning = csf.CreateTuning12TET(str); + CTuning *pT = csf.GetTuneSpecificTunings().AddTuning(std::move(pNewTuning)); + if(pT) { #ifdef MODPLUG_TRACKER - csf.AddToLog("Built-in tunings will no longer be used. Tuning '" + str + "' has been copied to Tune-specific tunings and will be saved in the module file."); - csf.Instruments[i]->pTuning = pNewTuning; + csf.AddToLog(U_("Built-in tunings will no longer be used. Tuning '") + str + U_("' has been copied to Tune-specific tunings and will be saved in the module file.")); + csf.Instruments[i]->pTuning = pT; if(csf.GetpModDoc() != nullptr) { csf.GetpModDoc()->SetModified(); @@ -244,9 +246,8 @@ static void ReadTuningMapImpl(std::istream& iStrm, CSoundFile& csf, const size_t continue; } else { - delete pNewTuning; #ifdef MODPLUG_TRACKER - csf.AddToLog("Copying Built-in tuning '" + str + "' to Tune-specific tunings failed."); + csf.AddToLog(U_("Copying Built-in tuning '") + str + U_("' to Tune-specific tunings failed.")); #endif } } @@ -255,7 +256,7 @@ static void ReadTuningMapImpl(std::istream& iStrm, CSoundFile& csf, const size_t if(std::find(notFoundTunings.begin(), notFoundTunings.end(), str) == notFoundTunings.end()) { notFoundTunings.push_back(str); - csf.AddToLog("Tuning '" + str + "' used by the module was not found."); + csf.AddToLog(U_("Tuning '") + str + U_("' used by the module was not found.")); #ifdef MODPLUG_TRACKER if(csf.GetpModDoc() != nullptr) { @@ -278,9 +279,9 @@ static void ReadTuningMapImpl(std::istream& iStrm, CSoundFile& csf, const size_t } -static void ReadTuningMap(std::istream& iStrm, CSoundFile& csf, const size_t dummy = 0) +static void ReadTuningMap(std::istream& iStrm, CSoundFile& csf, const size_t dummy, mpt::Charset charset) { - ReadTuningMapImpl(iStrm, csf, dummy, false); + ReadTuningMapImpl(iStrm, csf, charset, dummy, false); } @@ -485,32 +486,32 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) // OpenMPT 1.17.02.26 (r122) to 1.18 (raped IT format) // Exact version number will be determined later. interpretModPlugMade = true; - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 00, 00); + m_dwLastSavedWithVersion = MPT_V("1.17.00.00"); } else if(fileHeader.cwtv == 0x0217 && fileHeader.cmwt == 0x0200 && fileHeader.reserved == 0) { if(memchr(fileHeader.chnpan, 0xFF, sizeof(fileHeader.chnpan)) != nullptr) { // ModPlug Tracker 1.16 (semi-raped IT format) or BeRoTracker (will be determined later) - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 16, 00, 00); + m_dwLastSavedWithVersion = MPT_V("1.16.00.00"); madeWithTracker = U_("ModPlug Tracker 1.09 - 1.16"); } else { // OpenMPT 1.17 disguised as this in compatible mode, // but never writes 0xFF in the pan map for unused channels (which is an invalid value). - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 00, 00); + m_dwLastSavedWithVersion = MPT_V("1.17.00.00"); madeWithTracker = U_("OpenMPT 1.17 (compatibility export)"); } interpretModPlugMade = true; } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0202 && fileHeader.reserved == 0) { // ModPlug Tracker b3.3 - 1.09, instruments 557 bytes apart - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 09, 00, 00); + m_dwLastSavedWithVersion = MPT_V("1.09.00.00"); madeWithTracker = U_("ModPlug Tracker b3.3 - 1.09"); interpretModPlugMade = true; } else if(fileHeader.cwtv == 0x0300 && fileHeader.cmwt == 0x0300 && fileHeader.reserved == 0 && fileHeader.ordnum == 256 && fileHeader.sep == 128 && fileHeader.pwd == 0) { // A rare variant used from OpenMPT 1.17.02.20 (r113) to 1.17.02.25 (r121), found e.g. in xTr1m-SD.it - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 02, 20); + m_dwLastSavedWithVersion = MPT_V("1.17.02.20"); interpretModPlugMade = true; } } @@ -521,7 +522,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) m_SongFlags.set(SONG_ITCOMPATGXX, (fileHeader.flags & ITFileHeader::itCompatGxx) != 0); m_SongFlags.set(SONG_EXFILTERRANGE, (fileHeader.flags & ITFileHeader::extendedFilterRange) != 0); - mpt::String::Read(m_songName, fileHeader.songname); + m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songname); // Read row highlights if((fileHeader.special & ITFileHeader::embedPatternHighlights)) @@ -536,7 +537,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) // Luckily OpenMPT 1.17.03.02 should not be very wide-spread. // - In normal mode the time signature is always present in the song extensions anyway. So it's okay if we read // the signature here and maybe overwrite it later when parsing the song extensions. - if(!m_dwLastSavedWithVersion || m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 03, 02)) + if(!m_dwLastSavedWithVersion || m_dwLastSavedWithVersion >= MPT_V("1.17.03.02")) { m_nDefaultRowsPerBeat = fileHeader.highlight_minor; m_nDefaultRowsPerMeasure = fileHeader.highlight_major; @@ -545,10 +546,12 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) // Global Volume m_nDefaultGlobalVolume = fileHeader.globalvol << 1; - if(m_nDefaultGlobalVolume > MAX_GLOBAL_VOLUME) m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; - if(fileHeader.speed) m_nDefaultSpeed = fileHeader.speed; - m_nDefaultTempo.Set(std::max(31, fileHeader.tempo)); - m_nSamplePreAmp = std::min(fileHeader.mv, 128); + if(m_nDefaultGlobalVolume > MAX_GLOBAL_VOLUME) + m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; + if(fileHeader.speed) + m_nDefaultSpeed = fileHeader.speed; + m_nDefaultTempo.Set(std::max(uint8(31), static_cast(fileHeader.tempo))); + m_nSamplePreAmp = std::min(static_cast(fileHeader.mv), uint8(128)); // Reading Channels Pan Positions for(CHANNELINDEX i = 0; i < 64; i++) if(fileHeader.chnpan[i] != 0xFF) @@ -590,22 +593,25 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) // This is used for finding out whether the edit history is actually stored in the file or not, // as some early versions of Schism Tracker set the history flag, but didn't save anything. // We will consider the history invalid if it ends after the first parapointer. - uint32 minPtr = Util::MaxValueOfType(minPtr); + uint32 minPtr = std::numeric_limits::max(); for(uint32 pos : insPos) { - if(pos > 0 && pos < minPtr) minPtr = pos; + if(pos > 0 && pos < minPtr) + minPtr = pos; } for(uint32 pos : smpPos) { - if(pos > 0 && pos < minPtr) minPtr = pos; + if(pos > 0 && pos < minPtr) + minPtr = pos; } for(uint32 pos : patPos) { - if(pos > 0 && pos < minPtr) minPtr = pos; + if(pos > 0 && pos < minPtr) + minPtr = pos; } if(fileHeader.special & ITFileHeader::embedSongMessage) { - minPtr = std::min(minPtr, fileHeader.msgoffset); + minPtr = std::min(minPtr, fileHeader.msgoffset.get()); } const bool possiblyUNMO3 = fileHeader.cmwt == 0x0214 && (fileHeader.cwtv == 0x0214 || fileHeader.cwtv == 0) @@ -663,9 +669,9 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) if(possiblyUNMO3 && nflt == 0) { if(fileHeader.special & ITFileHeader::embedPatternHighlights) - madeWithTracker = U_("UNMO3 <= 2.4.0.1"); // Set together with MIDI macro embed flag + madeWithTracker = U_("UNMO3 <= 2.4.0.1"); // Set together with MIDI macro embed flag else - madeWithTracker = U_("UNMO3"); // Either 2.4.0.2+ or no MIDI macros embedded + madeWithTracker = U_("UNMO3"); // Either 2.4.0.2+ or no MIDI macros embedded } } else { @@ -741,7 +747,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) m_nInstruments = 0; if(fileHeader.flags & ITFileHeader::instrumentMode) { - m_nInstruments = std::min(fileHeader.insnum, MAX_INSTRUMENTS - 1); + m_nInstruments = std::min(static_cast(fileHeader.insnum), static_cast(MAX_INSTRUMENTS - 1)); } for(INSTRUMENTINDEX i = 0; i < GetNumInstruments(); i++) { @@ -769,7 +775,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) bool possibleXMconversion = false; // Reading Samples - m_nSamples = std::min(fileHeader.smpnum, MAX_SAMPLES - 1); + m_nSamples = std::min(static_cast(fileHeader.smpnum), static_cast(MAX_SAMPLES - 1)); bool lastSampleCompressed = false; for(SAMPLEINDEX i = 0; i < GetNumSamples(); i++) { @@ -780,7 +786,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) ModSample &sample = Samples[i + 1]; size_t sampleOffset = sampleHeader.ConvertToMPT(sample); - mpt::String::Read(m_szNames[i + 1], sampleHeader.name); + m_szNames[i + 1] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); if(!file.Seek(sampleOffset)) continue; @@ -825,8 +831,8 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) #if defined(MPT_EXTERNAL_SAMPLES) SetSamplePath(i + 1, mpt::PathString::FromUTF8(filenameU8)); #elif !defined(LIBOPENMPT_BUILD_TEST) - AddToLog(LogWarning, mpt::format(U_("Loading external sample %1 ('%2') failed: External samples are not supported."))(i + 1, mpt::ToUnicode(mpt::CharsetUTF8, filenameU8))); -#endif // MPT_EXTERNAL_SAMPLES + AddToLog(LogWarning, mpt::format(U_("Loading external sample %1 ('%2') failed: External samples are not supported."))(i + 1, mpt::ToUnicode(mpt::Charset::UTF8, filenameU8))); +#endif // MPT_EXTERNAL_SAMPLES } else { file.Skip(strLen); @@ -851,7 +857,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) if(vol != 0x40) possibleXMconversion = false; } - for(size_t i = 20; i < mpt::size(fileHeader.songname); i++) + for(size_t i = 20; i < std::size(fileHeader.songname); i++) { if(fileHeader.songname[i] != 0) possibleXMconversion = false; @@ -928,13 +934,17 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) } // Now we actually update the pattern-row entry the note,instrument etc. // Note - if(chnMask[ch] & 1) patternData.Skip(1); + if(chnMask[ch] & 1) + patternData.Skip(1); // Instrument - if(chnMask[ch] & 2) patternData.Skip(1); + if(chnMask[ch] & 2) + patternData.Skip(1); // Volume - if(chnMask[ch] & 4) patternData.Skip(1); + if(chnMask[ch] & 4) + patternData.Skip(1); // Effect - if(chnMask[ch] & 8) patternData.Skip(2); + if(chnMask[ch] & 8) + patternData.Skip(2); } lastSampleOffset = std::max(lastSampleOffset, file.GetPosition()); } @@ -998,7 +1008,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) if(!file.Skip(4) || !Patterns.Insert(pat, numRows)) continue; - + FileReader patternData = file.ReadChunk(len); // Now (after the Insert() call), we can read the pattern name. @@ -1063,7 +1073,8 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) if(chnMask[ch] & 1) // Note { uint8 note = patternData.ReadUint8(); - if(note < 0x80) note += NOTE_MIN; + if(note < 0x80) + note += NOTE_MIN; if(!(GetType() & MOD_TYPE_MPT)) { if(note > NOTE_MAX && note < 0xFD) note = NOTE_FADE; @@ -1105,7 +1116,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) m.volcmd = VOLCMD_VIBRATODEPTH; m.vol = vol - 203; // Old versions of ModPlug saved this as vibrato speed instead, so let's fix that. - if(m.vol && m_dwLastSavedWithVersion && m_dwLastSavedWithVersion <= MAKE_VERSION_NUMERIC(1, 17, 02, 54)) + if(m.vol && m_dwLastSavedWithVersion && m_dwLastSavedWithVersion <= MPT_V("1.17.02.54")) m.volcmd = VOLCMD_VIBRATOSPEED; } else // 213-222: Unused (was velocity) @@ -1117,8 +1128,9 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) // Reading command/param if(chnMask[ch] & 8) { - m.command = patternData.ReadUint8(); - m.param = patternData.ReadUint8(); + const auto [command, param] = patternData.ReadArray(); + m.command = command; + m.param = param; S3MConvert(m, true); // In some IT-compatible trackers, it is possible to input a parameter without a command. // In this case, we still need to update the last value memory. OpenMPT didn't do this until v1.25.01.07. @@ -1133,7 +1145,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) { // Up to OpenMPT 1.17.02.45 (r165), it was possible that the "last saved with" field was 0 // when saving a file in OpenMPT for the first time. - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 00, 00); + m_dwLastSavedWithVersion = MPT_V("1.17.00.00"); } if(m_dwLastSavedWithVersion && madeWithTracker.empty()) @@ -1165,15 +1177,16 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0200 && fileHeader.highlight_major == 0 && fileHeader.highlight_minor == 0 && fileHeader.reserved == 0) { // ModPlug Tracker 1.00a5, instruments 560 bytes apart - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 00, 00, A5); + m_dwLastSavedWithVersion = MPT_V("1.00.00.A5"); madeWithTracker = U_("ModPlug Tracker 1.00a5"); interpretModPlugMade = true; } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0214 && !memcmp(&fileHeader.reserved, "CHBI", 4)) { madeWithTracker = U_("ChibiTracker"); + m_playBehaviour.reset(kITShortSampleRetrig); } else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0214 && fileHeader.special <= 1 && fileHeader.pwd == 0 && fileHeader.reserved == 0 && (fileHeader.flags & (ITFileHeader::vol0Optimisations | ITFileHeader::instrumentMode | ITFileHeader::useMIDIPitchController | ITFileHeader::reqEmbeddedMIDIConfig | ITFileHeader::extendedFilterRange)) == ITFileHeader::instrumentMode - && m_nSamples > 0 && !strcmp(Samples[1].filename, "XXXXXXXX.YYY")) + && m_nSamples > 0 && (Samples[1].filename == "XXXXXXXX.YYY")) { madeWithTracker = U_("CheeseTracker"); } else if(fileHeader.cwtv == 0 && madeWithTracker.empty()) @@ -1244,7 +1257,7 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = (GetType() == MOD_TYPE_MPT) ? U_("OpenMPT MPTM") : mpt::format(U_("Impulse Tracker %1.%2"))(fileHeader.cmwt >> 8, mpt::ufmt::hex0<2>(fileHeader.cmwt & 0xFF)); m_modFormat.type = (GetType() == MOD_TYPE_MPT) ? U_("mptm") : U_("it"); m_modFormat.madeWithTracker = std::move(madeWithTracker); - m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::CharsetWindows1252 : mpt::CharsetCP437; + m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::Charset::Windows1252 : mpt::Charset::CP437; return true; } @@ -1252,17 +1265,21 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags) void CSoundFile::LoadMPTMProperties(FileReader &file, uint16 cwtv) { - mpt::istringstream iStrm(file.GetRawDataAsString()); + std::istringstream iStrm(file.GetRawDataAsString()); if(cwtv >= 0x88D) { srlztn::SsbRead ssb(iStrm); ssb.BeginRead("mptm", Version::Current().GetRawVersion()); - ssb.ReadItem(GetTuneSpecificTunings(), "0", &ReadTuningCollection); - ssb.ReadItem(*this, "1", &ReadTuningMap); + int8 useUTF8Tuning = 0; + ssb.ReadItem(useUTF8Tuning, "UTF8Tuning"); + mpt::Charset TuningCharset = useUTF8Tuning ? mpt::Charset::UTF8 : GetCharsetInternal(); + ssb.ReadItem(GetTuneSpecificTunings(), "0", [TuningCharset](std::istream &iStrm, CTuningCollection &tc, const std::size_t dummy){ return ReadTuningCollection(iStrm, tc, dummy, TuningCharset); }); + ssb.ReadItem(*this, "1", [TuningCharset](std::istream& iStrm, CSoundFile& csf, const std::size_t dummy){ return ReadTuningMap(iStrm, csf, dummy, TuningCharset); }); ssb.ReadItem(Order, "2", &ReadModSequenceOld); ssb.ReadItem(Patterns, FileIdPatterns, &ReadModPatterns); - ssb.ReadItem(Order, FileIdSequences, &ReadModSequences); + mpt::Charset sequenceDefaultCharset = GetCharsetInternal(); + ssb.ReadItem(Order, FileIdSequences, [sequenceDefaultCharset](std::istream &iStrm, ModSequenceSet &seq, std::size_t nSize){ return ReadModSequences(iStrm, seq, nSize, sequenceDefaultCharset); }); if(ssb.GetStatus() & srlztn::SNT_FAILURE) { @@ -1271,13 +1288,13 @@ void CSoundFile::LoadMPTMProperties(FileReader &file, uint16 cwtv) } else { // Loading for older files. - std::string name; - if(GetTuneSpecificTunings().Deserialize(iStrm, name) != Tuning::SerializationResult::Success) + mpt::ustring name; + if(GetTuneSpecificTunings().Deserialize(iStrm, name, GetCharsetInternal()) != Tuning::SerializationResult::Success) { AddToLog(LogError, U_("Loading tune specific tunings failed.")); } else { - ReadTuningMapImpl(iStrm, *this, 0, cwtv < 0x88C); + ReadTuningMapImpl(iStrm, *this, GetCharsetInternal(), 0, cwtv < 0x88C); } } } @@ -1359,7 +1376,7 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c MemsetZero(itHeader); dwChnNamLen = 0; memcpy(itHeader.id, "IMPM", 4); - mpt::String::Write(itHeader.songname, m_songName); + mpt::String::WriteBuf(mpt::String::nullTerminated, itHeader.songname) = m_songName; itHeader.highlight_minor = (uint8)std::min(m_nDefaultRowsPerBeat, ROWINDEX(uint8_max)); itHeader.highlight_major = (uint8)std::min(m_nDefaultRowsPerMeasure, ROWINDEX(uint8_max)); @@ -1377,7 +1394,8 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c // An additional "---" pattern is appended so Impulse Tracker won't ignore the last order item. // Interestingly, this can exceed IT's 256 order limit. Also, IT will always save at least two orders. itHeader.ordnum = std::min(Order().GetLengthTailTrimmed(), specs.ordersMax) + 1; - if(itHeader.ordnum < 2) itHeader.ordnum = 2; + if(itHeader.ordnum < 2) + itHeader.ordnum = 2; } itHeader.insnum = std::min(m_nInstruments, specs.instrumentsMax); @@ -1436,7 +1454,7 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c { if(Instruments[ins] != nullptr && Instruments[ins]->midiPWD != 0) { - itHeader.pwd = static_cast(mpt::abs(Instruments[ins]->midiPWD)); + itHeader.pwd = static_cast(std::abs(Instruments[ins]->midiPWD)); break; } } @@ -1528,7 +1546,7 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c for(PATTERNINDEX pat = 0; pat < numNamedPats; pat++) { char name[MAX_PATTERNNAME]; - mpt::String::Write(name, Patterns[pat].GetName()); + mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = Patterns[pat].GetName(); mpt::IO::Write(f, name); } } @@ -1542,7 +1560,7 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c for(uint32 inam = 0; inam < nChnNames; inam++) { char name[MAX_CHANNELNAME]; - mpt::String::Write(name, ChnSettings[inam].szName); + mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = ChnSettings[inam].szName; mpt::IO::Write(f, name); } } @@ -1792,7 +1810,7 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c itss.ConvertToIT(sample, GetType(), compress, itHeader.cmwt >= 0x215, GetType() == MOD_TYPE_MPT); const bool isExternal = itss.cvt == ITSample::cvtExternalSample; - mpt::String::Write(itss.name, m_szNames[smp]); + mpt::String::WriteBuf(mpt::String::nullTerminated, itss.name) = m_szNames[smp]; itss.samplepointer = static_cast(dwPos); if(dwPos > uint32_max) @@ -1813,7 +1831,7 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c mpt::IO::SeekAbsolute(f, dwPos); if(!isExternal) { - if(sample.nLength > smpLength) + if(sample.nLength > smpLength && smpLength != 0) { // Sample length does not fit into IT header! AddToLog(mpt::format("Truncating sample %1: Length exceeds exceeds 4 gigasamples.")(smp)); @@ -1865,10 +1883,12 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c mpt::IO::SeekEnd(f); const mpt::IO::Offset MPTStartPos = mpt::IO::TellWrite(f); - + srlztn::SsbWrite ssb(f); ssb.BeginWrite("mptm", Version::Current().GetRawVersion()); + if(GetTuneSpecificTunings().GetNumTunings() > 0 || AreNonDefaultTuningsUsed(*this)) + ssb.WriteItem(int8(1), "UTF8Tuning"); if(GetTuneSpecificTunings().GetNumTunings() > 0) ssb.WriteItem(GetTuneSpecificTunings(), "0", &WriteTuningCollection); if(AreNonDefaultTuningsUsed(*this)) @@ -1959,13 +1979,13 @@ uint32 CSoundFile::SaveMixPlugins(std::ostream *file, bool updatePlugData) // Dry/Wet ratio mpt::IO::WriteRaw(f, "DWRT", 4); // DWRT chunk does not include a size, so better make sure we always write 4 bytes here. - STATIC_ASSERT(sizeof(IEEE754binary32LE) == 4); + static_assert(sizeof(IEEE754binary32LE) == 4); mpt::IO::Write(f, IEEE754binary32LE(m_MixPlugins[i].fDryRatio)); // Default program mpt::IO::WriteRaw(f, "PROG", 4); // PROG chunk does not include a size, so better make sure we always write 4 bytes here. - STATIC_ASSERT(sizeof(m_MixPlugins[i].defaultProgram) == sizeof(int32)); + static_assert(sizeof(m_MixPlugins[i].defaultProgram) == sizeof(int32)); mpt::IO::WriteIntLE(f, m_MixPlugins[i].defaultProgram); // Please, if you add any more chunks here, don't repeat history (see above) and *do* add a size field for your chunk, mmmkay? @@ -2013,11 +2033,11 @@ bool CSoundFile::LoadMixPlugins(FileReader &file) char code[4]; file.ReadArray(code); const uint32 chunkSize = file.ReadUint32LE(); - if(!memcmp(code, "IMPI", 4) // IT instrument, we definitely read too far - || !memcmp(code, "IMPS", 4) // IT sample, ditto - || !memcmp(code, "XTPM", 4) // Instrument extensions, ditto - || !memcmp(code, "STPM", 4) // Song extensions, ditto - || !file.CanRead(chunkSize)) + if(!memcmp(code, "IMPI", 4) // IT instrument, we definitely read too far + || !memcmp(code, "IMPS", 4) // IT sample, ditto + || !memcmp(code, "XTPM", 4) // Instrument extensions, ditto + || !memcmp(code, "STPM", 4) // Song extensions, ditto + || !file.CanRead(chunkSize)) { file.SkipBack(8); return isBeRoTracker; @@ -2027,9 +2047,9 @@ bool CSoundFile::LoadMixPlugins(FileReader &file) // Channel FX if(!memcmp(code, "CHFX", 4)) { - for (size_t ch = 0; ch < MAX_BASECHANNELS; ch++) + for(auto &chn : ChnSettings) { - ChnSettings[ch].nMixPlugin = static_cast(chunk.ReadUint32LE()); + chn.nMixPlugin = static_cast(chunk.ReadUint32LE()); } #ifndef NO_PLUGINS } @@ -2061,8 +2081,8 @@ void CSoundFile::ReadMixPluginChunk(FileReader &file, SNDMIXPLUGIN &plugin) { // MPT's standard plugin data. Size not specified in file.. grrr.. file.ReadStruct(plugin.Info); - mpt::String::SetNullTerminator(plugin.Info.szName); - mpt::String::SetNullTerminator(plugin.Info.szLibraryName); + mpt::String::SetNullTerminator(plugin.Info.szName.buf); + mpt::String::SetNullTerminator(plugin.Info.szLibraryName.buf); plugin.editorX = plugin.editorY = int32_min; // Plugin user data @@ -2215,7 +2235,7 @@ void CSoundFile::SaveExtendedSongProperties(std::ostream &f) const // Write one chunk for every sample. // Rationale: chunks are limited to 65536 bytes, which can easily be reached // with the amount of samples that OpenMPT supports. - WRITEMODULARHEADER(MagicLE("CUES"), 2 + CountOf(sample.cues) * 4); + WRITEMODULARHEADER(MagicLE("CUES"), static_cast(2 + std::size(sample.cues) * 4)); mpt::IO::WriteIntLE(f, smp); for(auto cue : sample.cues) { @@ -2228,7 +2248,7 @@ void CSoundFile::SaveExtendedSongProperties(std::ostream &f) const // Tempo Swing Factors if(!m_tempoSwing.empty()) { - mpt::ostringstream oStrm; + std::ostringstream oStrm; TempoSwing::Serialize(oStrm, m_tempoSwing); std::string data = oStrm.str(); uint16 length = mpt::saturate_cast(data.size()); @@ -2256,7 +2276,7 @@ void CSoundFile::SaveExtendedSongProperties(std::ostream &f) const if(!m_songArtist.empty() && specs.hasArtistName) { - std::string songArtistU8 = mpt::ToCharset(mpt::CharsetUTF8, m_songArtist); + std::string songArtistU8 = mpt::ToCharset(mpt::Charset::UTF8, m_songArtist); uint16 length = mpt::saturate_cast(songArtistU8.length()); WRITEMODULARHEADER(MagicLE("AUTH"), length); mpt::IO::WriteRaw(f, songArtistU8.c_str(), length); @@ -2296,7 +2316,7 @@ void ReadField(FileReader &chunk, std::size_t size, T &field) template void ReadFieldCast(FileReader &chunk, std::size_t size, T &field) { - STATIC_ASSERT(sizeof(T) <= sizeof(int32)); + static_assert(sizeof(T) <= sizeof(int32)); field = static_cast(chunk.ReadSizedIntLE(size)); } @@ -2359,19 +2379,19 @@ void CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannel { std::string artist; chunk.ReadString(artist, chunk.GetLength()); - m_songArtist = mpt::ToUnicode(mpt::CharsetUTF8, artist); + m_songArtist = mpt::ToUnicode(mpt::Charset::UTF8, artist); } break; case MagicBE("ChnS"): // Channel settings for channels 65+ if(size <= (MAX_BASECHANNELS - 64) * 2 && (size % 2u) == 0) { - STATIC_ASSERT(CountOf(ChnSettings) >= 64); - const CHANNELINDEX loopLimit = std::min(uint16(64 + size / 2), uint16(CountOf(ChnSettings))); + static_assert(CountOf(ChnSettings) >= 64); + const CHANNELINDEX loopLimit = std::min(uint16(64 + size / 2), uint16(std::size(ChnSettings))); for(CHANNELINDEX chn = 64; chn < loopLimit; chn++) { - uint8 pan = chunk.ReadUint8(), vol = chunk.ReadUint8(); + auto [pan, vol] = chunk.ReadArray(); if(pan != 0xFF) { ChnSettings[chn].nVolume = vol; @@ -2406,7 +2426,7 @@ void CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannel // Tempo Swing Factors if(size > 2) { - mpt::istringstream iStrm(chunk.ReadRawDataAsString()); + std::istringstream iStrm(chunk.ReadRawDataAsString()); TempoSwing::Deserialize(iStrm, m_tempoSwing, chunk.GetLength()); } break; @@ -2430,12 +2450,10 @@ void CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannel } break; } - } // Validate read values. Limit(m_nDefaultTempo, GetModSpecifications().GetTempoMin(), GetModSpecifications().GetTempoMax()); - if(m_nDefaultRowsPerMeasure < m_nDefaultRowsPerBeat) m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat; if(m_nTempoMode >= tempoModeMax) m_nTempoMode = tempoModeClassic; if(m_nMixLevels >= mixLevelsMax) m_nMixLevels = mixLevelsOriginal; //m_dwCreatedWithVersion diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_itp.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_itp.cpp index 7c4f982b6..db85fee72 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_itp.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_itp.cpp @@ -47,7 +47,7 @@ struct ITPModCommand operator ModCommand() const { ModCommand result; - result.note = (ModCommand::IsNote(note) || ModCommand::IsSpecialNote(note)) ? note : NOTE_NONE; + result.note = (ModCommand::IsNote(note) || ModCommand::IsSpecialNote(note)) ? static_cast(note.get()) : static_cast(NOTE_NONE); result.instr = instr; result.command = (command < MAX_EFFECTS) ? static_cast(command.get()) : CMD_NONE; result.volcmd = (volcmd < MAX_VOLCMDS) ? static_cast(volcmd.get()) : VOLCMD_NONE; @@ -237,9 +237,9 @@ bool CSoundFile::ReadITP(FileReader &file, ModLoadingFlags loadFlags) instrPaths[ins] = mpt::PathString::FromUTF8(path); } #ifdef MODPLUG_TRACKER - if(!file.GetFileName().empty()) + if(const auto fileName = file.GetFileName(); !fileName.empty()) { - instrPaths[ins] = instrPaths[ins].RelativePathToAbsolute(file.GetFileName().GetPath()); + instrPaths[ins] = instrPaths[ins].RelativePathToAbsolute(fileName.GetPath()); } else if(GetpModDoc() != nullptr) { instrPaths[ins] = instrPaths[ins].RelativePathToAbsolute(GetpModDoc()->GetPathNameMpt().GetPath()); @@ -327,7 +327,7 @@ bool CSoundFile::ReadITP(FileReader &file, ModLoadingFlags loadFlags) && !memcmp(sampleHeader.id, "IMPS", 4)) { sampleHeader.ConvertToMPT(Samples[realSample]); - mpt::String::Read(m_szNames[realSample], sampleHeader.name); + m_szNames[realSample] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name); // Read sample data sampleHeader.GetSampleFormat().ReadSample(Samples[realSample], sampleData); @@ -341,7 +341,7 @@ bool CSoundFile::ReadITP(FileReader &file, ModLoadingFlags loadFlags) continue; #ifdef MPT_EXTERNAL_SAMPLES - InputFile f(instrPaths[ins]); + InputFile f(instrPaths[ins], SettingCacheCompleteFileBeforeLoading()); FileReader instrFile = GetFileReader(f); if(!ReadInstrumentFromFile(ins + 1, instrFile, true)) { @@ -390,7 +390,7 @@ bool CSoundFile::ReadITP(FileReader &file, ModLoadingFlags loadFlags) m_nMinPeriod = 8; // Before OpenMPT 1.20.01.09, the MIDI macros were always read from the file, even if the "embed" flag was not set. - if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1,20,01,09) && !(songFlags & ITP_EMBEDMIDICFG)) + if(m_dwLastSavedWithVersion >= MPT_V("1.20.01.09") && !(songFlags & ITP_EMBEDMIDICFG)) { m_MidiCfg.Reset(); } @@ -398,7 +398,7 @@ bool CSoundFile::ReadITP(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Impulse Tracker Project"); m_modFormat.type = U_("itp"); m_modFormat.madeWithTracker = U_("OpenMPT ") + mpt::ufmt::val(m_dwLastSavedWithVersion); - m_modFormat.charset = mpt::CharsetWindows1252; + m_modFormat.charset = mpt::Charset::Windows1252; return true; #endif // MPT_EXTERNAL_SAMPLES diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp index 6401aa334..e383294b3 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mdl.cpp @@ -154,9 +154,9 @@ enum }; -static const VibratoType MDLVibratoType[] = { VIB_SINE, VIB_RAMP_DOWN, VIB_SQUARE, VIB_SINE }; +static constexpr VibratoType MDLVibratoType[] = { VIB_SINE, VIB_RAMP_DOWN, VIB_SQUARE, VIB_SINE }; -static const ModCommand::COMMAND MDLEffTrans[] = +static constexpr ModCommand::COMMAND MDLEffTrans[] = { /* 0 */ CMD_NONE, /* 1st column only */ @@ -187,7 +187,7 @@ static const ModCommand::COMMAND MDLEffTrans[] = // receive an MDL effect, give back a 'normal' one. -static void ConvertMDLCommand(uint8_t &cmd, uint8_t ¶m) +static void ConvertMDLCommand(uint8 &cmd, uint8 ¶m) { if(cmd >= CountOf(MDLEffTrans)) return; @@ -224,11 +224,11 @@ static void ConvertMDLCommand(uint8_t &cmd, uint8_t ¶m) break; case 0x1: // Pan Slide Left cmd = CMD_PANNINGSLIDE; - param = (std::min(param & 0x0F, 0x0E) << 4) | 0x0F; + param = (std::min(static_cast(param & 0x0F), uint8(0x0E)) << 4) | 0x0F; break; case 0x2: // Pan Slide Right cmd = CMD_PANNINGSLIDE; - param = 0xF0 | std::min(param & 0x0F, 0x0E); + param = 0xF0 | std::min(static_cast(param & 0x0F), uint8(0x0E)); break; case 0x4: // Vibrato Waveform param = 0x30 | (param & 0x0F); @@ -361,7 +361,7 @@ static bool ImportMDLCommands(ModCommand &m, uint8 vol, uint8 e1, uint8 e2, uint e1 = CMD_NONE; } else if(!vol) { - lostCommand |= !ModCommand::TwoRegularCommandsToMPT(e1, p1, e2, p2); + lostCommand |= (ModCommand::TwoRegularCommandsToMPT(e1, p1, e2, p2).first != CMD_NONE); m.volcmd = e1; m.vol = p1; } else @@ -473,14 +473,10 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) : (fileHeader.version == 0x10) ? U_("2.3") : (fileHeader.version == 0x00) ? U_("2.0 - 2.2b") // there was no 1.x release : U_("")); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; - mpt::String::Read(m_songName, info.title); - { - std::string artist; - mpt::String::Read(artist, info.composer); - m_songArtist = mpt::ToUnicode(mpt::CharsetCP437, artist); - } + m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, info.title); + m_songArtist = mpt::ToUnicode(mpt::Charset::CP437, mpt::String::ReadBuf(mpt::String::spacePadded, info.composer)); m_nDefaultGlobalVolume = info.globalVol + 1; m_nDefaultSpeed = Clamp(info.speed, 1, 255); @@ -582,8 +578,7 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) uint8 numInstruments = chunk.ReadUint8(); for(uint8 i = 0; i < numInstruments; i++) { - uint8 ins = chunk.ReadUint8(); - uint8 numSamples = chunk.ReadUint8(); + const auto [ins, numSamples] = chunk.ReadArray(); uint8 firstNote = 0; ModInstrument *mptIns = nullptr; if(ins == 0 @@ -595,27 +590,14 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) } chunk.ReadString(mptIns->name, 32); - while(numSamples--) + for(uint8 smp = 0; smp < numSamples; smp++) { MDLSampleHeader sampleHeader; chunk.ReadStruct(sampleHeader); - if(sampleHeader.smpNum == 0) + if(sampleHeader.smpNum == 0 || sampleHeader.smpNum > GetNumSamples()) continue; - #if 1 - #if MPT_GCC_BEFORE(6,1,0) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wtype-limits" - #endif - STATIC_ASSERT((mpt::limits::max)() < MAX_SAMPLES); - #if MPT_GCC_BEFORE(6,1,0) - #pragma GCC diagnostic pop - #endif - #else - MPT_MAYBE_CONSTANT_IF(sampleHeader.smpNum >= MAX_SAMPLES) - continue; - #endif - LimitMax(sampleHeader.lastNote, static_cast(CountOf(mptIns->Keyboard))); + LimitMax(sampleHeader.lastNote, static_cast(std::size(mptIns->Keyboard))); for(uint8 n = firstNote; n <= sampleHeader.lastNote; n++) { mptIns->Keyboard[n] = sampleHeader.smpNum; @@ -650,7 +632,7 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) mptSmp.nVolume = sampleHeader.volume; else mptSmp.uFlags.set(SMP_NODEFAULTVOLUME); - mptSmp.nPan = std::min(sampleHeader.panning * 2, 254); + mptSmp.nPan = std::min(static_cast(sampleHeader.panning * 2), uint16(254)); mptSmp.nVibType = MDLVibratoType[sampleHeader.vibType & 3]; mptSmp.nVibSweep = sampleHeader.vibSweep; mptSmp.nVibDepth = sampleHeader.vibDepth; @@ -703,14 +685,14 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) { CHANNELINDEX numChans = 32; ROWINDEX numRows = 64; - char name[17] = ""; + std::string name; if(fileHeader.version >= 0x10) { MDLPatternHeader patHead; chunk.ReadStruct(patHead); numChans = patHead.channels; numRows = patHead.lastRow + 1; - mpt::String::Read(name, patHead.name); + name = mpt::String::ReadBuf(mpt::String::spacePadded, patHead.name); } if(!Patterns.Insert(pat, numRows)) @@ -766,7 +748,7 @@ bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags) if(x & MDLNOTE_NOTE) { b = track.ReadUint8(); - m->note = (b > 120) ? NOTE_KEYOFF : b; + m->note = (b > 120) ? static_cast(NOTE_KEYOFF) : static_cast(b); } if(x & MDLNOTE_SAMPLE) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp index 64d1ec257..ac1e88940 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_med.cpp @@ -2,502 +2,626 @@ * Load_med.cpp * ------------ * Purpose: OctaMED / MED Soundstudio module loader - * Notes : (currently none) - * Authors: Olivier Lapicque - * OpenMPT Devs + * Notes : Support for synthesized instruments is still missing. + * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ - #include "stdafx.h" #include "Loaders.h" -#include "../common/mptStringBuffer.h" + +#ifndef NO_VST +#include "../mptrack/Vstplug.h" +#include "plugins/PluginManager.h" +#endif + +#include OPENMPT_NAMESPACE_BEGIN -//#define MED_LOG - -#define MED_MAX_COMMENT_LENGTH 5*1024 //: Is 5 kB enough? - -// flags -#define MMD_FLAG_FILTERON 0x1 -#define MMD_FLAG_JUMPINGON 0x2 -#define MMD_FLAG_JUMP8TH 0x4 -#define MMD_FLAG_INSTRSATT 0x8 // instruments are attached (this is a module) -#define MMD_FLAG_VOLHEX 0x10 -#define MMD_FLAG_STSLIDE 0x20 // SoundTracker mode for slides -#define MMD_FLAG_8CHANNEL 0x40 // OctaMED 8 channel song -#define MMD_FLAG_SLOWHQ 0x80 // HQ slows playing speed (V2-V4 compatibility) -// flags2 -#define MMD_FLAG2_BMASK 0x1F -#define MMD_FLAG2_BPM 0x20 -#define MMD_FLAG2_MIX 0x80 // uses Mixing (V7+) -// flags3: -#define MMD_FLAG3_STEREO 0x1 // mixing in Stereo mode -#define MMD_FLAG3_FREEPAN 0x2 // free panning -#define MMD_FLAG3_GM 0x4 // module designed for GM/XG compatibility - - -// generic MMD tags -#define MMDTAG_END 0 -#define MMDTAG_PTR 0x80000000 // data needs relocation -#define MMDTAG_MUSTKNOW 0x40000000 // loader must fail if this isn't recognized -#define MMDTAG_MUSTWARN 0x20000000 // loader must warn if this isn't recognized - -// ExpData tags -// # of effect groups, including the global group (will -// override settings in MMDSong struct), default = 1 -#define MMDTAG_EXP_NUMFXGROUPS 1 -#define MMDTAG_TRK_NAME (MMDTAG_PTR|1) // trackinfo tags -#define MMDTAG_TRK_NAMELEN 2 // namelen includes zero term. -#define MMDTAG_TRK_FXGROUP 3 -// effectinfo tags -#define MMDTAG_FX_ECHOTYPE 1 -#define MMDTAG_FX_ECHOLEN 2 -#define MMDTAG_FX_ECHODEPTH 3 -#define MMDTAG_FX_STEREOSEP 4 -#define MMDTAG_FX_GROUPNAME (MMDTAG_PTR|5) // the Global Effects group shouldn't have name saved! -#define MMDTAG_FX_GRPNAMELEN 6 // namelen includes zero term. - - -struct MEDMODULEHEADER +struct MMD0FileHeader { - char id[4]; // MMD1-MMD3 - uint32be modlen; // Size of file - uint32be song; // Position in file for this song - uint16be psecnum; - uint16be pseq; - uint32be blockarr; // Position in file for blocks - uint32be mmdflags; - uint32be smplarr; // Position in file for samples - uint32be reserved; - uint32be expdata; // Absolute offset in file for ExpData (0 if not present) + char mmd[3]; // "MMD" for the first song in file, "MCN" for the rest + uint8be version; // '0'-'3' + uint32be modLength; // Size of file + uint32be songOffset; // Position in file for the first song + uint16be playerSettings1[2]; // Internal variables for the play routine + uint32be blockArrOffset; // Position in file for blocks (patterns) + uint8be flags; + uint8be reserved1[3]; + uint32be sampleArrOffset; // Position in file for samples (should be identical between songs) uint32be reserved2; - uint16be pstate; - uint16be pblock; - uint16be pline; - uint16be pseqnum; - uint16be actplayline; - uint8be counter; - uint8be extra_songs; // # of songs - 1 + uint32be expDataOffset; // Absolute offset in file for ExpData (0 if not present) + uint32be reserved3; + char playerSettings2[11]; // Internal variables for the play routine + uint8be extraSongs; // Number of songs - 1 }; -MPT_BINARY_STRUCT(MEDMODULEHEADER, 52) +MPT_BINARY_STRUCT(MMD0FileHeader, 52) -struct MMD0SAMPLE +struct MMD0Sample { - uint16be rep, replen; - uint8be midich; - uint8be midipreset; - uint8be svol; - int8be strans; + uint16be loopStart; + uint16be loopLength; + uint8be midiChannel; + uint8be midiPreset; + uint8be sampleVolume; + int8be sampleTranspose; }; -MPT_BINARY_STRUCT(MMD0SAMPLE, 8) +MPT_BINARY_STRUCT(MMD0Sample, 8) -// Sample header is immediately followed by sample data... -struct MMDSAMPLEHEADER +// Song header for MMD0/MMD1 +struct MMD0Song { - uint32be length; // length of *one* *unpacked* channel in *bytes* - uint16be type; - // if non-negative - // bits 0-3 reserved for multi-octave instruments, not supported on the PC - // 0x10: 16 bit (otherwise 8 bit) - // 0x20: Stereo (otherwise mono) - // 0x40: Uses DeltaCode - // 0x80: Packed data - // -1: Synth - // -2: Hybrid - // if type indicates packed data, these fields follow, otherwise we go right to the data - uint16be packtype; // Only 1 = ADPCM is supported - uint16be subtype; // Packing subtype - // ADPCM subtype - // 1: g723_40 - // 2: g721 - // 3: g723_24 - uint8be commonflags; // flags common to all packtypes (none defined so far) - uint8be packerflags; // flags for the specific packtype - uint32be leftchlen; // packed length of left channel in bytes - uint32be rightchlen; // packed length of right channel in bytes (ONLY PRESENT IN STEREO SAMPLES) - uint8be SampleData[1]; // Sample Data + uint8be sequence[256]; }; -MPT_BINARY_STRUCT(MMDSAMPLEHEADER, 21) +MPT_BINARY_STRUCT(MMD0Song, 256) -// MMD0/MMD1 song header -struct MMD0SONGHEADER +// Song header for MMD2/MMD3 +struct MMD2Song { - MMD0SAMPLE sample[63]; - uint16be numblocks; // # of blocks - uint16be songlen; // # of entries used in playseq - uint8be playseq[256]; // Play sequence - uint16be deftempo; // BPM tempo - int8be playtransp; // Play transpose - uint8be flags; // 0x10: Hex Volumes | 0x20: ST/NT/PT Slides | 0x40: 8 Channels song - uint8be flags2; // [b4-b0]+1: Tempo LPB, 0x20: tempo mode, 0x80: mix_conv=on - uint8be tempo2; // tempo TPL - uint8be trkvol[16]; // track volumes - uint8be mastervol; // master volume - uint8be numsamples; // # of samples (max=63) + enum Flags3 + { + FLAG3_STEREO = 0x01, // Mixing in stereo + FLAG3_FREEPAN = 0x02, // Mixing flag: free pan + }; + + uint32be playSeqTableOffset; + uint32be sectionTableOffset; + uint32be trackVolsOffset; + uint16be numTracks; + uint16be numPlaySeqs; + uint32be trackPanOffset; // 0: all centered (according to docs, MED Soundstudio uses Amiga hard-panning instead) + uint32be flags3; + uint16be volAdjust; // Volume adjust (%) + uint16be mixChannels; // Mixing channels, 0 means 4 + uint8be mixEchoType; // 0 = nothing, 1 = normal, 2 = cross + uint8be mixEchoDepth; // 1 - 6, 0 = default + uint16be mixEchoLength; // Echo length in milliseconds + int8be mixStereoSep; // Stereo separation + char pad0[223]; }; -MPT_BINARY_STRUCT(MMD0SONGHEADER, 788) +MPT_BINARY_STRUCT(MMD2Song, 256) -// MMD2/MMD3 song header -struct MMD2SONGHEADER +// Common song header +struct MMDSong { - MMD0SAMPLE sample[63]; - uint16be numblocks; // # of blocks - uint16be numsections; // # of sections - uint32be playseqtable; // filepos of play sequence - uint32be sectiontable; // filepos of sections table (uint16_be array) - uint32be trackvols; // filepos of tracks volume (uint8_be array) - uint16be numtracks; // # of tracks (max 64) - uint16be numpseqs; // # of play sequences - uint32be trackpans; // filepos of tracks pan values (uint8_be array) - int32be flags3; // 0x1:stereo_mix, 0x2:free_panning, 0x4:GM/XG compatibility - uint16be voladj; // vol_adjust (set to 100 if 0) - uint16be channels; // # of channels (4 if =0) - uint8be mix_echotype; // 1:normal,2:xecho - uint8be mix_echodepth; // 1..6 - uint16be mix_echolen; // > 0 - int8be mix_stereosep; // -4..4 - uint8be pad0[223]; - uint16be deftempo; // BPM tempo - int8be playtransp; // play transpose - uint8be flags; // 0x1:filteron, 0x2:jumpingon, 0x4:jump8th, 0x8:instr_attached, 0x10:hex_vol, 0x20:PT_slides, 0x40:8ch_conv,0x80:hq slows playing speed - uint8be flags2; // 0x80:mix_conv=on, [b4-b0]+1:tempo LPB, 0x20:tempo_mode - uint8be tempo2; // tempo TPL - uint8be pad1[16]; - uint8be mastervol; // master volume - uint8be numsamples; // # of samples (max 63) + enum Flags + { + FLAG_FILTERON = 0x01, // The hardware audio filter is on + FLAG_JUMPINGON = 0x02, // Mouse pointer jumping on + FLAG_JUMP8TH = 0x04, // ump every 8th line (not in OctaMED Pro) + FLAG_INSTRSATT = 0x08, // sng+samples indicator (not useful in MMDs) + FLAG_VOLHEX = 0x10, // volumes are HEX + FLAG_STSLIDE = 0x20, // use ST/NT/PT compatible sliding + FLAG_8CHANNEL = 0x40, // this is OctaMED 5-8 channel song + FLAG_SLOWHQ = 0x80, // HQ V2-4 compatibility mode + }; + + enum Flags2 + { + FLAG2_BMASK = 0x1F, // (bits 0-4) BPM beat length (in lines) + FLAG2_BPM = 0x20, // BPM mode on + FLAG2_MIX = 0x80, // Module uses mixing + }; + + uint16be numBlocks; // Number of blocks in current song + uint16be songLength; // MMD0: Number of sequence numbers in the play sequence list, MMD2: Number of sections + char song[256]; + MMD0Song GetMMD0Song() const + { + static_assert(sizeof(MMD0Song) == sizeof(song)); + return mpt::bit_cast(song); + } + MMD2Song GetMMD2Song() const + { + static_assert(sizeof(MMD2Song) == sizeof(song)); + return mpt::bit_cast(song); + } + uint16be defaultTempo; + int8be playTranspose; // The global play transpose value for current song + uint8be flags; + uint8be flags2; + uint8be tempo2; // Timing pulses per line (ticks) + uint8be trackVol[16]; // 1...64 in MMD0/MMD1, reserved in MMD2 + uint8be masterVol; // 1...64 + uint8be numSamples; }; -MPT_BINARY_STRUCT(MMD2SONGHEADER, 788) +MPT_BINARY_STRUCT(MMDSong, 284) -// For MMD0 the note information is held in 3 bytes, byte0, byte1, byte2. For reference we -// number the bits in each byte 0..7, where 0 is the low bit. -// The note is held as bits 5..0 of byte0 -// The instrument is encoded in 6 bits, bits 7 and 6 of byte0 and bits 7,6,5,4 of byte1 -// The command number is bits 3,2,1,0 of byte1, command data is in byte2: -// For command 0, byte2 represents the second data byte, otherwise byte2 -// represents the first data byte. -struct MMD0BLOCK -{ - uint8be numtracks; - uint8be lines; // File value is 1 less than actual, so 0 -> 1 line -}; // uint8_be data[lines+1][tracks][3]; - -MPT_BINARY_STRUCT(MMD0BLOCK, 2) - - -// For MMD1,MMD2,MMD3 the note information is carried in 4 bytes, byte0, byte1, -// byte2 and byte3 -// The note is held as byte0 (values above 0x84 are ignored) -// The instrument is held as byte1 -// The command number is held as byte2, command data is in byte3 -// For commands 0 and 0x19 byte3 represents the second data byte, -// otherwise byte2 represents the first data byte. -struct MMD1BLOCK -{ - uint16be numtracks; // Number of tracks, may be > 64, but then that data is skipped. - uint16be lines; // Stored value is 1 less than actual, so 0 -> 1 line - uint32be info; // Offset of BlockInfo (if 0, no block_info is present) -}; - -MPT_BINARY_STRUCT(MMD1BLOCK, 8) - - -struct MMD1BLOCKINFO -{ - uint32be hlmask; // Unimplemented - ignore - uint32be blockname; // file offset of block name - uint32be blocknamelen; // length of block name (including term. 0) - uint32be pagetable; // file offset of command page table - uint32be cmdexttable; // file offset of command extension table - uint32be reserved[4]; // future expansion -}; - -MPT_BINARY_STRUCT(MMD1BLOCKINFO, 36) - - -// A set of play sequences is stored as an array of uint32_be files offsets -// Each offset points to the play sequence itself. -struct MMD2PLAYSEQ +struct MMD2PlaySeq { char name[32]; - uint32be command_offs; // filepos of command table + uint32be commandTableOffset; uint32be reserved; - uint16be length; - uint16be seq[512]; // skip if > 0x8000 + uint16be length; // Number of entries }; -MPT_BINARY_STRUCT(MMD2PLAYSEQ, 1066) +MPT_BINARY_STRUCT(MMD2PlaySeq, 42) -// A command table contains commands that effect a particular play sequence -// entry. The only commands read in are STOP or POSJUMP, all others are ignored -// POSJUMP is presumed to have extra bytes containing a uint16 for the position -struct MMDCOMMAND +struct MMD0PatternHeader { - uint16be offset; // Offset within current sequence entry - uint8be cmdnumber; // STOP (537) or POSJUMP (538) (others skipped) - uint8be extra_count; - uint8be extra_bytes[4]; // [extra_count]; -}; // Last entry has offset == 0xFFFF, cmd_number == 0 and 0 extrabytes - -MPT_BINARY_STRUCT(MMDCOMMAND, 8) - - -struct MMD0EXP -{ - uint32be nextmod; // File offset of next Hdr - uint32be exp_smp; // Pointer to extra instrument data - uint16be s_ext_entries; // Number of extra instrument entries - uint16be s_ext_entrsz; // Size of extra instrument data - uint32be annotxt; - uint32be annolen; - uint32be iinfo; // Instrument names - uint16be i_ext_entries; - uint16be i_ext_entrsz; - uint32be jumpmask; - uint32be rgbtable; - uint8be channelsplit[4]; // Only used if 8ch_conv (extra channel for every nonzero entry) - uint32be n_info; - uint32be songname; // Song name - uint32be songnamelen; - uint32be dumps; - uint32be mmdinfo; - uint32be mmdrexx; - uint32be mmdcmd3x; - uint32be trackinfo_ofs; // ptr to song->numtracks ptrs to tag lists - uint32be effectinfo_ofs; // ptr to group ptrs - uint32be tag_end; + uint8be numTracks; + uint8be numRows; }; -MPT_BINARY_STRUCT(MMD0EXP, 80) +MPT_BINARY_STRUCT(MMD0PatternHeader, 2) -static const uint8 bpmvals[9] = { 179,164,152,141,131,123,116,110,104}; - -static void MedConvert(ModCommand &p, const MMD0SONGHEADER *pmsh) +struct MMD1PatternHeader { - ModCommand::COMMAND command = p.command; - uint32 param = p.param; - switch(command) + uint16be numTracks; + uint16be numRows; + uint32be blockInfoOffset; +}; + +MPT_BINARY_STRUCT(MMD1PatternHeader, 8) + + +struct MMDPlaySeqCommand +{ + enum Command { - case 0x00: if (param) command = CMD_ARPEGGIO; else command = CMD_NONE; break; - case 0x01: command = CMD_PORTAMENTOUP; break; - case 0x02: command = CMD_PORTAMENTODOWN; break; - case 0x03: command = CMD_TONEPORTAMENTO; break; - case 0x04: command = CMD_VIBRATO; break; - case 0x05: command = CMD_TONEPORTAVOL; break; - case 0x06: command = CMD_VIBRATOVOL; break; - case 0x07: command = CMD_TREMOLO; break; - case 0x0A: if (param & 0xF0) param &= 0xF0; command = CMD_VOLUMESLIDE; if (!param) command = CMD_NONE; break; - case 0x0B: command = CMD_POSITIONJUMP; break; - case 0x0C: command = CMD_VOLUME; - if (pmsh->flags & MMD_FLAG_VOLHEX) - { - if (param < 0x80) - { - param = (param+1) / 2; - } else command = CMD_NONE; - } else - { - if (param <= 0x99) - { - param = (param >> 4)*10+((param & 0x0F) % 10); - if (param > 64) param = 64; - } else command = CMD_NONE; - } - break; - case 0x09: command = static_cast((param <= 0x20) ? CMD_SPEED : CMD_TEMPO); break; - case 0x0D: if (param & 0xF0) param &= 0xF0; command = CMD_VOLUMESLIDE; if (!param) command = CMD_NONE; break; - case 0x0F: // Set Tempo / Special - // F.00 = Pattern Break - if (!param) command = CMD_PATTERNBREAK; else - // F.01 - F.F0: Set tempo/speed - if (param <= 0xF0) - { - if (pmsh->flags & MMD_FLAG_8CHANNEL) - { - param = (param == 0 || param >= 10) ? 99 : bpmvals[param-1]; - } else - // F.01 - F.0A: Set Speed - if (param <= 0x0A) - { - command = CMD_SPEED; - } else - // Old tempo - if (!(pmsh->flags2 & MMD_FLAG2_BPM)) - { - param = Util::muldiv(param, 5*715909, 2*474326); - } - // F.0B - F.F0: Set Tempo (assumes LPB=4) - if (param > 0x0A) - { - command = CMD_TEMPO; - if (param < 0x21) param = 0x21; - if (param > 240) param = 240; - } - } else - switch(param) - { - // F.F1: Retrig 2x - case 0xF1: - command = CMD_MODCMDEX; - param = 0x93; - break; - // F.F2: Note Delay 2x - case 0xF2: - command = CMD_MODCMDEX; - param = 0xD3; - break; - // F.F3: Retrig 3x - case 0xF3: - command = CMD_MODCMDEX; - param = 0x92; - break; - // F.F4: Note Delay 1/3 - case 0xF4: - command = CMD_MODCMDEX; - param = 0xD2; - break; - // F.F5: Note Delay 2/3 - case 0xF5: - command = CMD_MODCMDEX; - param = 0xD4; - break; - // F.F8: Filter Off - case 0xF8: - command = CMD_MODCMDEX; - param = 0x00; - break; - // F.F9: Filter On - case 0xF9: - command = CMD_MODCMDEX; - param = 0x01; - break; - // F.FD: Very fast tone-portamento - case 0xFD: - command = CMD_TONEPORTAMENTO; - param = 0xFF; - break; - // F.FE: End Song - case 0xFE: - command = CMD_SPEED; - param = 0; - break; - // F.FF: Note Cut - case 0xFF: - command = CMD_MODCMDEX; - param = 0xC0; - break; - default: -#ifdef MED_LOG - Log("Unknown Fxx command: cmd=0x%02X param=0x%02X\n", command, param); -#endif - command = CMD_NONE; - param = 0; - } - break; - // 11.0x: Fine Slide Up - case 0x11: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0x10; - break; - // 12.0x: Fine Slide Down - case 0x12: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0x20; - break; - // 14.xx: Vibrato - case 0x14: - command = CMD_VIBRATO; - break; - // 15.xx: FineTune - case 0x15: - command = CMD_MODCMDEX; - param &= 0x0F; - param |= 0x50; - break; - // 16.xx: Pattern Loop - case 0x16: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0x60; - break; - // 18.xx: Note Cut - case 0x18: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0xC0; - break; - // 19.xx: Sample Offset - case 0x19: - command = CMD_OFFSET; - break; - // 1A.0x: Fine Volume Up - case 0x1A: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0xA0; - break; - // 1B.0x: Fine Volume Down - case 0x1B: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0xB0; - break; - // 1D.xx: Pattern Break - case 0x1D: - command = CMD_PATTERNBREAK; - break; - // 1E.0x: Pattern Delay - case 0x1E: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0xE0; - break; - // 1F.xy: Retrig - case 0x1F: - command = CMD_RETRIG; - param &= 0x0F; - break; - // 2E.xx: set panning - case 0x2E: - command = CMD_MODCMDEX; - param = ((param + 0x10) & 0xFF) >> 1; - if (param > 0x0F) param = 0x0F; - param |= 0x80; - break; - default: -#ifdef MED_LOG - // 0x2E ? - Log("Unknown command: cmd=0x%02X param=0x%02X\n", command, param); -#endif - command = CMD_NONE; - param = 0; + kStop = 1, + kJump = 2, + }; + + uint16be offset; // Offset within current play sequence, 0xFFFF = end of list + uint8be command; // Stop = 1, Jump = 2 + uint8be extraSize; +}; + +MPT_BINARY_STRUCT(MMDPlaySeqCommand, 4) + + +struct MMDBlockInfo +{ + uint32be highlightMaskOffset; + uint32be nameOffset; + uint32be nameLength; + uint32be pageTableOffset; // File offset of command page table + uint32be cmdExtTableOffset; // File offset of command extension table (second parameter) + uint32be reserved[4]; +}; + +MPT_BINARY_STRUCT(MMDBlockInfo, 36) + + +struct MMDInstrHeader +{ + enum Types + { + VSTI = -4, + HIGHLIFE = -3, + HYBRID = -2, + SYNTHETIC = -1, + SAMPLE = 0, // an ordinary 1-octave sample (or MIDI) + IFF5OCT = 1, // 5 octaves + IFF3OCT = 2, // 3 octaves + // The following ones are recognized by OctaMED Pro only + IFF2OCT = 3, // 2 octaves + IFF4OCT = 4, // 4 octaves + IFF6OCT = 5, // 6 octaves + IFF7OCT = 6, // 7 octaves + // OctaMED Pro V5 + later + EXTSAMPLE = 7, // two extra-low octaves + + TYPEMASK = 0x0F, + + S_16 = 0x10, + STEREO = 0x20, + DELTA = 0x40, + PACKED = 0x80, // MMDPackedSampleHeader follows + OBSOLETE_MD16 = 0x18, + }; + + uint32be length; + int16be type; +}; + +MPT_BINARY_STRUCT(MMDInstrHeader, 6) + + +struct MMDPackedSampleHeader +{ + uint16be packType; // Only 1 = ADPCM is supported + uint16be subType; // Packing subtype + // ADPCM subtype + // 1: g723_40 + // 2: g721 + // 3: g723_24 + uint8be commonFlags; // flags common to all packtypes (none defined so far) + uint8be packerFlags; // flags for the specific packtype + uint32be leftChLen; // packed length of left channel in bytes + uint32be rightChLen; // packed length of right channel in bytes (ONLY PRESENT IN STEREO SAMPLES) +}; + +MPT_BINARY_STRUCT(MMDPackedSampleHeader, 14) + + +struct MMDInstrExt +{ + enum + { + SSFLG_LOOP = 0x01, // Loop On / Off + SSFLG_EXTPSET = 0x02, // Ext.Preset + SSFLG_DISABLED = 0x04, // Disabled + SSFLG_PINGPONG = 0x08, // Ping-pong looping + }; + + uint8be hold; // 0...127 + uint8be decay; // 0...127 + uint8be suppressMidiOff; + int8be finetune; + // Below fields saved by >= V5 + uint8be defaultPitch; + uint8be instrFlags; + uint16be longMidiPreset; + // Below fields saved by >= V5.02 + uint8be outputDevice; + uint8be reserved; + // Below fields saved by >= V7 + uint32be loopStart; + uint32be loopLength; +}; + +MPT_BINARY_STRUCT(MMDInstrExt, 18) + + +struct MMDInstrInfo +{ + char name[40]; +}; + +MPT_BINARY_STRUCT(MMDInstrInfo, 40) + + +struct MMD0Exp +{ + uint32be nextModOffset; + uint32be instrExtOffset; + uint16be instrExtEntries; + uint16be instrExtEntrySize; + uint32be annoText; + uint32be annoLength; + uint32be instrInfoOffset; + uint16be instrInfoEntries; + uint16be instrInfoEntrySize; + uint32be jumpMask; + uint32be rgbTable; + uint8be channelSplit[4]; + uint32be notationInfoOffset; + uint32be songNameOffset; + uint32be songNameLength; + uint32be midiDumpOffset; + uint32be mmdInfoOffset; + uint32be arexxOffset; + uint32be midiCmd3xOffset; + uint32be trackInfoOffset; // Pointer to song->numtracks pointers to tag lists + uint32be effectInfoOffset; // Pointers to group pointers + uint32be tagEnd; +}; + +MPT_BINARY_STRUCT(MMD0Exp, 80) + + +struct MMDTag +{ + enum TagType + { + // Generic MMD tags + MMDTAG_END = 0x00000000, + MMDTAG_PTR = 0x80000000, // Data needs relocation + MMDTAG_MUSTKNOW = 0x40000000, // Loader must fail if this isn't recognized + MMDTAG_MUSTWARN = 0x20000000, // Loader must warn if this isn't recognized + MMDTAG_MASK = 0x1FFFFFFF, + + // ExpData tags + // # of effect groups, including the global group (will override settings in MMDSong struct), default = 1 + MMDTAG_EXP_NUMFXGROUPS = 1, + MMDTAG_TRK_FXGROUP = 3, + + MMDTAG_TRK_NAME = 1, // trackinfo tags + MMDTAG_TRK_NAMELEN = 2, // namelen includes zero term. + + // effectinfo tags + MMDTAG_FX_ECHOTYPE = 1, + MMDTAG_FX_ECHOLEN = 2, + MMDTAG_FX_ECHODEPTH = 3, + MMDTAG_FX_STEREOSEP = 4, + MMDTAG_FX_GROUPNAME = 5, // the Global Effects group shouldn't have name saved! + MMDTAG_FX_GRPNAMELEN = 6, // namelen includes zero term. + }; + + uint32be type; + uint32be data; +}; + +MPT_BINARY_STRUCT(MMDTag, 8) + + +static TEMPO MMDTempoToBPM(uint32 tempo, bool is8Ch, bool bpmMode, uint8 rowsPerBeat) +{ + if(bpmMode) + { + // You would have thought that we could use modern tempo mode here. + // Alas, the number of ticks per row still influences the tempo. :( + return TEMPO((tempo * rowsPerBeat) / 4.0); } - p.command = command; - p.param = static_cast(param); + if(is8Ch && tempo > 0) + { + LimitMax(tempo, 10u); + static constexpr uint8 tempos[10] = { 47, 43, 40, 37, 35, 32, 30, 29, 27, 26 }; + tempo = tempos[tempo - 1]; + } else if(tempo > 0 && tempo <= 10) + { + // SoundTracker compatible tempo + return TEMPO((6.0 * 1773447.0 / 14500.0) / tempo); + } + + return TEMPO(tempo / 0.264); } -static bool ValidateHeader(const MEDMODULEHEADER &pmmh) +static void ConvertMEDEffect(ModCommand &m, bool is8ch, bool bpmMode, uint8 rowsPerBeat, bool volHex) { - if(std::memcmp(pmmh.id, "MMD", 3) - || pmmh.id[3] < '0' || pmmh.id[3] > '3' - || pmmh.song == 0 - ) + switch(m.command) + { + case 0x04: // Vibrato (twice as deep as in ProTracker) + m.command = CMD_VIBRATO; + m.param = (std::min(m.param >> 3, 0x0F) << 4) | std::min((m.param & 0x0F) * 2, 0x0F); + break; + case 0x08: // Hold and decay + m.command = CMD_NONE; + break; + case 0x09: // Set secondary speed + if(m.param > 0 && m.param <= 20) + m.command = CMD_SPEED; + else + m.command = CMD_NONE; + break; + case 0x0C: // Set Volume + m.command = CMD_VOLUME; + if(!volHex && m.param < 0x99) + m.param = (m.param >> 4) * 10 + (m.param & 0x0F); + else if(volHex) + m.param = ((m.param & 0x7F) + 1) / 2; + else + m.command = CMD_NONE; + break; + case 0x0D: + m.command = CMD_VOLUMESLIDE; + break; + case 0x0E: // Synth jump + m.command = CMD_NONE; + break; + case 0x0F: // Misc + if(m.param == 0) + { + m.command = CMD_PATTERNBREAK; + } else if(m.param <= 0xF0) + { + m.command = CMD_TEMPO; + m.param = mpt::saturate_round(MMDTempoToBPM(m.param, is8ch, bpmMode, rowsPerBeat).ToDouble()); + } else switch(m.command) + { + case 0xF1: // Play note twice + m.command = CMD_MODCMDEX; + m.param = 0x93; + break; + case 0xF2: // Delay note + m.command = CMD_MODCMDEX; + m.param = 0xD3; + break; + case 0xF3: // Play note three times + m.command = CMD_MODCMDEX; + m.param = 0x92; + break; + case 0xF8: // Turn filter off + case 0xF9: // Turn filter on + m.command = CMD_MODCMDEX; + m.param = 0xF9 - m.param; + break; + case 0xFA: // MIDI pedal on + case 0xFB: // MIDI pedal off + case 0xFD: // Set pitch + case 0xFE: // End of song + m.command = CMD_NONE; + break; + case 0xFF: // Turn note off + m.note = NOTE_NOTECUT; + m.command = CMD_NONE; + break; + default: + m.command = CMD_NONE; + break; + } + break; + case 0x11: // Slide pitch up + m.command = CMD_MODCMDEX; + m.param = 0x10 | std::min(m.param, 0x0F); + break; + case 0x12: // Slide pitch down + m.command = CMD_MODCMDEX; + m.param = 0x20 | std::min(m.param, 0x0F); + break; + case 0x14: // Vibrato (ProTracker compatible depth, but faster) + m.command = CMD_VIBRATO; + m.param = (std::min((m.param >> 4) + 1, 0x0F) << 4) | (m.param & 0x0F); + break; + case 0x15: // Set finetune + m.command = CMD_MODCMDEX; + m.param = 0x50 | (m.param & 0x0F); + break; + case 0x16: // Loop + m.command = CMD_MODCMDEX; + m.param = 0x60 | std::min(m.param, 0x0F); + break; + case 0x18: // Stop note + m.command = CMD_MODCMDEX; + m.param = 0xC0 | std::min(m.param, 0x0F); + break; + case 0x19: // Sample Offset + m.command = CMD_OFFSET; + break; + case 0x1A: // Slide volume up once + m.command = CMD_MODCMDEX; + m.param = 0xA0 | std::min(m.param, 0x0F); + break; + case 0x1B: // Slide volume down once + m.command = CMD_MODCMDEX; + m.param = 0xB0 | std::min(m.param, 0x0F); + break; + case 0x1D: // Pattern break (in hex) + m.command = CMD_PATTERNBREAK; + break; + case 0x1E: // Repeat row + m.command = CMD_MODCMDEX; + m.param = 0xE0 | std::min(m.param, 0x0F); + break; + case 0x1F: // Note delay and retrigger + { + if(m.param & 0xF0) + { + m.command = CMD_MODCMDEX; + m.param = 0xD0 | (m.param >> 4); + } else if(m.param & 0x0F) + { + m.command = CMD_MODCMDEX; + m.param = 0x90 | m.param; + } else + { + m.command = CMD_NONE; + } + break; + } + case 0x20: // Reverse sample + skip samples + if(m.param == 0 && m.vol == 0) + { + m.command = CMD_S3MCMDEX; + m.param = 0x9F; + } else + { + // Skip given number of samples + m.command = CMD_NONE; + } + break; + case 0x29: // Relative sample offset + if(m.vol > 0) + { + m.command = CMD_OFFSETPERCENTAGE; + m.param = mpt::saturate_cast(Util::muldiv_unsigned(m.param, 0x100, m.vol)); + } else + { + m.command = CMD_NONE; + } + break; + case 0x2E: // Set panning + if(m.param <= 0x10 || m.param >= 0xF0) + { + m.command = CMD_PANNING8; + m.param = mpt::saturate_cast(((m.param ^ 0x80) - 0x70) * 8); + } else + { + m.command = CMD_NONE; + } + break; + default: + if(m.command < 0x10) + CSoundFile::ConvertModCommand(m); + else + m.command = CMD_NONE; + break; + } +} + +#ifndef NO_VST +static std::wstring ReadMEDStringUTF16BE(FileReader &file) +{ + auto chunk = file.ReadChunk(file.ReadUint32BE()); + std::wstring s(chunk.GetLength() / 2u, L'\0'); + for(auto &c : s) + { + c = chunk.ReadUint16BE(); + } + return s; +} +#endif + + +static void MEDReadNextSong(FileReader &file, MMD0FileHeader &fileHeader, MMD0Exp &expData, MMDSong &songHeader) +{ + file.ReadStruct(fileHeader); + file.Seek(fileHeader.songOffset + 63 * sizeof(MMD0Sample)); + file.ReadStruct(songHeader); + if(fileHeader.expDataOffset && file.Seek(fileHeader.expDataOffset)) + file.ReadStruct(expData); + else + expData = {}; +} + + +static std::pair MEDScanNumChannels(FileReader &file, const uint8 version) +{ + MMD0FileHeader fileHeader; + MMD0Exp expData; + MMDSong songHeader; + + file.Rewind(); + uint32 songOffset = 0; + MEDReadNextSong(file, fileHeader, expData, songHeader); + + SEQUENCEINDEX numSongs = std::min(MAX_SEQUENCES, mpt::saturate_cast(fileHeader.expDataOffset ? fileHeader.extraSongs + 1 : 1)); + CHANNELINDEX numChannels = 4; + // Scan patterns for max number of channels + for(SEQUENCEINDEX song = 0; song < numSongs; song++) + { + const PATTERNINDEX numPatterns = songHeader.numBlocks; + if(songHeader.numSamples > 63 || numPatterns > 0x7FFF) + return {}; + + for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) + { + if(!file.Seek(fileHeader.blockArrOffset + pat * 4u) + || !file.Seek(file.ReadUint32BE())) + { + continue; + } + numChannels = std::max(numChannels, static_cast(version < 1 ? file.ReadUint8() : file.ReadUint16BE())); + } + + // If song offsets are going backwards, reject the file + if(expData.nextModOffset <= songOffset || !file.Seek(expData.nextModOffset)) + { + numSongs = song + 1; + break; + } + songOffset = expData.nextModOffset; + MEDReadNextSong(file, fileHeader, expData, songHeader); + } + return {numChannels, numSongs}; +} + + +static bool ValidateHeader(const MMD0FileHeader &fileHeader) +{ + if(std::memcmp(fileHeader.mmd, "MMD", 3) + || fileHeader.version < '0' || fileHeader.version > '3' + || fileHeader.songOffset < sizeof(MMD0FileHeader) + || fileHeader.songOffset > uint32_max - 63 * sizeof(MMD0Sample) - sizeof(MMDSong) + || fileHeader.blockArrOffset < sizeof(MMD0FileHeader) + || fileHeader.sampleArrOffset < sizeof(MMD0FileHeader) + || fileHeader.expDataOffset > uint32_max - sizeof(MMD0Exp)) { return false; } @@ -505,295 +629,227 @@ static bool ValidateHeader(const MEDMODULEHEADER &pmmh) } -static uint64 GetHeaderMinimumAdditionalSize(const MEDMODULEHEADER &pmmh) +static uint64 GetHeaderMinimumAdditionalSize(const MMD0FileHeader &fileHeader) { - MPT_UNREFERENCED_PARAMETER(pmmh); - return sizeof(MMD0SONGHEADER); + return std::max({ fileHeader.songOffset + 63 * sizeof(MMD0Sample) + sizeof(MMDSong), + fileHeader.blockArrOffset, + fileHeader.sampleArrOffset, + fileHeader.expDataOffset + sizeof(MMD0Exp) }) - sizeof(MMD0FileHeader); } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMED(MemoryFileReader file, const uint64 *pfilesize) { - MEDMODULEHEADER pmmh; - if(!file.ReadStruct(pmmh)) - { + MMD0FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) return ProbeWantMoreData; - } - if(!ValidateHeader(pmmh)) - { + if(!ValidateHeader(fileHeader)) return ProbeFailure; - } - return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(pmmh)); + return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader)); } bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); - MEDMODULEHEADER pmmh; - if(!file.ReadStruct(pmmh)) - { + MMD0FileHeader fileHeader; + if(!file.ReadStruct(fileHeader)) return false; - } - if(!ValidateHeader(pmmh)) - { + if(!ValidateHeader(fileHeader)) return false; - } - if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(pmmh)))) - { + if(!file.CanRead(mpt::saturate_cast(GetHeaderMinimumAdditionalSize(fileHeader)))) return false; - } - const uint32 dwSong = pmmh.song; - if(!file.LengthIsAtLeast(dwSong + sizeof(MMD0SONGHEADER))) - { - return false; - } if(loadFlags == onlyVerifyHeader) - { return true; - } - - file.Rewind(); - const FileReader::off_t dwMemLength = file.GetLength(); - const uint8 *lpStream = file.GetRawData(); - const MMD0SONGHEADER *pmsh; - const MMD2SONGHEADER *pmsh2; - const MMD0EXP *pmex; - uint32 dwBlockArr, dwSmplArr, dwExpData; - const_unaligned_ptr_be pdwTable; - int8 version = pmmh.id[3]; - uint32 deftempo; - int playtransp = 0; - + InitializeGlobals(MOD_TYPE_MED); InitializeChannels(); - // Setup channel pan positions and volume - SetupMODPanning(true); + const uint8 version = fileHeader.version - '0'; - const MPT_UCHAR_TYPE *madeWithTracker = UL_(""); - switch(version) + file.Seek(fileHeader.songOffset); + FileReader sampleHeaderChunk = file.ReadChunk(63 * sizeof(MMD0Sample)); + + MMDSong songHeader; + file.ReadStruct(songHeader); + + if(songHeader.numSamples > 63 || songHeader.numBlocks > 0x7FFF) + return false; + + MMD0Exp expData{}; + if(fileHeader.expDataOffset && file.Seek(fileHeader.expDataOffset)) { - case '0': madeWithTracker = m_nChannels > 4 ? UL_("OctaMED v2.10") : UL_("MED v2"); break; - case '1': madeWithTracker = UL_("OctaMED v4"); break; - case '2': madeWithTracker = UL_("OctaMED v5"); break; - case '3': madeWithTracker = UL_("OctaMED Soundstudio"); break; + file.ReadStruct(expData); } - m_modFormat.formatName = mpt::format(U_("OctaMED (MMD%1)"))(version - '0'); - m_modFormat.type = U_("med"); - m_modFormat.madeWithTracker = madeWithTracker; - m_modFormat.charset = mpt::CharsetISO8859_1; - m_nSamplePreAmp = 32; - dwBlockArr = pmmh.blockarr; - dwSmplArr = pmmh.smplarr; - dwExpData = pmmh.expdata; - if ((dwExpData) && (dwExpData < dwMemLength - sizeof(MMD0EXP))) - pmex = (const MMD0EXP *)(lpStream+dwExpData); - else - pmex = NULL; - pmsh = (const MMD0SONGHEADER *)(lpStream + dwSong); - pmsh2 = (const MMD2SONGHEADER *)pmsh; + const auto [numChannels, numSongs] = MEDScanNumChannels(file, version); + if(numChannels < 1 || numChannels > MAX_BASECHANNELS) + return false; + m_nChannels = numChannels; - uint16 wNumBlocks = pmsh->numblocks; - m_nChannels = 4; - m_nSamples = pmsh->numsamples; - if (m_nSamples > 63) m_nSamples = 63; - // Tempo - m_nDefaultTempo.Set(125); - deftempo = pmsh->deftempo; - if (!deftempo) deftempo = 125; - if (pmsh->flags2 & MMD_FLAG2_BPM) - { - uint32 tempo_tpl = (pmsh->flags2 & MMD_FLAG2_BMASK) + 1; - if (!tempo_tpl) tempo_tpl = 4; - deftempo *= tempo_tpl; - deftempo /= 4; - #ifdef MED_LOG - Log("newtempo: %3d bpm (bpm=%3d lpb=%2d)\n", deftempo, pmsh->deftempo, (pmsh->flags2 & MMD_FLAG2_BMASK)+1); - #endif - } else - { - if((pmsh->flags & MMD_FLAG_8CHANNEL) && deftempo > 0 && deftempo <= 9) - deftempo = bpmvals[deftempo-1]; - else - deftempo = Util::muldiv(deftempo, 5 * 715909, 2 * 474326); - #ifdef MED_LOG - Log("oldtempo: %3d bpm (bpm=%3d)\n", deftempo, pmsh->deftempo); - #endif - } - // Speed - m_nDefaultSpeed = pmsh->tempo2; - if (!m_nDefaultSpeed) m_nDefaultSpeed = 6; - if (deftempo < 0x21) deftempo = 0x21; - m_nDefaultTempo.Set(deftempo); - // Reading Samples - for (uint32 iSHdr=0; iSHdrsample[iSHdr].rep * 2u; - sample.nLoopEnd = sample.nLoopStart + (pmsh->sample[iSHdr].replen * 2u); - sample.nVolume = (pmsh->sample[iSHdr].svol << 2); - sample.nGlobalVol = 64; - if (sample.nVolume > 256) sample.nVolume = 256; - // Was: sample.RelativeTone = -12 * pmsh->sample[iSHdr].strans; - // But that breaks MMD1 modules (e.g. "94' summer.mmd1" from Modland) - "automatic terminated to.mmd0" still sounds broken, probably "play transpose" is broken there. - sample.RelativeTone = pmsh->sample[iSHdr].strans; - sample.nPan = 128; - if (sample.nLoopEnd <= 2) sample.nLoopEnd = 0; - if (sample.nLoopEnd) sample.uFlags.set(CHN_LOOP); - } - // Common Flags - m_SongFlags.set(SONG_FASTVOLSLIDES, !(pmsh->flags & 0x20)); + // Start with the instruments, as those are shared between songs - // Reading play sequence - if (version < '2') + std::vector instrOffsets; + file.Seek(fileHeader.sampleArrOffset); + file.ReadVector(instrOffsets, songHeader.numSamples); + m_nInstruments = m_nSamples = songHeader.numSamples; + + // In MMD0 / MMD1, octave wrapping is only done in 4-channel modules (hardware mixing!), and not for synth instruments + // - It's required e.g. for automatic terminated to.mmd0 + // - dissociate.mmd0 (8 channels) and starkelsesirap.mmd0 (synth) on the other hand don't need it + // In MMD2 / MMD3, the mix flag is used instead. + const bool hardwareMixSamples = (version < 2 && m_nChannels == 4) || (version >= 2 && !(songHeader.flags2 & MMDSong::FLAG2_MIX)); + + bool needInstruments = false; + bool anySynthInstrs = false; +#ifndef NO_VST + PLUGINDEX numPlugins = 0; +#endif + for(SAMPLEINDEX ins = 1, smp = 1; ins <= m_nInstruments; ins++) { - uint32 nbo = pmsh->songlen; - if (!nbo) nbo = 1; - ReadOrderFromArray(Order(), pmsh->playseq, nbo); - playtransp = pmsh->playtransp; - } else - { - uint32 nSections; - ORDERINDEX nOrders = 0; - uint16 nTrks = pmsh2->numtracks; - if ((nTrks >= 4) && (nTrks <= 32)) m_nChannels = nTrks; - uint32 playseqtable = pmsh2->playseqtable; - uint32 numplayseqs = pmsh2->numpseqs; - if (!numplayseqs) numplayseqs = 1; - nSections = pmsh2->numsections; - uint32 sectiontable = pmsh2->sectiontable; - if ((!nSections) || (!sectiontable) || (sectiontable >= dwMemLength-2)) nSections = 1; - nOrders = 0; - Order().clear(); - for (uint32 iSection=0; iSection= dwMemLength - sectiontable) - { - nplayseq = *const_unaligned_ptr_be(lpStream + sectiontable); - sectiontable += 2; - } else - { - nSections = 0; - } - uint32 pseq = 0; + file.ReadStruct(instrHeader); + sampleChunk = file.ReadChunk(instrHeader.length); + } + const bool isSynth = instrHeader.type < 0; + const size_t maskedType = static_cast(instrHeader.type & MMDInstrHeader::TYPEMASK); - if ((playseqtable) && (playseqtable < dwMemLength) && (nplayseq * 4 <= dwMemLength - playseqtable)) +#ifndef NO_VST + if(instrHeader.type == MMDInstrHeader::VSTI) + { + needInstruments = true; + sampleChunk.Skip(6); // 00 00 + const std::wstring type = ReadMEDStringUTF16BE(sampleChunk); + const std::wstring name = ReadMEDStringUTF16BE(sampleChunk); + if(type == L"VST") { - pseq = (const_unaligned_ptr_be(lpStream+playseqtable))[nplayseq]; + auto &mixPlug = m_MixPlugins[numPlugins]; + mixPlug = {}; + mixPlug.Info.dwPluginId1 = Vst::kEffectMagic; + mixPlug.Info.gain = 10; + mixPlug.Info.szName = mpt::ToCharset(mpt::CharsetLocaleOrUTF8, name); + mixPlug.Info.szLibraryName = mpt::ToCharset(mpt::Charset::UTF8, name); + instr.nMixPlug = numPlugins + 1; + instr.nMidiChannel = MidiFirstChannel; + instr.Transpose(-24); + instr.AssignSample(0); + // TODO: Figure out patch and routing data + + numPlugins++; } - if (pseq && pseq < dwMemLength && sizeof(MMD2PLAYSEQ) <= dwMemLength - pseq) + } else +#endif // NO_VST + if(isSynth) + { + // TODO: Figure out synth instruments + anySynthInstrs = true; + instr.AssignSample(0); + } + + uint8 numSamples = 1; + static constexpr uint8 SamplesPerType[] = {1, 5, 3, 2, 4, 6, 7}; + if(!isSynth && maskedType < std::size(SamplesPerType)) + numSamples = SamplesPerType[maskedType]; + if(numSamples > 1) + { + static_assert(MAX_SAMPLES > 63 * 9, "Check IFFOCT multisample code"); + m_nSamples += numSamples - 1; + needInstruments = true; + static constexpr uint8 OctSampleMap[][8] = { - const MMD2PLAYSEQ *pmps = (const MMD2PLAYSEQ *)(lpStream + pseq); - if(m_songName.empty()) mpt::String::Read(m_songName, pmps->name); - ORDERINDEX n = std::min(pmps->length, MAX_ORDERS - nOrders); - if (n <= (dwMemLength - pseq + 42) / 2u && n < MPT_ARRAY_COUNT(pmps->seq)) + {1, 1, 0, 0, 0, 0, 0, 0}, // 2 + {2, 2, 1, 1, 0, 0, 0, 0}, // 3 + {3, 3, 2, 2, 1, 0, 0, 0}, // 4 + {4, 3, 2, 1, 1, 0, 0, 0}, // 5 + {5, 4, 3, 2, 1, 0, 0, 0}, // 6 + {6, 5, 4, 3, 2, 1, 0, 0}, // 7 + }; + + static constexpr int8 OctTransposeMap[][8] = + { + { 0, 0, -12, -12, -24, -36, -48, -60}, // 2 + { 0, 0, -12, -12, -24, -36, -48, -60}, // 3 + { 0, 0, -12, -12, -24, -36, -48, -60}, // 4 + {12, 0, -12, -24, -24, -36, -48, -60}, // 5 + {12, 0, -12, -24, -36, -48, -48, -60}, // 6 + {12, 0, -12, -24, -36, -48, -60, -72}, // 7 + }; + + // TODO: Move octaves so that they align better (C-4 = lowest, we don't have access to the highest four octaves) + for(int octave = 4; octave < 10; octave++) + { + for(int note = 0; note < 12; note++) { - Order().resize(nOrders + n); - for (uint32 i=0; iseq[i]; - if (seqval < wNumBlocks) - { - Order()[nOrders++] = static_cast(seqval); - } - } + instr.Keyboard[12 * octave + note] = smp + OctSampleMap[numSamples - 2][octave - 4]; + instr.NoteMap[12 * octave + note] += OctTransposeMap[numSamples - 2][octave - 4]; + } + } + } else if(maskedType == MMDInstrHeader::EXTSAMPLE) + { + needInstruments = true; + instr.Transpose(-24); + } else if(!isSynth && hardwareMixSamples) + { + for(int octave = 7; octave < 10; octave++) + { + for(int note = 0; note < 12; note++) + { + instr.NoteMap[12 * octave + note] -= static_cast((octave - 6) * 12); } } } - playtransp = pmsh2->playtransp; - } - // Reading Expansion structure - if (pmex) - { - // Channel Split - if ((m_nChannels == 4) && (pmsh->flags & MMD_FLAG_8CHANNEL)) - { - for (uint32 i8ch=0; i8ch<4; i8ch++) - { - if (pmex->channelsplit[i8ch]) m_nChannels++; - } - } - // Song Comments (null-terminated) - uint32 annotxt = pmex->annotxt; - uint32 annolen = pmex->annolen; - annolen = std::min(annolen, MED_MAX_COMMENT_LENGTH); //Thanks to Luigi Auriemma for pointing out an overflow risk - if ((annotxt) && (annolen) && (annolen <= dwMemLength) && (annotxt <= dwMemLength - annolen) ) - { - m_songMessage.Read(mpt::byte_cast(lpStream) + annotxt, annolen - 1, SongMessage::leAutodetect); - } - // Song Name - uint32 songname = pmex->songname; - uint32 songnamelen = pmex->songnamelen; - if ((songname) && (songnamelen) && (songname <= dwMemLength) && (songnamelen <= dwMemLength-songname)) - { - mpt::String::Read(m_songName, lpStream + songname, songnamelen); - } - // Sample Names - uint32 smpinfoex = pmex->iinfo; - if (smpinfoex) - { - uint32 iinfoptr = pmex->iinfo; - uint32 ientries = pmex->i_ext_entries; - uint32 ientrysz = pmex->i_ext_entrsz; - if ((iinfoptr) && (ientrysz < 256) && (ientries*ientrysz < dwMemLength) && (iinfoptr < dwMemLength - ientries*ientrysz)) - { - const char *psznames = (const char *)(lpStream + iinfoptr); - for (uint32 i=0; i(m_szNames[i + 1], (psznames + i * ientrysz), ientrysz); - } - } - } - // Track Names - uint32 trackinfo_ofs = pmex->trackinfo_ofs; - if ((trackinfo_ofs) && (trackinfo_ofs < dwMemLength) && (m_nChannels * 4u < dwMemLength - trackinfo_ofs)) + MMD0Sample sampleHeader; + sampleHeaderChunk.ReadStruct(sampleHeader); + + // midiChannel = 0xFF == midi instrument but with invalid channel, midiChannel = 0x00 == sample-based instrument? + if(sampleHeader.midiChannel > 0 && sampleHeader.midiChannel <= 16) { - const_unaligned_ptr_be ptrktags = const_unaligned_ptr_be(lpStream + trackinfo_ofs); - for (uint32 i=0; i(lpStream + trktagofs); - if (ntag == MMDTAG_END) break; - uint32 tagdata = *const_unaligned_ptr_be(lpStream + trktagofs + 4); - switch(ntag) - { - case MMDTAG_TRK_NAMELEN: trknamelen = tagdata; break; - case MMDTAG_TRK_NAME: trknameofs = tagdata; break; - } - trktagofs += 8; - } - if ((trknameofs) && (trknameofs < dwMemLength - trknamelen) && trknamelen < dwMemLength) - { - mpt::String::Read(ChnSettings[i].szName, lpStream + trknameofs, trknamelen); - } - } + auto &mixPlug = m_MixPlugins[numPlugins]; + mixPlug = {}; + mixPlug.Info.dwPluginId1 = PLUGMAGIC('V', 's', 't', 'P'); + mixPlug.Info.dwPluginId2 = PLUGMAGIC('M', 'M', 'I', 'D'); + mixPlug.Info.gain = 10; + mixPlug.Info.szName = "MIDI Input Output"; + mixPlug.Info.szLibraryName = "MIDI Input Output"; + + instr.nMixPlug = numPlugins + 1; + instr.Transpose(-24); + + numPlugins++; } +#endif // NO_VST + } + if(sampleHeader.midiPreset > 0 && sampleHeader.midiPreset <= 128) + { + instr.nMidiProgram = sampleHeader.midiPreset; + } + + for(SAMPLEINDEX i = 0; i < numSamples; i++) + { + ModSample &mptSmp = Samples[smp + i]; + mptSmp.Initialize(MOD_TYPE_MED); + mptSmp.nVolume = 4u * std::min(sampleHeader.sampleVolume, 64u); + mptSmp.RelativeTone = sampleHeader.sampleTranspose; + } + + if(isSynth || !(loadFlags & loadSampleData)) + { + smp += numSamples; + continue; } - } - // Reading samples - if (dwSmplArr > dwMemLength - 4*m_nSamples) return true; - pdwTable = const_unaligned_ptr_be(lpStream + dwSmplArr); - for (uint32 iSmp=0; iSmp= dwMemLength) || (dwPos + sizeof(MMDSAMPLEHEADER) >= dwMemLength) || !(loadFlags & loadSampleData)) continue; - const MMDSAMPLEHEADER *psdh = (const MMDSAMPLEHEADER *)(lpStream + dwPos); - uint32 len = psdh->length; - #ifdef MED_LOG - Log("SampleData %d: stype=0x%02X len=%d\n", iSmp, psdh->type, len); - #endif - if(dwPos + len + 6 > dwMemLength) len = 0; - uint32 stype = psdh->type; - FileReader chunk(mpt::byte_cast(mpt::as_span(lpStream + dwPos + 6, dwMemLength - dwPos - 6))); SampleIO sampleIO( SampleIO::_8bit, @@ -801,144 +857,500 @@ bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags) SampleIO::bigEndian, SampleIO::signedPCM); - if (stype & 0x80) + const bool hasLoop = sampleHeader.loopLength > 1; + SmpLength loopStart = sampleHeader.loopStart * 2; + SmpLength loopEnd = loopStart + sampleHeader.loopLength * 2; + + SmpLength length = mpt::saturate_cast(sampleChunk.GetLength()); + if(instrHeader.type & MMDInstrHeader::S_16) { - chunk.Skip((stype & 0x20) ? 14 : 6); - } else - { - if(stype & 0x10) - { - sampleIO |= SampleIO::_16bit; - len /= 2; - } - if(stype & 0x20) - { - sampleIO |= SampleIO::stereoSplit; - len /= 2; - } + sampleIO |= SampleIO::_16bit; + length /= 2; } - Samples[iSmp + 1].nLength = len; - sampleIO.ReadSample(Samples[iSmp + 1], chunk); - } - // Reading patterns (blocks) - if(!(loadFlags & loadPatternData)) - { - return true; - } - if (wNumBlocks > MAX_PATTERNS) wNumBlocks = MAX_PATTERNS; - if ((!dwBlockArr) || (dwBlockArr > dwMemLength - 4u*wNumBlocks) || (4u*wNumBlocks > dwMemLength)) return true; - pdwTable = const_unaligned_ptr_be(lpStream + dwBlockArr); - playtransp += (version == '3') ? 24 : 48; - Patterns.ResizeArray(wNumBlocks); - for (PATTERNINDEX iBlk=0; iBlk= dwMemLength) || (dwPos >= dwMemLength - 8)) continue; - uint32 lines = 64, tracks = 4; - if (version == '0') + if (instrHeader.type & MMDInstrHeader::STEREO) { - const MMD0BLOCK *pmb = (const MMD0BLOCK *)(lpStream + dwPos); - lines = pmb->lines + 1; - tracks = pmb->numtracks; - if (!tracks) tracks = m_nChannels; - if(!Patterns.Insert(iBlk, lines)) continue; - const uint8 * s = (const uint8 *)(lpStream + dwPos + 2); - uint32 maxlen = tracks*lines*3; - if (maxlen + dwPos > dwMemLength - 2) break; - for (uint32 y=0; y 1) + length = length / ((1u << numSamples) - 1); + + for(SAMPLEINDEX i = 0; i < numSamples; i++) + { + ModSample &mptSmp = Samples[smp + i]; + + mptSmp.nLength = length; + sampleIO.ReadSample(mptSmp, sampleChunk); + + if(hasLoop) { - ModCommand *p = Patterns[iBlk].GetpModCommand(y, 0); - for (uint32 x=0; x> 4; - if (s[0] & 0x80) instr |= 0x10; - if (s[0] & 0x40) instr |= 0x20; - if ((note) && (note <= 132)) p->note = static_cast(note + playtransp); - p->instr = instr; - p->command = s[1] & 0x0F; - p->param = s[2]; - // if (!iBlk) Log("%02X.%02X.%02X | ", s[0], s[1], s[2]); - MedConvert(*p, pmsh); - p++; - } - //if (!iBlk) Log("\n"); + mptSmp.nLoopStart = loopStart; + mptSmp.nLoopEnd = loopEnd; + mptSmp.uFlags.set(CHN_LOOP); } - } else + + length *= 2; + loopStart *= 2; + loopEnd *= 2; + } + + smp += numSamples; + } + + if(expData.instrExtOffset != 0 && expData.instrExtEntries != 0 && file.Seek(expData.instrExtOffset)) + { + const uint16 entries = std::min(expData.instrExtEntries, songHeader.numSamples); + const uint16 size = expData.instrExtEntrySize; + for(uint16 i = 0; i < entries; i++) { - const MMD1BLOCK *pmb = (const MMD1BLOCK *)(lpStream + dwPos); - #ifdef MED_LOG - Log("MMD1BLOCK: lines=%2d, tracks=%2d, offset=0x%04X\n", - pmb->lines, pmb->numtracks, pmb->info); - #endif - const MMD1BLOCKINFO *pbi = NULL; - const uint8 *pcmdext = NULL; - lines = pmb->lines + 1; - tracks = pmb->numtracks; - if (!tracks) tracks = m_nChannels; - Patterns.Insert(iBlk, lines); - uint32 dwBlockInfo = pmb->info; - if ((dwBlockInfo) && (dwBlockInfo < dwMemLength - sizeof(MMD1BLOCKINFO))) + MMDInstrExt instrExt; + file.ReadStructPartial(instrExt, size); + + ModInstrument &ins = *Instruments[i + 1]; + if(instrExt.hold) { - pbi = (const MMD1BLOCKINFO *)(lpStream + dwBlockInfo); - #ifdef MED_LOG - Log(" BLOCKINFO: blockname=0x%04X namelen=%d pagetable=0x%04X &cmdexttable=0x%04X\n", - pbi->blockname, pbi->blocknamelen, pbi->pagetable, pbi->cmdexttable); - #endif - if ((pbi->blockname) && (pbi->blocknamelen)) + ins.VolEnv.assign({ + EnvelopeNode{0u, ENVELOPE_MAX}, + EnvelopeNode{static_cast(instrExt.hold - 1), ENVELOPE_MAX}, + EnvelopeNode{static_cast(instrExt.hold + (instrExt.decay ? 64u / instrExt.decay : 0u)), ENVELOPE_MIN}, + }); + if(instrExt.hold == 1) + ins.VolEnv.erase(ins.VolEnv.begin()); + ins.nFadeOut = instrExt.decay ? (instrExt.decay * 512) : 32767; + ins.VolEnv.dwFlags.set(ENV_ENABLED); + needInstruments = true; + } + if(size > offsetof(MMDInstrExt, longMidiPreset)) + { + ins.wMidiBank = instrExt.longMidiPreset; + } +#ifndef NO_VST + if(ins.nMixPlug > 0) + { + PLUGINDEX plug = ins.nMixPlug - 1; + auto &mixPlug = m_MixPlugins[plug]; + if(mixPlug.Info.dwPluginId2 == PLUGMAGIC('M', 'M', 'I', 'D')) { - uint32 nameofs = pbi->blockname; - uint32 namelen = pbi->blocknamelen; - if ((nameofs < dwMemLength) && (namelen < dwMemLength - nameofs)) + float dev = (instrExt.outputDevice + 1) / 65536.0f; // Magic code from MidiInOut.h :( + mixPlug.pluginData.resize(3 * sizeof(uint32)); + auto memFile = std::make_pair(mpt::as_span(mixPlug.pluginData), mpt::IO::Offset(0)); + mpt::IO::WriteIntLE(memFile, 0); // Plugin data type + mpt::IO::Write(memFile, IEEE754binary32LE{0}); // Input device + mpt::IO::Write(memFile, IEEE754binary32LE{dev}); // Output device + + // Check if we already have another plugin referencing this output device + for(PLUGINDEX p = 0; p < plug; p++) { - Patterns[iBlk].SetName((const char *)(lpStream + nameofs), namelen); - } - } - if (pbi->cmdexttable) - { - uint32 cmdexttable = pbi->cmdexttable; - if (cmdexttable < dwMemLength - 4) - { - cmdexttable = *const_unaligned_ptr_be(lpStream + cmdexttable); - if ((cmdexttable) && (cmdexttable <= dwMemLength - lines*tracks)) + const auto &otherPlug = m_MixPlugins[p]; + if(otherPlug.Info.dwPluginId1 == mixPlug.Info.dwPluginId1 + && otherPlug.Info.dwPluginId2 == mixPlug.Info.dwPluginId2 + && otherPlug.pluginData == mixPlug.pluginData) { - pcmdext = (const uint8 *)(lpStream + cmdexttable); + ins.nMixPlug = p + 1; + mixPlug = {}; + break; } } } } - const uint8 * s = (const uint8 *)(lpStream + dwPos + 8); - uint32 maxlen = tracks*lines*4; - if (maxlen + dwPos > dwMemLength - 8 || !Patterns.IsValidPat(iBlk)) break; - for (uint32 y=0; y offsetof(MMDInstrExt, loopLength)) { - ModCommand *p = Patterns[iBlk].GetpModCommand(y, 0); - for (uint32 x=0; x NOTE_MAX) rnote = NOTE_MAX; - p->note = (uint8)rnote; - } else if(note == 0x80) - { - p->note = NOTE_NOTECUT; - } - p->instr = s[1]; - p->command = s[2]; - p->param = s[3]; - if (pcmdext) p->vol = pcmdext[x]; - MedConvert(*p, pmsh); - p++; - } - if (pcmdext) pcmdext += tracks; + sample.nLoopStart = instrExt.loopStart; + sample.nLoopEnd = instrExt.loopStart + instrExt.loopLength; + } + if(size > offsetof(MMDInstrExt, instrFlags)) + { + if(instrExt.instrFlags & MMDInstrExt::SSFLG_LOOP) + sample.uFlags.set(CHN_LOOP); + if(instrExt.instrFlags & MMDInstrExt::SSFLG_PINGPONG) + sample.uFlags.set(CHN_LOOP | CHN_PINGPONGLOOP); + if(instrExt.instrFlags & MMDInstrExt::SSFLG_DISABLED) + sample.nGlobalVol = 0; } } } + if(expData.instrInfoOffset != 0 && expData.instrInfoEntries != 0 && file.Seek(expData.instrInfoOffset)) + { + const uint16 entries = std::min(expData.instrInfoEntries, songHeader.numSamples); + const uint16 size = expData.instrInfoEntrySize; + for(uint16 i = 0; i < entries; i++) + { + MMDInstrInfo instrInfo; + file.ReadStructPartial(instrInfo, size); + Instruments[i + 1]->name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrInfo.name); + for(auto smp : Instruments[i + 1]->GetSamples()) + { + m_szNames[smp] = Instruments[i + 1]->name; + } + } + } + + file.Rewind(); + PATTERNINDEX basePattern = 0; + for(SEQUENCEINDEX song = 0; song < numSongs; song++) + { + MEDReadNextSong(file, fileHeader, expData, songHeader); + + if(song != 0) + { + if(Order.AddSequence() == SEQUENCEINDEX_INVALID) + return false; + } + + ModSequence &order = Order(song); + + std::map jumpTargets; + order.clear(); + uint32 preamp = 32; + if(version < 2) + { + if(songHeader.songLength > 256 || m_nChannels > 16) + return false; + ReadOrderFromArray(order, songHeader.GetMMD0Song().sequence, songHeader.songLength); + for(auto &ord : order) + { + ord += basePattern; + } + + SetupMODPanning(true); + for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) + { + ChnSettings[chn].nVolume = std::min(songHeader.trackVol[chn], 64); + } + } else + { + const MMD2Song header = songHeader.GetMMD2Song(); + if(header.numTracks < 1 || header.numTracks > 64 || m_nChannels > 64) + return false; + + const bool freePan = (header.flags3 & MMD2Song::FLAG3_FREEPAN); + if(header.volAdjust) + preamp = Util::muldivr_unsigned(preamp, std::min(header.volAdjust, 800), 100); + if (freePan) + preamp /= 2; + + if(file.Seek(header.trackVolsOffset)) + { + for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) + { + ChnSettings[chn].nVolume = std::min(file.ReadUint8(), 64); + } + } + if(header.trackPanOffset && file.Seek(header.trackPanOffset)) + { + for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) + { + ChnSettings[chn].nPan = (Clamp(file.ReadInt8(), -16, 16) + 16) * 8; + } + } else + { + SetupMODPanning(true); + } + + std::vector sections; + if(!file.Seek(header.sectionTableOffset) + || !file.CanRead(songHeader.songLength * 2) + || !file.ReadVector(sections, songHeader.songLength)) + continue; + for(uint16 section : sections) + { + if(section > header.numPlaySeqs) + continue; + + file.Seek(header.playSeqTableOffset + section * 4); + if(file.Seek(file.ReadUint32BE()) || !file.CanRead(sizeof(MMD2PlaySeq))) + { + MMD2PlaySeq playSeq; + file.ReadStruct(playSeq); + + if(!order.empty()) + order.push_back(order.GetIgnoreIndex()); + + size_t readOrders = playSeq.length; + if(!file.CanRead(readOrders)) + LimitMax(readOrders, file.BytesLeft()); + LimitMax(readOrders, ORDERINDEX_MAX); + + size_t orderStart = order.size(); + order.reserve(orderStart + readOrders); + for(size_t ord = 0; ord < readOrders; ord++) + { + PATTERNINDEX pat = file.ReadUint16BE(); + if(pat < 0x8000) + { + order.push_back(basePattern + pat); + } + } + if(playSeq.name[0]) + order.SetName(mpt::ToUnicode(mpt::Charset::ISO8859_1, playSeq.name)); + + // Play commands (jump / stop) + if(playSeq.commandTableOffset > 0 && file.Seek(playSeq.commandTableOffset)) + { + MMDPlaySeqCommand command; + while(file.ReadStruct(command)) + { + FileReader chunk = file.ReadChunk(command.extraSize); + ORDERINDEX ord = mpt::saturate_cast(orderStart + command.offset); + if(command.offset == 0xFFFF || ord >= order.size()) + break; + if(command.command == MMDPlaySeqCommand::kStop) + { + order[ord] = order.GetInvalidPatIndex(); + } else if(command.command == MMDPlaySeqCommand::kJump) + { + jumpTargets[ord] = chunk.ReadUint16BE(); + order[ord] = order.GetIgnoreIndex(); + } + } + } + } + } + } + + const bool volHex = (songHeader.flags & MMDSong::FLAG_VOLHEX) != 0; + const bool is8Ch = (songHeader.flags & MMDSong::FLAG_8CHANNEL) != 0; + const bool bpmMode = (songHeader.flags2 & MMDSong::FLAG2_BPM) != 0; + const uint8 rowsPerBeat = 1 + (songHeader.flags2 & MMDSong::FLAG2_BMASK); + m_nDefaultTempo = MMDTempoToBPM(songHeader.defaultTempo, is8Ch, bpmMode, rowsPerBeat); + m_nDefaultSpeed = Clamp(songHeader.tempo2, 1, 32); + if(bpmMode) + { + m_nDefaultRowsPerBeat = rowsPerBeat; + m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat * 4u; + } + + if(songHeader.masterVol) + m_nDefaultGlobalVolume = std::min(songHeader.masterVol, 64) * 4; + m_nSamplePreAmp = m_nVSTiVolume = preamp; + + // For MED, this affects both volume and pitch slides + m_SongFlags.set(SONG_FASTVOLSLIDES, !(songHeader.flags & MMDSong::FLAG_STSLIDE)); + + if(expData.songNameOffset && file.Seek(expData.songNameOffset)) + { + file.ReadString(m_songName, expData.songNameLength); + if(numSongs > 1) + order.SetName(mpt::ToUnicode(mpt::Charset::ISO8859_1, m_songName)); + } + if(expData.annoLength > 1 && file.Seek(expData.annoText)) + { + m_songMessage.Read(file, expData.annoLength - 1, SongMessage::leAutodetect); + } + + if(expData.mmdInfoOffset && file.Seek(expData.mmdInfoOffset)) + { + file.Skip(6); // Next info file (unused) + reserved + if(file.ReadUint16BE() == 1) // ASCII text + { + uint32 length = file.ReadUint32BE(); + if(length && file.CanRead(length)) + { + const auto oldMsg = std::move(m_songMessage); + m_songMessage.Read(file, length, SongMessage::leAutodetect); + if(!oldMsg.empty()) + m_songMessage.SetRaw(oldMsg + std::string(2, SongMessage::InternalLineEnding) + m_songMessage); + } + } + } + + // Track Names + if(version >= 2 && expData.trackInfoOffset) + { + for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++) + { + if(file.Seek(expData.trackInfoOffset + chn * 4) + && file.Seek(file.ReadUint32BE())) + { + uint32 nameOffset = 0, nameLength = 0; + while(file.CanRead(sizeof(MMDTag))) + { + MMDTag tag; + file.ReadStruct(tag); + if(tag.type == MMDTag::MMDTAG_END) + break; + switch(tag.type & MMDTag::MMDTAG_MASK) + { + case MMDTag::MMDTAG_TRK_NAME: nameOffset = tag.data; break; + case MMDTag::MMDTAG_TRK_NAMELEN: nameLength = tag.data; break; + } + } + if(nameOffset > 0 && nameLength > 0 && file.Seek(nameOffset)) + { + file.ReadString(ChnSettings[chn].szName, nameLength); + } + } + } + } + + PATTERNINDEX numPatterns = songHeader.numBlocks; + Patterns.ResizeArray(basePattern + numPatterns); + for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) + { + if(!(loadFlags & loadPatternData) + || !file.Seek(fileHeader.blockArrOffset + pat * 4u) + || !file.Seek(file.ReadUint32BE())) + { + continue; + } + + CHANNELINDEX numTracks; + ROWINDEX numRows; + std::string patName; + int transpose; + FileReader cmdExt; + + if(version < 1) + { + transpose = NOTE_MIN + 47; + MMD0PatternHeader patHeader; + file.ReadStruct(patHeader); + numTracks = patHeader.numTracks; + numRows = patHeader.numRows + 1; + } else + { + transpose = NOTE_MIN + (version <= 2 ? 47 : 23) + songHeader.playTranspose; + MMD1PatternHeader patHeader; + file.ReadStruct(patHeader); + numTracks = patHeader.numTracks; + numRows = patHeader.numRows + 1; + if(patHeader.blockInfoOffset) + { + auto offset = file.GetPosition(); + file.Seek(patHeader.blockInfoOffset); + MMDBlockInfo blockInfo; + file.ReadStruct(blockInfo); + if(file.Seek(blockInfo.nameOffset)) + { + // We have now chased four pointers to get this far... lovely format. + file.ReadString(patName, blockInfo.nameLength); + } + if(blockInfo.cmdExtTableOffset + && file.Seek(blockInfo.cmdExtTableOffset) + && file.Seek(file.ReadUint32BE())) + { + cmdExt = file.ReadChunk(numTracks * numRows); + } + + file.Seek(offset); + } + } + + if(!Patterns.Insert(basePattern + pat, numRows)) + continue; + + CPattern &pattern = Patterns[basePattern + pat]; + pattern.SetName(patName); + LimitMax(numTracks, m_nChannels); + + for(ROWINDEX row = 0; row < numRows; row++) + { + ModCommand *m = pattern.GetpModCommand(row, 0); + for(CHANNELINDEX chn = 0; chn < numTracks; chn++, m++) + { + int note = NOTE_NONE; + if(version < 1) + { + const auto [noteInstr, instrCmd, param] = file.ReadArray(); + + if(noteInstr & 0x3F) + note = (noteInstr & 0x3F) + transpose; + + m->instr = (instrCmd >> 4) | ((noteInstr & 0x80) >> 3) | ((noteInstr & 0x40) >> 1); + + m->command = instrCmd & 0x0F; + m->param = param; + } else + { + const auto [noteVal, instr, command, param1] = file.ReadArray(); + m->vol = cmdExt.ReadUint8(); + + if(noteVal & 0x7F) + note = (noteVal & 0x7F) + transpose; + else if(noteVal == 0x80) + m->note = NOTE_NOTECUT; + + m->instr = instr & 0x3F; + m->command = command; + m->param = param1; + } + // Octave wrapping for 4-channel modules (TODO: this should not be set because of synth instruments) + if(hardwareMixSamples && note >= NOTE_MIDDLEC + 2 * 12) + needInstruments = true; + + if(note >= NOTE_MIN && note <= NOTE_MAX) + m->note = static_cast(note); + ConvertMEDEffect(*m, is8Ch, bpmMode, rowsPerBeat, volHex); + } + } + } + + // Fix jump order commands + for(const auto & [from, to] : jumpTargets) + { + PATTERNINDEX pat; + if(from > 0 && order.IsValidPat(from - 1)) + { + pat = order.EnsureUnique(from - 1); + } else + { + if(to == from + 1) // No action required + continue; + pat = Patterns.InsertAny(1); + if(pat == PATTERNINDEX_INVALID) + continue; + order[from] = pat; + } + Patterns[pat].WriteEffect(EffectWriter(CMD_POSITIONJUMP, mpt::saturate_cast(to)).Row(Patterns[pat].GetNumRows() - 1).RetryPreviousRow()); + if(pat >= numPatterns) + numPatterns = pat + 1; + } + + basePattern += numPatterns; + + if(!expData.nextModOffset || !file.Seek(expData.nextModOffset)) + break; + } + Order.SetSequence(0); + + if(!needInstruments) + { + for(INSTRUMENTINDEX ins = 1; ins <= m_nInstruments; ins++) + { + delete Instruments[ins]; + Instruments[ins] = nullptr; + } + m_nInstruments = 0; + } + + if(anySynthInstrs) + AddToLog(LogWarning, U_("Synthesized MED instruments are not supported.")); + + const mpt::uchar *madeWithTracker = MPT_ULITERAL(""); + switch(version) + { + case 0: madeWithTracker = m_nChannels > 4 ? MPT_ULITERAL("OctaMED v2.10 (MMD0)") : MPT_ULITERAL("MED v2 (MMD0)"); break; + case 1: madeWithTracker = MPT_ULITERAL("OctaMED v4 (MMD1)"); break; + case 2: madeWithTracker = MPT_ULITERAL("OctaMED v5 (MMD2)"); break; + case 3: madeWithTracker = MPT_ULITERAL("OctaMED Soundstudio (MMD3)"); break; + } + + m_modFormat.formatName = mpt::format(MPT_USTRING("OctaMED (MMD%1)"))(version); + m_modFormat.type = MPT_USTRING("med"); + m_modFormat.madeWithTracker = madeWithTracker; + m_modFormat.charset = mpt::Charset::ISO8859_1; + return true; } - OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp index 02b1f1029..4c3fe140a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mid.cpp @@ -292,8 +292,9 @@ uint32 CSoundFile::MapMidiInstrument(uint8 program, uint16 bank, uint8 midiChann if (program + 1 == p->nMidiProgram && bank + 1 == p->wMidiBank && p->nMidiDrumKey == 0) return i; } } - if ((m_nInstruments + 1 >= MAX_INSTRUMENTS) || (m_nSamples + 1 >= MAX_SAMPLES)) return 0; - + if(!CanAddMoreInstruments() || !CanAddMoreSamples()) + return 0; + pIns = AllocateInstrument(m_nInstruments + 1); if(pIns == nullptr) { @@ -327,13 +328,13 @@ uint32 CSoundFile::MapMidiInstrument(uint8 program, uint16 bank, uint8 midiChann // Set GM program / drum name if (!isDrum) { - strcpy(pIns->name, szMidiProgramNames[program]); + pIns->name = szMidiProgramNames[program]; } else { if (note >= 24 && note <= 84) - strcpy(pIns->name, szMidiPercussionNames[note - 24]); + pIns->name = szMidiPercussionNames[note - 24]; else - strcpy(pIns->name, "Percussions"); + pIns->name = "Percussions"; } return m_nInstruments; } @@ -347,10 +348,10 @@ struct MThd uint16be division; // Delta timing value: positive = units/beat; negative = smpte compatible units }; -MPT_BINARY_STRUCT(MThd, 10); +MPT_BINARY_STRUCT(MThd, 10) -typedef uint32 tick_t; +using tick_t = uint32; struct TrackState { @@ -362,15 +363,15 @@ struct TrackState struct ModChannelState { - enum : uint8 { NOMIDI = 0xFF }; // No MIDI channel assigned. + static constexpr uint8 NOMIDI = 0xFF; // No MIDI channel assigned. - tick_t age = 0; // At which MIDI tick the channel was triggered - int32 porta = 0; // Current portamento position in extra-fine slide units (1/64th of a semitone) - uint8 vol = 100; // MIDI note volume (0...127) - uint8 pan = 128; // MIDI channel panning (0...256) - uint8 midiCh = NOMIDI; // MIDI channel that was last played on this channel - ModCommand::NOTE note = NOTE_NONE; // MIDI note that was last played on this channel - bool sustained = false; // If true, the note was already released by a note-off event, but sustain pedal CC is still active + tick_t age = 0; // At which MIDI tick the channel was triggered + int32 porta = 0; // Current portamento position in extra-fine slide units (1/64th of a semitone) + uint8 vol = 100; // MIDI note volume (0...127) + uint8 pan = 128; // MIDI channel panning (0...256) + uint8 midiCh = NOMIDI; // MIDI channel that was last played on this channel + ModCommand::NOTE note = NOTE_NONE; // MIDI note that was last played on this channel + bool sustained = false; // If true, the note was already released by a note-off event, but sustain pedal CC is still active }; struct MidiChannelState @@ -389,7 +390,7 @@ struct MidiChannelState bool monoMode = false; // Mono/Poly operation 126/127 n/a Poly bool sustain = false; // Sustain pedal 64 on/off off - std::array noteOn; // Value != CHANNELINDEX_INVALID: Note is active and mapped to mod channel in value + std::array noteOn; // Value != CHANNELINDEX_INVALID: Note is active and mapped to mod channel in value MidiChannelState() { @@ -638,7 +639,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Standard MIDI File"); m_modFormat.type = isRIFF ? UL_("rmi") : UL_("mid"); m_modFormat.madeWithTracker = U_("Standard MIDI File"); - m_modFormat.charset = mpt::CharsetISO8859_1; + m_modFormat.charset = mpt::Charset::ISO8859_1; SetMixLevels(mixLevels1_17RC3); m_nTempoMode = tempoModeModern; @@ -669,14 +670,28 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) std::bitset<16> drumChns; drumChns.set(MIDI_DRUMCHANNEL - 1); - for(uint16 t = 0; t < numTracks; t++) + tick_t timeShift = 0; + for(auto &track : tracks) { if(!file.ReadMagic("MTrk")) return false; - tracks[t].track = file.ReadChunk(file.ReadUint32BE()); + track.track = file.ReadChunk(file.ReadUint32BE()); tick_t delta = 0; - tracks[t].track.ReadVarInt(delta); - tracks[t].nextEvent = delta; + track.track.ReadVarInt(delta); + // Work-around for some MID files that assume that negative deltas exist (they don't according to the standard) + if(delta > int32_max) + timeShift = std::max(static_cast(~delta + 1), timeShift); + track.nextEvent = delta; + } + if(timeShift != 0) + { + for(auto &track : tracks) + { + if(track.nextEvent > int32_max) + track.nextEvent = timeShift - static_cast(~track.nextEvent + 1); + else + track.nextEvent += timeShift; + } } uint16 finishedTracks = 0; @@ -687,7 +702,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) int8 masterTranspose = 0; bool isXG = false; bool isEMIDI = false; - bool isType2 = (fileHeader.format == 2); + const bool isType2 = (fileHeader.format == 2); while(finishedTracks < numTracks) { @@ -901,7 +916,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) midiChnStatus[midiCh].pan = data2 * 2u; for(auto chn : midiChnStatus[midiCh].noteOn) { - if(chn != CHANNELINDEX_INVALID) + if(chn != CHANNELINDEX_INVALID && modChnStatus[chn].pan != midiChnStatus[midiCh].pan) { if(Patterns[pat].WriteEffect(EffectWriter(CMD_PANNING8, midiChnStatus[midiCh].pan).Channel(chn).Row(row))) { @@ -1151,7 +1166,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) } m.command = static_cast(diff < 0 ? CMD_PORTAMENTODOWN : CMD_PORTAMENTOUP); - int32 absDiff = mpt::abs(diff); + int32 absDiff = std::abs(diff); int32 realDiff = 0; if(absDiff < 16) { @@ -1187,7 +1202,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) // Add another sub-song for type-2 files if(isType2 && finishedTracks < numTracks) { - if(Order.AddSequence(false) == SEQUENCEINDEX_INVALID) + if(Order.AddSequence() == SEQUENCEINDEX_INVALID) break; Order().clear(); } @@ -1222,11 +1237,11 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) { channels.push_back(i); if(modChnStatus[i].midiCh != ModChannelState::NOMIDI) - mpt::String::WriteAutoBuf(ChnSettings[i].szName) = mpt::format("MIDI Ch %1")(1 + modChnStatus[i].midiCh); + ChnSettings[i].szName = mpt::format("MIDI Ch %1")(1 + modChnStatus[i].midiCh); else if(i == tempoChannel) - mpt::String::WriteAutoBuf(ChnSettings[i].szName) = "Tempo"; + ChnSettings[i].szName = "Tempo"; else if(i == globalVolChannel) - mpt::String::WriteAutoBuf(ChnSettings[i].szName) = "Global Volume"; + ChnSettings[i].szName = "Global Volume"; } } if(channels.empty()) @@ -1236,7 +1251,7 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) if(GetpModDoc() != nullptr) { // Keep MIDI channels in patterns neatly grouped - std::sort(channels.begin(), channels.end(), [&modChnStatus] (CHANNELINDEX c1, CHANNELINDEX c2) -> bool + std::sort(channels.begin(), channels.end(), [&modChnStatus] (CHANNELINDEX c1, CHANNELINDEX c2) { if(modChnStatus[c1].midiCh == modChnStatus[c2].midiCh) return c1 < c2; @@ -1309,24 +1324,24 @@ bool CSoundFile::ReadMID(FileReader &file, ModLoadingFlags loadFlags) { // Load from Instrument or Sample file InputFile f; - if(f.Open(midiMapName)) + if(f.Open(midiMapName, SettingCacheCompleteFileBeforeLoading())) { FileReader insFile = GetFileReader(f); if(ReadInstrumentFromFile(ins, insFile, false)) { mpt::PathString filename = midiMapName.GetFullFileName(); pIns = Instruments[ins]; - if(!pIns->filename[0]) mpt::String::Copy(pIns->filename, filename.ToLocale().c_str()); + if(!pIns->filename[0]) pIns->filename = filename.ToLocale(); if(!pIns->name[0]) { if(midiCode < 0x80) { - mpt::String::CopyN(pIns->name, szMidiProgramNames[midiCode]); + pIns->name = szMidiProgramNames[midiCode]; } else { uint32 key = midiCode & 0x7F; - if((key >= 24) && (key < 24 + mpt::size(szMidiPercussionNames))) - mpt::String::CopyN(pIns->name, szMidiPercussionNames[key - 24]); + if((key >= 24) && (key < 24 + std::size(szMidiPercussionNames))) + pIns->name = szMidiPercussionNames[key - 24]; } } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp index d9401542f..174768784 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mo3.cpp @@ -21,16 +21,31 @@ #include "MPEGFrame.h" #include "OggStream.h" + #if defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) -#include "../common/mptBufferIO.h" +#include #endif #if defined(MPT_WITH_VORBIS) +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG #include +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG #endif #if defined(MPT_WITH_VORBISFILE) +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG #include +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG #include "../soundbase/SampleFormatConverters.h" #include "../soundbase/SampleFormatCopy.h" #endif @@ -39,7 +54,7 @@ #include #include "../soundbase/SampleFormatConverters.h" #include "../soundbase/SampleFormatCopy.h" -#endif // MPT_WITH_STBVORBIS +#endif // MPT_WITH_STBVORBIS OPENMPT_NAMESPACE_BEGIN @@ -63,7 +78,7 @@ struct MO3FileHeader itCompatGxx = 0x0400, itOldFX = 0x0800, modplugMode = 0x10000, - unknown = 0x20000, // Always set + unknown = 0x20000, // Always set (internal BASS flag to designate modules) modVBlank = 0x80000, hasPlugins = 0x100000, extFilterRange = 0x200000, @@ -118,7 +133,7 @@ struct MO3Envelope if(flags & envLoop) mptEnv.dwFlags.set(ENV_LOOP); if(flags & envFilter) mptEnv.dwFlags.set(ENV_FILTER); if(flags & envCarry) mptEnv.dwFlags.set(ENV_CARRY); - mptEnv.resize(std::min(numNodes, 25)); + mptEnv.resize(std::min(numNodes.get(), uint8(25))); mptEnv.nSustainStart = sustainStart; mptEnv.nSustainEnd = sustainEnd; mptEnv.nLoopStart = loopStart; @@ -224,7 +239,7 @@ struct MO3Instrument } if(type == MOD_TYPE_IT) - mptIns.nGlobalVol = std::min(globalVol, 128) / 2u; + mptIns.nGlobalVol = std::min(static_cast(globalVol), uint8(128)) / 2u; if(panning <= 256) { mptIns.nPan = panning; @@ -235,8 +250,8 @@ struct MO3Instrument mptIns.nPPC = ppc; mptIns.nDCT = static_cast(dct.get()); mptIns.nDNA = static_cast(dca.get()); - mptIns.nVolSwing = static_cast(std::min(volSwing, 100)); - mptIns.nPanSwing = static_cast(std::min(panSwing, 256) / 4u); + mptIns.nVolSwing = static_cast(std::min(volSwing.get(), uint16(100))); + mptIns.nPanSwing = static_cast(std::min(panSwing.get(), uint16(256)) / 4u); mptIns.SetCutoff(cutoff & 0x7F, (cutoff & 0x80) != 0); mptIns.SetResonance(resonance & 0x7F, (resonance & 0x80) != 0); } @@ -249,18 +264,19 @@ struct MO3Sample { enum MO3SampleFlags { - smp16Bit = 0x01, - smpLoop = 0x10, - smpPingPongLoop = 0x20, - smpSustain = 0x100, - smpSustainPingPong = 0x200, - smpStereo = 0x400, - smpCompressionMPEG = 0x1000, // MPEG 1.0 / 2.0 / 2.5 sample - smpCompressionOgg = 0x1000 | 0x2000, // Ogg sample - smpSharedOgg = 0x1000 | 0x2000 | 0x4000, // Ogg sample with shared vorbis header - smpDeltaCompression = 0x2000, // Deltas + compression - smpDeltaPrediction = 0x4000, // Delta prediction + compression - smpCompressionMask = 0x1000 | 0x2000 | 0x4000 + smp16Bit = 0x01, + smpLoop = 0x10, + smpPingPongLoop = 0x20, + smpSustain = 0x100, + smpSustainPingPong = 0x200, + smpStereo = 0x400, + smpCompressionMPEG = 0x1000, // MPEG 1.0 / 2.0 / 2.5 sample + smpCompressionOgg = 0x1000 | 0x2000, // Ogg sample + smpSharedOgg = 0x1000 | 0x2000 | 0x4000, // Ogg sample with shared vorbis header + smpDeltaCompression = 0x2000, // Deltas + compression + smpDeltaPrediction = 0x4000, // Delta prediction + compression + smpOPLInstrument = 0x8000, // OPL patch data + smpCompressionMask = 0x1000 | 0x2000 | 0x4000 | 0x8000 }; int32le freqFinetune; // Frequency in S3M and IT, finetune (0...255) in MOD, MTM, XM @@ -294,10 +310,11 @@ struct MO3Sample } else { mptSmp.nFineTune = static_cast(freqFinetune); - if(type != MOD_TYPE_MTM) mptSmp.nFineTune -= 128; + if(type != MOD_TYPE_MTM) + mptSmp.nFineTune -= 128; mptSmp.RelativeTone = transpose; } - mptSmp.nVolume = std::min(defaultVolume, 64) * 4u; + mptSmp.nVolume = std::min(defaultVolume.get(), uint8(64)) * 4u; if(panning <= 256) { mptSmp.nPan = panning; @@ -306,10 +323,14 @@ struct MO3Sample mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; - if(flags & smpLoop) mptSmp.uFlags.set(CHN_LOOP); - if(flags & smpPingPongLoop) mptSmp.uFlags.set(CHN_PINGPONGLOOP); - if(flags & smpSustain) mptSmp.uFlags.set(CHN_SUSTAINLOOP); - if(flags & smpSustainPingPong) mptSmp.uFlags.set(CHN_PINGPONGSUSTAIN); + if(flags & smpLoop) + mptSmp.uFlags.set(CHN_LOOP); + if(flags & smpPingPongLoop) + mptSmp.uFlags.set(CHN_PINGPONGLOOP); + if(flags & smpSustain) + mptSmp.uFlags.set(CHN_SUSTAINLOOP); + if(flags & smpSustainPingPong) + mptSmp.uFlags.set(CHN_PINGPONGSUSTAIN); mptSmp.nVibType = static_cast(AutoVibratoIT2XM[vibType & 7]); mptSmp.nVibSweep = vibSweep; @@ -317,7 +338,7 @@ struct MO3Sample mptSmp.nVibRate = vibRate; if(type == MOD_TYPE_IT) - mptSmp.nGlobalVol = std::min(globalVol, 64); + mptSmp.nGlobalVol = std::min(static_cast(globalVol), uint8(64)); mptSmp.nSustainStart = sustainStart; mptSmp.nSustainEnd = sustainEnd; } @@ -335,7 +356,7 @@ struct MO3SampleChunk uint16 headerSize; int16 sharedHeader; MO3SampleChunk(const FileReader &chunk_ = FileReader(), uint16 headerSize_ = 0, int16 sharedHeader_ = 0) - : chunk(chunk_), headerSize(headerSize_), sharedHeader(sharedHeader_) { } + : chunk(chunk_), headerSize(headerSize_), sharedHeader(sharedHeader_) {} }; @@ -353,7 +374,10 @@ struct MO3SampleChunk data &= 0xFF; \ if(data == 0) \ { \ - data = file.ReadUint8(); \ + uint8 nextByte; \ + if(!file.Read(nextByte)) \ + break; \ + data = nextByte; \ data = (data << 1) + 1; \ carry = (data > 0xFF); \ data &= 0xFF; \ @@ -365,14 +389,15 @@ struct MO3SampleChunk // until second bit is 0 (noted n0) #define DECODE_CTRL_BITS \ -{ \ - strLen++; \ - do { \ - READ_CTRL_BIT; \ - strLen = (strLen << 1) + carry; \ - READ_CTRL_BIT; \ - } while(carry); \ -} + { \ + strLen++; \ + do \ + { \ + READ_CTRL_BIT; \ + strLen = (strLen << 1) + carry; \ + READ_CTRL_BIT; \ + } while(carry); \ + } static bool UnpackMO3Data(FileReader &file, uint8 *dst, uint32 size) { @@ -382,9 +407,9 @@ static bool UnpackMO3Data(FileReader &file, uint8 *dst, uint32 size) } uint16 data = 0; - int8 carry = 0; // x86 carry (used to propagate the most significant bit from one byte to another) - int32 strLen = 0; // length of previous string - int32 strOffset; // string offset + int8 carry = 0; // x86 carry (used to propagate the most significant bit from one byte to another) + int32 strLen = 0; // length of previous string + int32 strOffset; // string offset uint8 *initDst = dst; uint32 ebp, previousPtr = 0; uint32 initSize = size; @@ -399,31 +424,36 @@ static bool UnpackMO3Data(FileReader &file, uint8 *dst, uint32 size) if(!carry) { // a 0 ctrl bit means 'copy', not compressed byte - *dst++ = file.ReadUint8(); + if(!file.Read(*dst)) + break; + dst++; size--; } else { // a 1 ctrl bit means compressed bytes are following - ebp = 0; // length adjustment - DECODE_CTRL_BITS; // read length, and if strLen > 3 (coded using more than 1 bits pair) also part of the offset value - strLen -=3; + ebp = 0; // length adjustment + DECODE_CTRL_BITS; // read length, and if strLen > 3 (coded using more than 1 bits pair) also part of the offset value + strLen -= 3; if(strLen < 0) { // means LZ ptr with same previous relative LZ ptr (saved one) - strOffset = previousPtr; // restore previous Ptr + strOffset = previousPtr; // restore previous Ptr strLen++; } else { // LZ ptr in ctrl stream - strOffset = (strLen << 8) | file.ReadUint8(); // read less significant offset byte from stream + uint8 b; + if(!file.Read(b)) + break; + strOffset = (strLen << 8) | b; // read less significant offset byte from stream strLen = 0; strOffset = ~strOffset; if(strOffset < -1280) ebp++; - ebp++; // length is always at least 1 + ebp++; // length is always at least 1 if(strOffset < -32000) ebp++; - previousPtr = strOffset; // save current Ptr + previousPtr = strOffset; // save current Ptr } // read the next 2 bits as part of strLen @@ -434,10 +464,10 @@ static bool UnpackMO3Data(FileReader &file, uint8 *dst, uint32 size) if(strLen == 0) { // length does not fit in 2 bits - DECODE_CTRL_BITS; // decode length: 1 is the most significant bit, - strLen += 2; // then first bit of each bits pairs (noted n1), until n0. + DECODE_CTRL_BITS; // decode length: 1 is the most significant bit, + strLen += 2; // then first bit of each bits pairs (noted n1), until n0. } - strLen += ebp; // length adjustment + strLen += ebp; // length adjustment if(size >= static_cast(strLen) && strLen > 0) { // Copy previous string @@ -472,12 +502,12 @@ static bool UnpackMO3Data(FileReader &file, uint8 *dst, uint32 size) struct MO3Delta8BitParams { - typedef int8 sample_t; - typedef uint8 unsigned_t; - enum : int { shift = 7 }; - enum : uint8 { dhInit = 4 }; + using sample_t = int8; + using unsigned_t = uint8; + static constexpr int shift = 7; + static constexpr uint8 dhInit = 4; - static inline void Decode(FileReader &file, int8 &carry, uint16 &data, uint8 &/*dh*/, unsigned_t &val) + static inline void Decode(FileReader &file, int8 &carry, uint16 &data, uint8 & /*dh*/, unsigned_t &val) { do { @@ -490,10 +520,10 @@ struct MO3Delta8BitParams struct MO3Delta16BitParams { - typedef int16 sample_t; - typedef uint16 unsigned_t; - enum : int { shift = 15 }; - enum : uint8 { dhInit = 8 }; + using sample_t = int16; + using unsigned_t = uint16; + static constexpr int shift = 15; + static constexpr uint8 dhInit = 8; static inline void Decode(FileReader &file, int8 &carry, uint16 &data, uint8 &dh, unsigned_t &val) { @@ -505,7 +535,7 @@ struct MO3Delta16BitParams val = (val << 1) + carry; READ_CTRL_BIT; val = (val << 1) + carry; - READ_CTRL_BIT; \ + READ_CTRL_BIT; } while(carry); } else { @@ -520,7 +550,7 @@ struct MO3Delta16BitParams }; -template +template static void UnpackMO3DeltaSample(FileReader &file, typename Properties::sample_t *dst, uint32 length, uint8 numChannels) { uint8 dh = Properties::dhInit, cl = 0; @@ -532,7 +562,7 @@ static void UnpackMO3DeltaSample(FileReader &file, typename Properties::sample_t for(uint8 chn = 0; chn < numChannels; chn++) { typename Properties::sample_t *p = dst + chn; - const typename Properties::sample_t * const pEnd = p + length * numChannels; + const typename Properties::sample_t *const pEnd = p + length * numChannels; while(p < pEnd) { val = 0; @@ -552,12 +582,12 @@ static void UnpackMO3DeltaSample(FileReader &file, typename Properties::sample_t cl--; } dh = dh + cl; - dh >>= 1; // next length in bits of encoded delta second part - carry = val & 1; // sign of delta 1=+, 0=not + dh >>= 1; // next length in bits of encoded delta second part + carry = val & 1; // sign of delta 1=+, 0=not val >>= 1; if(carry == 0) - val = ~val; // negative delta - val += previous; // previous value + delta + val = ~val; // negative delta + val += previous; // previous value + delta *p = val; p += numChannels; previous = val; @@ -566,7 +596,7 @@ static void UnpackMO3DeltaSample(FileReader &file, typename Properties::sample_t } -template +template static void UnpackMO3DeltaPredictionSample(FileReader &file, typename Properties::sample_t *dst, uint32 length, uint8 numChannels) { uint8 dh = Properties::dhInit, cl = 0; @@ -579,12 +609,12 @@ static void UnpackMO3DeltaPredictionSample(FileReader &file, typename Properties for(uint8 chn = 0; chn < numChannels; chn++) { typename Properties::sample_t *p = dst + chn; - const typename Properties::sample_t * const pEnd = p + length * numChannels; + const typename Properties::sample_t *const pEnd = p + length * numChannels; while(p < pEnd) { val = 0; Properties::Decode(file, carry, data, dh, val); - cl = dh; // length in bits of: delta second part (right most bits of delta) and sign bit + cl = dh; // length in bits of: delta second part (right most bits of delta) and sign bit while(cl > 0) { READ_CTRL_BIT; @@ -599,18 +629,18 @@ static void UnpackMO3DeltaPredictionSample(FileReader &file, typename Properties cl--; } dh = dh + cl; - dh >>= 1; // next length in bits of encoded delta second part - carry = val & 1; // sign of delta 1=+, 0=not + dh >>= 1; // next length in bits of encoded delta second part + carry = val & 1; // sign of delta 1=+, 0=not val >>= 1; if(carry == 0) - val = ~val; // negative delta + val = ~val; // negative delta delta = static_cast(val); - val = val + static_cast(next); // predicted value + delta + val = val + static_cast(next); // predicted value + delta *p = val; p += numChannels; sval = static_cast(val); - next = (sval * (1<<1)) + (delta >> 1) - previous; // corrected next value + next = (sval * (1 << 1)) + (delta >> 1) - previous; // corrected next value Limit(next, std::numeric_limits::min(), std::numeric_limits::max()); @@ -628,60 +658,55 @@ static void UnpackMO3DeltaPredictionSample(FileReader &file, typename Properties static size_t VorbisfileFilereaderRead(void *ptr, size_t size, size_t nmemb, void *datasource) { - FileReader &file = *reinterpret_cast(datasource); - return file.ReadRaw(mpt::void_cast(ptr), size * nmemb) / size; + FileReader &file = *reinterpret_cast(datasource); + return file.ReadRaw(mpt::void_cast(ptr), size * nmemb) / size; } static int VorbisfileFilereaderSeek(void *datasource, ogg_int64_t offset, int whence) { - FileReader &file = *reinterpret_cast(datasource); + FileReader &file = *reinterpret_cast(datasource); switch(whence) { case SEEK_SET: + if(!Util::TypeCanHoldValue(offset)) { - if(!Util::TypeCanHoldValue(offset)) - { - return -1; - } - return file.Seek(mpt::saturate_cast(offset)) ? 0 : -1; + return -1; } - break; + return file.Seek(mpt::saturate_cast(offset)) ? 0 : -1; + case SEEK_CUR: + if(offset < 0) { - if(offset < 0) + if(offset == std::numeric_limits::min()) { - if(offset == std::numeric_limits::min()) - { - return -1; - } - if(!Util::TypeCanHoldValue(0-offset)) - { - return -1; - } - return file.SkipBack(mpt::saturate_cast(0 - offset)) ? 0 : -1; - } else - { - if(!Util::TypeCanHoldValue(offset)) - { - return -1; - } - return file.Skip(mpt::saturate_cast(offset)) ? 0 : -1; + return -1; } - } - break; - case SEEK_END: + if(!Util::TypeCanHoldValue(0 - offset)) + { + return -1; + } + return file.SkipBack(mpt::saturate_cast(0 - offset)) ? 0 : -1; + } else { if(!Util::TypeCanHoldValue(offset)) { return -1; } - if(!Util::TypeCanHoldValue(file.GetLength() + offset)) - { - return -1; - } - return file.Seek(mpt::saturate_cast(file.GetLength() + offset)) ? 0 : -1; + return file.Skip(mpt::saturate_cast(offset)) ? 0 : -1; } break; + + case SEEK_END: + if(!Util::TypeCanHoldValue(offset)) + { + return -1; + } + if(!Util::TypeCanHoldValue(file.GetLength() + offset)) + { + return -1; + } + return file.Seek(mpt::saturate_cast(file.GetLength() + offset)) ? 0 : -1; + default: return -1; } @@ -689,7 +714,7 @@ static int VorbisfileFilereaderSeek(void *datasource, ogg_int64_t offset, int wh static long VorbisfileFilereaderTell(void *datasource) { - FileReader &file = *reinterpret_cast(datasource); + FileReader &file = *reinterpret_cast(datasource); FileReader::off_t result = file.GetPosition(); if(!Util::TypeCanHoldValue(result)) { @@ -698,7 +723,7 @@ static long VorbisfileFilereaderTell(void *datasource) return static_cast(result); } -#endif // MPT_WITH_VORBIS && MPT_WITH_VORBISFILE +#endif // MPT_WITH_VORBIS && MPT_WITH_VORBISFILE struct MO3ContainerHeader @@ -776,7 +801,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) { return false; } -#endif // !MPT_BUILD_FUZZER +#endif // !MPT_BUILD_FUZZER } std::vector musicData(musicSize); @@ -799,9 +824,9 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) MO3FileHeader fileHeader; if(!musicChunk.ReadStruct(fileHeader) - || fileHeader.numChannels == 0 || fileHeader.numChannels > MAX_BASECHANNELS - || fileHeader.numInstruments >= MAX_INSTRUMENTS - || fileHeader.numSamples >= MAX_SAMPLES) + || fileHeader.numChannels == 0 || fileHeader.numChannels > MAX_BASECHANNELS + || fileHeader.numInstruments >= MAX_INSTRUMENTS + || fileHeader.numSamples >= MAX_SAMPLES) { return false; } @@ -813,34 +838,16 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) m_nDefaultSpeed = fileHeader.defaultSpeed ? fileHeader.defaultSpeed : 6; m_nDefaultTempo.Set(fileHeader.defaultTempo ? fileHeader.defaultTempo : 125, 0); - mpt::ustring originalFormatType; - mpt::ustring originalFormatName; if(fileHeader.flags & MO3FileHeader::isIT) - { SetType(MOD_TYPE_IT); - originalFormatType = U_("it"); - originalFormatName = U_("Impulse Tracker"); - } else if(fileHeader.flags & MO3FileHeader::isS3M) - { + else if(fileHeader.flags & MO3FileHeader::isS3M) SetType(MOD_TYPE_S3M); - originalFormatType = U_("s3m"); - originalFormatName = U_("ScreamTracker 3"); - } else if(fileHeader.flags & MO3FileHeader::isMOD) - { + else if(fileHeader.flags & MO3FileHeader::isMOD) SetType(MOD_TYPE_MOD); - originalFormatType = U_("mod"); - originalFormatName = U_("Generic MOD"); - } else if(fileHeader.flags & MO3FileHeader::isMTM) - { + else if(fileHeader.flags & MO3FileHeader::isMTM) SetType(MOD_TYPE_MTM); - originalFormatType = U_("mtm"); - originalFormatName = U_("MultiTracker"); - } else - { + else SetType(MOD_TYPE_XM); - originalFormatType = U_("xm"); - originalFormatName = U_("FastTracker 2"); - } if(fileHeader.flags & MO3FileHeader::linearSlides) m_SongFlags.set(SONG_LINEARSLIDES); @@ -858,9 +865,9 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) m_playBehaviour.set(kMODVBlankTiming); if(m_nType == MOD_TYPE_IT) - m_nDefaultGlobalVolume = std::min(fileHeader.globalVol, 128) * 2; + m_nDefaultGlobalVolume = std::min(fileHeader.globalVol.get(), uint8(128)) * 2; else if(m_nType == MOD_TYPE_S3M) - m_nDefaultGlobalVolume = std::min(fileHeader.globalVol, 64) * 4; + m_nDefaultGlobalVolume = std::min(fileHeader.globalVol.get(), uint8(64)) * 4; if(fileHeader.sampleVolume < 0) m_nSamplePreAmp = fileHeader.sampleVolume + 52; @@ -872,7 +879,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) for(CHANNELINDEX i = 0; i < headerChannels; i++) { if(m_nType == MOD_TYPE_IT) - ChnSettings[i].nVolume = std::min(fileHeader.chnVolume[i], 64); + ChnSettings[i].nVolume = std::min(fileHeader.chnVolume[i].get(), uint8(64)); if(m_nType != MOD_TYPE_XM) { if(fileHeader.chnPan[i] == 127) @@ -990,7 +997,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) L= 06 00 22 x */ - static const ModCommand::COMMAND effTrans[] = + static constexpr ModCommand::COMMAND effTrans[] = { CMD_NONE, CMD_NONE, CMD_NONE, CMD_ARPEGGIO, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO, CMD_VIBRATO, @@ -1051,11 +1058,16 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) case 0x01: // Note m.note = cmd[1]; - if(m.note < 120) m.note += noteOffset; - else if(m.note == 0xFF) m.note = NOTE_KEYOFF; - else if(m.note == 0xFE) m.note = NOTE_NOTECUT; - else m.note = NOTE_FADE; - if(!m.IsAmigaNote()) onlyAmigaNotes = false; + if(m.note < 120) + m.note += noteOffset; + else if(m.note == 0xFF) + m.note = NOTE_KEYOFF; + else if(m.note == 0xFE) + m.note = NOTE_NOTECUT; + else + m.note = NOTE_FADE; + if(!m.IsAmigaNote()) + onlyAmigaNotes = false; break; case 0x02: // Instrument @@ -1108,7 +1120,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) break; } if((m_nType == MOD_TYPE_IT && !(cmd[1] & 0x03)) - || (m_nType == MOD_TYPE_XM && !(cmd[1] & 0x0F))) + || (m_nType == MOD_TYPE_XM && !(cmd[1] & 0x0F))) { m.volcmd = VOLCMD_PANNING; m.vol = cmd[1] / 4; @@ -1259,10 +1271,12 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) if(isSampleMode || (pIns = AllocateInstrument(ins)) == nullptr) { // Even in IT sample mode, instrument headers are still stored.... - while(musicChunk.ReadUint8() != 0); + while(musicChunk.ReadUint8() != 0) + ; if(version >= 5) { - while(musicChunk.ReadUint8() != 0); + while(musicChunk.ReadUint8() != 0) + ; } musicChunk.Skip(sizeof(MO3Instrument)); continue; @@ -1270,11 +1284,11 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) std::string name; musicChunk.ReadNullString(name); - mpt::String::Copy(pIns->name, name); + pIns->name = name; if(version >= 5) { musicChunk.ReadNullString(name); - mpt::String::Copy(pIns->filename, name); + pIns->filename = name; } MO3Instrument insHeader; @@ -1297,11 +1311,11 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) ModSample &sample = Samples[smp]; std::string name; musicChunk.ReadNullString(name); - mpt::String::Copy(m_szNames[smp], name); + m_szNames[smp] = name; if(version >= 5) { musicChunk.ReadNullString(name); - mpt::String::Copy(sample.filename, name); + sample.filename = name; } MO3Sample smpHeader; @@ -1323,11 +1337,11 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) { // Uncompressed sample SampleIO( - (smpHeader.flags & MO3Sample::smp16Bit) ? SampleIO::_16bit : SampleIO::_8bit, - (smpHeader.flags & MO3Sample::smpStereo) ? SampleIO::stereoSplit : SampleIO::mono, - SampleIO::littleEndian, - SampleIO::signedPCM) - .ReadSample(Samples[smp], file); + (smpHeader.flags & MO3Sample::smp16Bit) ? SampleIO::_16bit : SampleIO::_8bit, + (smpHeader.flags & MO3Sample::smpStereo) ? SampleIO::stereoSplit : SampleIO::mono, + SampleIO::littleEndian, + SampleIO::signedPCM) + .ReadSample(Samples[smp], file); } else if(smpHeader.compressedSize < 0 && (smp + smpHeader.compressedSize) > 0) { // Duplicate sample @@ -1341,8 +1355,10 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) } } else if(smpHeader.compressedSize > 0) { - if(smpHeader.flags & MO3Sample::smp16Bit) sample.uFlags.set(CHN_16BIT); - if(smpHeader.flags & MO3Sample::smpStereo) sample.uFlags.set(CHN_STEREO); + if(smpHeader.flags & MO3Sample::smp16Bit) + sample.uFlags.set(CHN_16BIT); + if(smpHeader.flags & MO3Sample::smpStereo) + sample.uFlags.set(CHN_STEREO); FileReader sampleData = file.ReadChunk(smpHeader.compressedSize); const uint8 numChannels = sample.GetNumChannels(); @@ -1351,7 +1367,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) { // In the best case, MO3 compression represents each sample point as two bits. // As a result, if we have a file length of n, we know that the sample can be at most n*4 sample points long. - size_t maxLength = smpHeader.compressedSize; + auto maxLength = sampleData.GetLength(); uint8 maxSamplesPerByte = 4 / numChannels; if(Util::MaxValueOfType(maxLength) / maxSamplesPerByte >= maxLength) maxLength *= maxSamplesPerByte; @@ -1414,6 +1430,13 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) { unsupportedSamples = true; } + } else if(compression == MO3Sample::smpOPLInstrument) + { + OPLPatch patch; + if(sampleData.ReadArray(patch)) + { + sample.SetAdlib(true, patch); + } } else { unsupportedSamples = true; @@ -1460,7 +1483,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) // We could in theory only adjust the header and pass 2 chunks to libvorbisfile. // Another option would be to demux both chunks on our own (or using libogg) and pass the raw packet data to libvorbis directly. - mpt::ostringstream mergedStream(std::ios::binary); + std::ostringstream mergedStream(std::ios::binary); mergedStream.imbue(std::locale::classic()); sampleChunks[sharedOggHeader - 1].chunk.Rewind(); @@ -1509,7 +1532,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) // We assume same ordering of streams in both header and data if // multiple streams are present. - mpt::ostringstream mergedStream(std::ios::binary); + std::ostringstream mergedStream(std::ios::binary); mergedStream.imbue(std::locale::classic()); sampleChunks[sharedOggHeader - 1].chunk.Rewind(); @@ -1585,7 +1608,8 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) sampleChunk.chunk.Rewind(); FileReader::PinnedRawDataView sampleChunkView = sampleChunk.chunk.GetPinnedRawDataView(); - mergedData.insert(mergedData.end(), mpt::byte_cast(sampleChunkView.begin()), mpt::byte_cast(sampleChunkView.end())); + mpt::span sampleChunkViewSpan = mpt::byte_cast>(sampleChunkView.span()); + mergedData.insert(mergedData.end(), sampleChunkViewSpan.begin(), sampleChunkViewSpan.end()); #endif } @@ -1594,15 +1618,15 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) FileReader &sampleData = sharedHeader ? mergedDataChunk : sampleChunk.chunk; FileReader &headerChunk = sampleData; -#else // !(MPT_WITH_VORBIS && MPT_WITH_VORBISFILE) +#else // !(MPT_WITH_VORBIS && MPT_WITH_VORBISFILE) FileReader &sampleData = sampleChunk.chunk; FileReader &headerChunk = sharedHeader ? sampleChunks[sharedOggHeader - 1].chunk : sampleData; #if defined(MPT_WITH_STBVORBIS) std::size_t initialRead = sharedHeader ? sampleChunk.headerSize : headerChunk.GetLength(); -#endif // MPT_WITH_STBVORBIS +#endif // MPT_WITH_STBVORBIS -#endif // MPT_WITH_VORBIS && MPT_WITH_VORBISFILE +#endif // MPT_WITH_VORBIS && MPT_WITH_VORBISFILE headerChunk.Rewind(); if(sharedHeader && !headerChunk.CanRead(sampleChunk.headerSize)) @@ -1611,17 +1635,16 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) #if defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) ov_callbacks callbacks = { - &VorbisfileFilereaderRead, - &VorbisfileFilereaderSeek, - NULL, - &VorbisfileFilereaderTell - }; + &VorbisfileFilereaderRead, + &VorbisfileFilereaderSeek, + nullptr, + &VorbisfileFilereaderTell}; OggVorbis_File vf; MemsetZero(vf); if(ov_open_callbacks(&sampleData, &vf, nullptr, 0, callbacks) == 0) { if(ov_streams(&vf) == 1) - { // we do not support chained vorbis samples + { // we do not support chained vorbis samples vorbis_info *vi = ov_info(&vf, -1); if(vi && vi->rate > 0 && vi->channels > 0) { @@ -1696,7 +1719,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) headerChunk.Skip(consumed); } FileReader::PinnedRawDataView sampleDataView = sampleData.GetPinnedRawDataView(); - const mpt::byte* data = sampleDataView.data(); + const std::byte *data = sampleDataView.data(); std::size_t dataLeft = sampleDataView.size(); if(!sharedHeader) { @@ -1712,7 +1735,7 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) sample.AllocateSample(); SmpLength offset = 0; while((error == VORBIS__no_error || (error == VORBIS_need_more_data && dataLeft > 0)) - && offset < sample.nLength && sample.HasSampleData()) + && offset < sample.nLength && sample.HasSampleData()) { int channels = 0, decodedSamples = 0; float **output; @@ -1740,11 +1763,11 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) unsupportedSamples = true; } -#else // !VORBIS +#else // !VORBIS unsupportedSamples = true; -#endif // VORBIS +#endif // VORBIS } } @@ -1780,14 +1803,13 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) { ReadMixPluginChunk(pluginChunk, m_MixPlugins[plug - 1]); } -#endif // NO_PLUGINS +#endif // NO_PLUGINS } } mpt::ustring madeWithTracker; uint16 cwtv = 0; uint16 cmwt = 0; - MPT_UNUSED_VARIABLE(cmwt); while(musicChunk.CanRead(8)) { uint32 id = musicChunk.ReadUint32LE(); @@ -1811,14 +1833,14 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) cwtv = chunk.ReadUint16LE(); break; case MOD_TYPE_XM: - chunk.ReadString(madeWithTracker, mpt::CharsetCP437, std::min(FileReader::off_t(32), chunk.GetLength())); + chunk.ReadString(madeWithTracker, mpt::Charset::CP437, std::min(FileReader::off_t(32), chunk.GetLength())); break; case MOD_TYPE_MTM: - { - uint8 mtmVersion = chunk.ReadUint8(); - madeWithTracker = mpt::format(U_("MultiTracker %1.%2"))(mtmVersion >> 4, mtmVersion & 0x0F); - } - break; + { + uint8 mtmVersion = chunk.ReadUint8(); + madeWithTracker = mpt::format(U_("MultiTracker %1.%2"))(mtmVersion >> 4, mtmVersion & 0x0F); + } + break; default: break; } @@ -1875,8 +1897,8 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) } if((GetType() == MOD_TYPE_IT && cwtv >= 0x0100 && cwtv < 0x0214) - || (GetType() == MOD_TYPE_S3M && cwtv >= 0x3100 && cwtv < 0x3214) - || (GetType() == MOD_TYPE_S3M && cwtv >= 0x1300 && cwtv < 0x1320)) + || (GetType() == MOD_TYPE_S3M && cwtv >= 0x3100 && cwtv < 0x3214) + || (GetType() == MOD_TYPE_S3M && cwtv >= 0x1300 && cwtv < 0x1320)) { // Ignore MIDI data in files made with IT older than version 2.14 and old ST3 versions. m_MidiCfg.ClearZxxMacros(); @@ -1885,26 +1907,28 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) if(fileHeader.flags & MO3FileHeader::modplugMode) { // Apply some old ModPlug (mis-)behaviour - for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) + if(!m_dwLastSavedWithVersion) { - if(ModInstrument *ins = Instruments[i]) + // These fixes are only applied when the OpenMPT version number is not known, as otherwise the song upgrade feature will take care of it. + for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) { - // Fix pitch / filter envelope being shortened by one tick - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00)) + if(ModInstrument *ins = Instruments[i]) + { + // Fix pitch / filter envelope being shortened by one tick (for files before v1.20) ins->GetEnvelope(ENV_PITCH).Convert(MOD_TYPE_XM, GetType()); - // Fix excessive pan swing range - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00)) + // Fix excessive pan swing range (for files before v1.26) ins->nPanSwing = (ins->nPanSwing + 3) / 4u; + } } } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("1.18.00.00")) { m_playBehaviour.reset(kITOffset); - m_playBehaviour.reset(kFT2OffsetOutOfRange); + m_playBehaviour.reset(kFT2ST3OffsetOutOfRange); } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 23, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("1.23.00.00")) m_playBehaviour.reset(kFT2Periods); - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("1.26.00.00")) m_playBehaviour.reset(kITInstrWithNoteOff); } @@ -1915,15 +1939,46 @@ bool CSoundFile::ReadMO3(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = mpt::format(U_("Un4seen MO3 v%1"))(version); m_modFormat.type = U_("mo3"); - m_modFormat.originalType = std::move(originalFormatType); - m_modFormat.originalFormatName = std::move(originalFormatName); + + switch(GetType()) + { + case MOD_TYPE_MTM: + m_modFormat.originalType = U_("mtm"); + m_modFormat.originalFormatName = U_("MultiTracker"); + break; + case MOD_TYPE_MOD: + m_modFormat.originalType = U_("mod"); + m_modFormat.originalFormatName = U_("Generic MOD"); + break; + case MOD_TYPE_XM: + m_modFormat.originalType = U_("xm"); + m_modFormat.originalFormatName = U_("FastTracker 2"); + break; + case MOD_TYPE_S3M: + m_modFormat.originalType = U_("s3m"); + m_modFormat.originalFormatName = U_("ScreamTracker 3"); + break; + case MOD_TYPE_IT: + m_modFormat.originalType = U_("it"); + if(cmwt) + m_modFormat.originalFormatName = mpt::format(U_("Impulse Tracker %1.%2"))(cmwt >> 8, mpt::ufmt::hex0<2>(cmwt & 0xFF)); + else + m_modFormat.originalFormatName = U_("Impulse Tracker"); + break; + case MOD_TYPE_MPT: + m_modFormat.originalType = U_("mptm"); + m_modFormat.originalFormatName = U_("OpenMPT MPTM"); + break; + default: + MPT_ASSERT_NOTREACHED(); + } m_modFormat.madeWithTracker = std::move(madeWithTracker); if(m_dwLastSavedWithVersion) - m_modFormat.charset = mpt::CharsetWindows1252; + m_modFormat.charset = mpt::Charset::Windows1252; else if(GetType() == MOD_TYPE_MOD) - m_modFormat.charset = mpt::CharsetISO8859_1; + m_modFormat.charset = mpt::Charset::ISO8859_1; else - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; if(unsupportedSamples) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp index 43d5976a9..f909e9b60 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mod.cpp @@ -19,7 +19,7 @@ #ifdef MPT_EXTERNAL_SAMPLES // For loading external data in Startrekker files #include "../common/mptPathString.h" -#endif // MPT_EXTERNAL_SAMPLES +#endif // MPT_EXTERNAL_SAMPLES OPENMPT_NAMESPACE_BEGIN @@ -61,6 +61,7 @@ void CSoundFile::ConvertModCommand(ModCommand &m) case 'P' - 55: m.command = CMD_PANNINGSLIDE; break; case 'R' - 55: m.command = CMD_RETRIG; break; case 'T' - 55: m.command = CMD_TREMOR; break; + case 'W' - 55: m.command = CMD_DUMMY; break; case 'X' - 55: m.command = CMD_XFINEPORTAUPDOWN; break; case 'Y' - 55: m.command = CMD_PANBRELLO; break; //34 case 'Z' - 55: m.command = CMD_MIDI; break; //35 @@ -106,7 +107,7 @@ void CSoundFile::ModSaveCommand(uint8 &command, uint8 ¶m, bool toXM, bool co { if(param <= 0x80) { - param = MIN(param << 1, 0xFF); + param = mpt::saturate_cast(param * 2); } else if(param == 0xA4) // surround { @@ -128,8 +129,8 @@ void CSoundFile::ModSaveCommand(uint8 &command, uint8 ¶m, bool toXM, bool co case CMD_VOLUME: command = 0x0C; break; case CMD_PATTERNBREAK: command = 0x0D; param = ((param / 10) << 4) | (param % 10); break; case CMD_MODCMDEX: command = 0x0E; break; - case CMD_SPEED: command = 0x0F; param = std::min(param, 0x1F); break; - case CMD_TEMPO: command = 0x0F; param = std::max(param, 0x20); break; + case CMD_SPEED: command = 0x0F; param = std::min(param, uint8(0x1F)); break; + case CMD_TEMPO: command = 0x0F; param = std::max(param, uint8(0x20)); break; case CMD_GLOBALVOLUME: command = 'G' - 55; break; case CMD_GLOBALVOLSLIDE: command = 'H' - 55; break; case CMD_KEYOFF: command = 'K' - 55; break; @@ -137,6 +138,7 @@ void CSoundFile::ModSaveCommand(uint8 &command, uint8 ¶m, bool toXM, bool co case CMD_PANNINGSLIDE: command = 'P' - 55; break; case CMD_RETRIG: command = 'R' - 55; break; case CMD_TREMOR: command = 'T' - 55; break; + case CMD_DUMMY: command = 'W' - 55; break; case CMD_XFINEPORTAUPDOWN: command = 'X' - 55; if(compatibilityExport && param >= 0x30) // X1x and X2x are legit, everything above are MPT extensions, which don't belong here. param = 0; // Don't set command to 0 to indicate that there *was* some X command here... @@ -197,7 +199,7 @@ void CSoundFile::ModSaveCommand(uint8 &command, uint8 ¶m, bool toXM, bool co } } -#endif // MODPLUG_NO_FILESAVE +#endif // MODPLUG_NO_FILESAVE // File Header @@ -227,13 +229,13 @@ struct MODSampleHeader mptSmp.Initialize(MOD_TYPE_MOD); mptSmp.nLength = length * 2; mptSmp.nFineTune = MOD2XMFineTune(finetune & 0x0F); - mptSmp.nVolume = 4u * std::min(volume, 64); + mptSmp.nVolume = 4u * std::min(volume.get(), uint8(64)); SmpLength lStart = loopStart * 2; SmpLength lLength = loopLength * 2; // See if loop start is incorrect as words, but correct as bytes (like in Soundtracker modules) if(lLength > 2 && (lStart + lLength > mptSmp.nLength) - && (lStart / 2 + lLength <= mptSmp.nLength)) + && (lStart / 2 + lLength <= mptSmp.nLength)) { lStart /= 2; } @@ -315,19 +317,19 @@ struct MODSampleHeader uint32 GetInvalidByteScore() const { return ((volume > 64) ? 1 : 0) - + ((finetune > 15) ? 1 : 0) - + ((loopStart > length * 2) ? 1 : 0); + + ((finetune > 15) ? 1 : 0) + + ((loopStart > length * 2) ? 1 : 0); } // Suggested threshold for rejecting invalid files based on cumulated score returned by GetInvalidByteScore - enum : uint32 { INVALID_BYTE_THRESHOLD = 40 }; - + static constexpr uint32 INVALID_BYTE_THRESHOLD = 40; + // This threshold is used for files where the file magic only gives a // fragile result which alone would lead to too many false positives. // In particular, the files from Inconexia demo by Iguana // (https://www.pouet.net/prod.php?which=830) which have 3 \0 bytes in // the file magic tend to cause misdetection of random files. - enum : uint32 { INVALID_BYTE_FRAGILE_THRESHOLD = 1 }; + static constexpr uint32 INVALID_BYTE_FRAGILE_THRESHOLD = 1; // Retrieve the internal sample format flags for this sample. static SampleIO GetSampleFormat() @@ -342,6 +344,9 @@ struct MODSampleHeader MPT_BINARY_STRUCT(MODSampleHeader, 30) +// Pattern data of a 4-channel MOD file +using MODPatternData = std::array, 4>, 64>; + // Synthesized StarTrekker instruments struct AMInstrument { @@ -369,7 +374,7 @@ struct AMInstrument sample.nLoopStart = 0; sample.nLoopEnd = sample.nLength; sample.uFlags.set(CHN_LOOP); - sample.nVolume = 256; // prelude.mod has volume 0 in sample header + sample.nVolume = 256; // prelude.mod has volume 0 in sample header sample.nVibDepth = mpt::saturate_cast(vibAmp * 2); sample.nVibRate = static_cast(vibSpeed); sample.nVibType = VIB_SINE; @@ -398,7 +403,7 @@ struct AMInstrument const struct { uint16 level, speed; - } points[] = { { startLevel, 0 }, { attack1Level, attack1Speed }, { attack2Level, attack2Speed }, { sustainLevel, decaySpeed }, { sustainLevel, sustainTime }, { 0, releaseSpeed } }; + } points[] = {{startLevel, 0}, {attack1Level, attack1Speed}, {attack2Level, attack2Speed}, {sustainLevel, decaySpeed}, {sustainLevel, sustainTime}, {0, releaseSpeed}}; for(uint8 i = 1; i < CountOf(points); i++) { @@ -499,14 +504,14 @@ static bool IsMagic(const char *magic1, const char (&magic2)[5]) } -static uint32 ReadSample(FileReader &file, MODSampleHeader &sampleHeader, ModSample &sample, char (&sampleName)[MAX_SAMPLENAME], bool is4Chn) +static uint32 ReadSample(FileReader &file, MODSampleHeader &sampleHeader, ModSample &sample, mpt::charbuf &sampleName, bool is4Chn) { file.ReadStruct(sampleHeader); sampleHeader.ConvertToMPT(sample, is4Chn); - mpt::String::Read(sampleName, sampleHeader.name); + sampleName = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); // Get rid of weird characters in sample names. - for(auto &c : sampleName) + for(auto &c : sampleName.buf) { if(c > 0 && c < ' ') { @@ -518,8 +523,36 @@ static uint32 ReadSample(FileReader &file, MODSampleHeader &sampleHeader, ModSam } +// Count malformed bytes in MOD pattern data +static uint32 CountMalformedMODPatternData(const MODPatternData &patternData, const bool allow31Samples) +{ + const uint8 mask = allow31Samples ? 0xE0 : 0xF0; + uint32 malformedBytes = 0; + for(const auto &row : patternData) + { + for(const auto &data : row) + { + if(data[0] & mask) + malformedBytes++; + } + } + return malformedBytes; +} + + +// Check if number of malformed bytes in MOD pattern data exceeds some threshold +template +static bool ValidateMODPatternData(TFileReader &file, const uint32 threshold, const bool allow31Samples) +{ + MODPatternData patternData; + if(!file.Read(patternData)) + return false; + return CountMalformedMODPatternData(patternData, allow31Samples) <= threshold; +} + + // Parse the order list to determine how many patterns are used in the file. -static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERINDEX numOrders, SmpLength totalSampleLen, CHANNELINDEX &numChannels, bool checkForWOW) +static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERINDEX numOrders, SmpLength totalSampleLen, CHANNELINDEX &numChannels, SmpLength wowSampleLen = 0) { PATTERNINDEX numPatterns = 0; // Total number of patterns in file (determined by going through the whole order list) with pattern number < 128 PATTERNINDEX officialPatterns = 0; // Number of patterns only found in the "official" part of the order list (i.e. order positions < claimed order length) @@ -548,12 +581,16 @@ static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERIN const size_t patternStartOffset = file.GetPosition(); const size_t sizeWithoutPatterns = totalSampleLen + patternStartOffset; - if(checkForWOW && sizeWithoutPatterns + numPatterns * 8 * 256 == file.GetLength()) + if(wowSampleLen && (wowSampleLen + patternStartOffset) + numPatterns * 8 * 256 == (file.GetLength() & ~1)) { - // Check if this is a Mod's Grave WOW file... Never seen one of those, but apparently they *do* exist. - // WOW files should use the M.K. magic but are actually 8CHN files. - numChannels = 8; - } else if(numPatterns != officialPatterns && numChannels == 4 && !checkForWOW) + // Check if this is a Mod's Grave WOW file... WOW files use the M.K. magic but are actually 8CHN files. + // We do a simple pattern validation as well for regular MOD files that have non-module data attached at the end + // (e.g. ponylips.mod, MD5 c039af363b1d99a492dafc5b5f9dd949, SHA1 1bee1941c47bc6f913735ce0cf1880b248b8fc93) + file.Seek(patternStartOffset + numPatterns * 4 * 256); + if(ValidateMODPatternData(file, 16, true)) + numChannels = 8; + file.Seek(patternStartOffset); + } else if(numPatterns != officialPatterns && numChannels == 4 && !wowSampleLen) { // Fix SoundTracker modules where "hidden" patterns should be ignored. // razor-1911.mod (MD5 b75f0f471b0ae400185585ca05bf7fe8, SHA1 4de31af234229faec00f1e85e1e8f78f405d454b) @@ -568,21 +605,8 @@ static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERIN // Hence, we have a peek at the first hidden pattern and check if it contains a lot of illegal data. // If that is the case, we assume it's part of the sample data and only consider the "official" patterns. file.Seek(patternStartOffset + officialPatterns * 1024); - int illegalBytes = 0; - for(int i = 0; i < 256; i++) - { - uint8 data[4]; - file.ReadArray(data); - if(data[0] & 0xE0) - { - illegalBytes++; - if(illegalBytes > 64) - { - numPatterns = officialPatterns; - break; - } - } - } + if(!ValidateMODPatternData(file, 64, true)) + numPatterns = officialPatterns; file.Seek(patternStartOffset); } @@ -620,21 +644,19 @@ static PATTERNINDEX GetNumPatterns(FileReader &file, ModSequence &Order, ORDERIN void CSoundFile::ReadMODPatternEntry(FileReader &file, ModCommand &m) { - uint8 data[4]; - file.ReadArray(data); - ReadMODPatternEntry(data, m); + ReadMODPatternEntry(file.ReadArray(), m); } -void CSoundFile::ReadMODPatternEntry(const uint8 (&data)[4], ModCommand &m) +void CSoundFile::ReadMODPatternEntry(const std::array data, ModCommand &m) { // Read Period uint16 period = (((static_cast(data[0]) & 0x0F) << 8) | data[1]); size_t note = NOTE_NONE; if(period > 0 && period != 0xFFF) { - note = mpt::size(ProTrackerPeriodTable) + 23 + NOTE_MIN; - for(size_t i = 0; i < mpt::size(ProTrackerPeriodTable); i++) + note = std::size(ProTrackerPeriodTable) + 23 + NOTE_MIN; + for(size_t i = 0; i < std::size(ProTrackerPeriodTable); i++) { if(period >= ProTrackerPeriodTable[i]) { @@ -664,7 +686,7 @@ void CSoundFile::ReadMODPatternEntry(const uint8 (&data)[4], ModCommand &m) struct MODMagicResult { - const MPT_UCHAR_TYPE *madeWithTracker = nullptr; + const mpt::uchar *madeWithTracker = nullptr; uint32 invalidByteThreshold = MODSampleHeader::INVALID_BYTE_THRESHOLD; CHANNELINDEX numChannels = 0; bool isNoiseTracker = false; @@ -676,29 +698,29 @@ struct MODMagicResult static bool CheckMODMagic(const char magic[4], MODMagicResult &result) { - if(IsMagic(magic, "M.K.") // ProTracker and compatible - || IsMagic(magic, "M!K!") // ProTracker (>64 patterns) - || IsMagic(magic, "PATT") // ProTracker 3.6 - || IsMagic(magic, "NSMS") // kingdomofpleasure.mod by bee hunter - || IsMagic(magic, "LARD")) // judgement_day_gvine.mod by 4-mat + if(IsMagic(magic, "M.K.") // ProTracker and compatible + || IsMagic(magic, "M!K!") // ProTracker (>64 patterns) + || IsMagic(magic, "PATT") // ProTracker 3.6 + || IsMagic(magic, "NSMS") // kingdomofpleasure.mod by bee hunter + || IsMagic(magic, "LARD")) // judgement_day_gvine.mod by 4-mat { result.madeWithTracker = UL_("Generic ProTracker or compatible"); result.numChannels = 4; - } else if(IsMagic(magic, "M&K!") // "His Master's Noise" musicdisk - || IsMagic(magic, "FEST") // "His Master's Noise" musicdisk - || IsMagic(magic, "N.T.")) + } else if(IsMagic(magic, "M&K!") // "His Master's Noise" musicdisk + || IsMagic(magic, "FEST") // "His Master's Noise" musicdisk + || IsMagic(magic, "N.T.")) { result.madeWithTracker = UL_("NoiseTracker"); result.isNoiseTracker = true; result.numChannels = 4; } else if(IsMagic(magic, "OKTA") - || IsMagic(magic, "OCTA")) + || IsMagic(magic, "OCTA")) { // Oktalyzer result.madeWithTracker = UL_("Oktalyzer"); result.numChannels = 8; } else if(IsMagic(magic, "CD81") - || IsMagic(magic, "CD61")) + || IsMagic(magic, "CD61")) { // Octalyser on Atari STe/Falcon result.madeWithTracker = UL_("Octalyser (Atari)"); @@ -727,8 +749,8 @@ static bool CheckMODMagic(const char magic[4], MODMagicResult &result) result.madeWithTracker = UL_("Generic MOD-compatible Tracker"); result.isGenericMultiChannel = true; result.numChannels = magic[0] - '0'; - } else if(magic[0] >= '1' && magic[0] <= '9' && magic[1]>='0' && magic[1] <= '9' - && (!memcmp(magic + 2, "CH", 2) || !memcmp(magic + 2, "CN", 2))) + } else if(magic[0] >= '1' && magic[0] <= '9' && magic[1] >= '0' && magic[1] <= '9' + && (!memcmp(magic + 2, "CH", 2) || !memcmp(magic + 2, "CN", 2))) { // xxCN / xxCH - Many trackers result.madeWithTracker = UL_("Generic MOD-compatible Tracker"); @@ -792,8 +814,8 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) MODMagicResult modMagicResult; if(!CheckMODMagic(magic, modMagicResult) - || modMagicResult.numChannels < 1 - || modMagicResult.numChannels > MAX_BASECHANNELS) + || modMagicResult.numChannels < 1 + || modMagicResult.numChannels > MAX_BASECHANNELS) { return false; } @@ -822,13 +844,14 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) const bool isMdKd = IsMagic(magic, "M.K."); // Adjust finetune values for modules saved with "His Master's Noisetracker" const bool isHMNT = IsMagic(magic, "M&K!") || IsMagic(magic, "FEST"); + bool maybeWOW = isMdKd; // Reading song title file.Seek(0); file.ReadString(m_songName, 20); // Load Sample Headers - SmpLength totalSampleLen = 0; + SmpLength totalSampleLen = 0, wowSampleLen = 0; m_nSamples = 31; uint32 invalidBytes = 0; for(SAMPLEINDEX smp = 1; smp <= 31; smp++) @@ -838,15 +861,22 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) totalSampleLen += Samples[smp].nLength; if(isHMNT) - { Samples[smp].nFineTune = -static_cast(sampleHeader.finetune << 3); - } else if(Samples[smp].nLength > 65535) - { + else if(Samples[smp].nLength > 65535) isNoiseTracker = false; - } + if(sampleHeader.length && !sampleHeader.loopLength) - { hasRepLen0 = true; + + if(maybeWOW) + { + // Some WOW files rely on sample length 1 being counted as well + wowSampleLen += sampleHeader.length * 2; + // WOW files are converted 669 files, which don't support finetune or default volume + if(sampleHeader.finetune) + maybeWOW = false; + else if(sampleHeader.length > 0 && sampleHeader.volume != 64) + maybeWOW = false; } } // If there is too much binary garbage in the sample headers, reject the file. @@ -858,7 +888,12 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) // Read order information MODFileHeader fileHeader; file.ReadStruct(fileHeader); - file.Skip(4); // Magic bytes (we already parsed these) + file.Skip(4); // Magic bytes (we already parsed these) + + if(fileHeader.restartPos > 0) + maybeWOW = false; + if(!maybeWOW) + wowSampleLen = 0; ReadOrderFromArray(Order(), fileHeader.orderList); @@ -878,11 +913,12 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) } // Get number of patterns (including some order list sanity checks) - PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), realOrders, totalSampleLen, m_nChannels, isMdKd); - if(isMdKd && GetNumChannels() == 8) + PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), realOrders, totalSampleLen, m_nChannels, wowSampleLen); + if(maybeWOW && GetNumChannels() == 8) { - // M.K. with 8 channels = Grave Composer + // M.K. with 8 channels = Mod's Grave modMagicResult.madeWithTracker = UL_("Mod's Grave"); + isGenericMultiChannel = true; } if(isFLT8) @@ -963,7 +999,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) extendedPanning = true; } else if(m.command == 0x0E && (m.param & 0xF0) == 0x80) { - maxPanning = std::max(maxPanning, (m.param & 0x0F) << 4); + maxPanning = std::max(maxPanning, static_cast((m.param & 0x0F) << 4)); } } } @@ -1089,7 +1125,8 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) lastInstrument[chn] = m.instr; } } - if(hasSpeedOnRow && hasTempoOnRow) definitelyCIA = true; + if(hasSpeedOnRow && hasTempoOnRow) + definitelyCIA = true; } } @@ -1103,7 +1140,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) m_playBehaviour.set(kMODOutOfRangeNoteDelay); m_playBehaviour.set(kMODTempoOnSecondTick); // Arbitrary threshold for deciding that 8xx effects are only used as sync markers - if(maxPanning < 0x20) + if(maxPanning < 0x30) { m_playBehaviour.set(kMODIgnorePanning); if(fileHeader.restartPos != 0x7F) @@ -1174,12 +1211,12 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) if(!filename.empty()) { // Find instrument definition file - const mpt::PathString exts[] = { P_(".nt"), P_(".NT"), P_(".as"), P_(".AS") }; + const mpt::PathString exts[] = {P_(".nt"), P_(".NT"), P_(".as"), P_(".AS")}; for(const auto &ext : exts) { mpt::PathString infoName = filename + ext; char stMagic[16]; - if(infoName.IsFile() && amFile.Open(infoName) && (amData = GetFileReader(amFile)).IsValid() && amData.ReadArray(stMagic)) + if(infoName.IsFile() && amFile.Open(infoName, SettingCacheCompleteFileBeforeLoading()) && (amData = GetFileReader(amFile)).IsValid() && amData.ReadArray(stMagic)) { if(!memcmp(stMagic, "ST1.2 ModuleINFO", 16)) modMagicResult.madeWithTracker = UL_("Startrekker 1.2"); @@ -1213,7 +1250,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) { break; } - mpt::String::Copy(ins->name, m_szNames[smp]); + ins->name = m_szNames[smp]; AMInstrument am; // Allow partial reads for fa.worse face.mod @@ -1226,7 +1263,7 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) amData.Skip(120 - sizeof(AMInstrument)); } } -#endif // MPT_EXTERNAL_SAMPLES || MPT_BUILD_FUZZER +#endif // MPT_EXTERNAL_SAMPLES || MPT_BUILD_FUZZER // Fix VBlank MODs. Arbitrary threshold: 10 minutes. // Basically, this just converts all tempo commands into speed commands @@ -1257,17 +1294,18 @@ bool CSoundFile::ReadMOD(FileReader &file, ModLoadingFlags loadFlags) } std::transform(std::begin(magic), std::end(magic), std::begin(magic), [](unsigned char c) -> unsigned char { return (c < ' ') ? ' ' : c; }); - m_modFormat.formatName = mpt::format(U_("ProTracker MOD (%1)"))(mpt::ToUnicode(mpt::CharsetASCII, std::string(std::begin(magic), std::end(magic)))); + m_modFormat.formatName = mpt::format(U_("ProTracker MOD (%1)"))(mpt::ToUnicode(mpt::Charset::ASCII, std::string(std::begin(magic), std::end(magic)))); m_modFormat.type = U_("mod"); - if(modMagicResult.madeWithTracker) m_modFormat.madeWithTracker = modMagicResult.madeWithTracker; - m_modFormat.charset = mpt::CharsetISO8859_1; + if(modMagicResult.madeWithTracker) + m_modFormat.madeWithTracker = modMagicResult.madeWithTracker; + m_modFormat.charset = mpt::Charset::ISO8859_1; return true; } // Check if a name string is valid (i.e. doesn't contain binary garbage data) -template +template static uint32 CountInvalidChars(const char (&name)[N]) { uint32 invalidChars = 0; @@ -1286,13 +1324,13 @@ static uint32 CountInvalidChars(const char (&name)[N]) // Thanks for Fraggie for this information! (https://www.un4seen.com/forum/?topic=14471.msg100829#msg100829) enum STVersions { - UST1_00, // Ultimate Soundtracker 1.0-1.21 (K. Obarski) - UST1_80, // Ultimate Soundtracker 1.8-2.0 (K. Obarski) - ST2_00_Exterminator, // SoundTracker 2.0 (The Exterminator), D.O.C. Sountracker II (Unknown/D.O.C.) - ST_III, // Defjam Soundtracker III (Il Scuro/Defjam), Alpha Flight SoundTracker IV (Alpha Flight), D.O.C. SoundTracker IV (Unknown/D.O.C.), D.O.C. SoundTracker VI (Unknown/D.O.C.) - ST_IX, // D.O.C. SoundTracker IX (Unknown/D.O.C.) - MST1_00, // Master Soundtracker 1.0 (Tip/The New Masters) - ST2_00, // SoundTracker 2.0, 2.1, 2.2 (Unknown/D.O.C.) + UST1_00, // Ultimate Soundtracker 1.0-1.21 (K. Obarski) + UST1_80, // Ultimate Soundtracker 1.8-2.0 (K. Obarski) + ST2_00_Exterminator, // SoundTracker 2.0 (The Exterminator), D.O.C. Sountracker II (Unknown/D.O.C.) + ST_III, // Defjam Soundtracker III (Il Scuro/Defjam), Alpha Flight SoundTracker IV (Alpha Flight), D.O.C. SoundTracker IV (Unknown/D.O.C.), D.O.C. SoundTracker VI (Unknown/D.O.C.) + ST_IX, // D.O.C. SoundTracker IX (Unknown/D.O.C.) + MST1_00, // Master Soundtracker 1.0 (Tip/The New Masters) + ST2_00, // SoundTracker 2.0, 2.1, 2.2 (Unknown/D.O.C.) }; @@ -1306,8 +1344,6 @@ struct M15FileHeaders MPT_BINARY_STRUCT(M15FileHeaders, 20 + 15 * 30 + 130) -typedef uint8 M15PatternData[64][4][4]; - static bool ValidateHeader(const M15FileHeaders &fileHeaders) { @@ -1333,9 +1369,9 @@ static bool ValidateHeader(const M15FileHeaders &fileHeaders) // Sanity checks - invalid character count adjusted for ata.mod (MD5 937b79b54026fa73a1a4d3597c26eace, SHA1 3322ca62258adb9e0ae8e9afe6e0c29d39add874) if(invalidChars > 48 - || sampleHeader.volume > 64 - || sampleHeader.finetune != 0 - || sampleHeader.length > 32768) + || sampleHeader.volume > 64 + || sampleHeader.finetune != 0 + || sampleHeader.length > 32768) { return false; } @@ -1374,39 +1410,11 @@ static bool ValidateHeader(const M15FileHeaders &fileHeaders) } -static uint32 CountIllegalM15PatternBytes(const M15PatternData &patternData) -{ - uint32 illegalBytes = 0; - for(uint8 row = 0; row < 64; ++row) - { - for(uint8 channel = 0; channel < 4; ++channel) - { - if(patternData[row][channel][0] & 0xF0u) - { - illegalBytes++; - } - } - } - return illegalBytes; -} - - template static bool ValidateFirstM15Pattern(TFileReader &file) { - M15PatternData patternData; - if(!file.ReadArray(patternData)) - { - return false; - } - file.SkipBack(sizeof(patternData)); - uint32 invalidBytes = CountIllegalM15PatternBytes(patternData); - // [threshold for all patterns combined] / [max patterns] * [margin, do not reject too much] - if(invalidBytes > 512 / 64 * 2) - { - return false; - } - return true; + // threshold is chosen as: [threshold for all patterns combined] / [max patterns] * [margin, do not reject too much] + return ValidateMODPatternData(file, 512 / 64 * 2, false); } @@ -1421,7 +1429,7 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderM15(MemoryFileReader file, co { return ProbeFailure; } - if(!file.CanRead(sizeof(M15PatternData))) + if(!file.CanRead(sizeof(MODPatternData))) { return ProbeWantMoreData; } @@ -1458,7 +1466,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) InitializeGlobals(MOD_TYPE_MOD); m_playBehaviour.reset(kMODOneShotLoops); m_playBehaviour.set(kMODIgnorePanning); - m_playBehaviour.set(kMODSampleSwap); // untested + m_playBehaviour.set(kMODSampleSwap); // untested m_nChannels = 4; STVersions minVersion = UST1_00; @@ -1475,7 +1483,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) totalSampleLen += Samples[smp].nLength; - if(m_szNames[smp][0] && ((memcmp(m_szNames[smp], "st-", 3) && memcmp(m_szNames[smp], "ST-", 3)) || m_szNames[smp][5] != ':')) + if(m_szNames[smp][0] && ((memcmp(m_szNames[smp].buf, "st-", 3) && memcmp(m_szNames[smp].buf, "ST-", 3)) || m_szNames[smp][5] != ':')) { // Ultimate Soundtracker 1.8 and D.O.C. SoundTracker IX always have sample names containing disk names. hasDiskNames = false; @@ -1498,7 +1506,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) file.ReadStruct(fileHeader); ReadOrderFromArray(Order(), fileHeader.orderList); - PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), fileHeader.numOrders, totalSampleLen, m_nChannels, false); + PATTERNINDEX numPatterns = GetNumPatterns(file, Order(), fileHeader.numOrders, totalSampleLen, m_nChannels); // Most likely just a file with lots of NULs at the start if(fileHeader.restartPos == 0 && fileHeader.numOrders == 0 && numPatterns <= 1) @@ -1521,7 +1529,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) if(!memcmp(songname, "jjk55", 6)) fileHeader.restartPos = 0x78; // Sample 7 in echoing.mod won't "loop" correctly if we don't convert the VBlank tempo. - m_nDefaultTempo.Set(fileHeader.restartPos * 25 / 24); + m_nDefaultTempo.Set(125); if(fileHeader.restartPos != 0x78) { // Convert to CIA timing @@ -1540,7 +1548,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) m_nMaxPeriod = 856 * 4; m_nSamplePreAmp = 64; m_SongFlags.set(SONG_PT_MODE); - mpt::String::Read(m_songName, songname); + m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, songname); // Setup channel pan positions and volume SetupMODPanning(); @@ -1548,17 +1556,17 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) FileReader::off_t patOffset = file.GetPosition(); // Scan patterns to identify Ultimate Soundtracker modules. - uint32 illegalBytes = 0; + uint32 illegalBytes = 0, totalNumDxx = 0; for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) { bool patternInUse = std::find(Order().cbegin(), Order().cend(), pat) != Order().cend(); uint8 numDxx = 0; uint8 emptyCmds = 0; - M15PatternData patternData; + MODPatternData patternData; file.ReadArray(patternData); if(patternInUse) { - illegalBytes += CountIllegalM15PatternBytes(patternData); + illegalBytes += CountMalformedMODPatternData(patternData, false); // Reject files that contain a lot of illegal pattern data. // STK.the final remix (MD5 5ff13cdbd77211d1103be7051a7d89c9, SHA1 e94dba82a5da00a4758ba0c207eb17e3a89c3aa3) // has one illegal byte, so we only reject after an arbitrary threshold has been passed. @@ -1577,10 +1585,10 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) { for(CHANNELINDEX chn = 0; chn < 4; chn++) { - const uint8 (&data)[4] = patternData[row][chn]; + const auto &data = patternData[row][chn]; const uint8 eff = data[2] & 0x0F, param = data[3]; // Check for empty space between the last Dxx command and the beginning of another pattern - if(emptyCmds != 0 && !memcmp(data, "\0\0\0\0", 4)) + if(emptyCmds != 0 && !memcmp(data.data(), "\0\0\0\0", 4)) { emptyCmds++; if(emptyCmds > 32) @@ -1643,8 +1651,13 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) // Not many Dxx commands in one pattern means they were probably pattern breaks minVersion = ST2_00; } + totalNumDxx += numDxx; } + // If there is a huge number of Dxx commands, this is extremely unlikely to be a SoundTracker 2.0 module + if(totalNumDxx > numPatterns + 32u && minVersion == ST2_00) + minVersion = MST1_00; + file.Seek(patOffset); // Reading patterns @@ -1652,7 +1665,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) Patterns.ResizeArray(numPatterns); for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) { - M15PatternData patternData; + MODPatternData patternData; file.ReadArray(patternData); if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, 64)) @@ -1660,7 +1673,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) continue; } - uint8 autoSlide[4] = { 0, 0, 0, 0 }; + uint8 autoSlide[4] = {0, 0, 0, 0}; for(ROWINDEX row = 0; row < 64; row++) { PatternRow rowBase = Patterns[pat].GetpModCommand(row, 0); @@ -1757,7 +1770,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) } } - const MPT_UCHAR_TYPE *madeWithTracker = UL_(""); + const mpt::uchar *madeWithTracker = UL_(""); switch(minVersion) { case UST1_00: @@ -1786,7 +1799,7 @@ bool CSoundFile::ReadM15(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Soundtracker"); m_modFormat.type = U_("stk"); m_modFormat.madeWithTracker = madeWithTracker; - m_modFormat.charset = mpt::CharsetISO8859_1; + m_modFormat.charset = mpt::Charset::ISO8859_1; // Reading samples if(loadFlags & loadSampleData) @@ -1835,8 +1848,7 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderICE(MemoryFileReader file, co { return ProbeFailure; } - const uint8 numOrders = file.ReadUint8(); - const uint8 numTracks = file.ReadUint8(); + const auto [numOrders, numTracks] = file.ReadArray(); if(numOrders > 128) { return ProbeFailure; @@ -1869,20 +1881,20 @@ bool CSoundFile::ReadICE(FileReader &file, ModLoadingFlags loadFlags) InitializeGlobals(MOD_TYPE_MOD); m_playBehaviour.reset(kMODOneShotLoops); m_playBehaviour.set(kMODIgnorePanning); - m_playBehaviour.set(kMODSampleSwap); // untested + m_playBehaviour.set(kMODSampleSwap); // untested if(IsMagic(magic, "MTN\0")) { m_modFormat.formatName = U_("MnemoTroN SoundTracker"); m_modFormat.type = U_("st26"); m_modFormat.madeWithTracker = U_("SoundTracker 2.6"); - m_modFormat.charset = mpt::CharsetISO8859_1; + m_modFormat.charset = mpt::Charset::ISO8859_1; } else if(IsMagic(magic, "IT10")) { m_modFormat.formatName = U_("Ice Tracker"); m_modFormat.type = U_("ice"); m_modFormat.madeWithTracker = U_("Ice Tracker 1.0 / 1.1"); - m_modFormat.charset = mpt::CharsetISO8859_1; + m_modFormat.charset = mpt::Charset::ISO8859_1; } else { return false; @@ -1905,8 +1917,7 @@ bool CSoundFile::ReadICE(FileReader &file, ModLoadingFlags loadFlags) return false; } - const uint8 numOrders = file.ReadUint8(); - const uint8 numTracks = file.ReadUint8(); + const auto [numOrders, numTracks] = file.ReadArray(); if(numOrders > 128) { return false; @@ -1942,7 +1953,7 @@ bool CSoundFile::ReadICE(FileReader &file, ModLoadingFlags loadFlags) // Reading patterns Order().resize(numOrders); - uint8 speed[2] = { 0, 0 }, speedPos = 0; + uint8 speed[2] = {0, 0}, speedPos = 0; Patterns.ResizeArray(numOrders); for(PATTERNINDEX pat = 0; pat < numOrders; pat++) { @@ -1960,8 +1971,8 @@ bool CSoundFile::ReadICE(FileReader &file, ModLoadingFlags loadFlags) ReadMODPatternEntry(file, *m); if((m->command || m->param) - && !(m->command == 0x0E && m->param >= 0x10) // Exx only sets filter - && !(m->command >= 0x05 && m->command <= 0x09)) // These don't exist in ST2.6 + && !(m->command == 0x0E && m->param >= 0x10) // Exx only sets filter + && !(m->command >= 0x05 && m->command <= 0x09)) // These don't exist in ST2.6 { ConvertModCommand(*m); } else @@ -1998,7 +2009,8 @@ bool CSoundFile::ReadICE(FileReader &file, ModLoadingFlags loadFlags) { Patterns[pat].WriteEffect(EffectWriter(CMD_SPEED, speed[speedPos - 1]).Row(row)); speedPos++; - if(speedPos == 3) speedPos = 1; + if(speedPos == 3) + speedPos = 1; } } } @@ -2116,7 +2128,7 @@ bool CSoundFile::ReadPT36(FileReader &file, ModLoadingFlags loadFlags) chunk.Skip(4); if(chunk.ReadMagic("PT") && iffHead.chunksize > 6) { - chunk.ReadString(version, mpt::CharsetISO8859_1, iffHead.chunksize - 6); + chunk.ReadString(version, mpt::Charset::ISO8859_1, iffHead.chunksize - 6); } break; @@ -2145,15 +2157,15 @@ bool CSoundFile::ReadPT36(FileReader &file, ModLoadingFlags loadFlags) bool vblank = (info.flags & 0x100) == 0; m_playBehaviour.set(kMODVBlankTiming, vblank); if(info.volume != 0) - m_nSamplePreAmp = std::min(64, info.volume); + m_nSamplePreAmp = std::min(uint16(64), static_cast(info.volume)); if(info.tempo != 0 && !vblank) m_nDefaultTempo.Set(info.tempo); if(info.name[0]) - mpt::String::Read(m_songName, info.name); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, info.name); if(IsInRange(info.dateMonth, 1, 12) && IsInRange(info.dateDay, 1, 31) && IsInRange(info.dateHour, 0, 23) - && IsInRange(info.dateMinute, 0, 59) && IsInRange(info.dateSecond, 0, 59)) + && IsInRange(info.dateMinute, 0, 59) && IsInRange(info.dateSecond, 0, 59)) { FileHistory mptHistory; mptHistory.loadDate.tm_year = info.dateYear; @@ -2172,7 +2184,7 @@ bool CSoundFile::ReadPT36(FileReader &file, ModLoadingFlags loadFlags) std::string author; commentChunk.ReadString(author, 32); if(author != "UNNAMED AUTHOR") - m_songArtist = mpt::ToUnicode(mpt::CharsetISO8859_1, author); + m_songArtist = mpt::ToUnicode(mpt::Charset::ISO8859_1, author); if(!commentChunk.NoBytesLeft()) { m_songMessage.ReadFixedLineLength(commentChunk, commentChunk.BytesLeft(), 40, 0); @@ -2202,7 +2214,7 @@ bool CSoundFile::SaveMod(std::ostream &f) const // Write song title { char name[20]; - mpt::String::Write(name, m_songName); + mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = m_songName; mpt::IO::Write(f, name); } @@ -2215,11 +2227,11 @@ bool CSoundFile::SaveMod(std::ostream &f) const for(INSTRUMENTINDEX ins = 1; ins <= lastIns; ins++) if (Instruments[ins]) { // Find some valid sample associated with this instrument. - for(size_t i = 0; i < CountOf(Instruments[ins]->Keyboard); i++) + for(auto smp : Instruments[ins]->Keyboard) { - if(Instruments[ins]->Keyboard[i] > 0 && Instruments[ins]->Keyboard[i] <= GetNumSamples()) + if(smp > 0 && smp <= GetNumSamples()) { - sampleSource[ins] = Instruments[ins]->Keyboard[i]; + sampleSource[ins] = smp; break; } } @@ -2236,7 +2248,7 @@ bool CSoundFile::SaveMod(std::ostream &f) const for(SAMPLEINDEX smp = 1; smp <= 31; smp++) { MODSampleHeader sampleHeader; - mpt::String::Write(sampleHeader.name, m_szNames[sampleSource[smp]]); + mpt::String::WriteBuf(mpt::String::maybeNullTerminated, sampleHeader.name) = m_szNames[sampleSource[smp]]; sampleLength[smp] = sampleHeader.ConvertToMOD(sampleSource[smp] <= GetNumSamples() ? GetSample(sampleSource[smp]) : ModSample(MOD_TYPE_MOD)); mpt::IO::Write(f, sampleHeader); } @@ -2316,7 +2328,7 @@ bool CSoundFile::SaveMod(std::ostream &f) const size_t eventByte = 0; for(CHANNELINDEX chn = 0; chn < writeChannels; chn++, eventByte += 4) { - ModCommand &m = rowBase[chn]; + const ModCommand &m = rowBase[chn]; uint8 command = m.command, param = m.param; ModSaveCommand(command, param, false, true); @@ -2324,12 +2336,12 @@ bool CSoundFile::SaveMod(std::ostream &f) const { // Maybe we can save some volume commands... command = 0x0C; - param = MIN(m.vol, 64); + param = std::min(m.vol, uint8(64)); } uint16 period = 0; // Convert note to period - if(m.note >= 24 + NOTE_MIN && m.note < mpt::size(ProTrackerPeriodTable) + 24 + NOTE_MIN) + if(m.note >= 24 + NOTE_MIN && m.note < std::size(ProTrackerPeriodTable) + 24 + NOTE_MIN) { period = ProTrackerPeriodTable[m.note - 24 - NOTE_MIN]; } @@ -2396,7 +2408,7 @@ bool CSoundFile::SaveMod(std::ostream &f) const return true; } -#endif // MODPLUG_NO_FILESAVE +#endif // MODPLUG_NO_FILESAVE OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp index bdd6b73d8..b28a76305 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mt2.cpp @@ -457,10 +457,10 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = mpt::format(U_("MadTracker %1.%2"))(fileHeader.version >> 8, mpt::ufmt::hex0<2>(fileHeader.version & 0xFF)); m_modFormat.type = U_("mt2"); - mpt::String::Read(m_modFormat.madeWithTracker, mpt::CharsetWindows1252, fileHeader.trackerName); - m_modFormat.charset = mpt::CharsetWindows1252; + m_modFormat.madeWithTracker = mpt::ToUnicode(mpt::Charset::Windows1252, mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.trackerName)); + m_modFormat.charset = mpt::Charset::Windows1252; - mpt::String::Read(m_songName, fileHeader.songName); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName); m_nChannels = fileHeader.numChannels; m_nDefaultSpeed = Clamp(fileHeader.ticksPerLine, 1, 31); m_nDefaultTempo.Set(125); @@ -482,7 +482,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) const CHANNELINDEX channelsWithoutDrums = m_nChannels; const bool hasDrumChannels = drumData.CanRead(sizeof(MT2DrumsData)); - STATIC_ASSERT(MAX_BASECHANNELS >= 64 + 8); + static_assert(MAX_BASECHANNELS >= 64 + 8); if(hasDrumChannels) { m_nChannels += 8; @@ -549,12 +549,12 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) { for(ROWINDEX row = 0; row < numRows; row++) { - ModCommand *m = Patterns[pat].GetRow(row); - for(CHANNELINDEX chn = 0; chn < channelsWithoutDrums; chn++, m++) + auto rowData = Patterns[pat].GetRow(row); + for(CHANNELINDEX chn = 0; chn < channelsWithoutDrums; chn++) { MT2Command cmd; chunk.ReadStruct(cmd); - hasLegacyTempo |= ConvertMT2Command(this, *m, cmd); + hasLegacyTempo |= ConvertMT2Command(this, rowData[chn], cmd); } } } @@ -609,9 +609,8 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) MT2TrackSettings trackSettings; if(chunk.ReadStruct(trackSettings)) { - ChnSettings[c].nVolume = trackSettings.volume >> 10; // 32768 is 0dB + ChnSettings[c].nVolume = static_cast(trackSettings.volume >> 10); // 32768 is 0dB trackRouting[c] = trackSettings.output; - LimitMax(ChnSettings[c].nVolume, uint16(64)); } } break; @@ -621,7 +620,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) { std::string name; chunk.ReadNullString(name); - mpt::String::Read(ChnSettings[i].szName, name.c_str(), name.length()); + ChnSettings[i].szName = mpt::String::ReadBuf(mpt::String::spacePadded, name.c_str(), name.length()); } break; @@ -650,7 +649,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) chunk.ReadNullString(artist); if(artist != "Unregistered") { - m_songArtist = mpt::ToUnicode(mpt::CharsetWindows1252, artist); + m_songArtist = mpt::ToUnicode(mpt::Charset::Windows1252, artist); } } break; @@ -679,14 +678,14 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) SNDMIXPLUGIN &mixPlug = m_MixPlugins[i]; mixPlug.Destroy(); - mpt::String::Read(mixPlug.Info.szLibraryName, vstHeader.dll); - mpt::String::Read(mixPlug.Info.szName, vstHeader.programName); - const size_t len = strlen(mixPlug.Info.szLibraryName); - if(len > 4 && mixPlug.Info.szLibraryName[len - 4] == '.') + std::string libraryName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, vstHeader.dll); + mixPlug.Info.szName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, vstHeader.programName); + if(libraryName.length() > 4 && libraryName[libraryName.length() - 4] == '.') { // Remove ".dll" from library name - mixPlug.Info.szLibraryName[len - 4] = '\0'; + libraryName.resize(libraryName.length() - 4 ); } + mixPlug.Info.szLibraryName = libraryName; mixPlug.Info.dwPluginId1 = Vst::kEffectMagic; mixPlug.Info.dwPluginId2 = vstHeader.fxID; if(vstHeader.track >= m_nChannels) @@ -797,8 +796,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) ModInstrument *mptIns = AllocateInstrument(drumMap[i], drumHeader.DrumSamples[i] + 1); if(mptIns != nullptr) { - strcpy(mptIns->name, "Drum #x"); - mptIns->name[6] = '1' + char(i); + mptIns->name = mpt::format("Drum #%1")(i+1); } } else { @@ -910,7 +908,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) if(mptIns == nullptr) continue; - mpt::String::Read(mptIns->name, instrName); + mptIns->name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrName); if(!dataLength) continue; @@ -945,7 +943,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) mptEnv.dwFlags.set(ENV_ENABLED, (mt2Env.flags & 1) != 0); mptEnv.dwFlags.set(ENV_SUSTAIN, (mt2Env.flags & 2) != 0); mptEnv.dwFlags.set(ENV_LOOP, (mt2Env.flags & 4) != 0); - mptEnv.resize(std::min(mt2Env.numPoints, 16)); + mptEnv.resize(std::min(mt2Env.numPoints.get(), uint8(16))); mptEnv.nSustainStart = mptEnv.nSustainEnd = mt2Env.sustainPos; mptEnv.nLoopStart = mt2Env.loopStart; mptEnv.nLoopEnd = mt2Env.loopEnd; @@ -976,7 +974,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) mptIns->SetCutoff(FrequencyToCutOff(synthData.cutoff), true); mptIns->SetResonance(synthData.resonance, true); } - mptIns->nFilterMode = synthData.effectID == 1 ? FLTMODE_HIGHPASS : FLTMODE_LOWPASS; + mptIns->filterMode = synthData.effectID == 1 ? FilterMode::HighPass : FilterMode::LowPass; if(flags & 4) { // VSTi / MIDI synth enabled @@ -989,7 +987,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) } if(synthData.transpose) { - for(uint32 n = 0; n < CountOf(mptIns->NoteMap); n++) + for(uint32 n = 0; n < std::size(mptIns->NoteMap); n++) { int note = NOTE_MIN + n + synthData.transpose; Limit(note, NOTE_MIN, NOTE_MAX); @@ -1015,7 +1013,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) if(i < fileHeader.numSamples) { - mpt::String::Read(m_szNames[i + 1], sampleName); + m_szNames[i + 1] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleName); } if(dataLength && i < fileHeader.numSamples) @@ -1144,7 +1142,7 @@ bool CSoundFile::ReadMT2(FileReader &file, ModLoadingFlags loadFlags) file.Skip(12); // Reserved std::string filename; file.ReadString(filename, filenameSize); - mpt::String::Copy(mptSmp.filename, filename); + mptSmp.filename = filename; #if defined(MPT_EXTERNAL_SAMPLES) if(filename.length() >= 2 diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mtm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mtm.cpp index 0c2231a0e..ca24f4e6a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mtm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_mtm.cpp @@ -134,14 +134,14 @@ bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags) } InitializeGlobals(MOD_TYPE_MTM); - mpt::String::Read(m_songName, fileHeader.songName); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName); m_nSamples = fileHeader.numSamples; m_nChannels = fileHeader.numChannels; m_modFormat.formatName = U_("MultiTracker"); m_modFormat.type = U_("mtm"); m_modFormat.madeWithTracker = mpt::format(U_("MultiTracker %1.%2"))(fileHeader.version >> 4, fileHeader.version & 0x0F); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; // Reading instruments for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) @@ -149,7 +149,7 @@ bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags) MTMSampleHeader sampleHeader; file.ReadStruct(sampleHeader); sampleHeader.ConvertToMPT(Samples[smp]); - mpt::String::Read(m_szNames[smp], sampleHeader.samplename); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.samplename); } // Setting Channel Pan Position @@ -190,13 +190,13 @@ bool CSoundFile::ReadMTM(FileReader &file, ModLoadingFlags loadFlags) ModCommand *m = Patterns[pat].GetpModCommand(0, chn); for(ROWINDEX row = 0; row < rowsPerPat; row++, m += GetNumChannels()) { - uint8 data[3]; - tracks.ReadArray(data); + const auto [noteInstr, instrCmd, par] = tracks.ReadArray(); - if(data[0] & 0xFC) m->note = (data[0] >> 2) + 36 + NOTE_MIN; - m->instr = ((data[0] & 0x03) << 4) | (data[1] >> 4); - uint8 cmd = data[1] & 0x0F; - uint8 param = data[2]; + if(noteInstr & 0xFC) + m->note = (noteInstr >> 2) + 36 + NOTE_MIN; + m->instr = ((noteInstr & 0x03) << 4) | (instrCmd >> 4); + uint8 cmd = instrCmd & 0x0F; + uint8 param = par; if(cmd == 0x0A) { if(param & 0xF0) param &= 0xF0; else param &= 0x0F; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp index e2be1a04a..3544a89bb 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_okt.cpp @@ -51,7 +51,7 @@ MPT_BINARY_STRUCT(OktSample, 32) // Parse the sample header block static void ReadOKTSamples(FileReader &chunk, std::vector &sample7bit, CSoundFile &sndFile) { - sndFile.m_nSamples = std::min(static_cast(chunk.BytesLeft() / sizeof(OktSample)), MAX_SAMPLES - 1u); + sndFile.m_nSamples = std::min(static_cast(chunk.BytesLeft() / sizeof(OktSample)), static_cast(MAX_SAMPLES - 1)); sample7bit.resize(sndFile.GetNumSamples()); for(SAMPLEINDEX smp = 1; smp <= sndFile.GetNumSamples(); smp++) @@ -61,11 +61,11 @@ static void ReadOKTSamples(FileReader &chunk, std::vector &sample7bit, CSo chunk.ReadStruct(oktSmp); mptSmp.Initialize(); - mpt::String::Read(sndFile.m_szNames[smp], oktSmp.name); + sndFile.m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, oktSmp.name); mptSmp.nC5Speed = 8287; mptSmp.nGlobalVol = 64; - mptSmp.nVolume = std::min(oktSmp.volume, 64u) * 4u; + mptSmp.nVolume = std::min(oktSmp.volume.get(), uint16(64)) * 4u; mptSmp.nLength = oktSmp.length & ~1; // round down // Parse loops const SmpLength loopStart = oktSmp.loopStart * 2; @@ -106,10 +106,8 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF for(CHANNELINDEX chn = 0; chn < chns; chn++) { ModCommand &m = rowCmd[chn]; - uint8 note = chunk.ReadUint8(); - uint8 instr = chunk.ReadUint8(); - uint8 effect = chunk.ReadUint8(); - m.param = chunk.ReadUint8(); + const auto [note, instr, effect, param] = chunk.ReadArray(); + m.param = param; if(note > 0 && note <= 36) { @@ -154,14 +152,14 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF if (m.param) { m.command = CMD_NOTESLIDEDOWN; - m.param = 0x10 | MIN(0x0F, m.param); + m.param = 0x10 | std::min(uint8(0x0F), m.param); } break; case 30: // U Slide Up (Notes) if (m.param) { m.command = CMD_NOTESLIDEUP; - m.param = 0x10 | MIN(0x0F, m.param); + m.param = 0x10 | std::min(uint8(0x0F), m.param); } break; // We don't have fine note slide, but this is supposed to happen once @@ -171,14 +169,14 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF if (m.param) { m.command = CMD_NOTESLIDEDOWN; - m.param = 0x50 | MIN(0x0F, m.param); + m.param = 0x50 | std::min(uint8(0x0F), m.param); } break; case 17: // H Slide Up Once (Notes) if (m.param) { m.command = CMD_NOTESLIDEUP; - m.param = 0x50 | MIN(0x0F, m.param); + m.param = 0x50 | std::min(uint8(0x0F), m.param); } break; @@ -211,7 +209,7 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF break; } // 0x40 is set volume -- fall through - MPT_FALLTHROUGH; + [[fallthrough]]; case 0: case 1: case 2: case 3: m.volcmd = VOLCMD_VOLUME; m.vol = m.param; @@ -222,10 +220,10 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF m.param = (m.param & 0x0F) << 4; // Dx0 break; case 6: - m.param = 0xF0 | MIN(m.param & 0x0F, 0x0E); // DFx + m.param = 0xF0 | std::min(static_cast(m.param & 0x0F), uint8(0x0E)); // DFx break; case 7: - m.param = (MIN(m.param & 0x0F, 0x0E) << 4) | 0x0F; // DxF + m.param = (std::min(static_cast(m.param & uint8(0x0F)), uint8(0x0E)) << 4) | 0x0F; // DxF break; default: // Junk. @@ -243,7 +241,8 @@ static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndF #endif default: - m.command = m.param = 0; + m.command = CMD_NONE; + m.param = 0; break; } } @@ -297,7 +296,7 @@ bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Oktalyzer"); m_modFormat.type = U_("okt"); - m_modFormat.charset = mpt::CharsetISO8859_1; + m_modFormat.charset = mpt::Charset::ISO8859_1; // Go through IFF chunks... while(file.CanRead(sizeof(OktIffChunk))) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_plm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_plm.cpp index 0eef50654..3bf698ea2 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_plm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_plm.cpp @@ -151,10 +151,10 @@ bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Disorder Tracker 2"); m_modFormat.type = U_("plm"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; // Some PLMs use ASCIIZ, some space-padding strings...weird. Oh, and the file browser stops at 0 bytes in the name, the main GUI doesn't. - mpt::String::Read(m_songName, fileHeader.songName); + m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songName); m_nChannels = fileHeader.numChannels + 1; // Additional channel for writing pattern breaks m_nSamplePreAmp = fileHeader.amplify; m_nDefaultTempo.Set(fileHeader.tempo); @@ -183,14 +183,14 @@ bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags) || !file.ReadStruct(sampleHeader)) continue; - mpt::String::Read(m_szNames[smp + 1], sampleHeader.name); - mpt::String::Read(sample.filename, sampleHeader.filename); + m_szNames[smp + 1] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); + sample.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.filename); if(sampleHeader.panning <= 15) { sample.uFlags.set(CHN_PANNING); sample.nPan = sampleHeader.panning * 0x11; } - sample.nGlobalVol = std::min(sampleHeader.volume, 64); + sample.nGlobalVol = std::min(sampleHeader.volume.get(), uint8(64)); sample.nC5Speed = sampleHeader.sampleRate; sample.nLoopStart = sampleHeader.loopStart; sample.nLoopEnd = sampleHeader.loopEnd; @@ -229,7 +229,7 @@ bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags) const ROWINDEX rowsPerPat = 64; uint32 maxPos = 0; - static const ModCommand::COMMAND effTrans[] = + static constexpr ModCommand::COMMAND effTrans[] = { CMD_NONE, CMD_PORTAMENTOUP, @@ -267,10 +267,10 @@ bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags) file.ReadStruct(patHeader); if(!patHeader.numRows) continue; - STATIC_ASSERT(ORDERINDEX_MAX >= ((mpt::limits::max)() + 255) / rowsPerPat); + static_assert(ORDERINDEX_MAX >= ((mpt::limits::max)() + 255) / rowsPerPat); ORDERINDEX curOrd = static_cast(ord.x / rowsPerPat); ROWINDEX curRow = static_cast(ord.x % rowsPerPat); - const CHANNELINDEX numChannels = std::min(patHeader.numChannels, fileHeader.numChannels - ord.y); + const CHANNELINDEX numChannels = std::min(patHeader.numChannels.get(), static_cast(fileHeader.numChannels - ord.y)); const uint32 patternEnd = ord.x + patHeader.numRows; maxPos = std::max(maxPos, patternEnd); @@ -293,25 +293,24 @@ bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags) ModCommand *m = Patterns[pat].GetpModCommand(curRow, ord.y); for(CHANNELINDEX c = 0; c < numChannels; c++, m++) { - uint8 data[5]; - file.ReadArray(data); - if(data[0]) - lastNote[c] = m->note = (data[0] >> 4) * 12 + (data[0] & 0x0F) + 12 + NOTE_MIN; + const auto [note, instr, volume, command, param] = file.ReadArray(); + if(note > 0 && note < 0x90) + lastNote[c] = m->note = (note >> 4) * 12 + (note & 0x0F) + 12 + NOTE_MIN; else m->note = NOTE_NONE; - m->instr = data[1]; + m->instr = instr; m->volcmd = VOLCMD_VOLUME; - if(data[2] != 0xFF) - m->vol = data[2]; + if(volume != 0xFF) + m->vol = volume; else m->volcmd = VOLCMD_NONE; - if(data[3] < CountOf(effTrans)) + if(command < CountOf(effTrans)) { - m->command = effTrans[data[3]]; - m->param = data[4]; + m->command = effTrans[command]; + m->param = param; // Fix some commands - switch(data[3]) + switch(command) { case 0x07: // Tremolo waveform m->param = 0x40 | (m->param & 0x03); @@ -341,13 +340,13 @@ bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags) m->param = 0x80 | (m->param & 0x0F); break; case 0x10: // Delay Note - m->param = 0xD0 | std::min(m->param, 0x0F); + m->param = 0xD0 | std::min(m->param, ModCommand::PARAM(0x0F)); break; case 0x11: // Cut Note - m->param = 0xC0 | std::min(m->param, 0x0F); + m->param = 0xC0 | std::min(m->param, ModCommand::PARAM(0x0F)); break; case 0x12: // Pattern Delay - m->param = 0xE0 | std::min(m->param, 0x0F); + m->param = 0xE0 | std::min(m->param, ModCommand::PARAM(0x0F)); break; case 0x04: // Volume Slide case 0x14: // Vibrato + Volume Slide diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_psm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_psm.cpp index d03f09260..9f0f41c73 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_psm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_psm.cpp @@ -27,9 +27,9 @@ OPENMPT_NAMESPACE_BEGIN // PSM File Header struct PSMFileHeader { - char formatID[4]; // "PSM " (new format) - uint32le fileSize; // Filesize - 12 - char fileInfoID[4]; // "FILE" + char formatID[4]; // "PSM " (new format) + uint32le fileSize; // Filesize - 12 + char fileInfoID[4]; // "FILE" }; MPT_BINARY_STRUCT(PSMFileHeader, 12) @@ -71,9 +71,9 @@ MPT_BINARY_STRUCT(PSMChunk, 8) // Song Information struct PSMSongHeader { - char songType[9]; // Mostly "MAINSONG " (But not in Extreme Pinball!) - uint8 compression; // 1 - uncompressed - uint8 numChannels; // Number of channels + char songType[9]; // Mostly "MAINSONG " (But not in Extreme Pinball!) + uint8 compression; // 1 - uncompressed + uint8 numChannels; // Number of channels }; @@ -83,26 +83,26 @@ MPT_BINARY_STRUCT(PSMSongHeader, 11) struct PSMSampleHeader { uint8le flags; - char fileName[8]; // Filename of the original module (without extension) - char sampleID[4]; // INS0...INS9 (only last digit of sample ID, i.e. sample 1 and sample 11 are equal) + char fileName[8]; // Filename of the original module (without extension) + char sampleID[4]; // Identifier like "INS0" (only last digit of sample ID, i.e. sample 1 and sample 11 are equal) or "I0 " char sampleName[33]; - uint8le unknown1[6]; // 00 00 00 00 00 FF + uint8le unknown1[6]; // 00 00 00 00 00 FF uint16le sampleNumber; uint32le sampleLength; uint32le loopStart; - uint32le loopEnd; // FF FF FF FF = end of sample + uint32le loopEnd; // FF FF FF FF = end of sample uint8le unknown3; - uint8le finetune; // unused? always 0 + uint8le finetune; // unused? always 0 uint8le defaultVolume; uint32le unknown4; - uint32le c5Freq; // MASI ignores the high 16 bits - char padding[19]; // 00 ... 00 + uint32le c5Freq; // MASI ignores the high 16 bits + char padding[19]; // Convert header data to OpenMPT's internal format void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); - mpt::String::Read(mptSmp.filename, fileName); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileName); mptSmp.nC5Speed = c5Freq; mptSmp.nLength = sampleLength; @@ -126,26 +126,26 @@ MPT_BINARY_STRUCT(PSMSampleHeader, 96) struct PSMSinariaSampleHeader { uint8le flags; - char fileName[8]; // Filename of the original module (without extension) - char sampleID[8]; // INS0...INS99999 + char fileName[8]; // Filename of the original module (without extension) + char sampleID[8]; // INS0...INS99999 char sampleName[33]; - uint8le unknown1[6]; // 00 00 00 00 00 FF + uint8le unknown1[6]; // 00 00 00 00 00 FF uint16le sampleNumber; uint32le sampleLength; uint32le loopStart; uint32le loopEnd; uint16le unknown3; - uint8le finetune; // Possibly finetune like in PSM16, but sounds even worse than just ignoring it + uint8le finetune; // Appears to be unused uint8le defaultVolume; uint32le unknown4; uint16le c5Freq; - char padding[16]; // 00 ... 00 + char padding[16]; // Convert header data to OpenMPT's internal format void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); - mpt::String::Read(mptSmp.filename, fileName); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileName); mptSmp.nC5Speed = c5Freq; mptSmp.nLength = sampleLength; @@ -167,15 +167,13 @@ struct PSMSubSong // For internal use (pattern conversion) std::vector channelSurround; ORDERINDEX startOrder = ORDERINDEX_INVALID, endOrder = ORDERINDEX_INVALID, restartPos = 0; uint8 defaultTempo = 125, defaultSpeed = 6; - char songName[10]; + char songName[10] = {}; PSMSubSong() - { - channelPanning.assign(MAX_BASECHANNELS, 128); - channelVolume.assign(MAX_BASECHANNELS, 64); - channelSurround.assign(MAX_BASECHANNELS, false); - MemsetZero(songName); - } + : channelPanning(MAX_BASECHANNELS, 128) + , channelVolume(MAX_BASECHANNELS, 64) + , channelSurround(MAX_BASECHANNELS, false) + { } }; @@ -245,7 +243,7 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPSM(MemoryFileReader file, co { return ProbeFailure; } - if((chunkHeader.id & 0x7f7f7f7fu) != chunkHeader.id) // ASCII? + if((chunkHeader.id & 0x7F7F7F7Fu) != chunkHeader.id) // ASCII? { return ProbeFailure; } @@ -265,7 +263,7 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) #ifdef MPT_PSM_DECRYPT // CONVERT.EXE /K - I don't think any game ever used this. - std::vector decrypted; + std::vector decrypted; if(!memcmp(fileHeader.formatID, "QUP$", 4) && !memcmp(fileHeader.fileInfoID, "OSWQ", 4)) { @@ -331,16 +329,16 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) m_nChannels = Clamp(static_cast(songHeader.numChannels), m_nChannels, MAX_BASECHANNELS); PSMSubSong subsong; - mpt::String::Read(subsong.songName, songHeader.songType); + mpt::String::WriteAutoBuf(subsong.songName) = mpt::String::ReadBuf(mpt::String::nullTerminated, songHeader.songType); #ifdef MPT_PSM_USE_REAL_SUBSONGS if(!Order().empty()) { // Add a new sequence for this subsong - if(Order.AddSequence(false) == SEQUENCEINDEX_INVALID) + if(Order.AddSequence() == SEQUENCEINDEX_INVALID) break; } - Order().SetName(subsong.songName); + Order().SetName(mpt::ToUnicode(mpt::Charset::CP437, subsong.songName)); #endif // MPT_PSM_USE_REAL_SUBSONGS // Read "Sub chunks" @@ -454,9 +452,7 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) case 0x0D: // Channel panning table - can be set using CONVERT.EXE /E { - uint8 chn = subChunk.ReadUint8(); - uint8 pan = subChunk.ReadUint8(); - uint8 type = subChunk.ReadUint8(); + const auto [chn, pan, type] = subChunk.ReadArray(); if(chn < subsong.channelPanning.size()) { switch(type) @@ -489,8 +485,7 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) case 0x0E: // Channel volume table (0...255) - can be set using CONVERT.EXE /E, is 255 in all "official" PSMs except for some OMF 2097 tracks { - uint8 chn = subChunk.ReadUint8(); - uint8 vol = subChunk.ReadUint8(); + const auto [chn, vol] = subChunk.ReadArray(); if(chn < subsong.channelVolume.size()) { subsong.channelVolume[chn] = (vol / 4u) + 1; @@ -516,8 +511,7 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) if(!subChunk.CanRead(2)) break; - uint8 type = subChunk.ReadUint8(); - uint8 pan = subChunk.ReadUint8(); + const auto [type, pan] = subChunk.ReadArray(); switch(type) { case 0: // use panning @@ -583,34 +577,28 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) // Original header PSMSampleHeader sampleHeader; if(!chunk.ReadStruct(sampleHeader)) - { continue; - } smp = static_cast(sampleHeader.sampleNumber + 1); if(smp > 0 && smp < MAX_SAMPLES) { m_nSamples = std::max(m_nSamples, smp); - mpt::String::Read(m_szNames[smp], sampleHeader.sampleName); - sampleHeader.ConvertToMPT(Samples[smp]); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.sampleName); } } else { // Sinaria uses a slightly different sample header PSMSinariaSampleHeader sampleHeader; if(!chunk.ReadStruct(sampleHeader)) - { continue; - } smp = static_cast(sampleHeader.sampleNumber + 1); if(smp > 0 && smp < MAX_SAMPLES) { m_nSamples = std::max(m_nSamples, smp); - mpt::String::Read(m_szNames[smp], sampleHeader.sampleName); - sampleHeader.ConvertToMPT(Samples[smp]); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.sampleName); } } if(smp > 0 && smp < MAX_SAMPLES) @@ -638,7 +626,7 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = sinariaFormat ? U_("Epic MegaGames MASI (New Version / Sinaria)") : U_("Epic MegaGames MASI (New Version)"); m_modFormat.type = U_("psm"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; if(!(loadFlags & loadPatternData) || m_nChannels == 0) { @@ -687,10 +675,9 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) while(rowChunk.CanRead(3)) { - uint8 flags = rowChunk.ReadUint8(); - uint8 channel = rowChunk.ReadUint8(); + const auto [flags, channel] = rowChunk.ReadArray(); // Point to the correct channel - ModCommand &m = rowBase[std::min(m_nChannels - 1, channel)]; + ModCommand &m = rowBase[std::min(static_cast(m_nChannels - 1), static_cast(channel))]; if(flags & noteFlag) { @@ -720,17 +707,17 @@ bool CSoundFile::ReadPSM(FileReader &file, ModLoadingFlags loadFlags) // Volume present uint8 vol = rowChunk.ReadUint8(); m.volcmd = VOLCMD_VOLUME; - m.vol = (MIN(vol, 127) + 1) / 2; + m.vol = (std::min(vol, uint8(127)) + 1) / 2; } if(flags & effectFlag) { // Effect present - convert - m.command = rowChunk.ReadUint8(); - m.param = rowChunk.ReadUint8(); + const auto [command, param] = rowChunk.ReadArray(); + m.param = param; // This list is annoyingly similar to PSM16, but not quite identical. - switch(m.command) + switch(command) { // Volslides case 0x01: // fine volslide up @@ -1014,7 +1001,7 @@ struct PSM16SampleHeader void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); - mpt::String::Read(mptSmp.filename, filename); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::nullTerminated, filename); mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; @@ -1024,7 +1011,7 @@ struct PSM16SampleHeader mptSmp.nC5Speed = c2freq; mptSmp.Transpose(((finetune ^ 0x08) - 0x78) / (12.0 * 16.0)); - mptSmp.nVolume = std::min(volume, 64u) * 4u; + mptSmp.nVolume = std::min(volume.get(), uint8(64)) * 4u; mptSmp.uFlags.reset(); if(flags & PSM16SampleHeader::smp16Bit) @@ -1132,7 +1119,7 @@ bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("Epic MegaGames MASI (Old Version)"); m_modFormat.type = U_("psm"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; m_nChannels = Clamp(CHANNELINDEX(fileHeader.numChannelsPlay), CHANNELINDEX(fileHeader.numChannelsReal), MAX_BASECHANNELS); m_nSamplePreAmp = fileHeader.masterVolume; @@ -1144,7 +1131,7 @@ bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags) m_nDefaultSpeed = fileHeader.songSpeed; m_nDefaultTempo.Set(fileHeader.songTempo); - mpt::String::Read(m_songName, fileHeader.songName); + m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songName); // Read orders if(fileHeader.orderOffset > 4 && file.Seek(fileHeader.orderOffset - 4) && file.ReadMagic("PORD")) @@ -1176,13 +1163,13 @@ bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags) break; } - SAMPLEINDEX smp = sampleHeader.sampleNumber; - if(smp > 0 && smp < MAX_SAMPLES) + const SAMPLEINDEX smp = sampleHeader.sampleNumber; + if(smp > 0 && smp < MAX_SAMPLES && !Samples[smp].HasSampleData()) { m_nSamples = std::max(m_nSamples, smp); sampleHeader.ConvertToMPT(Samples[smp]); - mpt::String::Read(m_szNames[smp], sampleHeader.name); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name); if(loadFlags & loadSampleData) { @@ -1241,13 +1228,14 @@ bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags) continue; } - ModCommand &m = *Patterns[pat].GetpModCommand(curRow, std::min(chnFlag & channelMask, m_nChannels - 1)); + ModCommand &m = *Patterns[pat].GetpModCommand(curRow, std::min(static_cast(chnFlag & channelMask), static_cast(m_nChannels - 1))); if(chnFlag & noteFlag) { // note + instr present - m.note = patternChunk.ReadUint8() + 36; - m.instr = patternChunk.ReadUint8(); + const auto [note, instr] = patternChunk.ReadArray(); + m.note = note + 36; + m.instr = instr; } if(chnFlag & volFlag) { @@ -1258,10 +1246,10 @@ bool CSoundFile::ReadPSM16(FileReader &file, ModLoadingFlags loadFlags) if(chnFlag & effectFlag) { // effect present - convert - m.command = patternChunk.ReadUint8(); - m.param = patternChunk.ReadUint8(); + const auto [command, param] = patternChunk.ReadArray(); + m.param = param; - switch(m.command) + switch(command) { // Volslides case 0x01: // fine volslide up diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ptm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ptm.cpp index 3e2d77602..22c3bfddd 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ptm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ptm.cpp @@ -65,10 +65,10 @@ struct PTMSampleHeader SampleIO ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(MOD_TYPE_S3M); - mptSmp.nVolume = std::min(volume, 64) * 4; + mptSmp.nVolume = std::min(volume.get(), uint8(64)) * 4; mptSmp.nC5Speed = c4speed * 2; - mpt::String::Read(mptSmp.filename, filename); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename); SampleIO sampleIO( SampleIO::_8bit, @@ -168,16 +168,16 @@ bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags) InitializeGlobals(MOD_TYPE_PTM); - mpt::String::Read(m_songName, fileHeader.songname); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songname); m_modFormat.formatName = U_("PolyTracker"); m_modFormat.type = U_("ptm"); m_modFormat.madeWithTracker = mpt::format(U_("PolyTracker %1.%2"))(fileHeader.versionHi.get(), mpt::ufmt::hex0<2>(fileHeader.versionLo.get())); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS; m_nChannels = fileHeader.numChannels; - m_nSamples = std::min(fileHeader.numSamples, MAX_SAMPLES - 1); + m_nSamples = std::min(static_cast(fileHeader.numSamples), static_cast(MAX_SAMPLES - 1)); ReadOrderFromArray(Order(), fileHeader.orders, fileHeader.numOrders, 0xFF, 0xFE); // Reading channel panning @@ -195,7 +195,7 @@ bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags) sampleHeaderChunk.ReadStruct(sampleHeader); ModSample &sample = Samples[smp + 1]; - mpt::String::Read(m_szNames[smp + 1], sampleHeader.samplename); + m_szNames[smp + 1] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.samplename); SampleIO sampleIO = sampleHeader.ConvertToMPT(sample); if((loadFlags & loadSampleData) && sample.nLength && file.Seek(sampleHeader.dataOffset)) @@ -238,8 +238,9 @@ bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags) if(b & 0x20) { - m.note = file.ReadUint8(); - m.instr = file.ReadUint8(); + const auto [note, instr] = file.ReadArray(); + m.note = note; + m.instr = instr; if(m.note == 254) m.note = NOTE_NOTECUT; else if(!m.note || m.note > 120) @@ -247,10 +248,11 @@ bool CSoundFile::ReadPTM(FileReader &file, ModLoadingFlags loadFlags) } if(b & 0x40) { - m.command = file.ReadUint8(); - m.param = file.ReadUint8(); + const auto [command, param] = file.ReadArray(); + m.command = command; + m.param = param; - static const EffectCommand effTrans[] = { CMD_GLOBALVOLUME, CMD_RETRIG, CMD_FINEVIBRATO, CMD_NOTESLIDEUP, CMD_NOTESLIDEDOWN, CMD_NOTESLIDEUPRETRIG, CMD_NOTESLIDEDOWNRETRIG, CMD_REVERSEOFFSET }; + static constexpr EffectCommand effTrans[] = { CMD_GLOBALVOLUME, CMD_RETRIG, CMD_FINEVIBRATO, CMD_NOTESLIDEUP, CMD_NOTESLIDEDOWN, CMD_NOTESLIDEUPRETRIG, CMD_NOTESLIDEDOWNRETRIG, CMD_REVERSEOFFSET }; if(m.command < 0x10) { // Beware: Effect letters are as in MOD, but portamento and volume slides behave like in S3M (i.e. fine slides share the same effect letters) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp index 5171166b0..c0f3a5151 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_s3m.cpp @@ -28,37 +28,38 @@ void CSoundFile::S3MConvert(ModCommand &m, bool fromIT) { switch(m.command | 0x40) { - case 'A': m.command = CMD_SPEED; break; - case 'B': m.command = CMD_POSITIONJUMP; break; - case 'C': m.command = CMD_PATTERNBREAK; if (!fromIT) m.param = (m.param >> 4) * 10 + (m.param & 0x0F); break; - case 'D': m.command = CMD_VOLUMESLIDE; break; - case 'E': m.command = CMD_PORTAMENTODOWN; break; - case 'F': m.command = CMD_PORTAMENTOUP; break; - case 'G': m.command = CMD_TONEPORTAMENTO; break; - case 'H': m.command = CMD_VIBRATO; break; - case 'I': m.command = CMD_TREMOR; break; - case 'J': m.command = CMD_ARPEGGIO; break; - case 'K': m.command = CMD_VIBRATOVOL; break; - case 'L': m.command = CMD_TONEPORTAVOL; break; - case 'M': m.command = CMD_CHANNELVOLUME; break; - case 'N': m.command = CMD_CHANNELVOLSLIDE; break; - case 'O': m.command = CMD_OFFSET; break; - case 'P': m.command = CMD_PANNINGSLIDE; break; - case 'Q': m.command = CMD_RETRIG; break; - case 'R': m.command = CMD_TREMOLO; break; - case 'S': m.command = CMD_S3MCMDEX; break; - case 'T': m.command = CMD_TEMPO; break; - case 'U': m.command = CMD_FINEVIBRATO; break; - case 'V': m.command = CMD_GLOBALVOLUME; break; - case 'W': m.command = CMD_GLOBALVOLSLIDE; break; - case 'X': m.command = CMD_PANNING8; break; - case 'Y': m.command = CMD_PANBRELLO; break; - case 'Z': m.command = CMD_MIDI; break; - case '\\': m.command = static_cast(fromIT ? CMD_SMOOTHMIDI : CMD_MIDI); break; + case '@': m.command = (m.param ? CMD_DUMMY : CMD_NONE); break; + case 'A': m.command = CMD_SPEED; break; + case 'B': m.command = CMD_POSITIONJUMP; break; + case 'C': m.command = CMD_PATTERNBREAK; if (!fromIT) m.param = (m.param >> 4) * 10 + (m.param & 0x0F); break; + case 'D': m.command = CMD_VOLUMESLIDE; break; + case 'E': m.command = CMD_PORTAMENTODOWN; break; + case 'F': m.command = CMD_PORTAMENTOUP; break; + case 'G': m.command = CMD_TONEPORTAMENTO; break; + case 'H': m.command = CMD_VIBRATO; break; + case 'I': m.command = CMD_TREMOR; break; + case 'J': m.command = CMD_ARPEGGIO; break; + case 'K': m.command = CMD_VIBRATOVOL; break; + case 'L': m.command = CMD_TONEPORTAVOL; break; + case 'M': m.command = CMD_CHANNELVOLUME; break; + case 'N': m.command = CMD_CHANNELVOLSLIDE; break; + case 'O': m.command = CMD_OFFSET; break; + case 'P': m.command = CMD_PANNINGSLIDE; break; + case 'Q': m.command = CMD_RETRIG; break; + case 'R': m.command = CMD_TREMOLO; break; + case 'S': m.command = CMD_S3MCMDEX; break; + case 'T': m.command = CMD_TEMPO; break; + case 'U': m.command = CMD_FINEVIBRATO; break; + case 'V': m.command = CMD_GLOBALVOLUME; break; + case 'W': m.command = CMD_GLOBALVOLSLIDE; break; + case 'X': m.command = CMD_PANNING8; break; + case 'Y': m.command = CMD_PANBRELLO; break; + case 'Z': m.command = CMD_MIDI; break; + case '\\': m.command = static_cast(fromIT ? CMD_SMOOTHMIDI : CMD_MIDI); break; // Chars under 0x40 don't save properly, so map : to ] and # to [. - case ']': m.command = CMD_DELAYCUT; break; - case '[': m.command = CMD_XPARAM; break; - default: m.command = CMD_NONE; + case ']': m.command = CMD_DELAYCUT; break; + case '[': m.command = CMD_XPARAM; break; + default: m.command = CMD_NONE; } } @@ -68,29 +69,30 @@ void CSoundFile::S3MSaveConvert(uint8 &command, uint8 ¶m, bool toIT, bool co { switch(command) { - case CMD_SPEED: command = 'A'; break; - case CMD_POSITIONJUMP: command = 'B'; break; - case CMD_PATTERNBREAK: command = 'C'; if(!toIT) param = ((param / 10) << 4) + (param % 10); break; - case CMD_VOLUMESLIDE: command = 'D'; break; - case CMD_PORTAMENTODOWN: command = 'E'; if (param >= 0xE0 && (GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM))) param = 0xDF; break; - case CMD_PORTAMENTOUP: command = 'F'; if (param >= 0xE0 && (GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM))) param = 0xDF; break; - case CMD_TONEPORTAMENTO: command = 'G'; break; - case CMD_VIBRATO: command = 'H'; break; - case CMD_TREMOR: command = 'I'; break; - case CMD_ARPEGGIO: command = 'J'; break; - case CMD_VIBRATOVOL: command = 'K'; break; - case CMD_TONEPORTAVOL: command = 'L'; break; - case CMD_CHANNELVOLUME: command = 'M'; break; - case CMD_CHANNELVOLSLIDE: command = 'N'; break; - case CMD_OFFSET: command = 'O'; break; - case CMD_PANNINGSLIDE: command = 'P'; break; - case CMD_RETRIG: command = 'Q'; break; - case CMD_TREMOLO: command = 'R'; break; - case CMD_S3MCMDEX: command = 'S'; break; - case CMD_TEMPO: command = 'T'; break; - case CMD_FINEVIBRATO: command = 'U'; break; - case CMD_GLOBALVOLUME: command = 'V'; break; - case CMD_GLOBALVOLSLIDE: command = 'W'; break; + case CMD_DUMMY: command = (param ? '@' : 0); break; + case CMD_SPEED: command = 'A'; break; + case CMD_POSITIONJUMP: command = 'B'; break; + case CMD_PATTERNBREAK: command = 'C'; if(!toIT) param = ((param / 10) << 4) + (param % 10); break; + case CMD_VOLUMESLIDE: command = 'D'; break; + case CMD_PORTAMENTODOWN: command = 'E'; if (param >= 0xE0 && (GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM))) param = 0xDF; break; + case CMD_PORTAMENTOUP: command = 'F'; if (param >= 0xE0 && (GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM))) param = 0xDF; break; + case CMD_TONEPORTAMENTO: command = 'G'; break; + case CMD_VIBRATO: command = 'H'; break; + case CMD_TREMOR: command = 'I'; break; + case CMD_ARPEGGIO: command = 'J'; break; + case CMD_VIBRATOVOL: command = 'K'; break; + case CMD_TONEPORTAVOL: command = 'L'; break; + case CMD_CHANNELVOLUME: command = 'M'; break; + case CMD_CHANNELVOLSLIDE: command = 'N'; break; + case CMD_OFFSET: command = 'O'; break; + case CMD_PANNINGSLIDE: command = 'P'; break; + case CMD_RETRIG: command = 'Q'; break; + case CMD_TREMOLO: command = 'R'; break; + case CMD_S3MCMDEX: command = 'S'; break; + case CMD_TEMPO: command = 'T'; break; + case CMD_FINEVIBRATO: command = 'U'; break; + case CMD_GLOBALVOLUME: command = 'V'; break; + case CMD_GLOBALVOLSLIDE: command = 'W'; break; case CMD_PANNING8: command = 'X'; if(toIT && !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM | MOD_TYPE_MOD))) @@ -104,8 +106,8 @@ void CSoundFile::S3MSaveConvert(uint8 &command, uint8 ¶m, bool toIT, bool co param >>= 1; } break; - case CMD_PANBRELLO: command = 'Y'; break; - case CMD_MIDI: command = 'Z'; break; + case CMD_PANBRELLO: command = 'Y'; break; + case CMD_MIDI: command = 'Z'; break; case CMD_SMOOTHMIDI: if(compatibilityExport || !toIT) command = 'Z'; @@ -115,10 +117,10 @@ void CSoundFile::S3MSaveConvert(uint8 &command, uint8 ¶m, bool toIT, bool co case CMD_XFINEPORTAUPDOWN: switch(param & 0xF0) { - case 0x10: command = 'F'; param = (param & 0x0F) | 0xE0; break; - case 0x20: command = 'E'; param = (param & 0x0F) | 0xE0; break; - case 0x90: command = 'S'; break; - default: command = 0; + case 0x10: command = 'F'; param = (param & 0x0F) | 0xE0; break; + case 0x20: command = 'E'; param = (param & 0x0F) | 0xE0; break; + case 0x90: command = 'S'; break; + default: command = 0; } break; case CMD_MODCMDEX: @@ -134,16 +136,10 @@ void CSoundFile::S3MSaveConvert(uint8 &command, uint8 ¶m, bool toIT, bool co return; // Chars under 0x40 don't save properly, so map : to ] and # to [. case CMD_DELAYCUT: - if(compatibilityExport || !toIT) - command = 0; - else - command = ']'; + command = (compatibilityExport || !toIT) ? 0 : ']'; break; case CMD_XPARAM: - if(compatibilityExport || !toIT) - command = 0; - else - command = '['; + command = (compatibilityExport || !toIT) ? 0 : '['; break; default: command = 0; @@ -248,7 +244,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) if(fileHeader.cwtv == S3MFileHeader::trkST3_20 && fileHeader.special == 0 && (fileHeader.ordNum & 0x0F) == 0 && fileHeader.ultraClicks == 0 && (fileHeader.flags & ~0x50) == 0) { // MPT 1.16 and older versions of OpenMPT - Simply keep default (filter) MIDI macros - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 16, 00, 00); + m_dwLastSavedWithVersion = MPT_V("1.16.00.00"); madeWithTracker = U_("ModPlug Tracker / OpenMPT"); keepMidiMacros = true; nonCompatTracker = true; @@ -287,6 +283,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) m_FileHistory.push_back(hist); } nonCompatTracker = true; + m_playBehaviour.set(kST3SampleSwap); // Not exactly like ST3, but close enough m_nMinPeriod = 1; break; case S3MFileHeader::trkSchismTracker: @@ -327,7 +324,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("ScreamTracker 3"); m_modFormat.type = U_("s3m"); m_modFormat.madeWithTracker = std::move(madeWithTracker); - m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::CharsetWindows1252 : mpt::CharsetCP437; + m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::Charset::Windows1252 : mpt::Charset::CP437; if(nonCompatTracker) { @@ -355,12 +352,12 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) m_MidiCfg.ClearZxxMacros(); } - mpt::String::Read(m_songName, fileHeader.name); + m_songName = mpt::String::ReadBuf(mpt::String::nullTerminated, fileHeader.name); if(fileHeader.flags & S3MFileHeader::amigaLimits) m_SongFlags.set(SONG_AMIGALIMITS); if(fileHeader.flags & S3MFileHeader::st2Vibrato) m_SongFlags.set(SONG_S3MOLDVIBRATO); - if(fileHeader.cwtv < S3MFileHeader::trkST3_20 || (fileHeader.flags & S3MFileHeader::fastVolumeSlides) != 0) + if(fileHeader.cwtv == S3MFileHeader::trkST3_00 || (fileHeader.flags & S3MFileHeader::fastVolumeSlides) != 0) { m_SongFlags.set(SONG_FASTVOLSLIDES); } @@ -382,7 +379,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) } // Global Volume - m_nDefaultGlobalVolume = std::min(fileHeader.globalVol, 64) * 4u; + m_nDefaultGlobalVolume = std::min(fileHeader.globalVol.get(), uint8(64)) * 4u; // The following check is probably not very reliable, but it fixes a few tunes, e.g. // DARKNESS.S3M by Purple Motion (ST 3.00) and "Image of Variance" by C.C.Catch (ST 3.01): if(m_nDefaultGlobalVolume == 0 && fileHeader.cwtv < S3MFileHeader::trkST3_20) @@ -390,8 +387,15 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; } - // Bit 7 = Stereo (we always use stereo) - m_nSamplePreAmp = std::max(fileHeader.masterVolume & 0x7F, 0x10); + if(fileHeader.formatVersion == S3MFileHeader::oldVersion && fileHeader.masterVolume < 8) + m_nSamplePreAmp = std::min((fileHeader.masterVolume + 1) * 0x10, 0x7F); + // These changes were probably only supposed to be done for older format revisions, where supposedly 0x10 was the stereo flag. + // However, this version check is missing in ST3, so any mono file with a master volume of 18 will be converted to a stereo file with master volume 32. + else if(fileHeader.masterVolume == 2 || fileHeader.masterVolume == (2 | 0x10)) + m_nSamplePreAmp = 0x20; + else + m_nSamplePreAmp = std::max(fileHeader.masterVolume & 0x7F, 0x10); // Bit 7 = Stereo (we always use stereo) + // Approximately as loud as in DOSBox and a real SoundBlaster 16 m_nVSTiVolume = 36; if(isSchism && fileHeader.cwtv < SchismVersionFromDate<2018, 11, 12>::Version(S3MFileHeader::trkSchismTracker)) @@ -451,7 +455,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) } // Reading sample headers - m_nSamples = std::min(fileHeader.smpNum, MAX_SAMPLES - 1); + m_nSamples = std::min(static_cast(fileHeader.smpNum), static_cast(MAX_SAMPLES - 1)); for(SAMPLEINDEX smp = 0; smp < m_nSamples; smp++) { S3MSampleHeader sampleHeader; @@ -462,7 +466,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) } sampleHeader.ConvertToMPT(Samples[smp + 1]); - mpt::String::Read(m_szNames[smp + 1], sampleHeader.name); + m_szNames[smp + 1] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name); if(sampleHeader.sampleType < S3MSampleHeader::typeAdMel) { @@ -489,7 +493,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) return true; } // Order list cannot contain pattern indices > 255, so do not even try to load higher patterns - const PATTERNINDEX readPatterns = std::min(fileHeader.patNum, uint8_max); + const PATTERNINDEX readPatterns = std::min(static_cast(fileHeader.patNum), static_cast(uint8_max)); Patterns.ResizeArray(readPatterns); for(PATTERNINDEX pat = 0; pat < readPatterns; pat++) { @@ -528,23 +532,13 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) if(info & s3mNotePresent) { - uint8 note = file.ReadUint8(), instr = file.ReadUint8(); - + const auto [note, instr] = file.ReadArray(); if(note < 0xF0) - { - // Note - note = (note & 0x0F) + 12 * (note >> 4) + 12 + NOTE_MIN; - } else if(note == s3mNoteOff) - { - // ^^ - note = NOTE_NOTECUT; - } else if(note == s3mNoteNone) - { - // .. - note = NOTE_NONE; - } - - m.note = note; + m.note = (note & 0x0F) + 12 * (note >> 4) + 12 + NOTE_MIN; + else if(note == s3mNoteOff) + m.note = NOTE_NOTECUT; + else if(note == s3mNoteNone) + m.note = NOTE_NONE; m.instr = instr; } @@ -558,20 +552,16 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) } else { m.volcmd = VOLCMD_VOLUME; - m.vol = MIN(volume, 64); + m.vol = std::min(volume, uint8(64)); } } if(info & s3mEffectPresent) { - uint8 command = file.ReadUint8(), param = file.ReadUint8(); - - if(command != 0) - { - m.command = command; - m.param = param; - S3MConvert(m, false); - } + const auto [command, param] = file.ReadArray(); + m.command = command; + m.param = param; + S3MConvert(m, false); if(m.command == CMD_S3MCMDEX && (m.param & 0xF0) == 0xA0 && fileHeader.cwtv < S3MFileHeader::trkST3_20) { @@ -620,7 +610,7 @@ bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags) bool CSoundFile::SaveS3M(std::ostream &f) const { - static const uint8 filler[16] = + static constexpr uint8 filler[16] = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, @@ -641,7 +631,7 @@ bool CSoundFile::SaveS3M(std::ostream &f) const S3MFileHeader fileHeader; MemsetZero(fileHeader); - mpt::String::Write(fileHeader.name, m_songName); + mpt::String::WriteBuf(mpt::String::nullTerminated, fileHeader.name) = m_songName; fileHeader.dosEof = S3MFileHeader::idEOF; fileHeader.fileType = S3MFileHeader::idS3MType; @@ -668,7 +658,7 @@ bool CSoundFile::SaveS3M(std::ostream &f) const fileHeader.smpNum = static_cast(writeSamples); // Patterns - PATTERNINDEX writePatterns = MIN(Patterns.GetNumPatterns(), 100u); + PATTERNINDEX writePatterns = std::min(Patterns.GetNumPatterns(), PATTERNINDEX(100)); fileHeader.patNum = static_cast(writePatterns); // Flags @@ -693,7 +683,7 @@ bool CSoundFile::SaveS3M(std::ostream &f) const memcpy(fileHeader.magic, "SCRM", 4); // Song Variables - fileHeader.globalVol = static_cast(std::min(m_nDefaultGlobalVolume / 4u, 64u)); + fileHeader.globalVol = static_cast(std::min(m_nDefaultGlobalVolume / 4u, uint32(64))); fileHeader.speed = static_cast(Clamp(m_nDefaultSpeed, 1u, 254u)); fileHeader.tempo = static_cast(Clamp(m_nDefaultTempo.GetInt(), 33u, 255u)); fileHeader.masterVolume = static_cast(Clamp(m_nSamplePreAmp, 16u, 127u) | 0x80); @@ -719,7 +709,7 @@ bool CSoundFile::SaveS3M(std::ostream &f) const std::vector sampleOffsets(writeSamples); for(SAMPLEINDEX smp = 0; smp < writeSamples; smp++) { - STATIC_ASSERT((sizeof(S3MSampleHeader) % 16) == 0); + static_assert((sizeof(S3MSampleHeader) % 16) == 0); sampleOffsets[smp] = static_cast((sampleHeaderOffset + smp * sizeof(S3MSampleHeader)) / 16); } mpt::IO::Write(f, sampleOffsets); @@ -840,7 +830,7 @@ bool CSoundFile::SaveS3M(std::ostream &f) const { command = CMD_NONE; volcmd = VOLCMD_VOLUME; - vol = MIN(param, 64); + vol = std::min(param, uint8(64)); } if(volcmd == VOLCMD_VOLUME) @@ -855,7 +845,7 @@ bool CSoundFile::SaveS3M(std::ostream &f) const if(command != CMD_NONE) { S3MSaveConvert(command, param, false, true); - if(command) + if(command || param) { info |= s3mEffectPresent; if(saveMuteStatus && ChnSettings[chn].dwFlags[CHN_MUTE] && m.IsGlobalCommand()) @@ -907,7 +897,7 @@ bool CSoundFile::SaveS3M(std::ostream &f) const } if(globalCmdOnMutedChn) { - //AddToLog(LogWarning, U_("Global commands on muted channels are interpreted by only some S3M players.")); + //AddToLog(LogWarning, U_("Global commands on muted channels are interpreted only by some S3M players.")); } mpt::IO::Offset sampleDataOffset = mpt::IO::TellWrite(f); @@ -937,7 +927,7 @@ bool CSoundFile::SaveS3M(std::ostream &f) const } const SmpLength smpLength = sampleHeader[smp].ConvertToS3M(Samples[realSmp]); - mpt::String::Write(sampleHeader[smp].name, m_szNames[realSmp]); + mpt::String::WriteBuf(mpt::String::nullTerminated, sampleHeader[smp].name) = m_szNames[realSmp]; if(smpLength != 0) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_sfx.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_sfx.cpp index 7a4134dc0..21674d620 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_sfx.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_sfx.cpp @@ -40,7 +40,7 @@ struct SFXSampleHeader mptSmp.Initialize(MOD_TYPE_MOD); mptSmp.nLength = length; mptSmp.nFineTune = MOD2XMFineTune(finetune); - mptSmp.nVolume = 4u * std::min(volume, 64); + mptSmp.nVolume = 4u * std::min(volume.get(), uint8(64)); SmpLength lStart = loopStart; SmpLength lLength = loopLength * 2u; @@ -89,7 +89,7 @@ static uint8 ClampSlideParam(uint8 value, uint8 lowNote, uint8 highNote) // with a fixed speed of 6 ticks/row, and excluding the first row, // 1xx/2xx param has a max value of (low-high)/5 to avoid sliding too far - return std::min(value, static_cast((lowPeriod - highPeriod) / 5)); + return std::min(value, static_cast((lowPeriod - highPeriod) / 5)); } return 0; @@ -236,13 +236,13 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) } if(invalidChars >= 128) return false; - mpt::String::Read(m_szNames[smp], sampleHeader.name); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); } // Broken conversions of the "Operation Stealth" soundtrack (BOND23 / BOND32) // There is a converter that shifts all note values except FFFD (empty note) to the left by 1 bit, // but it should not do that for FFFE (STP) notes - as a consequence, they turn into pattern breaks (FFFC). - const bool fixPatternBreaks = !strcmp(m_szNames[1], "BASSE2.AMI") || !strcmp(m_szNames[1], "PRA1.AMI"); + const bool fixPatternBreaks = (m_szNames[1] == "BASSE2.AMI") || (m_szNames[1] == "PRA1.AMI"); SFXFileHeader fileHeader; if(!file.ReadStruct(fileHeader)) @@ -261,7 +261,7 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) PATTERNINDEX numPatterns = 0; for(ORDERINDEX ord = 0; ord < fileHeader.numOrders; ord++) { - numPatterns = std::max(numPatterns, fileHeader.orderList[ord] + 1u); + numPatterns = std::max(numPatterns, static_cast(fileHeader.orderList[ord] + 1)); } if(fileHeader.restartPos < fileHeader.numOrders) @@ -297,8 +297,7 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) for(CHANNELINDEX chn = 0; chn < 4; chn++) { ModCommand &m = rowBase[chn]; - uint8 data[4]; - file.ReadArray(data); + auto data = file.ReadArray(); if(data[0] == 0xFF) { @@ -438,7 +437,7 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) case 0x9: // 9xy: Auto slide version = std::max(version, uint8(8)); - MPT_FALLTHROUGH; + [[fallthrough]]; default: m.command = CMD_NONE; break; @@ -476,7 +475,7 @@ bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = m_nSamples == 15 ? mpt::format(U_("SoundFX 1.%1"))(version) : U_("SoundFX 2.0 / MultiMedia Sound"); m_modFormat.type = m_nSamples == 15 ? UL_("sfx") : UL_("sfx2"); - m_modFormat.charset = mpt::CharsetISO8859_1; + m_modFormat.charset = mpt::Charset::ISO8859_1; return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp index f5c5557ad..9f8147900 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stm.cpp @@ -17,14 +17,14 @@ OPENMPT_NAMESPACE_BEGIN // STM sample header struct struct STMSampleHeader { - char filename[12]; // Can't have long comments - just filename comments :) + char filename[12]; // Can't have long comments - just filename comments :) uint8le zero; - uint8le disk; // A blast from the past - uint16le offset; // 20-bit offset in file (lower 4 bits are zero) - uint16le length; // Sample length - uint16le loopStart; // Loop start point - uint16le loopEnd; // Loop end point - uint8le volume; // Volume + uint8le disk; // A blast from the past + uint16le offset; // 20-bit offset in file (lower 4 bits are zero) + uint16le length; // Sample length + uint16le loopStart; // Loop start point + uint16le loopEnd; // Loop end point + uint8le volume; // Volume uint8le reserved2; uint16le sampleRate; uint8le reserved3[6]; @@ -33,10 +33,10 @@ struct STMSampleHeader void ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(); - mpt::String::Read(mptSmp.filename, filename); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename); mptSmp.nC5Speed = sampleRate; - mptSmp.nVolume = std::min(volume, 64) * 4; + mptSmp.nVolume = std::min(volume.get(), uint8(64)) * 4; mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopEnd; @@ -60,13 +60,13 @@ MPT_BINARY_STRUCT(STMSampleHeader, 32) struct STMFileHeader { char songname[20]; - char trackername[8]; // !Scream! for ST 2.xx - uint8 dosEof; // 0x1A - uint8 filetype; // 1=song, 2=module (only 2 is supported, of course) :) + char trackername[8]; // !Scream! for ST 2.xx + uint8 dosEof; // 0x1A + uint8 filetype; // 1=song, 2=module (only 2 is supported, of course) :) uint8 verMajor; uint8 verMinor; - uint8 initTempo; // Ticks per row. - uint8 numPatterns; // number of patterns + uint8 initTempo; + uint8 numPatterns; uint8 globalVolume; uint8 reserved[13]; }; @@ -144,12 +144,12 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) InitializeGlobals(MOD_TYPE_STM); - mpt::String::Read(m_songName, fileHeader.songname); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songname); m_modFormat.formatName = U_("Scream Tracker 2"); m_modFormat.type = U_("stm"); m_modFormat.madeWithTracker = mpt::format(U_("Scream Tracker %1.%2"))(fileHeader.verMajor, mpt::ufmt::dec0<2>(fileHeader.verMinor)); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; m_nSamples = 31; m_nChannels = 4; @@ -165,7 +165,7 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) m_nDefaultTempo = ConvertST2Tempo(initTempo); m_nDefaultSpeed = initTempo >> 4; if(fileHeader.verMinor > 10) - m_nDefaultGlobalVolume = std::min(fileHeader.globalVolume, 64) * 4u; + m_nDefaultGlobalVolume = std::min(fileHeader.globalVolume, uint8(64)) * 4u; // Setting up channels for(CHANNELINDEX chn = 0; chn < 4; chn++) @@ -183,7 +183,7 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) if(sampleHeader.zero != 0 && sampleHeader.zero != 46) // putup10.stm has zero = 46 return false; sampleHeader.ConvertToMPT(Samples[smp]); - mpt::String::Read(m_szNames[smp], sampleHeader.filename); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.filename); sampleOffsets[smp - 1] = sampleHeader.offset; } @@ -214,126 +214,137 @@ bool CSoundFile::ReadSTM(FileReader &file, ModLoadingFlags loadFlags) auto m = Patterns[pat].begin(); ORDERINDEX breakPos = ORDERINDEX_INVALID; - ROWINDEX breakRow = 63; // Candidate row for inserting pattern break - - for(unsigned int i = 0; i < 64 * 4; i++, m++) + ROWINDEX breakRow = 63; // Candidate row for inserting pattern break + + for(ROWINDEX row = 0; row < 64; row++) { - uint8 note = file.ReadUint8(), insvol, volcmd, cmdinf; - switch(note) + uint8 newTempo = 0; + for(CHANNELINDEX chn = 0; chn < 4; chn++, m++) { - case 0xFB: - note = insvol = volcmd = cmdinf = 0x00; - break; - case 0xFC: - continue; - case 0xFD: - m->note = NOTE_NOTECUT; - continue; - default: + uint8 note = file.ReadUint8(), insVol, volCmd, cmdInf; + switch(note) { - uint8 patData[3]; - file.ReadArray(patData); - insvol = patData[0]; - volcmd = patData[1]; - cmdinf = patData[2]; + case 0xFB: + note = insVol = volCmd = cmdInf = 0x00; + break; + case 0xFC: + continue; + case 0xFD: + m->note = NOTE_NOTECUT; + continue; + default: + { + const auto patData = file.ReadArray(); + insVol = patData[0]; + volCmd = patData[1]; + cmdInf = patData[2]; + } + break; } - break; - } - if(note == 0xFE) - m->note = NOTE_NOTECUT; - else if(note < 0x60) - m->note = (note >> 4) * 12 + (note & 0x0F) + 36 + NOTE_MIN; + if(note == 0xFE) + m->note = NOTE_NOTECUT; + else if(note < 0x60) + m->note = (note >> 4) * 12 + (note & 0x0F) + 36 + NOTE_MIN; - m->instr = insvol >> 3; - if(m->instr > 31) - { - m->instr = 0; - } + m->instr = insVol >> 3; + if(m->instr > 31) + { + m->instr = 0; + } - uint8 vol = (insvol & 0x07) | ((volcmd & 0xF0) >> 1); - if(vol <= 64) - { - m->volcmd = VOLCMD_VOLUME; - m->vol = vol; - } - - static const EffectCommand stmEffects[] = - { - CMD_NONE, CMD_SPEED, CMD_POSITIONJUMP, CMD_PATTERNBREAK, // .ABC - CMD_VOLUMESLIDE, CMD_PORTAMENTODOWN, CMD_PORTAMENTOUP, CMD_TONEPORTAMENTO, // DEFG - CMD_VIBRATO, CMD_TREMOR, CMD_ARPEGGIO, CMD_NONE, // HIJK - CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, // LMNO - // KLMNO can be entered in the editor but don't do anything - }; - - m->command = stmEffects[volcmd & 0x0F]; - m->param = cmdinf; - - switch(m->command) - { - case CMD_VOLUMESLIDE: - // Lower nibble always has precedence, and there are no fine slides. - if(m->param & 0x0F) - m->param &= 0x0F; - else - m->param &= 0xF0; - break; - - case CMD_PATTERNBREAK: - m->param = (m->param & 0xF0) * 10 + (m->param & 0x0F); - if(breakPos != ORDERINDEX_INVALID && m->param == 0) + uint8 vol = (insVol & 0x07) | ((volCmd & 0xF0) >> 1); + if(vol <= 64) { - // Merge Bxx + C00 into just Bxx - m->command = CMD_POSITIONJUMP; - m->param = static_cast(breakPos); - breakPos = ORDERINDEX_INVALID; + m->volcmd = VOLCMD_VOLUME; + m->vol = vol; } - LimitMax(breakRow, i / 4u); - break; - case CMD_POSITIONJUMP: - // This effect is also very weird. - // Bxx doesn't appear to cause an immediate break -- it merely - // sets the next order for when the pattern ends (either by - // playing it all the way through, or via Cxx effect) - breakPos = m->param; - breakRow = 63; - m->command = CMD_NONE; - break; - - case CMD_TREMOR: - // this actually does something with zero values, and has no - // effect memory. which makes SENSE for old-effects tremor, - // but ST3 went and screwed it all up by adding an effect - // memory and IT followed that, and those are much more popular - // than STM so we kind of have to live with this effect being - // broken... oh well. not a big loss. - break; - - case CMD_SPEED: - if(fileHeader.verMinor < 21) + static constexpr EffectCommand stmEffects[] = { - m->param = ((m->param / 10u) << 4u) + m->param % 10u; - } + CMD_NONE, CMD_SPEED, CMD_POSITIONJUMP, CMD_PATTERNBREAK, // .ABC + CMD_VOLUMESLIDE, CMD_PORTAMENTODOWN, CMD_PORTAMENTOUP, CMD_TONEPORTAMENTO, // DEFG + CMD_VIBRATO, CMD_TREMOR, CMD_ARPEGGIO, CMD_NONE, // HIJK + CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, // LMNO + // KLMNO can be entered in the editor but don't do anything + }; + + m->command = stmEffects[volCmd & 0x0F]; + m->param = cmdInf; + + switch(m->command) + { + case CMD_VOLUMESLIDE: + // Lower nibble always has precedence, and there are no fine slides. + if(m->param & 0x0F) + m->param &= 0x0F; + else + m->param &= 0xF0; + break; + + case CMD_PATTERNBREAK: + m->param = (m->param & 0xF0) * 10 + (m->param & 0x0F); + if(breakPos != ORDERINDEX_INVALID && m->param == 0) + { + // Merge Bxx + C00 into just Bxx + m->command = CMD_POSITIONJUMP; + m->param = static_cast(breakPos); + breakPos = ORDERINDEX_INVALID; + } + LimitMax(breakRow, row); + break; + + case CMD_POSITIONJUMP: + // This effect is also very weird. + // Bxx doesn't appear to cause an immediate break -- it merely + // sets the next order for when the pattern ends (either by + // playing it all the way through, or via Cxx effect) + breakPos = m->param; + breakRow = 63; + m->command = CMD_NONE; + break; + + case CMD_TREMOR: + // this actually does something with zero values, and has no + // effect memory. which makes SENSE for old-effects tremor, + // but ST3 went and screwed it all up by adding an effect + // memory and IT followed that, and those are much more popular + // than STM so we kind of have to live with this effect being + // broken... oh well. not a big loss. + break; + + case CMD_SPEED: + if(fileHeader.verMinor < 21) + { + m->param = ((m->param / 10u) << 4u) + m->param % 10u; + } + + if(!m->param) + { + m->command = CMD_NONE; + break; + } #ifdef MODPLUG_TRACKER - // ST2 has a very weird tempo mode where the length of a tick depends both - // on the ticks per row and a scaling factor. This is probably the closest - // we can get with S3M-like semantics. - m->param >>= 4; + // ST2 has a very weird tempo mode where the length of a tick depends both + // on the ticks per row and a scaling factor. Try to write the tempo into a separate command. + newTempo = m->param; + m->param >>= 4; #endif // MODPLUG_TRACKER + break; - MPT_FALLTHROUGH; - - default: - // Anything not listed above is a no-op if there's no value. - // (ST2 doesn't have effect memory) - if(!m->param) - { - m->command = CMD_NONE; + default: + // Anything not listed above is a no-op if there's no value, as ST2 doesn't have effect memory. + if(!m->param) + { + m->command = CMD_NONE; + } + break; } - break; + } + if(newTempo != 0) + { + Patterns[pat].WriteEffect(EffectWriter(CMD_TEMPO, mpt::saturate_round(ConvertST2Tempo(newTempo).ToDouble())).Row(row).RetryPreviousRow()); } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp index afbf18c91..fb4b38606 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_stp.cpp @@ -59,7 +59,7 @@ struct STPSampleHeader void ConvertToMPT(ModSample &mptSmp) const { mptSmp.nLength = length; - mptSmp.nVolume = 4u * std::min(volume, 64); + mptSmp.nVolume = 4u * std::min(volume.get(), uint8(64)); mptSmp.nLoopStart = loopStart; mptSmp.nLoopEnd = loopStart + loopLength; @@ -188,7 +188,7 @@ static void ConvertLoopSequence(ModSample &smp, STPLoopList &loopList) // update loop info based on position in edited sample info.loopStart = start; - if(i > 0 && i <= mpt::size(newSmp.cues)) + if(i > 0 && i <= std::size(newSmp.cues)) { newSmp.cues[i - 1] = start; } @@ -258,7 +258,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = mpt::format(U_("Soundtracker Pro II v%1"))(fileHeader.version); m_modFormat.type = U_("stp"); - m_modFormat.charset = mpt::CharsetISO8859_1; + m_modFormat.charset = mpt::Charset::ISO8859_1; m_nChannels = 4; m_nSamples = 0; @@ -306,12 +306,12 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) std::string str; // Read path chunk.ReadNullString(str, 257); - mpt::String::Copy(mptSmp.filename, str); + mptSmp.filename = str; // Ignore flags, they are all not relevant for us chunk.Skip(1); // Read filename / sample text chunk.ReadNullString(str, 31); - mpt::String::Copy(m_szNames[actualSmp], str); + m_szNames[actualSmp] = str; // Seek to even boundary if(chunk.GetPosition() % 2u) chunk.Skip(1); @@ -433,13 +433,11 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) { ChannelMemory &chnMem = channelMemory[chn]; ModCommand &m = rowBase[chn]; - uint8 data[4]; - file.ReadArray(data); + const auto [instr, note, command, param] = file.ReadArray(); - m.instr = data[0]; - m.note = data[1]; - m.command = data[2]; - m.param = data[3]; + m.instr = instr; + m.note = note; + m.param = param; if(m.note) { @@ -451,10 +449,10 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) // and auto global fine volside uint8 swapped = (m.param >> 4) | (m.param << 4); - if((m.command & 0xF0) == 0xF0) + if((command & 0xF0) == 0xF0) { // 12-bit CIA tempo - uint16 ciaTempo = (static_cast(m.command & 0x0F) << 8) | m.param; + uint16 ciaTempo = (static_cast(command & 0x0F) << 8) | m.param; if(ciaTempo) { m.param = mpt::saturate_round(ConvertTempo(ciaTempo).ToDouble()); @@ -463,11 +461,13 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) { m.command = CMD_NONE; } - } else switch(m.command) + } else switch(command) { case 0x00: // arpeggio if(m.param) m.command = CMD_ARPEGGIO; + else + m.command = CMD_NONE; break; case 0x01: // portamento up @@ -609,7 +609,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) STPLoopList &loopList = loopInfo[m.instr - 1]; m.param--; - if(m.param < std::min(mpt::size(ModSample().cues), loopList.size())) + if(m.param < std::min(std::size(ModSample().cues), loopList.size())) { m.volcmd = VOLCMD_OFFSET; m.vol = m.param; @@ -627,7 +627,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) m.param--; if(m.param < loopList.size()) { - if(!loopList[m.param].looped && m_nSamples < MAX_SAMPLES - 1) + if(!loopList[m.param].looped && CanAddMoreSamples()) loopList[m.param].looped = ++m_nSamples; m.instr = static_cast(loopList[m.param].looped); } @@ -642,13 +642,13 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) STPLoopList &loopList = loopInfo[m.instr - 1]; m.param--; - if(m.param < std::min(mpt::size(ModSample().cues), loopList.size())) + if(m.param < std::min(std::size(ModSample().cues), loopList.size())) { m.volcmd = VOLCMD_OFFSET; m.vol = m.param; } // switch to non-looped version of sample and create it if needed - if(!nonLooped[m.instr - 1] && m_nSamples < MAX_SAMPLES - 1) + if(!nonLooped[m.instr - 1] && CanAddMoreSamples()) nonLooped[m.instr - 1] = ++m_nSamples; m.instr = static_cast(nonLooped[m.instr - 1]); } @@ -664,7 +664,7 @@ bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags) m.param--; if(m.param < loopList.size()) { - if(!loopList[m.param].nonLooped && m_nSamples < MAX_SAMPLES-1) + if(!loopList[m.param].nonLooped && CanAddMoreSamples()) loopList[m.param].nonLooped = ++m_nSamples; m.instr = static_cast(loopList[m.param].nonLooped); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_uax.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_uax.cpp index f46234955..ea55d36f3 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_uax.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_uax.cpp @@ -90,7 +90,7 @@ bool CSoundFile::ReadUAX(FileReader &file, ModLoadingFlags loadFlags) InitializeGlobals(); m_modFormat.formatName = mpt::format(U_("Unreal Package v%1"))(fileHeader.packageVersion); m_modFormat.type = U_("uax"); - m_modFormat.charset = mpt::CharsetWindows1252; + m_modFormat.charset = mpt::Charset::Windows1252; for(uint32 i = 0; i < fileHeader.exportCount && file.CanRead(4); i++) { @@ -168,14 +168,14 @@ bool CSoundFile::ReadUAX(FileReader &file, ModLoadingFlags loadFlags) FileReader fileChunk = chunk.ReadChunk(size); - if(GetNumSamples() < MAX_SAMPLES - 1) + if(CanAddMoreSamples()) { // Read as sample if(ReadSampleFromFile(GetNumSamples() + 1, fileChunk, true)) { if(static_cast(objName) < names.size()) { - mpt::String::Copy(m_szNames[GetNumSamples()], names[objName]); + m_szNames[GetNumSamples()] = names[objName]; } } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp index c960cf3ce..2a76aeb2c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_ult.cpp @@ -50,7 +50,7 @@ struct UltSample { mptSmp.Initialize(); - mpt::String::Read(mptSmp.filename, filename); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename); if(sizeEnd <= sizeStart) { @@ -85,21 +85,8 @@ struct UltSample MPT_BINARY_STRUCT(UltSample, 66) -struct UltPatternCommand -{ - uint8 instr; - uint8 cmd; - uint8 param1; - uint8 param2; -}; - -MPT_BINARY_STRUCT(UltPatternCommand, 4) - - /* Unhandled effects: 5x1 - do not loop sample (x is unused) -9xx - set sample offset to xx * 1024 - with 9yy: set sample offset to xxyy * 4 E0x - set vibrato strength (2 is normal) The logarithmic volume scale used in older format versions here, or pretty @@ -110,7 +97,7 @@ convert them. */ static void TranslateULTCommands(uint8 &effect, uint8 ¶m, uint8 version) { - static const uint8 ultEffTrans[] = + static constexpr uint8 ultEffTrans[] = { CMD_ARPEGGIO, CMD_PORTAMENTOUP, @@ -215,25 +202,23 @@ static void TranslateULTCommands(uint8 &effect, uint8 ¶m, uint8 version) static int ReadULTEvent(ModCommand &m, FileReader &file, uint8 version) { - uint8 b, repeat = 1; - uint8 cmd1, cmd2; // 1 = vol col, 2 = fx col in the original schismtracker code - uint8 param1, param2; - - b = file.ReadUint8(); - if (b == 0xFC) // repeat event + uint8 repeat = 1; + uint8 b = file.ReadUint8(); + if(b == 0xFC) // repeat event { repeat = file.ReadUint8(); b = file.ReadUint8(); } m.note = (b > 0 && b < 61) ? (b + 35 + NOTE_MIN) : NOTE_NONE; - UltPatternCommand patCmd; - file.ReadStruct(patCmd); - m.instr = patCmd.instr; - cmd1 = patCmd.cmd & 0x0F; - cmd2 = patCmd.cmd >> 4; - param1 = patCmd.param1; - param2 = patCmd.param2; + + const auto [instr, cmd, para1, para2] = file.ReadArray(); + + m.instr = instr; + uint8 cmd1 = cmd & 0x0F; + uint8 cmd2 = cmd >> 4; + uint8 param1 = para1; + uint8 param2 = para2; TranslateULTCommands(cmd1, param1, version); TranslateULTCommands(cmd2, param2, version); @@ -287,7 +272,7 @@ struct PostFixUltCommands isPortaActive.resize(numChannels, false); } - void operator()(ModCommand& m) + void operator()(ModCommand &m) { // Attempt to fix portamentos. // UltraTracker will slide until the destination note is reached or 300 is encountered. @@ -359,7 +344,7 @@ static bool ValidateHeader(const UltFileHeader &fileHeader) static uint64 GetHeaderMinimumAdditionalSize(const UltFileHeader &fileHeader) { - return fileHeader.messageLength * 32u; + return fileHeader.messageLength * 32u + 3u + 256u; } CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderULT(MemoryFileReader file, const uint64 *pfilesize) @@ -400,21 +385,22 @@ bool CSoundFile::ReadULT(FileReader &file, ModLoadingFlags loadFlags) } InitializeGlobals(MOD_TYPE_ULT); - mpt::String::Read(m_songName, fileHeader.songName); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName); - const MPT_UCHAR_TYPE *versions[] = {UL_("<1.4"), UL_("1.4"), UL_("1.5"), UL_("1.6")}; + const mpt::uchar *versions[] = {UL_("<1.4"), UL_("1.4"), UL_("1.5"), UL_("1.6")}; m_modFormat.formatName = U_("UltraTracker"); m_modFormat.type = U_("ult"); m_modFormat.madeWithTracker = U_("UltraTracker ") + versions[fileHeader.version - '1']; - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; - m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS; // this will be converted to IT format by MPT. + m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS; // this will be converted to IT format by MPT. - // read "messageLength" lines, each containing 32 characters. + // Read "messageLength" lines, each containing 32 characters. m_songMessage.ReadFixedLineLength(file, fileHeader.messageLength * 32, 32, 0); - m_nSamples = static_cast(file.ReadUint8()); - if(GetNumSamples() >= MAX_SAMPLES) + if(SAMPLEINDEX numSamples = file.ReadUint8(); numSamples < MAX_SAMPLES) + m_nSamples = numSamples; + else return false; for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) @@ -433,17 +419,18 @@ bool CSoundFile::ReadULT(FileReader &file, ModLoadingFlags loadFlags) } sampleHeader.ConvertToMPT(Samples[smp]); - mpt::String::Read(m_szNames[smp], sampleHeader.name); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); } ReadOrderFromFile(Order(), file, 256, 0xFF, 0xFE); - m_nChannels = file.ReadUint8() + 1; - PATTERNINDEX numPats = file.ReadUint8() + 1; - - if(GetNumChannels() > MAX_BASECHANNELS) + if(CHANNELINDEX numChannels = file.ReadUint8() + 1u; numChannels <= MAX_BASECHANNELS) + m_nChannels = numChannels; + else return false; + PATTERNINDEX numPats = file.ReadUint8() + 1; + for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++) { ChnSettings[chn].Reset(); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_wav.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_wav.cpp index dfeac4f8c..a71f0bdf8 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_wav.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_wav.cpp @@ -36,7 +36,7 @@ static bool CopyWavChannel(ModSample &sample, const FileReader &file, size_t cha return false; } - const mpt::byte *inBuf = file.GetRawData(); + const std::byte *inBuf = file.GetRawData(); CopySample(reinterpret_cast(sample.samplev()), sample.nLength, 1, inBuf + offset, file.BytesLeft() - offset, numChannels, conv); return true; } @@ -87,7 +87,7 @@ bool CSoundFile::ReadWAV(FileReader &file, ModLoadingFlags loadFlags) m_modFormat.formatName = U_("RIFF WAVE"); m_modFormat.type = U_("wav"); - m_modFormat.charset = mpt::CharsetWindows1252; + m_modFormat.charset = mpt::Charset::Windows1252; const SmpLength sampleLength = wavFile.GetSampleLength(); @@ -95,7 +95,7 @@ bool CSoundFile::ReadWAV(FileReader &file, ModLoadingFlags loadFlags) // Calculate sample length in ticks at tempo 125 const uint32 sampleRate = std::max(uint32(1), wavFile.GetSampleRate()); const uint32 sampleTicks = mpt::saturate_cast(((sampleLength * 50) / sampleRate) + 1); - uint32 ticksPerRow = std::max((sampleTicks + 63u) / 63u, 1u); + uint32 ticksPerRow = std::max((sampleTicks + 63u) / 63u, uint32(1)); Order().assign(1, 0); ORDERINDEX numOrders = 1; @@ -136,7 +136,7 @@ bool CSoundFile::ReadWAV(FileReader &file, ModLoadingFlags loadFlags) sample.uFlags = CHN_PANNING; sample.nLength = sampleLength; sample.nC5Speed = wavFile.GetSampleRate(); - strcpy(m_szNames[channel + 1], ""); + m_szNames[channel + 1] = ""; wavFile.ApplySampleSettings(sample, GetCharsetInternal(), m_szNames[channel + 1]); if(wavFile.GetNumChannels() > 1) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp index 918969070..1c0b10846 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Load_xm.cpp @@ -13,18 +13,132 @@ #include "Loaders.h" #include "../common/version.h" #include "XMTools.h" +#include "mod_specifications.h" #ifndef MODPLUG_NO_FILESAVE #include "../common/mptFileIO.h" #endif +#include "OggStream.h" #include #ifdef MODPLUG_TRACKER #include "../mptrack/TrackerSettings.h" // For super smooth ramping option #endif // MODPLUG_TRACKER +#if defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) +#include +#endif + +#if defined(MPT_WITH_VORBIS) +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG +#include +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG +#endif + +#if defined(MPT_WITH_VORBISFILE) +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG +#include +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG +#include "../soundbase/SampleFormatConverters.h" +#include "../soundbase/SampleFormatCopy.h" +#endif + +#ifdef MPT_WITH_STBVORBIS +#include +#include "../soundbase/SampleFormatConverters.h" +#include "../soundbase/SampleFormatCopy.h" +#endif // MPT_WITH_STBVORBIS + OPENMPT_NAMESPACE_BEGIN + +#if defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) + +static size_t VorbisfileFilereaderRead(void *ptr, size_t size, size_t nmemb, void *datasource) +{ + FileReader &file = *reinterpret_cast(datasource); + return file.ReadRaw(mpt::void_cast(ptr), size * nmemb) / size; +} + +static int VorbisfileFilereaderSeek(void *datasource, ogg_int64_t offset, int whence) +{ + FileReader &file = *reinterpret_cast(datasource); + switch(whence) + { + case SEEK_SET: + { + if(!Util::TypeCanHoldValue(offset)) + { + return -1; + } + return file.Seek(mpt::saturate_cast(offset)) ? 0 : -1; + } + break; + case SEEK_CUR: + { + if(offset < 0) + { + if(offset == std::numeric_limits::min()) + { + return -1; + } + if(!Util::TypeCanHoldValue(0-offset)) + { + return -1; + } + return file.SkipBack(mpt::saturate_cast(0 - offset)) ? 0 : -1; + } else + { + if(!Util::TypeCanHoldValue(offset)) + { + return -1; + } + return file.Skip(mpt::saturate_cast(offset)) ? 0 : -1; + } + } + break; + case SEEK_END: + { + if(!Util::TypeCanHoldValue(offset)) + { + return -1; + } + if(!Util::TypeCanHoldValue(file.GetLength() + offset)) + { + return -1; + } + return file.Seek(mpt::saturate_cast(file.GetLength() + offset)) ? 0 : -1; + } + break; + default: + return -1; + } +} + +static long VorbisfileFilereaderTell(void *datasource) +{ + FileReader &file = *reinterpret_cast(datasource); + FileReader::off_t result = file.GetPosition(); + if(!Util::TypeCanHoldValue(result)) + { + return -1; + } + return static_cast(result); +} + +#endif // MPT_WITH_VORBIS && MPT_WITH_VORBISFILE + + // Allocate samples for an instrument static std::vector AllocateXMSamples(CSoundFile &sndFile, SAMPLEINDEX numSamples) { @@ -144,10 +258,10 @@ static void ReadXMPatterns(FileReader &file, const XMFileHeader &fileHeader, CSo // A packed size of 0 indicates a completely empty pattern. const uint16 packedSize = file.ReadUint16LE(); - if(numRows == 0 || numRows > MAX_PATTERN_ROWS) - { + if(numRows == 0) numRows = 64; - } + else if(numRows > MAX_PATTERN_ROWS) + numRows = MAX_PATTERN_ROWS; file.Seek(curPos + headerSize); FileReader patternChunk = file.ReadChunk(packedSize); @@ -221,7 +335,7 @@ static void ReadXMPatterns(FileReader &file, const XMFileHeader &fileHeader, CSo } else if (vol >= 0x60) { // Volume commands 6-F translation. - static const ModCommand::VOLCMD volEffTrans[] = + static constexpr ModCommand::VOLCMD volEffTrans[] = { VOLCMD_VOLSLIDEDOWN, VOLCMD_VOLSLIDEUP, VOLCMD_FINEVOLDOWN, VOLCMD_FINEVOLUP, VOLCMD_VIBRATOSPEED, VOLCMD_VIBRATODEPTH, VOLCMD_PANNING, VOLCMD_PANSLIDELEFT, @@ -294,6 +408,174 @@ CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderXM(MemoryFileReader file, con } +static bool ReadSampleData(ModSample &sample, SampleIO sampleFlags, FileReader &sampleChunk, bool &isOXM) +{ + bool unsupportedSample = false; + + bool isOGG = false; + if(sampleChunk.CanRead(8)) + { + isOGG = true; + sampleChunk.Skip(4); + // In order to avoid false-detecting PCM as OggVorbis as much as possible, + // we parse and verify the complete sample data and only assume OggVorbis, + // if all Ogg checksums are correct a no single byte of non-Ogg data exists. + // The fast-path for regular PCM will only check "OggS" magic and do no other work after failing that check. + while(!sampleChunk.EndOfFile()) + { + if(!Ogg::ReadPage(sampleChunk)) + { + isOGG = false; + break; + } + } + } + isOXM = isOXM || isOGG; + sampleChunk.Rewind(); + if(isOGG) + { + uint32 originalSize = sampleChunk.ReadInt32LE(); + FileReader sampleData = sampleChunk.ReadChunk(sampleChunk.BytesLeft()); + + sample.uFlags.set(CHN_16BIT, sampleFlags.GetBitDepth() >= 16); + sample.uFlags.set(CHN_STEREO, sampleFlags.GetChannelFormat() != SampleIO::mono); + sample.nLength = originalSize / (sample.uFlags[CHN_16BIT] ? 2 : 1) / (sample.uFlags[CHN_STEREO] ? 2 : 1); + +#if defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE) + + ov_callbacks callbacks = { + &VorbisfileFilereaderRead, + &VorbisfileFilereaderSeek, + NULL, + &VorbisfileFilereaderTell + }; + OggVorbis_File vf; + MemsetZero(vf); + if(ov_open_callbacks(&sampleData, &vf, nullptr, 0, callbacks) == 0) + { + if(ov_streams(&vf) == 1) + { // we do not support chained vorbis samples + vorbis_info *vi = ov_info(&vf, -1); + if(vi && vi->rate > 0 && vi->channels > 0) + { + sample.AllocateSample(); + SmpLength offset = 0; + int channels = vi->channels; + int current_section = 0; + long decodedSamples = 0; + bool eof = false; + while(!eof && offset < sample.nLength && sample.HasSampleData()) + { + float **output = nullptr; + long ret = ov_read_float(&vf, &output, 1024, ¤t_section); + if(ret == 0) + { + eof = true; + } else if(ret < 0) + { + // stream error, just try to continue + } else + { + decodedSamples = ret; + LimitMax(decodedSamples, mpt::saturate_cast(sample.nLength - offset)); + if(decodedSamples > 0 && channels == sample.GetNumChannels()) + { + for(int chn = 0; chn < channels; chn++) + { + if(sample.uFlags[CHN_16BIT]) + { + CopyChannelToInterleaved >(sample.sample16() + offset * sample.GetNumChannels(), output[chn], channels, decodedSamples, chn); + } else + { + CopyChannelToInterleaved >(sample.sample8() + offset * sample.GetNumChannels(), output[chn], channels, decodedSamples, chn); + } + } + } + offset += decodedSamples; + } + } + } else + { + unsupportedSample = true; + } + } else + { + unsupportedSample = true; + } + ov_clear(&vf); + } else + { + unsupportedSample = true; + } + +#elif defined(MPT_WITH_STBVORBIS) + + // NOTE/TODO: stb_vorbis does not handle inferred negative PCM sample + // position at stream start. (See + // ). + // This means that, for remuxed and re-aligned/cutted (at stream start) + // Vorbis files, stb_vorbis will include superfluous samples at the + // beginning. OXM files with this property are yet to be spotted in the + // wild, thus, this behaviour is currently not problematic. + + int consumed = 0, error = 0; + stb_vorbis *vorb = nullptr; + FileReader::PinnedRawDataView sampleDataView = sampleData.GetPinnedRawDataView(); + const std::byte* data = sampleDataView.data(); + std::size_t dataLeft = sampleDataView.size(); + vorb = stb_vorbis_open_pushdata(mpt::byte_cast(data), mpt::saturate_cast(dataLeft), &consumed, &error, nullptr); + sampleData.Skip(consumed); + data += consumed; + dataLeft -= consumed; + if(vorb) + { + // Header has been read, proceed to reading the sample data + sample.AllocateSample(); + SmpLength offset = 0; + while((error == VORBIS__no_error || (error == VORBIS_need_more_data && dataLeft > 0)) + && offset < sample.nLength && sample.HasSampleData()) + { + int channels = 0, decodedSamples = 0; + float **output; + consumed = stb_vorbis_decode_frame_pushdata(vorb, mpt::byte_cast(data), mpt::saturate_cast(dataLeft), &channels, &output, &decodedSamples); + sampleData.Skip(consumed); + data += consumed; + dataLeft -= consumed; + LimitMax(decodedSamples, mpt::saturate_cast(sample.nLength - offset)); + if(decodedSamples > 0 && channels == sample.GetNumChannels()) + { + for(int chn = 0; chn < channels; chn++) + { + if(sample.uFlags[CHN_16BIT]) + CopyChannelToInterleaved >(sample.sample16() + offset * sample.GetNumChannels(), output[chn], channels, decodedSamples, chn); + else + CopyChannelToInterleaved >(sample.sample8() + offset * sample.GetNumChannels(), output[chn], channels, decodedSamples, chn); + } + } + offset += decodedSamples; + error = stb_vorbis_get_error(vorb); + } + stb_vorbis_close(vorb); + } else + { + unsupportedSample = true; + } + +#else // !VORBIS + + unsupportedSample = true; + +#endif // VORBIS + + } else + { + sampleFlags.ReadSample(sample, sampleChunk); + } + + return !unsupportedSample; +} + + bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) { file.Rewind(); @@ -322,33 +604,25 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) FlagSet madeWith(verUnknown); mpt::ustring madeWithTracker; - if(!memcmp(fileHeader.trackerName, "FastTracker ", 12)) + if(!memcmp(fileHeader.trackerName, "FastTracker v2.00 ", 20) && fileHeader.size == 276) { - if(fileHeader.size == 276 && !memcmp(fileHeader.trackerName + 12, "v2.00 ", 8)) - { - if(fileHeader.version < 0x0104) - madeWith = verFT2Generic | verConfirmed; - else if(memchr(fileHeader.songName, '\0', 20) != nullptr) - // FT2 pads the song title with spaces, some other trackers use null chars - madeWith = verFT2Clone | verNewModPlug | verEmptyOrders; - else - madeWith = verFT2Generic | verNewModPlug; - } else if(!memcmp(fileHeader.trackerName + 12, "v 2.00 ", 8)) - { - // MPT 1.0 (exact version to be determined later) - madeWith = verOldModPlug; - } else - { - // ??? - madeWith.set(verConfirmed); - madeWithTracker = U_("FastTracker Clone"); - } + if(fileHeader.version < 0x0104) + madeWith = verFT2Generic | verConfirmed; + else if(memchr(fileHeader.songName, '\0', 20) != nullptr) + // FT2 pads the song title with spaces, some other trackers use null chars + madeWith = verFT2Clone | verNewModPlug | verEmptyOrders; + else + madeWith = verFT2Generic | verNewModPlug; + } else if(!memcmp(fileHeader.trackerName, "FastTracker v 2.00 ", 20)) + { + // MPT 1.0 (exact version to be determined later) + madeWith = verOldModPlug; } else { // Something else! madeWith = verUnknown | verConfirmed; - mpt::String::Read(madeWithTracker, mpt::CharsetCP437, fileHeader.trackerName); + madeWithTracker = mpt::ToUnicode(mpt::Charset::CP437, mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.trackerName)); if(!memcmp(fileHeader.trackerName, "OpenMPT ", 8)) { @@ -361,6 +635,10 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) { m_nMixLevels = mixLevelsCompatibleFT2; } + } else if(!memcmp(fileHeader.trackerName, "Fasttracker II clone", 20)) + { + // 8bitbubsy's FT2 clone should be treated exactly like FT2 + madeWith = verFT2Generic | verConfirmed; } else if(!memcmp(fileHeader.trackerName, "MadTracker 2.0\0", 15)) { // Fix channel 2 in m3_cha.xm @@ -369,25 +647,25 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) m_playBehaviour.reset(kFT2Arpeggio); } else if(!memcmp(fileHeader.trackerName, "Skale Tracker\0", 14)) { - m_playBehaviour.reset(kFT2OffsetOutOfRange); + m_playBehaviour.reset(kFT2ST3OffsetOutOfRange); } else if(!memcmp(fileHeader.trackerName, "*Converted ", 11)) { madeWith = verDigiTrakker; } } - mpt::String::Read(m_songName, fileHeader.songName); + m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songName); m_nMinPeriod = 1; m_nMaxPeriod = 31999; Order().SetRestartPos(fileHeader.restartPos); m_nChannels = fileHeader.channels; - m_nInstruments = std::min(fileHeader.instruments, MAX_INSTRUMENTS - 1u); + m_nInstruments = std::min(static_cast(fileHeader.instruments), static_cast(MAX_INSTRUMENTS - 1)); if(fileHeader.speed) m_nDefaultSpeed = fileHeader.speed; if(fileHeader.tempo) - m_nDefaultTempo.Set(Clamp(fileHeader.tempo, 32, 512)); + m_nDefaultTempo = Clamp(TEMPO(fileHeader.tempo, 0), ModSpecs::xmEx.GetTempoMin(), ModSpecs::xmEx.GetTempoMax()); m_SongFlags.reset(); m_SongFlags.set(SONG_LINEARSLIDES, (fileHeader.flags & XMFileHeader::linearSlides) != 0); @@ -410,10 +688,13 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) ReadXMPatterns(file, fileHeader, *this); } + bool isOXM = false; + // In case of XM versions < 1.04, we need to memorize the sample flags for all samples, as they are not stored immediately after the sample headers. std::vector sampleFlags; uint8 sampleReserved = 0; int instrType = -1; + bool unsupportedSamples = false; // Reading instruments for(INSTRUMENTINDEX instr = 1; instr <= m_nInstruments; instr++) @@ -437,12 +718,12 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) if(instrHeader.size == 245) { // ModPlug Tracker Alpha - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 00, 00, A5); + m_dwLastSavedWithVersion = MPT_V("1.00.00.A5"); madeWithTracker = U_("ModPlug Tracker 1.0 alpha"); } else if(instrHeader.size == 263) { // ModPlug Tracker Beta (Beta 1 still behaves like Alpha, but Beta 3.3 does it this way) - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 00, 00, B3); + m_dwLastSavedWithVersion = MPT_V("1.00.00.B3"); madeWithTracker = U_("ModPlug Tracker 1.0 beta"); } else { @@ -532,7 +813,7 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) sampleHeader.ConvertToMPT(Samples[mptSample]); instrHeader.instrument.ApplyAutoVibratoToMPT(Samples[mptSample]); - mpt::String::Read(m_szNames[mptSample], sampleHeader.name); + m_szNames[mptSample] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); if((sampleHeader.flags & 3) == 3 && madeWith[verNewModPlug]) { @@ -552,7 +833,10 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) FileReader sampleChunk = file.ReadChunk(sampleFlags[sample].GetEncoding() != SampleIO::ADPCM ? sampleSize[sample] : (16 + (sampleSize[sample] + 1) / 2)); if(sample < sampleSlots.size() && (loadFlags & loadSampleData)) { - sampleFlags[sample].ReadSample(Samples[sampleSlots[sample]], sampleChunk); + if(!ReadSampleData(Samples[sampleSlots[sample]], sampleFlags[sample], sampleChunk, isOXM)) + { + unsupportedSamples = true; + } } } } @@ -582,6 +866,11 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) } } + if(unsupportedSamples) + { + AddToLog(LogWarning, U_("Some compressed samples could not be loaded because they use an unsupported codec.")); + } + // Read song comments: "text" if(file.ReadMagic("text")) { @@ -639,11 +928,11 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) { if(madeWith[verModPlug1_09]) { - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 09, 00, 00); + m_dwLastSavedWithVersion = MPT_V("1.09.00.00"); madeWithTracker = U_("ModPlug Tracker 1.09"); } else if(madeWith[verNewModPlug]) { - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 16, 00, 00); + m_dwLastSavedWithVersion = MPT_V("1.16.00.00"); madeWithTracker = U_("ModPlug Tracker 1.10 - 1.16"); } } @@ -652,10 +941,10 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) { // Hey, I know this tracker! std::string mptVersion(fileHeader.trackerName + 8, 12); - m_dwLastSavedWithVersion = Version::Parse(mpt::ToUnicode(mpt::CharsetASCII, mptVersion)); + m_dwLastSavedWithVersion = Version::Parse(mpt::ToUnicode(mpt::Charset::ASCII, mptVersion)); madeWith = verOpenMPT | verConfirmed; - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 07, 19)) + if(m_dwLastSavedWithVersion < MPT_V("1.22.07.19")) m_nMixLevels = mixLevelsCompatible; else m_nMixLevels = mixLevelsCompatibleFT2; @@ -711,20 +1000,20 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) LoadExtendedSongProperties(file, true, &isOpenMPTMade); - if(isOpenMPTMade && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 00, 00)) + if(isOpenMPTMade && m_dwLastSavedWithVersion < MPT_V("1.17.00.00")) { // Up to OpenMPT 1.17.02.45 (r165), it was possible that the "last saved with" field was 0 // when saving a file in OpenMPT for the first time. - m_dwLastSavedWithVersion = MAKE_VERSION_NUMERIC(1, 17, 00, 00); + m_dwLastSavedWithVersion = MPT_V("1.17.00.00"); } - if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 00, 00)) + if(m_dwLastSavedWithVersion >= MPT_V("1.17.00.00")) { madeWithTracker = U_("OpenMPT ") + m_dwLastSavedWithVersion.ToUString(); } // We no longer allow any --- or +++ items in the order list now. - if(m_dwLastSavedWithVersion && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 02, 02)) + if(m_dwLastSavedWithVersion && m_dwLastSavedWithVersion < MPT_V("1.22.02.02")) { if(!Patterns.IsValidPat(0xFE)) Order().RemovePattern(0xFE); @@ -732,10 +1021,21 @@ bool CSoundFile::ReadXM(FileReader &file, ModLoadingFlags loadFlags) Order().Replace(0xFF, Order.GetInvalidPatIndex()); } - m_modFormat.formatName = mpt::format(U_("FastTracker 2 v%1.%2"))(fileHeader.version >> 8, mpt::ufmt::hex0<2>(fileHeader.version & 0xFF)); - m_modFormat.type = U_("xm"); - m_modFormat.madeWithTracker = std::move(madeWithTracker); - m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::CharsetWindows1252 : mpt::CharsetCP437; + if(isOXM) + { + m_modFormat.formatName = U_("OggMod FastTracker 2"); + m_modFormat.type = U_("oxm"); + m_modFormat.originalFormatName = mpt::format(U_("FastTracker 2 v%1.%2"))(fileHeader.version >> 8, mpt::ufmt::hex0<2>(fileHeader.version & 0xFF)); + m_modFormat.originalType = U_("xm"); + m_modFormat.madeWithTracker = std::move(madeWithTracker); + m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::Charset::Windows1252 : mpt::Charset::CP437; + } else + { + m_modFormat.formatName = mpt::format(U_("FastTracker 2 v%1.%2"))(fileHeader.version >> 8, mpt::ufmt::hex0<2>(fileHeader.version & 0xFF)); + m_modFormat.type = U_("xm"); + m_modFormat.madeWithTracker = std::move(madeWithTracker); + m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::Charset::Windows1252 : mpt::Charset::CP437; + } return true; } @@ -756,10 +1056,10 @@ bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport) MemsetZero(fileHeader); memcpy(fileHeader.signature, "Extended Module: ", 17); - mpt::String::Write(fileHeader.songName, m_songName); + mpt::String::WriteBuf(mpt::String::spacePadded, fileHeader.songName) = m_songName; fileHeader.eof = 0x1A; const std::string openMptTrackerName = mpt::ToCharset(GetCharsetFile(), Version::Current().GetOpenMPTVersionString()); - mpt::String::Write(fileHeader.trackerName, openMptTrackerName); + mpt::String::WriteBuf(mpt::String::spacePadded, fileHeader.trackerName) = openMptTrackerName; // Writing song header fileHeader.version = 0x0104; // XM Format v1.04 @@ -826,7 +1126,7 @@ bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport) mpt::IO::Write(f, fileHeader); // Write processed order list - mpt::IO::WriteRaw(f, orderList.data(), orderList.size()); + mpt::IO::Write(f, orderList); // Writing patterns @@ -1051,7 +1351,7 @@ bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport) } sampleFlags[smp] = xmSample.GetSampleFormat(); - mpt::String::Write(xmSample.name, m_szNames[samples[smp]]); + mpt::String::WriteBuf(mpt::String::spacePadded, xmSample.name) = m_szNames[samples[smp]]; mpt::IO::Write(f, xmSample); } @@ -1092,7 +1392,7 @@ bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport) for(PATTERNINDEX pat = 0; pat < numNamedPats; pat++) { char name[MAX_PATTERNNAME]; - mpt::String::Write(name, Patterns[pat].GetName()); + mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = Patterns[pat].GetName(); mpt::IO::Write(f, name); } } @@ -1111,7 +1411,7 @@ bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport) for(CHANNELINDEX chn = 0; chn < numNamedChannels; chn++) { char name[MAX_CHANNELNAME]; - mpt::String::Write(name, ChnSettings[chn].szName); + mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = ChnSettings[chn].szName; mpt::IO::Write(f, name); } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Loaders.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Loaders.h index ad3724a2c..a436448f9 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Loaders.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Loaders.h @@ -47,7 +47,7 @@ constexpr uint16 MagicBE(const char(&id)[3]) template bool ReadOrderFromArray(ModSequence &order, const T(&orders)[arraySize], size_t howMany = arraySize, uint16 stopIndex = uint16_max, uint16 ignoreIndex = uint16_max) { - STATIC_ASSERT(mpt::is_binary_safe::value); + static_assert(mpt::is_binary_safe::value); LimitMax(howMany, arraySize); LimitMax(howMany, MAX_ORDERS); ORDERINDEX readEntries = static_cast(howMany); @@ -69,7 +69,7 @@ bool ReadOrderFromArray(ModSequence &order, const T(&orders)[arraySize], size_t template bool ReadOrderFromFile(ModSequence &order, FileReader &file, size_t howMany, uint16 stopIndex = uint16_max, uint16 ignoreIndex = uint16_max) { - STATIC_ASSERT(mpt::is_binary_safe::value); + static_assert(mpt::is_binary_safe::value); if(!file.CanRead(howMany * sizeof(T))) return false; LimitMax(howMany, MAX_ORDERS); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp index a19eb46ce..a6124231c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.cpp @@ -96,6 +96,14 @@ void MIDIMacroConfig::CreateParameteredMacro(Macro ¶meteredMacro, Parametere } +std::string MIDIMacroConfig::CreateParameteredMacro(ParameteredMacro macroType, int subType) const +{ + Macro parameteredMacro; + CreateParameteredMacro(parameteredMacro, macroType, subType); + return mpt::String::ReadAutoBuf(parameteredMacro); +} + + // Create Zxx (Z80 - ZFF) from preset void MIDIMacroConfig::CreateFixedMacro(Macro (&fixedMacros)[128], FixedMacro macroType) const { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h index 3681bd458..5307fd0f1 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MIDIMacros.h @@ -117,12 +117,7 @@ public: { CreateParameteredMacro(szMidiSFXExt[macroIndex], macroType, subType); } - std::string CreateParameteredMacro(ParameteredMacro macroType, int subType = 0) const - { - Macro parameteredMacro; - CreateParameteredMacro(parameteredMacro, macroType, subType); - return std::string(parameteredMacro); - } + std::string CreateParameteredMacro(ParameteredMacro macroType, int subType = 0) const; protected: void CreateFixedMacro(Macro (&fixedMacros)[128], FixedMacro macroType) const; @@ -176,7 +171,7 @@ protected: }; -STATIC_ASSERT(sizeof(MIDIMacroConfig) == sizeof(MIDIMacroConfigData)); // this is directly written to files, so the size must be correct! +static_assert(sizeof(MIDIMacroConfig) == sizeof(MIDIMacroConfigData)); // this is directly written to files, so the size must be correct! OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MPEGFrame.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/MPEGFrame.cpp index 925c2967a..16547539b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MPEGFrame.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MPEGFrame.cpp @@ -15,13 +15,13 @@ OPENMPT_NAMESPACE_BEGIN // Samples per frame - for each MPEG version and all three layers -static const uint16 samplesPerFrame[2][3] = +static constexpr uint16 samplesPerFrame[2][3] = { { 384, 1152, 1152 }, // MPEG 1 { 384, 1152, 576 } // MPEG 2 / 2.5 }; // Bit rates for each MPEG version and all three layers -static const uint16 bitRates[2][3][15] = +static constexpr uint16 bitRates[2][3][15] = { // MPEG 1 { @@ -37,7 +37,7 @@ static const uint16 bitRates[2][3][15] = } }; // Sampling rates for each MPEG version and all three layers -static const uint16 samplingRates[4][3] = +static constexpr uint16 samplingRates[4][3] = { { 11025, 12000, 8000 }, // MPEG 2.5 { 0, 0, 0 }, // Invalid @@ -45,13 +45,13 @@ static const uint16 samplingRates[4][3] = { 44100, 48000, 32000 } // MPEG 1 }; // Samples per Frame / 8 -static const uint8 mpegCoefficients[2][3] = +static constexpr uint8 mpegCoefficients[2][3] = { { 12, 144, 144 }, // MPEG 1 { 12, 144, 72 } // MPEG 2 / 2.5 }; // Side info size = Offset in frame where Xing/Info magic starts -static const uint8 sideInfoSize[2][2] = +static constexpr uint8 sideInfoSize[2][2] = { { 17, 32 }, // MPEG 1 { 9, 17 } // MPEG 2 / 2.5 diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Message.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Message.cpp index 7f64676c6..60d826606 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Message.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Message.cpp @@ -24,7 +24,7 @@ OPENMPT_NAMESPACE_BEGIN // [in] length: number of characters that should be read, not including a possible trailing null terminator (it is automatically appended). // [in] lineEnding: line ending formatting of the text in memory. // [out] returns true on success. -bool SongMessage::Read(const mpt::byte *data, size_t length, LineEnding lineEnding) +bool SongMessage::Read(const std::byte *data, size_t length, LineEnding lineEnding) { const char *str = mpt::byte_cast(data); while(length != 0 && str[length - 1] == '\0') @@ -120,7 +120,7 @@ bool SongMessage::Read(FileReader &file, const size_t length, LineEnding lineEnd // [in] lineLength: The fixed length of a line. // [in] lineEndingLength: The padding space between two fixed lines. (there could for example be a null char after every line) // [out] returns true on success. -bool SongMessage::ReadFixedLineLength(const mpt::byte *data, const size_t length, const size_t lineLength, const size_t lineEndingLength) +bool SongMessage::ReadFixedLineLength(const std::byte *data, const size_t length, const size_t lineLength, const size_t lineEndingLength) { if(lineLength == 0) return false; @@ -203,7 +203,7 @@ std::string SongMessage::GetFormatted(const LineEnding lineEnding) const bool SongMessage::SetFormatted(std::string message, LineEnding lineEnding) { MPT_ASSERT(lineEnding == leLF || lineEnding == leCR || lineEnding == leCRLF); - switch (lineEnding) + switch(lineEnding) { case leLF: message = mpt::String::Replace(message, "\n", std::string(1, InternalLineEnding)); @@ -222,7 +222,7 @@ bool SongMessage::SetFormatted(std::string message, LineEnding lineEnding) { return false; } - assign(message); + assign(std::move(message)); return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Message.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Message.h index cd21343cf..a249eab0c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Message.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Message.h @@ -42,7 +42,7 @@ public: // [in] length: number of characters that should be read, not including a possible trailing null terminator (it is automatically appended). // [in] lineEnding: line ending formatting of the text in memory. // [out] returns true on success. - bool Read(const mpt::byte *data, const size_t length, LineEnding lineEnding); + bool Read(const std::byte *data, const size_t length, LineEnding lineEnding); bool Read(FileReader &file, const size_t length, LineEnding lineEnding); // Read comments with fixed line length from a mapped file. @@ -51,7 +51,7 @@ public: // [in] lineLength: The fixed length of a line. // [in] lineEndingLength: The padding space between two fixed lines. (there could for example be a null char after every line) // [out] returns true on success. - bool ReadFixedLineLength(const mpt::byte *data, const size_t length, const size_t lineLength, const size_t lineEndingLength); + bool ReadFixedLineLength(const std::byte *data, const size_t length, const size_t lineLength, const size_t lineEndingLength); bool ReadFixedLineLength(FileReader &file, const size_t length, const size_t lineLength, const size_t lineEndingLength); // Retrieve song message. @@ -64,6 +64,8 @@ public: // [out] returns true if the message has been changed. bool SetFormatted(std::string message, LineEnding lineEnding); + // Sets the song message. Expects the provided string to already use the internal line ending character. + void SetRaw(std::string message) noexcept { assign(std::move(message)); } }; OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.cpp index 347dc7c48..585f57c79 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.cpp @@ -29,15 +29,15 @@ OPENMPT_NAMESPACE_BEGIN namespace MixFuncTable { #ifdef MPT_INTMIXER - typedef Int8MToIntS I8M; - typedef Int16MToIntS I16M; - typedef Int8SToIntS I8S; - typedef Int16SToIntS I16S; +using I8M = Int8MToIntS; +using I16M = Int16MToIntS; +using I8S = Int8SToIntS; +using I16S = Int16SToIntS; #else - typedef Int8MToFloatS I8M; - typedef Int16MToFloatS I16M; - typedef Int8SToFloatS I8S; - typedef Int16SToFloatS I16S; +using I8M = Int8MToFloatS; +using I16M = Int16MToFloatS; +using I8S = Int8SToFloatS; +using I16S = Int16SToFloatS; #endif // MPT_INTMIXER // Build mix function table for given resampling, filter and ramping settings: One function each for 8-Bit / 16-Bit Mono / Stereo @@ -59,15 +59,14 @@ namespace MixFuncTable const MixFuncInterface Functions[6 * 16] = { - BuildMixFuncTable(NoInterpolation), // No SRC - BuildMixFuncTable(LinearInterpolation), // Linear SRC - BuildMixFuncTable(FastSincInterpolation), // Fast Sinc (Cubic Spline) SRC - BuildMixFuncTable(PolyphaseInterpolation), // Kaiser SRC - BuildMixFuncTable(FIRFilterInterpolation), // FIR SRC - BuildMixFuncTable(AmigaBlepInterpolation), // Amiga emulation + BuildMixFuncTable(NoInterpolation), // No SRC + BuildMixFuncTable(LinearInterpolation), // Linear SRC + BuildMixFuncTable(FastSincInterpolation), // Fast Sinc (Cubic Spline) SRC + BuildMixFuncTable(PolyphaseInterpolation), // Kaiser SRC + BuildMixFuncTable(FIRFilterInterpolation), // FIR SRC + BuildMixFuncTable(AmigaBlepInterpolation), // Amiga emulation }; - #undef BuildMixFuncTableRamp #undef BuildMixFuncTableFilter #undef BuildMixFuncTable @@ -77,13 +76,13 @@ ResamplingIndex ResamplingModeToMixFlags(ResamplingMode resamplingMode) { switch(resamplingMode) { - case SRCMODE_NEAREST: return ndxNoInterpolation; - case SRCMODE_LINEAR: return ndxLinear; - case SRCMODE_CUBIC: return ndxFastSinc; - case SRCMODE_SINC8LP: return ndxKaiser; - case SRCMODE_SINC8: return ndxFIRFilter; - case SRCMODE_AMIGA: return ndxAmigaBlep; - default: MPT_ASSERT_NOTREACHED(); + case SRCMODE_NEAREST: return ndxNoInterpolation; + case SRCMODE_LINEAR: return ndxLinear; + case SRCMODE_CUBIC: return ndxFastSinc; + case SRCMODE_SINC8LP: return ndxKaiser; + case SRCMODE_SINC8: return ndxFIRFilter; + case SRCMODE_AMIGA: return ndxAmigaBlep; + default: MPT_ASSERT_NOTREACHED(); } return ndxNoInterpolation; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.h b/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.h index fd90ec43f..843a3019d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MixFuncTable.h @@ -18,30 +18,30 @@ OPENMPT_NAMESPACE_BEGIN namespace MixFuncTable { - // Table index: - // [b1-b0] format (8-bit-mono, 16-bit-mono, 8-bit-stereo, 16-bit-stereo) - // [b2] ramp - // [b3] filter - // [b6-b4] src type + // Table index bits: + // [b1-b0] format (8-bit-mono, 16-bit-mono, 8-bit-stereo, 16-bit-stereo) + // [b2] ramp + // [b3] filter + // [b6-b4] src type // Sample type / processing type index enum FunctionIndex { - ndx16Bit = 0x01, - ndxStereo = 0x02, - ndxRamp = 0x04, - ndxFilter = 0x08, + ndx16Bit = 0x01, + ndxStereo = 0x02, + ndxRamp = 0x04, + ndxFilter = 0x08, }; // SRC index enum ResamplingIndex { - ndxNoInterpolation = 0x00, - ndxLinear = 0x10, - ndxFastSinc = 0x20, - ndxKaiser = 0x30, - ndxFIRFilter = 0x40, - ndxAmigaBlep = 0x50, + ndxNoInterpolation = 0x00, + ndxLinear = 0x10, + ndxFastSinc = 0x20, + ndxKaiser = 0x30, + ndxFIRFilter = 0x40, + ndxAmigaBlep = 0x50, }; extern const MixFuncInterface Functions[6 * 16]; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h index 94885abc2..6e2a547fa 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Mixer.h @@ -12,15 +12,33 @@ #include "BuildSettings.h" +#include "../soundbase/SampleTypes.h" + OPENMPT_NAMESPACE_BEGIN #define MPT_INTMIXER #ifdef MPT_INTMIXER -typedef int32 mixsample_t; -enum { MIXING_FILTER_PRECISION = 24 }; // Fixed point resonant filter bits +using mixsample_t = MixSampleIntTraits::sample_type; +enum { MIXING_FILTER_PRECISION = MixSampleIntTraits::filter_precision_bits() }; // Fixed point resonant filter bits #else -typedef float mixsample_t; +using mixsample_t = AudioSampleFloat; +#endif +enum { MIXING_ATTENUATION = MixSampleIntTraits::mix_headroom_bits() }; +enum { MIXING_FRACTIONAL_BITS = MixSampleIntTraits::mix_fractional_bits() }; + +constexpr float MIXING_SCALEF = MixSampleIntTraits::mix_scale(); + +#ifdef MPT_INTMIXER +static_assert(sizeof(mixsample_t) == 4); +static_assert(MIXING_FILTER_PRECISION == 24); +static_assert(MIXING_ATTENUATION == 4); +static_assert(MIXING_FRACTIONAL_BITS == 27); +static_assert(MixSampleIntTraits::mix_clip_max() == int32(0x7FFFFFF)); +static_assert(MixSampleIntTraits::mix_clip_min() == (0 - int32(0x7FFFFFF))); +static_assert(MIXING_SCALEF == 134217728.0f); +#else +static_assert(sizeof(mixsample_t) == 4); #endif #define MIXBUFFERSIZE 512 @@ -28,17 +46,6 @@ typedef float mixsample_t; #define VOLUMERAMPPRECISION 12 // Fractional bits in volume ramp variables -enum { MIXING_ATTENUATION = 4 }; -enum { MIXING_FRACTIONAL_BITS = 32 - 1 - MIXING_ATTENUATION }; - -enum -{ - MIXING_CLIPMAX = ((1<(1 << MIXING_FRACTIONAL_BITS); - // The absolute maximum number of sampling points any interpolation algorithm is going to look at in any direction from the current sampling point // Currently, the maximum is 4 sampling points forwards and 3 sampling points backwards (Polyphase / FIR algorithms). // Hence, this value must be at least 4. diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.cpp index f56df384f..5b4345e4e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.cpp @@ -12,6 +12,7 @@ #include "stdafx.h" #include "MixerLoops.h" +#include "..//soundbase/SampleBuffer.h" #include "Snd_defs.h" #include "ModChannel.h" #ifdef ENABLE_SSE2 @@ -21,137 +22,6 @@ OPENMPT_NAMESPACE_BEGIN -//////////////////////////////////////////////////////////////////////////////////// -// 3DNow! optimizations - -#ifdef ENABLE_X86_AMD - -// Convert integer mix to floating-point -static void AMD_StereoMixToFloat(const int32 *pSrc, float *pOut1, float *pOut2, uint32 nCount, const float _i2fc) -{ - _asm { - movd mm0, _i2fc - mov edx, pSrc - mov edi, pOut1 - mov ebx, pOut2 - mov ecx, nCount - punpckldq mm0, mm0 - inc ecx - shr ecx, 1 -mainloop: - movq mm1, qword ptr [edx] - movq mm2, qword ptr [edx+8] - add edi, 8 - pi2fd mm1, mm1 - pi2fd mm2, mm2 - add ebx, 8 - pfmul mm1, mm0 - pfmul mm2, mm0 - add edx, 16 - movq mm3, mm1 - punpckldq mm3, mm2 - punpckhdq mm1, mm2 - dec ecx - movq qword ptr [edi-8], mm3 - movq qword ptr [ebx-8], mm1 - jnz mainloop - emms - } -} - -static void AMD_FloatToStereoMix(const float *pIn1, const float *pIn2, int32 *pOut, uint32 nCount, const float _f2ic) -{ - _asm { - movd mm0, _f2ic - mov eax, pIn1 - mov ebx, pIn2 - mov edx, pOut - mov ecx, nCount - punpckldq mm0, mm0 - inc ecx - shr ecx, 1 - sub edx, 16 -mainloop: - movq mm1, [eax] - movq mm2, [ebx] - add edx, 16 - movq mm3, mm1 - punpckldq mm1, mm2 - punpckhdq mm3, mm2 - add eax, 8 - pfmul mm1, mm0 - pfmul mm3, mm0 - add ebx, 8 - pf2id mm1, mm1 - pf2id mm3, mm3 - dec ecx - movq qword ptr [edx], mm1 - movq qword ptr [edx+8], mm3 - jnz mainloop - emms - } -} - - -static void AMD_FloatToMonoMix(const float *pIn, int32 *pOut, uint32 nCount, const float _f2ic) -{ - _asm { - movd mm0, _f2ic - mov eax, pIn - mov edx, pOut - mov ecx, nCount - punpckldq mm0, mm0 - add ecx, 3 - shr ecx, 2 - sub edx, 16 -mainloop: - movq mm1, [eax] - movq mm2, [eax+8] - add edx, 16 - pfmul mm1, mm0 - pfmul mm2, mm0 - add eax, 16 - pf2id mm1, mm1 - pf2id mm2, mm2 - dec ecx - movq qword ptr [edx], mm1 - movq qword ptr [edx+8], mm2 - jnz mainloop - emms - } -} - - -static void AMD_MonoMixToFloat(const int32 *pSrc, float *pOut, uint32 nCount, const float _i2fc) -{ - _asm { - movd mm0, _i2fc - mov eax, pSrc - mov edx, pOut - mov ecx, nCount - punpckldq mm0, mm0 - add ecx, 3 - shr ecx, 2 - sub edx, 16 -mainloop: - movq mm1, qword ptr [eax] - movq mm2, qword ptr [eax+8] - add edx, 16 - pi2fd mm1, mm1 - pi2fd mm2, mm2 - add eax, 16 - pfmul mm1, mm0 - pfmul mm2, mm0 - dec ecx - movq qword ptr [edx], mm1 - movq qword ptr [edx+8], mm2 - jnz mainloop - emms - } -} - -#endif // ENABLE_X86_AMD - /////////////////////////////////////////////////////////////////////////////////////// // SSE Optimizations @@ -213,36 +83,6 @@ static void SSE2_FloatToStereoMix(const float *pIn1, const float *pIn2, int32 *p #if defined(ENABLE_X86) && defined(ENABLE_SSE) -static void SSE_StereoMixToFloat(const int32 *pSrc, float *pOut1, float *pOut2, uint32 nCount, const float _i2fc) -{ - _asm { - movss xmm0, _i2fc - mov edx, pSrc - mov eax, pOut1 - mov ebx, pOut2 - mov ecx, nCount - shufps xmm0, xmm0, 0x00 - xorps xmm1, xmm1 - xorps xmm2, xmm2 - inc ecx - shr ecx, 1 -mainloop: - cvtpi2ps xmm1, [edx] - cvtpi2ps xmm2, [edx+8] - add eax, 8 - add ebx, 8 - movlhps xmm1, xmm2 - mulps xmm1, xmm0 - add edx, 16 - shufps xmm1, xmm1, 0xD8 - dec ecx - movlps qword ptr [eax-8], xmm1 - movhps qword ptr [ebx-8], xmm1 - jnz mainloop - } -} - - static void SSE_MonoMixToFloat(const int32 *pSrc, float *pOut, uint32 nCount, const float _i2fc) { _asm { @@ -274,60 +114,6 @@ mainloop: #ifdef ENABLE_X86 -// Convert floating-point mix to integer - -static void X86_FloatToStereoMix(const float *pIn1, const float *pIn2, int32 *pOut, uint32 nCount, const float _f2ic) -{ - _asm { - mov esi, pIn1 - mov ebx, pIn2 - mov edi, pOut - mov ecx, nCount - fld _f2ic -mainloop: - fld dword ptr [ebx] - add edi, 8 - fld dword ptr [esi] - add ebx, 4 - add esi, 4 - fmul st(0), st(2) - fistp dword ptr [edi-8] - fmul st(0), st(1) - fistp dword ptr [edi-4] - dec ecx - jnz mainloop - fstp st(0) - } -} - - -// Convert integer mix to floating-point - -static void X86_StereoMixToFloat(const int32 *pSrc, float *pOut1, float *pOut2, uint32 nCount, const float _i2fc) -{ - _asm { - mov esi, pSrc - mov edi, pOut1 - mov ebx, pOut2 - mov ecx, nCount - fld _i2fc -mainloop: - fild dword ptr [esi] - fild dword ptr [esi+4] - add ebx, 4 - add edi, 4 - fmul st(0), st(2) - add esi, 8 - fstp dword ptr [ebx-4] - fmul st(0), st(1) - fstp dword ptr [edi-4] - dec ecx - jnz mainloop - fstp st(0) - } -} - - static void X86_FloatToMonoMix(const float *pIn, int32 *pOut, uint32 nCount, const float _f2ic) { _asm { @@ -348,27 +134,6 @@ R2I_Loop: } } - -static void X86_MonoMixToFloat(const int32 *pSrc, float *pOut, uint32 nCount, const float _i2fc) -{ - _asm { - mov edx, pOut - mov eax, pSrc - mov ecx, nCount - fld _i2fc - sub edx, 4 -I2R_Loop: - fild DWORD PTR [eax] - add edx, 4 - fmul ST(0), ST(1) - dec ecx - lea eax, [eax+4] - fstp DWORD PTR [edx] - jnz I2R_Loop - fstp st(0) - } -} - #endif // ENABLE_X86 @@ -422,26 +187,10 @@ void StereoMixToFloat(const int32 *pSrc, float *pOut1, float *pOut2, uint32 nCou return; } #endif // ENABLE_SSE2 - #if defined(ENABLE_X86) && defined(ENABLE_SSE) - if(GetProcSupport() & PROCSUPPORT_SSE) - { - SSE_StereoMixToFloat(pSrc, pOut1, pOut2, nCount, _i2fc); - return; - } - #endif // ENABLE_X86 && ENABLE_SSE - #ifdef ENABLE_X86_AMD - if(GetProcSupport() & PROCSUPPORT_AMD_3DNOW) - { - AMD_StereoMixToFloat(pSrc, pOut1, pOut2, nCount, _i2fc); - return; - } - #endif // ENABLE_X86_AMD - #ifdef ENABLE_X86 - X86_StereoMixToFloat(pSrc, pOut1, pOut2, nCount, _i2fc); - #else // !ENABLE_X86 + { C_StereoMixToFloat(pSrc, pOut1, pOut2, nCount, _i2fc); - #endif // ENABLE_X86 + } } @@ -455,19 +204,10 @@ void FloatToStereoMix(const float *pIn1, const float *pIn2, int32 *pOut, uint32 return; } #endif // ENABLE_SSE2 - #ifdef ENABLE_X86_AMD - if(GetProcSupport() & PROCSUPPORT_AMD_3DNOW) - { - AMD_FloatToStereoMix(pIn1, pIn2, pOut, nCount, _f2ic); - return; - } - #endif // ENABLE_X86_AMD - #ifdef ENABLE_X86 - X86_FloatToStereoMix(pIn1, pIn2, pOut, nCount, _f2ic); - #else // !ENABLE_X86 + { C_FloatToStereoMix(pIn1, pIn2, pOut, nCount, _f2ic); - #endif // ENABLE_X86 + } } @@ -482,19 +222,10 @@ void MonoMixToFloat(const int32 *pSrc, float *pOut, uint32 nCount, const float _ return; } #endif // ENABLE_X86 && ENABLE_SSE - #ifdef ENABLE_X86_AMD - if(GetProcSupport() & PROCSUPPORT_AMD_3DNOW) - { - AMD_MonoMixToFloat(pSrc, pOut, nCount, _i2fc); - return; - } - #endif // ENABLE_X86_AMD - #ifdef ENABLE_X86 - X86_MonoMixToFloat(pSrc, pOut, nCount, _i2fc); - #else // !ENABLE_X86 + { C_MonoMixToFloat(pSrc, pOut, nCount, _i2fc); - #endif // ENABLE_X86 + } } @@ -502,20 +233,18 @@ void MonoMixToFloat(const int32 *pSrc, float *pOut, uint32 nCount, const float _ void FloatToMonoMix(const float *pIn, int32 *pOut, uint32 nCount, const float _f2ic) { - #ifdef ENABLE_X86_AMD - if(GetProcSupport() & PROCSUPPORT_AMD_3DNOW) + #ifdef ENABLE_X86 + if(GetProcSupport() & PROCSUPPORT_ASM_INTRIN) { - AMD_FloatToMonoMix(pIn, pOut, nCount, _f2ic); + X86_FloatToMonoMix(pIn, pOut, nCount, _f2ic); return; } - #endif // ENABLE_X86_AMD - - #ifdef ENABLE_X86 - X86_FloatToMonoMix(pIn, pOut, nCount, _f2ic); - #else // !ENABLE_X86 - C_FloatToMonoMix(pIn, pOut, nCount, _f2ic); #endif // ENABLE_X86 + { + C_FloatToMonoMix(pIn, pOut, nCount, _f2ic); + } + } @@ -577,10 +306,15 @@ static void C_InterleaveFrontRear(mixsample_t *pFrontBuf, mixsample_t *pRearBuf, void InterleaveFrontRear(mixsample_t *pFrontBuf, mixsample_t *pRearBuf, uint32 nFrames) { #if defined(ENABLE_X86) && defined(MPT_INTMIXER) - X86_InterleaveFrontRear(pFrontBuf, pRearBuf, nFrames); - #else - C_InterleaveFrontRear(pFrontBuf, pRearBuf, nFrames); + if(GetProcSupport() & PROCSUPPORT_ASM_INTRIN) + { + X86_InterleaveFrontRear(pFrontBuf, pRearBuf, nFrames); + return; + } #endif + { + C_InterleaveFrontRear(pFrontBuf, pRearBuf, nFrames); + } } @@ -616,10 +350,15 @@ static void C_MonoFromStereo(mixsample_t *pMixBuf, uint32 nSamples) void MonoFromStereo(mixsample_t *pMixBuf, uint32 nSamples) { #if defined(ENABLE_X86) && defined(MPT_INTMIXER) - X86_MonoFromStereo(pMixBuf, nSamples); - #else - C_MonoFromStereo(pMixBuf, nSamples); + if(GetProcSupport() & PROCSUPPORT_ASM_INTRIN) + { + X86_MonoFromStereo(pMixBuf, nSamples); + return; + } #endif + { + C_MonoFromStereo(pMixBuf, nSamples); + } } @@ -740,10 +479,15 @@ static void C_StereoFill(mixsample_t *pBuffer, uint32 nSamples, mixsample_t &rof void StereoFill(mixsample_t *pBuffer, uint32 nSamples, mixsample_t &rofs, mixsample_t &lofs) { #if defined(ENABLE_X86) && defined(MPT_INTMIXER) - X86_StereoFill(pBuffer, nSamples, &rofs, &lofs); - #else - C_StereoFill(pBuffer, nSamples, rofs, lofs); + if(GetProcSupport() & PROCSUPPORT_ASM_INTRIN) + { + X86_StereoFill(pBuffer, nSamples, &rofs, &lofs); + return; + } #endif + { + C_StereoFill(pBuffer, nSamples, rofs, lofs); + } } @@ -818,8 +562,8 @@ static void C_EndChannelOfs(ModChannel &chn, mixsample_t *pBuffer, uint32 nSampl pBuffer[i*2+1] += lofs; } #ifndef MPT_INTMIXER - if(mpt::abs(rofs) < OFSTHRESHOLD) rofs = 0; - if(mpt::abs(lofs) < OFSTHRESHOLD) lofs = 0; + if(std::abs(rofs) < OFSTHRESHOLD) rofs = 0; + if(std::abs(lofs) < OFSTHRESHOLD) lofs = 0; #endif chn.nROfs = rofs; @@ -829,10 +573,15 @@ static void C_EndChannelOfs(ModChannel &chn, mixsample_t *pBuffer, uint32 nSampl void EndChannelOfs(ModChannel &chn, mixsample_t *pBuffer, uint32 nSamples) { #if defined(ENABLE_X86) && defined(MPT_INTMIXER) - X86_EndChannelOfs(&chn, pBuffer, nSamples); - #else - C_EndChannelOfs(chn, pBuffer, nSamples); + if(GetProcSupport() & PROCSUPPORT_ASM_INTRIN) + { + X86_EndChannelOfs(&chn, pBuffer, nSamples); + return; + } #endif + { + C_EndChannelOfs(chn, pBuffer, nSamples); + } } @@ -858,7 +607,7 @@ void DeinterleaveStereo(const mixsample_t * MPT_RESTRICT input, mixsample_t * MP #ifndef MODPLUG_TRACKER -void ApplyGain(int32 *soundBuffer, std::size_t channels, std::size_t countChunk, int32 gainFactor16_16) +void ApplyGain(MixSampleInt *soundBuffer, std::size_t channels, std::size_t countChunk, int32 gainFactor16_16) { if(gainFactor16_16 == (1<<16)) { @@ -866,7 +615,7 @@ void ApplyGain(int32 *soundBuffer, std::size_t channels, std::size_t countChunk, return; } // no clipping prevention is done here - int32 * buf = soundBuffer; + MixSampleInt * buf = soundBuffer; for(std::size_t i=0; i outputBuffer, std::size_t offset, std::size_t channels, std::size_t countChunk, float gainFactor) +{ + if(gainFactor == 1.0f) + { + // nothing to do, gain == +/- 0dB + return; + } + for(std::size_t i = 0; i < countChunk; ++i) { for(std::size_t channel = 0; channel < channels; ++channel) { - ApplyGain( - outputBuffers[channel] + offset, - outputBuffers[channel] + offset + countChunk, - gainFactor); + outputBuffer(channel, offset + i) *= gainFactor; } } } +void ApplyGain(audio_buffer_planar outputBuffer, std::size_t offset, std::size_t channels, std::size_t countChunk, float gainFactor) +{ + if(gainFactor == 1.0f) + { + // nothing to do, gain == +/- 0dB + return; + } + for(std::size_t i = 0; i < countChunk; ++i) + { + for(std::size_t channel = 0; channel < channels; ++channel) + { + outputBuffer(channel, offset + i) *= gainFactor; + } + } +} + + #endif // !MODPLUG_TRACKER diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.h b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.h index 27be04a99..c19e34643 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerLoops.h @@ -17,6 +17,8 @@ OPENMPT_NAMESPACE_BEGIN struct ModChannel; +template struct audio_buffer_interleaved; +template struct audio_buffer_planar; void StereoMixToFloat(const int32 *pSrc, float *pOut1, float *pOut2, uint32 nCount, const float _i2fc); void FloatToStereoMix(const float *pIn1, const float *pIn2, int32 *pOut, uint32 uint32, const float _f2ic); @@ -24,8 +26,10 @@ void MonoMixToFloat(const int32 *pSrc, float *pOut, uint32 uint32, const float _ void FloatToMonoMix(const float *pIn, int32 *pOut, uint32 uint32, const float _f2ic); #ifndef MODPLUG_TRACKER -void ApplyGain(int32 *soundBuffer, std::size_t channels, std::size_t countChunk, int32 gainFactor16_16); -void ApplyGain(float *outputBuffer, float * const *outputBuffers, std::size_t offset, std::size_t channels, std::size_t countChunk, float gainFactor); +void ApplyGain(MixSampleInt *soundBuffer, std::size_t channels, std::size_t countChunk, int32 gainFactor16_16); +void ApplyGain(MixSampleFloat *soundBuffer, std::size_t channels, std::size_t countChunk, float gainFactor); +void ApplyGain(audio_buffer_interleaved outputBuffer, std::size_t offset, std::size_t channels, std::size_t countChunk, float gainFactor); +void ApplyGain(audio_buffer_planar outputBuffer, std::size_t offset, std::size_t channels, std::size_t countChunk, float gainFactor); #endif // !MODPLUG_TRACKER void InitMixBuffer(mixsample_t *pBuffer, uint32 nSamples); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerSettings.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerSettings.cpp index f9615de4b..7a1057256 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/MixerSettings.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/MixerSettings.cpp @@ -26,7 +26,7 @@ MixerSettings::MixerSettings() // Mixing Configuration gnChannels = 2; - gdwMixingFreq = 44100; + gdwMixingFreq = 48000; m_nPreAmp = 128; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.cpp index 66ae13d44..6e9b45ea3 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.cpp @@ -11,6 +11,7 @@ #include "stdafx.h" #include "Sndfile.h" #include "ModChannel.h" +#include "tuning.h" OPENMPT_NAMESPACE_BEGIN @@ -46,6 +47,7 @@ void ModChannel::Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELI if(resetMask & resetSetPosAdvanced) { + increment = SamplePosition(0); nPeriod = 0; position.Set(0); nLength = 0; @@ -56,11 +58,11 @@ void ModChannel::Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELI pModInstrument = nullptr; nCutOff = 0x7F; nResonance = 0; - nFilterMode = 0; + nFilterMode = FilterMode::LowPass; rightVol = leftVol = 0; newRightVol = newLeftVol = 0; rightRamp = leftRamp = 0; - nVolume = 0; // Needs to be 0 for SMP_NODEFAULTVOLUME flag + nVolume = 0; // Needs to be 0 for SMP_NODEFAULTVOLUME flag nVibratoPos = nTremoloPos = nPanbrelloPos = 0; nOldHiOffset = 0; nLeftVU = nRightVU = 0; @@ -70,7 +72,6 @@ void ModChannel::Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELI m_CalculateFreq = false; m_PortamentoFineSteps = 0; m_PortamentoTickSlide = 0; - m_Freq = 0; } if(resetMask & resetChannelSettings) @@ -89,7 +90,6 @@ void ModChannel::Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELI nRestorePanOnNewNote = 0; nRestoreCutoffOnNewNote = 0; nRestoreResonanceOnNewNote = 0; - } } @@ -124,7 +124,7 @@ ModCommand::NOTE ModChannel::GetPluginNote(bool realNoteMapping) const } ModCommand::NOTE plugNote = mpt::saturate_cast(nNote - nTranspose); // Caution: When in compatible mode, ModChannel::nNote stores the "real" note, not the mapped note! - if(realNoteMapping && pModInstrument != nullptr && plugNote >= NOTE_MIN && plugNote < (mpt::size(pModInstrument->NoteMap) + NOTE_MIN)) + if(realNoteMapping && pModInstrument != nullptr && plugNote >= NOTE_MIN && plugNote < (std::size(pModInstrument->NoteMap) + NOTE_MIN)) { plugNote = pModInstrument->NoteMap[plugNote - NOTE_MIN]; } @@ -132,4 +132,32 @@ ModCommand::NOTE ModChannel::GetPluginNote(bool realNoteMapping) const } +void ModChannel::SetInstrumentPan(int32 pan, const CSoundFile &sndFile) +{ + // IT compatibility: Instrument and sample panning does not override channel panning + // Test case: PanResetInstr.it + if(sndFile.m_playBehaviour[kITDoNotOverrideChannelPan]) + { + nRestorePanOnNewNote = static_cast(nPan + 1); + if(dwFlags[CHN_SURROUND]) + nRestorePanOnNewNote |= 0x8000; + } + nPan = pan; +} + + +void ModChannel::RecalcTuningFreq(Tuning::RATIOTYPE vibratoFactor, Tuning::NOTEINDEXTYPE arpeggioSteps, const CSoundFile &sndFile) +{ + if(!HasCustomTuning()) + return; + + ModCommand::NOTE note = ModCommand::IsNote(nNote) ? nNote : nLastNote; + + if(sndFile.m_playBehaviour[kITRealNoteMapping] && note >= NOTE_MIN && note <= NOTE_MAX) + note = pModInstrument->NoteMap[note - NOTE_MIN]; + + nPeriod = mpt::saturate_round((nC5Speed << FREQ_FRACBITS) * vibratoFactor * pModInstrument->pTuning->GetRatio(note - NOTE_MIDDLEC + arpeggioSteps, nFineTune + m_PortamentoFineSteps)); +} + + OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.h index 4cc1f7fa2..8f870a4a9 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModChannel.h @@ -16,6 +16,7 @@ #include "ModInstrument.h" #include "modcommand.h" #include "Paula.h" +#include "tuningbase.h" OPENMPT_NAMESPACE_BEGIN @@ -39,17 +40,17 @@ struct ModChannel }; // Information used in the mixer (should be kept tight for better caching) - SamplePosition position; // Current play position (fixed point) - SamplePosition increment; // Sample speed relative to mixing frequency (fixed point) - const void *pCurrentSample; // Currently playing sample (nullptr if no sample is playing) - int32 leftVol; // 0...4096 (12 bits, since 16 bits + 12 bits = 28 bits = 0dB in integer mixer, see MIXING_ATTENUATION) - int32 rightVol; // Ditto - int32 leftRamp; // Ramping delta, 20.12 fixed point (see VOLUMERAMPPRECISION) - int32 rightRamp; // Ditto - int32 rampLeftVol; // Current ramping volume, 20.12 fixed point (see VOLUMERAMPPRECISION) - int32 rampRightVol; // Ditto - mixsample_t nFilter_Y[2][2]; // Filter memory - two history items per sample channel - mixsample_t nFilter_A0, nFilter_B0, nFilter_B1; // Filter coeffs + SamplePosition position; // Current play position (fixed point) + SamplePosition increment; // Sample speed relative to mixing frequency (fixed point) + const void *pCurrentSample; // Currently playing sample (nullptr if no sample is playing) + int32 leftVol; // 0...4096 (12 bits, since 16 bits + 12 bits = 28 bits = 0dB in integer mixer, see MIXING_ATTENUATION) + int32 rightVol; // Ditto + int32 leftRamp; // Ramping delta, 20.12 fixed point (see VOLUMERAMPPRECISION) + int32 rightRamp; // Ditto + int32 rampLeftVol; // Current ramping volume, 20.12 fixed point (see VOLUMERAMPPRECISION) + int32 rampRightVol; // Ditto + mixsample_t nFilter_Y[2][2]; // Filter memory - two history items per sample channel + mixsample_t nFilter_A0, nFilter_B0, nFilter_B1; // Filter coeffs mixsample_t nFilter_HP; SmpLength nLength; @@ -59,41 +60,42 @@ struct ModChannel mixsample_t nROfs, nLOfs; uint32 nRampLength; - const ModSample *pModSample; // Currently assigned sample slot (may already be stopped) + const ModSample *pModSample; // Currently assigned sample slot (may already be stopped) Paula::State paulaState; // Information not used in the mixer - const ModInstrument *pModInstrument; // Currently assigned instrument slot - SmpLength prevNoteOffset; // Offset for instrument-less notes for ProTracker/ScreamTracker + const ModInstrument *pModInstrument; // Currently assigned instrument slot + SmpLength prevNoteOffset; // Offset for instrument-less notes for ProTracker/ScreamTracker SmpLength oldOffset; - FlagSet dwOldFlags; // Flags from previous tick + FlagSet dwOldFlags; // Flags from previous tick int32 newLeftVol, newRightVol; int32 nRealVolume, nRealPan; int32 nVolume, nPan, nFadeOutVol; - int32 nPeriod, nC5Speed, nPortamentoDest; + int32 nPeriod; // Frequency in Hz if !CSoundFile::PeriodsAreFrequencies() or using custom tuning, 4x Amiga periods otherwise + int32 nC5Speed, nPortamentoDest; int32 cachedPeriod, glissandoPeriod; - int32 nCalcVolume; // Calculated channel volume, 14-Bit (without global volume, pre-amp etc applied) - for MIDI macros - EnvInfo VolEnv, PanEnv, PitchEnv; // Envelope playback info - int32 nGlobalVol; // Channel volume (CV in ITTECH.TXT) 0...64 - int32 nInsVol; // Sample / Instrument volume (SV * IV in ITTECH.TXT) 0...64 + int32 nCalcVolume; // Calculated channel volume, 14-Bit (without global volume, pre-amp etc applied) - for MIDI macros + EnvInfo VolEnv, PanEnv, PitchEnv; // Envelope playback info + int32 nGlobalVol; // Channel volume (CV in ITTECH.TXT) 0...64 + int32 nInsVol; // Sample / Instrument volume (SV * IV in ITTECH.TXT) 0...64 int32 nFineTune, nTranspose; int32 nPortamentoSlide, nAutoVibDepth; - uint32 nEFxOffset; // offset memory for Invert Loop (EFx, .MOD only) + uint32 nEFxOffset; // Offset memory for Invert Loop (EFx, .MOD only) int16 nVolSwing, nPanSwing; int16 nCutSwing, nResSwing; - int16 nRestorePanOnNewNote; //If > 0, nPan should be set to nRestorePanOnNewNote - 1 on new note. Used to recover from panswing. + uint16 nRestorePanOnNewNote; //If > 0, nPan should be set to nRestorePanOnNewNote - 1 on new note. Used to recover from pan swing and IT sample / instrument panning. High bit set = surround int16 nRetrigCount, nRetrigParam; ROWINDEX nPatternLoop; CHANNELINDEX nMasterChn; ModCommand rowCommand; // 8-bit members ResamplingMode resamplingMode; - uint8 nRestoreResonanceOnNewNote; // See nRestorePanOnNewNote - uint8 nRestoreCutoffOnNewNote; // ditto + uint8 nRestoreResonanceOnNewNote; // See nRestorePanOnNewNote + uint8 nRestoreCutoffOnNewNote; // ditto uint8 nNote; NewNoteAction nNNA; - uint8 nLastNote; // Last note, ignoring note offs and cuts - for MIDI macros - uint8 nArpeggioLastNote, nArpeggioBaseNote; // For plugin arpeggio + uint8 nLastNote; // Last note, ignoring note offs and cuts - for MIDI macros + uint8 nArpeggioLastNote, nArpeggioBaseNote; // For plugin arpeggio uint8 nNewNote, nNewIns, nOldIns, nCommand, nArpeggio; uint8 nOldVolumeSlide, nOldFineVolUpDown; uint8 nOldPortaUp, nOldPortaDown, nOldFinePortaUpDown, nOldExtraFinePortaUpDown; @@ -110,16 +112,17 @@ struct ModChannel uint8 nTremorCount, nTremorParam; uint8 nPatternLoopCount; uint8 nLeftVU, nRightVU; - uint8 nActiveMacro, nFilterMode; - uint8 nEFxSpeed, nEFxDelay; // memory for Invert Loop (EFx, .MOD only) - uint8 nNoteSlideCounter, nNoteSlideSpeed, nNoteSlideStep; // IMF / PTM Note Slide - uint8 lastZxxParam; // Memory for \xx slides + uint8 nActiveMacro; + FilterMode nFilterMode; + uint8 nEFxSpeed, nEFxDelay; // memory for Invert Loop (EFx, .MOD only) + uint8 nNoteSlideCounter, nNoteSlideSpeed, nNoteSlideStep; // IMF / PTM Note Slide + uint8 lastZxxParam; // Memory for \xx slides bool isFirstTick : 1; bool isPreviewNote : 1; //-->Variables used to make user-definable tuning modes work with pattern effects. //If true, freq should be recalculated in ReadNote() on first tick. - //Currently used only for vibrato things - using in other context might be + //Currently used only for vibrato things - using in other context might be //problematic. bool m_ReCalculateFreqOnFirstTick : 1; @@ -128,8 +131,6 @@ struct ModChannel int32 m_PortamentoFineSteps, m_PortamentoTickSlide; - uint32 m_Freq; - //NOTE_PCs memory. float m_plugParamValueStep, m_plugParamTargetValue; uint16 m_RowPlugParam; @@ -166,40 +167,46 @@ struct ModChannel enum ResetFlags { - resetChannelSettings = 1, // Reload initial channel settings - resetSetPosBasic = 2, // Reset basic runtime channel attributes - resetSetPosAdvanced = 4, // Reset more runtime channel attributes - resetSetPosFull = resetSetPosBasic | resetSetPosAdvanced | resetChannelSettings, // Reset all runtime channel attributes - resetTotal = resetSetPosFull, + resetChannelSettings = 1, // Reload initial channel settings + resetSetPosBasic = 2, // Reset basic runtime channel attributes + resetSetPosAdvanced = 4, // Reset more runtime channel attributes + resetSetPosFull = resetSetPosBasic | resetSetPosAdvanced | resetChannelSettings, // Reset all runtime channel attributes + resetTotal = resetSetPosFull, }; void Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELINDEX sourceChannel); void Stop(); - bool IsSamplePlaying() const { return !increment.IsZero(); } + bool IsSamplePlaying() const noexcept { return !increment.IsZero(); } - uint32 GetVSTVolume() { return (pModInstrument) ? pModInstrument->nGlobalVol * 4 : nVolume; } + uint32 GetVSTVolume() const noexcept { return (pModInstrument) ? pModInstrument->nGlobalVol * 4 : nVolume; } ModCommand::NOTE GetPluginNote(bool realNoteMapping) const; - // Check if the channel has a valid MIDI output. This function guarantees that pModInstrument != nullptr. - bool HasMIDIOutput() const { return pModInstrument != nullptr && pModInstrument->HasValidMIDIChannel(); } + // Check if the channel has a valid MIDI output. A return value of true implies that pModInstrument != nullptr. + bool HasMIDIOutput() const noexcept { return pModInstrument != nullptr && pModInstrument->HasValidMIDIChannel(); } + // Check if the channel uses custom tuning. A return value of true implies that pModInstrument != nullptr. + bool HasCustomTuning() const noexcept { return pModInstrument != nullptr && pModInstrument->pTuning != nullptr; } // Check if currently processed loop is a sustain loop. pModSample is not checked for validity! - bool InSustainLoop() const { return (dwFlags & (CHN_LOOP | CHN_KEYOFF)) == CHN_LOOP && pModSample->uFlags[CHN_SUSTAINLOOP]; } + bool InSustainLoop() const noexcept { return (dwFlags & (CHN_LOOP | CHN_KEYOFF)) == CHN_LOOP && pModSample->uFlags[CHN_SUSTAINLOOP]; } void UpdateInstrumentVolume(const ModSample *smp, const ModInstrument *ins); + + void SetInstrumentPan(int32 pan, const CSoundFile &sndFile); + + void RecalcTuningFreq(Tuning::RATIOTYPE vibratoFactor, Tuning::NOTEINDEXTYPE arpeggioSteps, const CSoundFile &sndFile); }; // Default pattern channel settings struct ModChannelSettings { - FlagSet dwFlags; // Channel flags - uint16 nPan; // Initial pan (0...256) - uint16 nVolume; // Initial channel volume (0...64) - PLUGINDEX nMixPlugin; // Assigned plugin - char szName[MAX_CHANNELNAME]; // Channel name + FlagSet dwFlags; // Channel flags + uint16 nPan; // Initial pan (0...256) + uint16 nVolume; // Initial channel volume (0...64) + PLUGINDEX nMixPlugin; // Assigned plugin + mpt::charbuf szName; // Channel name ModChannelSettings() { @@ -212,7 +219,7 @@ struct ModChannelSettings nPan = 128; nVolume = 64; nMixPlugin = 0; - szName[0] = '\0'; + szName = ""; } }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp index 604e4eaa0..6260ea5ed 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.cpp @@ -43,7 +43,7 @@ void InstrumentEnvelope::Convert(MODTYPE fromType, MODTYPE toType) } // XM -> IT / MPTM: Shorten loop by one tick by inserting bogus point - if(nLoopEnd > nLoopStart && dwFlags[ENV_LOOP]) + if(nLoopEnd > nLoopStart && dwFlags[ENV_LOOP] && nLoopEnd < size()) { if(at(nLoopEnd).tick - 1 > at(nLoopEnd - 1).tick) { @@ -138,46 +138,15 @@ void InstrumentEnvelope::Sanitize(uint8 maxValue) ModInstrument::ModInstrument(SAMPLEINDEX sample) { - nFadeOut = 256; - dwFlags.reset(); - nGlobalVol = 64; - nPan = 32 * 4; - - nNNA = NNA_NOTECUT; - nDCT = DCT_NONE; - nDNA = DNA_NOTECUT; - - nPanSwing = 0; - nVolSwing = 0; SetCutoff(0, false); SetResonance(0, false); - wMidiBank = 0; - nMidiProgram = 0; - nMidiChannel = 0; - nMidiDrumKey = 0; - midiPWD = 2; - - nPPC = NOTE_MIDDLEC - 1; - nPPS = 0; - - nMixPlug = 0; - nVolRampUp = 0; - resampling = SRCMODE_DEFAULT; - nCutSwing = 0; - nResSwing = 0; - nFilterMode = FLTMODE_UNCHANGED; pitchToTempoLock.Set(0); - pluginVelocityHandling = PLUGIN_VELOCITYHANDLING_CHANNEL; - pluginVolumeHandling = PLUGIN_VOLUMEHANDLING_IGNORE; pTuning = CSoundFile::GetDefaultTuning(); AssignSample(sample); ResetNoteMap(); - - MemsetZero(name); - MemsetZero(filename); } @@ -195,7 +164,7 @@ void ModInstrument::Convert(MODTYPE fromType, MODTYPE toType) dwFlags.reset(INS_SETPANNING); SetCutoff(GetCutoff(), false); SetResonance(GetResonance(), false); - nFilterMode = FLTMODE_UNCHANGED; + filterMode = FilterMode::Unchanged; nCutSwing = nPanSwing = nResSwing = nVolSwing = 0; @@ -208,11 +177,11 @@ void ModInstrument::Convert(MODTYPE fromType, MODTYPE toType) if(nMidiChannel == MidiMappedChannel) { - nMidiChannel = 1; + nMidiChannel = MidiFirstChannel; } // FT2 only has unsigned Pitch Wheel Depth, and it's limited to 0...36 (in the GUI, at least. As you would expect it from FT2, this value is actually not sanitized on load). - midiPWD = static_cast(mpt::abs(midiPWD)); + midiPWD = static_cast(std::abs(midiPWD)); Limit(midiPWD, int8(0), int8(36)); nGlobalVol = 64; @@ -253,7 +222,7 @@ void ModInstrument::Convert(MODTYPE fromType, MODTYPE toType) SetTuning(nullptr); pitchToTempoLock.Set(0); nCutSwing = nResSwing = 0; - nFilterMode = FLTMODE_UNCHANGED; + filterMode = FilterMode::Unchanged; nVolRampUp = 0; } } @@ -264,12 +233,11 @@ std::set ModInstrument::GetSamples() const { std::set referencedSamples; - for(size_t i = 0; i < CountOf(Keyboard); i++) + for(const auto sample : Keyboard) { - // 0 isn't a sample. - if(Keyboard[i] != 0) + if(sample) { - referencedSamples.insert(Keyboard[i]); + referencedSamples.insert(sample); } } @@ -281,12 +249,11 @@ std::set ModInstrument::GetSamples() const // The caller has to initialize the vector. void ModInstrument::GetSamples(std::vector &referencedSamples) const { - for(size_t i = 0; i < CountOf(Keyboard); i++) + for(const auto sample : Keyboard) { - // 0 isn't a sample. - if(Keyboard[i] != 0 && Keyboard[i] < referencedSamples.size()) + if(sample != 0 && sample < referencedSamples.size()) { - referencedSamples[Keyboard[i]] = true; + referencedSamples[sample] = true; } } } @@ -324,11 +291,23 @@ void ModInstrument::Sanitize(MODTYPE modType) PanEnv.Sanitize(); PitchEnv.Sanitize(range); - for(size_t i = 0; i < CountOf(NoteMap); i++) + for(size_t i = 0; i < std::size(NoteMap); i++) { if(NoteMap[i] < NOTE_MIN || NoteMap[i] > NOTE_MAX) NoteMap[i] = static_cast(i + NOTE_MIN); } + + if(!Resampling::IsKnownMode(resampling)) + resampling = SRCMODE_DEFAULT; +} + + +void ModInstrument::Transpose(int8 amount) +{ + for(auto ¬e : NoteMap) + { + note = static_cast(Clamp(note + amount, NOTE_MIN, NOTE_MAX)); + } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.h index 41c4b5121..b5f5a2a9d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModInstrument.h @@ -12,6 +12,7 @@ #include "BuildSettings.h" +#include "modcommand.h" #include "tuningbase.h" #include "Snd_defs.h" #include "../common/FlagSet.h" @@ -64,88 +65,94 @@ struct InstrumentEnvelope : public std::vector // Instrument Struct struct ModInstrument { - uint32 nFadeOut; // Instrument fadeout speed - uint32 nGlobalVol; // Global volume (0...64, all sample volumes are multiplied with this - TODO: This is 0...128 in Impulse Tracker) - uint32 nPan; // Default pan (0...256), if the appropriate flag is set. Sample panning overrides instrument panning. + uint32 nFadeOut = 256; // Instrument fadeout speed + uint32 nGlobalVol = 64; // Global volume (0...64, all sample volumes are multiplied with this - TODO: This is 0...128 in Impulse Tracker) + uint32 nPan = 32 * 4; // Default pan (0...256), if the appropriate flag is set. Sample panning overrides instrument panning. - uint16 nVolRampUp; // Default sample ramping up, 0 = use global default + uint16 nVolRampUp = 0; // Default sample ramping up, 0 = use global default - uint16 wMidiBank; // MIDI Bank (1...16384). 0 = Don't send. - uint8 nMidiProgram; // MIDI Program (1...128). 0 = Don't send. - uint8 nMidiChannel; // MIDI Channel (1...16). 0 = Don't send. 17 = Mapped (Send to tracker channel modulo 16). - uint8 nMidiDrumKey; // Drum set note mapping (currently only used by the .MID loader) - int8 midiPWD; // MIDI Pitch Wheel Depth in semitones + ResamplingMode resampling = SRCMODE_DEFAULT; // Resampling mode - FlagSet dwFlags; // Instrument flags - NewNoteAction nNNA; // New note action - DuplicateCheckType nDCT; // Duplicate check type (i.e. which condition will trigger the duplicate note action) - DuplicateNoteAction nDNA; // Duplicate note action - uint8 nPanSwing; // Random panning factor (0...64) - uint8 nVolSwing; // Random volume factor (0...100) - uint8 nIFC; // Default filter cutoff (0...127). Used if the high bit is set - uint8 nIFR; // Default filter resonance (0...127). Used if the high bit is set + FlagSet dwFlags; // Instrument flags + NewNoteAction nNNA = NNA_NOTECUT; // New note action + DuplicateCheckType nDCT = DCT_NONE; // Duplicate check type (i.e. which condition will trigger the duplicate note action) + DuplicateNoteAction nDNA = DNA_NOTECUT; // Duplicate note action + uint8 nPanSwing = 0; // Random panning factor (0...64) + uint8 nVolSwing = 0; // Random volume factor (0...100) - int8 nPPS; // Pitch/Pan separation (i.e. how wide the panning spreads, -32...32) - uint8 nPPC; // Pitch/Pan centre (zero-based, default is NOTE_MIDDLE_C - 1) + uint8 nIFC = 0; // Default filter cutoff (0...127). Used if the high bit is set + uint8 nIFR = 0; // Default filter resonance (0...127). Used if the high bit is set + uint8 nCutSwing = 0; // Random cutoff factor (0...64) + uint8 nResSwing = 0; // Random resonance factor (0...64) + FilterMode filterMode = FilterMode::Unchanged; // Default filter mode - PLUGINDEX nMixPlug; // Plugin assigned to this instrument (0 = no plugin, 1 = first plugin) - uint8 nCutSwing; // Random cutoff factor (0...64) - uint8 nResSwing; // Random resonance factor (0...64) - InstrFilterMode nFilterMode; // Default filter mode - PlugVelocityHandling pluginVelocityHandling; // How to deal with plugin velocity - PlugVolumeHandling pluginVolumeHandling; // How to deal with plugin volume - ResamplingMode resampling; // Resampling mode - TEMPO pitchToTempoLock; // BPM at which the samples assigned to this instrument loop correctly (0 = unset) - CTuning *pTuning; // sample tuning assigned to this instrument + int8 nPPS = 0; // Pitch/Pan separation (i.e. how wide the panning spreads, -32...32) + uint8 nPPC = NOTE_MIDDLEC - NOTE_MIN; // Pitch/Pan centre (zero-based) - InstrumentEnvelope VolEnv; // Volume envelope data - InstrumentEnvelope PanEnv; // Panning envelope data - InstrumentEnvelope PitchEnv; // Pitch / filter envelope data + uint16 wMidiBank = 0; // MIDI Bank (1...16384). 0 = Don't send. + uint8 nMidiProgram = 0; // MIDI Program (1...128). 0 = Don't send. + uint8 nMidiChannel = 0; // MIDI Channel (1...16). 0 = Don't send. 17 = Mapped (Send to tracker channel modulo 16). + uint8 nMidiDrumKey = 0; // Drum set note mapping (currently only used by the .MID loader) + int8 midiPWD = 2; // MIDI Pitch Wheel Depth in semitones + PLUGINDEX nMixPlug = 0; // Plugin assigned to this instrument (0 = no plugin, 1 = first plugin) - uint8 NoteMap[128]; // Note mapping, e.g. C-5 => D-5. - SAMPLEINDEX Keyboard[128]; // Sample mapping, e.g. C-5 => Sample 1 + PlugVelocityHandling pluginVelocityHandling = PLUGIN_VELOCITYHANDLING_CHANNEL; // How to deal with plugin velocity + PlugVolumeHandling pluginVolumeHandling = PLUGIN_VOLUMEHANDLING_IGNORE; // How to deal with plugin volume - char name[MAX_INSTRUMENTNAME]; - char filename[MAX_INSTRUMENTFILENAME]; + TEMPO pitchToTempoLock; // BPM at which the samples assigned to this instrument loop correctly (0 = unset) + CTuning *pTuning = nullptr; // sample tuning assigned to this instrument + + InstrumentEnvelope VolEnv; // Volume envelope data + InstrumentEnvelope PanEnv; // Panning envelope data + InstrumentEnvelope PitchEnv; // Pitch / filter envelope data + + std::array NoteMap; // Note mapping, e.g. C-5 => D-5 + std::array Keyboard; // Sample mapping, e.g. C-5 => Sample 1 + + mpt::charbuf name; + mpt::charbuf filename; + + std::string GetName() const { return name; } + std::string GetFilename() const { return filename; } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // WHEN adding new members here, ALSO update InstrumentExtensions.cpp // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - void SetTuning(CTuning* pT) - { - pTuning = pT; - } - ModInstrument(SAMPLEINDEX sample = 0); // Assign all notes to a given sample. void AssignSample(SAMPLEINDEX sample) { - for(size_t n = 0; n < CountOf(Keyboard); n++) - { - Keyboard[n] = sample; - } + Keyboard.fill(sample); } // Reset note mapping (i.e. every note is mapped to itself) void ResetNoteMap() { - for(size_t n = 0; n < CountOf(NoteMap); n++) + for(size_t n = 0; n < std::size(NoteMap); n++) { NoteMap[n] = static_cast(n + 1); } } + // Transpose entire note mapping by given number of semitones + void Transpose(int8 amount); + bool IsCutoffEnabled() const { return (nIFC & 0x80) != 0; } bool IsResonanceEnabled() const { return (nIFR & 0x80) != 0; } uint8 GetCutoff() const { return (nIFC & 0x7F); } uint8 GetResonance() const { return (nIFR & 0x7F); } - void SetCutoff(uint8 cutoff, bool enable) { nIFC = std::min(cutoff, 0x7F) | (enable ? 0x80 : 0x00); } - void SetResonance(uint8 resonance, bool enable) { nIFR = std::min(resonance, 0x7F) | (enable ? 0x80 : 0x00); } + void SetCutoff(uint8 cutoff, bool enable) { nIFC = std::min(cutoff, uint8(0x7F)) | (enable ? 0x80 : 0x00); } + void SetResonance(uint8 resonance, bool enable) { nIFR = std::min(resonance, uint8(0x7F)) | (enable ? 0x80 : 0x00); } bool HasValidMIDIChannel() const { return (nMidiChannel >= 1 && nMidiChannel <= 17); } + void SetTuning(CTuning *pT) + { + pTuning = pT; + } + // Get a reference to a specific envelope of this instrument const InstrumentEnvelope &GetEnvelope(EnvelopeType envType) const { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp index 05a01179a..e639f9ca1 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.cpp @@ -66,7 +66,7 @@ void ModSample::Convert(MODTYPE fromType, MODTYPE toType) uFlags.set(CHN_PINGPONGLOOP, uFlags[CHN_PINGPONGSUSTAIN]); } nSustainStart = nSustainEnd = 0; - uFlags.reset(CHN_SUSTAINLOOP|CHN_PINGPONGSUSTAIN); + uFlags.reset(CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN); } // All XM samples have default panning, and XM's autovibrato settings are rather limited. @@ -125,6 +125,7 @@ void ModSample::Convert(MODTYPE fromType, MODTYPE toType) // Initialize sample slot with default values. void ModSample::Initialize(MODTYPE type) { + FreeSample(); nLength = 0; nLoopStart = nLoopEnd = 0; nSustainStart = nSustainEnd = 0; @@ -144,7 +145,7 @@ void ModSample::Initialize(MODTYPE type) nVibDepth = 0; nVibRate = 0; rootNote = 0; - filename[0] = '\0'; + filename = ""; SetDefaultCuePoints(); } @@ -189,7 +190,7 @@ void *ModSample::AllocateSample(SmpLength numFrames, size_t bytesPerSample) if(allocSize != 0) { - char *p = new (std::nothrow) char[allocSize]; + char *p = new(std::nothrow) char[allocSize]; if(p != nullptr) { memset(p, 0, allocSize); @@ -209,7 +210,7 @@ size_t ModSample::GetRealSampleBufferSize(SmpLength numSamples, size_t bytesPerS // * 2x InterpolationMaxLookahead before the loop point (because we start at InterpolationMaxLookahead before the loop point and will look backwards from there as well) // * 2x InterpolationMaxLookahead after the loop point (for wrap-around) // * 4x InterpolationMaxLookahead for the sustain loop (same as the two points above) - + const SmpLength maxSize = Util::MaxValueOfType(numSamples); const SmpLength lookaheadBufferSize = (MaxSamplingPointSize + 1 + 4 + 4) * InterpolationMaxLookahead; @@ -239,7 +240,7 @@ void ModSample::FreeSample(void *samplePtr) { if(samplePtr) { - delete[] (((char *)samplePtr) - (InterpolationMaxLookahead * MaxSamplingPointSize)); + delete[](((char *)samplePtr) - (InterpolationMaxLookahead * MaxSamplingPointSize)); } } @@ -282,9 +283,144 @@ void ModSample::SetSustainLoop(SmpLength start, SmpLength end, bool enable, bool } +namespace // Unnamed namespace for local implementation functions. +{ + +template +class PrecomputeLoop +{ +protected: + T *target; + const T *sampleData; + SmpLength loopEnd; + int numChannels; + bool pingpong; + bool ITPingPongMode; + +public: + PrecomputeLoop(T *target, const T *sampleData, SmpLength loopEnd, int numChannels, bool pingpong, bool ITPingPongMode) + : target(target), sampleData(sampleData), loopEnd(loopEnd), numChannels(numChannels), pingpong(pingpong), ITPingPongMode(ITPingPongMode) + { + if(loopEnd > 0) + { + CopyLoop(true); + CopyLoop(false); + } + } + + void CopyLoop(bool direction) const + { + // Direction: true = start reading and writing forward, false = start reading and writing backward (write direction never changes) + const int numSamples = 2 * InterpolationMaxLookahead + (direction ? 1 : 0); // Loop point is included in forward loop expansion + T *dest = target + numChannels * (2 * InterpolationMaxLookahead - 1); // Write buffer offset + SmpLength readPosition = loopEnd - 1; + const int writeIncrement = direction ? 1 : -1; + int readIncrement = writeIncrement; + + for(int i = 0; i < numSamples; i++) + { + // Copy sample over to lookahead buffer + for(int c = 0; c < numChannels; c++) + { + dest[c] = sampleData[readPosition * numChannels + c]; + } + dest += writeIncrement * numChannels; + + if(readPosition == loopEnd - 1 && readIncrement > 0) + { + // Reached end of loop while going forward + if(pingpong) + { + readIncrement = -1; + if(ITPingPongMode && readPosition > 0) + { + readPosition--; + } + } else + { + readPosition = 0; + } + } else if(readPosition == 0 && readIncrement < 0) + { + // Reached start of loop while going backward + if(pingpong) + { + readIncrement = 1; + } else + { + readPosition = loopEnd - 1; + } + } else + { + readPosition += readIncrement; + } + } + } +}; + + +template +void PrecomputeLoopsImpl(ModSample &smp, const CSoundFile &sndFile) +{ + const int numChannels = smp.GetNumChannels(); + const int copySamples = numChannels * InterpolationMaxLookahead; + + T *sampleData = static_cast(smp.samplev()); + T *afterSampleStart = sampleData + smp.nLength * numChannels; + T *loopLookAheadStart = afterSampleStart + copySamples; + T *sustainLookAheadStart = loopLookAheadStart + 4 * copySamples; + + // Hold sample on the same level as the last sampling point at the end to prevent extra pops with interpolation. + // Do the same at the sample start, too. + for(int i = 0; i < (int)InterpolationMaxLookahead; i++) + { + for(int c = 0; c < numChannels; c++) + { + afterSampleStart[i * numChannels + c] = afterSampleStart[-numChannels + c]; + sampleData[-(i + 1) * numChannels + c] = sampleData[c]; + } + } + + if(smp.uFlags[CHN_LOOP]) + { + PrecomputeLoop(loopLookAheadStart, + sampleData + smp.nLoopStart * numChannels, + smp.nLoopEnd - smp.nLoopStart, + numChannels, + smp.uFlags[CHN_PINGPONGLOOP], + sndFile.m_playBehaviour[kITPingPongMode]); + } + if(smp.uFlags[CHN_SUSTAINLOOP]) + { + PrecomputeLoop(sustainLookAheadStart, + sampleData + smp.nSustainStart * numChannels, + smp.nSustainEnd - smp.nSustainStart, + numChannels, + smp.uFlags[CHN_PINGPONGSUSTAIN], + sndFile.m_playBehaviour[kITPingPongMode]); + } +} + +} // unnamed namespace + + void ModSample::PrecomputeLoops(CSoundFile &sndFile, bool updateChannels) { - ctrlSmp::PrecomputeLoops(*this, sndFile, updateChannels); + if(!HasSampleData()) + return; + + SanitizeLoops(); + + // Update channels with possibly changed loop values + if(updateChannels) + { + ctrlSmp::UpdateLoopPoints(*this, sndFile); + } + + if(GetElementarySampleSize() == 2) + PrecomputeLoopsImpl(*this, sndFile); + else if(GetElementarySampleSize() == 1) + PrecomputeLoopsImpl(*this, sndFile); } @@ -322,19 +458,21 @@ void ModSample::TransposeToFrequency() // Return tranpose.finetune as 25.7 fixed point value. -int ModSample::FrequencyToTranspose(uint32 freq) +int32 ModSample::FrequencyToTranspose(uint32 freq) { - return mpt::saturate_round(std::log(freq * (1.0 / 8363.0)) * (12.0 * 128.0 * (1.0 / M_LN2))); + if(!freq) + return 0; + else + return mpt::saturate_round(std::log(freq * (1.0 / 8363.0)) * (12.0 * 128.0 * (1.0 / M_LN2))); } void ModSample::FrequencyToTranspose() { - int f2t = 0; - if(nC5Speed) - f2t = FrequencyToTranspose(nC5Speed); - RelativeTone = static_cast(f2t >> 7); - nFineTune = static_cast(f2t & 0x7F); + const int f2t = Clamp(FrequencyToTranspose(nC5Speed), -16384, 16383); + const auto fine = std::div(f2t, 128); + RelativeTone = static_cast(fine.quot); + nFineTune = static_cast(fine.rem); } @@ -352,7 +490,8 @@ bool ModSample::HasCustomCuePoints() const { for(SmpLength i = 0; i < CountOf(cues); i++) { - if(cues[i] != (i + 1) << 11) return true; + if(cues[i] != (i + 1) << 11) + return true; } } return false; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.h index 924e8c52b..c23769ca3 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSample.h @@ -42,7 +42,9 @@ struct ModSample uint8 rootNote; // For multisample import //char name[MAX_SAMPLENAME]; // Maybe it would be nicer to have sample names here, but that would require some refactoring. - char filename[MAX_SAMPLEFILENAME]; + mpt::charbuf filename; + std::string GetFilename() const { return filename; } + union { SmpLength cues[9]; @@ -69,13 +71,13 @@ struct ModSample { return pData.pSample; } - MPT_FORCEINLINE const mpt::byte *sampleb() const noexcept + MPT_FORCEINLINE const std::byte *sampleb() const noexcept { - return mpt::void_cast(pData.pSample); + return mpt::void_cast(pData.pSample); } - MPT_FORCEINLINE mpt::byte *sampleb() noexcept + MPT_FORCEINLINE std::byte *sampleb() noexcept { - return mpt::void_cast(pData.pSample); + return mpt::void_cast(pData.pSample); } MPT_FORCEINLINE const int8 *sample8() const noexcept { @@ -144,7 +146,7 @@ struct ModSample // Transpose <-> Frequency conversions static uint32 TransposeToFrequency(int transpose, int finetune = 0); void TransposeToFrequency(); - static int FrequencyToTranspose(uint32 freq); + static int32 FrequencyToTranspose(uint32 freq); void FrequencyToTranspose(); // Transpose the sample by amount specified in octaves (i.e. amount=1 transposes one octave up) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSampleCopy.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSampleCopy.h index d03cbf35c..ce20e79ce 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSampleCopy.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSampleCopy.h @@ -29,10 +29,10 @@ size_t CopyMonoSample(ModSample &sample, const Tbyte *sourceBuffer, size_t sourc MPT_ASSERT(sample.GetElementarySampleSize() == sizeof(typename SampleConversion::output_t)); const size_t frameSize = SampleConversion::input_inc; - const size_t countFrames = std::min(sourceSize / frameSize, sample.nLength); + const size_t countFrames = std::min(sourceSize / frameSize, static_cast(sample.nLength)); size_t numFrames = countFrames; SampleConversion sampleConv(conv); - const mpt::byte * MPT_RESTRICT inBuf = mpt::byte_cast(sourceBuffer); + const std::byte * MPT_RESTRICT inBuf = mpt::byte_cast(sourceBuffer); typename SampleConversion::output_t * MPT_RESTRICT outBuf = static_cast(sample.samplev()); while(numFrames--) { @@ -52,11 +52,11 @@ size_t CopyStereoInterleavedSample(ModSample &sample, const Tbyte *sourceBuffer, MPT_ASSERT(sample.GetElementarySampleSize() == sizeof(typename SampleConversion::output_t)); const size_t frameSize = 2 * SampleConversion::input_inc; - const size_t countFrames = std::min(sourceSize / frameSize, sample.nLength); + const size_t countFrames = std::min(sourceSize / frameSize, static_cast(sample.nLength)); size_t numFrames = countFrames; SampleConversion sampleConvLeft(conv); SampleConversion sampleConvRight(conv); - const mpt::byte * MPT_RESTRICT inBuf = mpt::byte_cast(sourceBuffer); + const std::byte * MPT_RESTRICT inBuf = mpt::byte_cast(sourceBuffer); typename SampleConversion::output_t * MPT_RESTRICT outBuf = static_cast(sample.samplev()); while(numFrames--) { @@ -79,14 +79,14 @@ size_t CopyStereoSplitSample(ModSample &sample, const Tbyte *sourceBuffer, size_ MPT_ASSERT(sample.GetElementarySampleSize() == sizeof(typename SampleConversion::output_t)); const size_t sampleSize = SampleConversion::input_inc; - const size_t sourceSizeLeft = std::min(sample.nLength * SampleConversion::input_inc, sourceSize); - const size_t sourceSizeRight = std::min(sample.nLength * SampleConversion::input_inc, sourceSize - sourceSizeLeft); + const size_t sourceSizeLeft = std::min(static_cast(sample.nLength) * SampleConversion::input_inc, sourceSize); + const size_t sourceSizeRight = std::min(static_cast(sample.nLength) * SampleConversion::input_inc, sourceSize - sourceSizeLeft); const size_t countSamplesLeft = sourceSizeLeft / sampleSize; const size_t countSamplesRight = sourceSizeRight / sampleSize; size_t numSamplesLeft = countSamplesLeft; SampleConversion sampleConvLeft(conv); - const mpt::byte * MPT_RESTRICT inBufLeft = mpt::byte_cast(sourceBuffer); + const std::byte * MPT_RESTRICT inBufLeft = mpt::byte_cast(sourceBuffer); typename SampleConversion::output_t * MPT_RESTRICT outBufLeft = static_cast(sample.samplev()); while(numSamplesLeft--) { @@ -97,7 +97,7 @@ size_t CopyStereoSplitSample(ModSample &sample, const Tbyte *sourceBuffer, size_ size_t numSamplesRight = countSamplesRight; SampleConversion sampleConvRight(conv); - const mpt::byte * MPT_RESTRICT inBufRight = mpt::byte_cast(sourceBuffer) + sample.nLength * SampleConversion::input_inc; + const std::byte * MPT_RESTRICT inBufRight = mpt::byte_cast(sourceBuffer) + sample.nLength * SampleConversion::input_inc; typename SampleConversion::output_t * MPT_RESTRICT outBufRight = static_cast(sample.samplev()) + 1; while(numSamplesRight--) { @@ -121,7 +121,7 @@ size_t CopyAndNormalizeSample(ModSample &sample, const Tbyte *sourceBuffer, size size_t numSamples = sample.nLength * sample.GetNumChannels(); LimitMax(numSamples, sourceSize / inSize); - const mpt::byte * inBuf = mpt::byte_cast(sourceBuffer); + const std::byte * inBuf = mpt::byte_cast(sourceBuffer); // Finding max value SampleConversion sampleConv(conv); for(size_t i = numSamples; i != 0; i--) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.cpp index 15c3945c9..b7638a21d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.cpp @@ -12,9 +12,6 @@ #include "ModSequence.h" #include "Sndfile.h" #include "mod_specifications.h" -#ifdef MODPLUG_TRACKER -#include "../mptrack/Reporting.h" -#endif // MODPLUG_TRACKER #include "../common/version.h" #include "../common/serialization_utils.h" @@ -25,13 +22,13 @@ OPENMPT_NAMESPACE_BEGIN ModSequence::ModSequence(CSoundFile &sndFile) : m_sndFile(sndFile) - , m_restartPos(0) { } ModSequence& ModSequence::operator=(const ModSequence &other) { + MPT_ASSERT(&other.m_sndFile == &m_sndFile); if(&other == this) return *this; std::vector::assign(other.begin(), other.end()); @@ -210,6 +207,8 @@ bool ModSequence::IsValidPat(ORDERINDEX ord) const ORDERINDEX ModSequence::FindOrder(PATTERNINDEX pat, ORDERINDEX startSearchAt, bool searchForward) const { const ORDERINDEX length = GetLength(); + if(startSearchAt >= length) + return ORDERINDEX_INVALID; ORDERINDEX ord = startSearchAt; for(ORDERINDEX p = 0; p < length; p++) { @@ -234,6 +233,9 @@ ORDERINDEX ModSequence::FindOrder(PATTERNINDEX pat, ORDERINDEX startSearchAt, bo PATTERNINDEX ModSequence::EnsureUnique(ORDERINDEX ord) { PATTERNINDEX pat = at(ord); + if(!IsValidPat(ord)) + return pat; + for(const auto &sequence : m_sndFile.Order) { ORDERINDEX ords = sequence.GetLength(); @@ -281,18 +283,11 @@ void ModSequenceSet::SetSequence(SEQUENCEINDEX n) } -SEQUENCEINDEX ModSequenceSet::AddSequence(bool duplicate) +SEQUENCEINDEX ModSequenceSet::AddSequence() { - if(GetNumSequences() == MAX_SEQUENCES) + if(GetNumSequences() >= MAX_SEQUENCES) return SEQUENCEINDEX_INVALID; - if(duplicate) - { - m_Sequences.push_back(m_Sequences[m_currentSeq]); - m_Sequences.back().m_name.clear(); // Don't copy sequence name. - } else - { - m_Sequences.push_back(ModSequence(m_sndFile)); - } + m_Sequences.push_back(ModSequence{m_sndFile}); SetSequence(GetNumSequences() - 1); return GetNumSequences() - 1; } @@ -311,86 +306,97 @@ void ModSequenceSet::RemoveSequence(SEQUENCEINDEX i) #ifdef MODPLUG_TRACKER +bool ModSequenceSet::Rearrange(const std::vector &newOrder) +{ + if(newOrder.empty() || newOrder.size() > MAX_SEQUENCES) + return false; + + const auto oldSequences = std::move(m_Sequences); + m_Sequences.assign(newOrder.size(), ModSequence{m_sndFile}); + for(size_t i = 0; i < newOrder.size(); i++) + { + if(newOrder[i] < oldSequences.size()) + m_Sequences[i] = oldSequences[newOrder[i]]; + } + + if(m_currentSeq > m_Sequences.size()) + m_currentSeq = GetNumSequences() - 1u; + return true; +} + + void ModSequenceSet::OnModTypeChanged(MODTYPE oldType) { for(auto &seq : m_Sequences) { seq.AdjustToNewModType(oldType); } - // Multisequences not suppported by other formats - if(oldType != MOD_TYPE_NONE && m_sndFile.GetModSpecifications().sequencesMax <= 1) + if(m_sndFile.GetModSpecifications(oldType).sequencesMax > 1 && m_sndFile.GetModSpecifications().sequencesMax <= 1) MergeSequences(); - - // Convert sequence with separator patterns into multiple sequences? - if(oldType != MOD_TYPE_NONE && m_sndFile.GetModSpecifications().sequencesMax > 1 && GetNumSequences() == 1) - ConvertSubsongsToMultipleSequences(); } -bool ModSequenceSet::ConvertSubsongsToMultipleSequences() +bool ModSequenceSet::CanSplitSubsongs() const { - // Allow conversion only if there's only one sequence. - if(GetNumSequences() != 1 || m_sndFile.GetModSpecifications().sequencesMax <= 1) + return GetNumSequences() == 1 && m_sndFile.GetModSpecifications().sequencesMax > 1 && m_Sequences[0].HasSubsongs(); +} + + +bool ModSequenceSet::SplitSubsongsToMultipleSequences() +{ + if(!CanSplitSubsongs()) return false; - m_Sequences[0].Shrink(); - bool hasSepPatterns = std::find_if(m_Sequences[0].begin(), m_Sequences[0].end(), - [&] (PATTERNINDEX pat) { return pat != GetIgnoreIndex() && !m_sndFile.Patterns.IsValidPat(pat); }) != m_Sequences[0].end(); bool modified = false; + const ORDERINDEX length = m_Sequences[0].GetLengthTailTrimmed(); - if(hasSepPatterns && - Reporting::Confirm("The order list contains separator items.\nThe new format supports multiple sequences, do you want to convert those separate tracks into multiple song sequences?", - "Order list conversion", false, true) == cnfYes) + for(ORDERINDEX ord = 0; ord < length; ord++) { - ORDERINDEX length = m_Sequences[0].GetLength(); - for(ORDERINDEX ord = 0; ord < length; ord++) + // End of subsong? + if(!m_Sequences[0].IsValidPat(ord) && m_Sequences[0][ord] != GetIgnoreIndex()) { - // End of subsong? - if(!m_Sequences[0].IsValidPat(ord) && m_Sequences[0][ord] != GetIgnoreIndex()) + // Remove all separator patterns between current and next subsong first + while(ord < length && !m_sndFile.Patterns.IsValidPat(m_Sequences[0][ord])) { - // Remove all separator patterns between current and next subsong first - while(ord < length && !m_sndFile.Patterns.IsValidPat(m_Sequences[0][ord])) - { - m_Sequences[0][ord] = GetInvalidPatIndex(); - ord++; - modified = true; - } - if(ord >= length) - break; - - const SEQUENCEINDEX newSeq = AddSequence(false); - if(newSeq == SEQUENCEINDEX_INVALID) - break; - - const ORDERINDEX startOrd = ord; - m_Sequences[newSeq].reserve(length - startOrd); + m_Sequences[0][ord] = GetInvalidPatIndex(); + ord++; modified = true; + } + if(ord >= length) + break; - // Now, move all following orders to the new sequence - while(ord < length && m_Sequences[0][ord] != GetInvalidPatIndex()) + const SEQUENCEINDEX newSeq = AddSequence(); + if(newSeq == SEQUENCEINDEX_INVALID) + break; + + const ORDERINDEX startOrd = ord; + m_Sequences[newSeq].reserve(length - startOrd); + modified = true; + + // Now, move all following orders to the new sequence + while(ord < length && m_Sequences[0][ord] != GetInvalidPatIndex()) + { + PATTERNINDEX copyPat = m_Sequences[0][ord]; + m_Sequences[newSeq].push_back(copyPat); + m_Sequences[0][ord] = GetInvalidPatIndex(); + ord++; + + // Is this a valid pattern? adjust pattern jump commands, if necessary. + if(m_sndFile.Patterns.IsValidPat(copyPat)) { - PATTERNINDEX copyPat = m_Sequences[0][ord]; - m_Sequences[newSeq].push_back(copyPat); - m_Sequences[0][ord] = GetInvalidPatIndex(); - ord++; - - // Is this a valid pattern? adjust pattern jump commands, if necessary. - if(m_sndFile.Patterns.IsValidPat(copyPat)) + for(auto &m : m_sndFile.Patterns[copyPat]) { - for(auto &m : m_sndFile.Patterns[copyPat]) + if(m.command == CMD_POSITIONJUMP && m.param >= startOrd) { - if(m.command == CMD_POSITIONJUMP && m.param >= startOrd) - { - m.param = static_cast(m.param - startOrd); - } + m.param = static_cast(m.param - startOrd); } } } - ord--; } + ord--; } - SetSequence(0); } + SetSequence(0); return modified; } @@ -443,7 +449,7 @@ bool ModSequenceSet::MergeSequences() const ORDERINDEX lengthTrimmed = seq.GetLengthTailTrimmed(); if(firstOrder + lengthTrimmed > m_sndFile.GetModSpecifications().ordersMax) { - m_sndFile.AddToLog(mpt::format("WARNING: Cannot merge Sequence %1 (too long!)")(seqNum)); + m_sndFile.AddToLog(mpt::format("WARNING: Cannot merge Sequence %1 (too long!)")(seqNum + 1)); continue; } firstSeq.reserve(firstOrder + lengthTrimmed); @@ -497,6 +503,14 @@ bool ModSequence::IsPositionLocked(ORDERINDEX position) const return(m_sndFile.m_lockOrderStart != ORDERINDEX_INVALID && (position < m_sndFile.m_lockOrderStart || position > m_sndFile.m_lockOrderEnd)); } + + +bool ModSequence::HasSubsongs() const +{ + const auto endPat = begin() + GetLengthTailTrimmed(); + return std::find_if(begin(), endPat, + [&](PATTERNINDEX pat) { return pat != GetIgnoreIndex() && !m_sndFile.Patterns.IsValidPat(pat); }) != endPat; +} #endif // MODPLUG_TRACKER @@ -566,7 +580,9 @@ void WriteModSequence(std::ostream& oStrm, const ModSequence& seq) { srlztn::SsbWrite ssb(oStrm); ssb.BeginWrite(FileIdSequence, Version::Current().GetRawVersion()); - ssb.WriteItem(seq.GetName(), "n"); + int8 useUTF8 = 1; + ssb.WriteItem(useUTF8, "u"); + ssb.WriteItem(mpt::ToCharset(mpt::Charset::UTF8, seq.GetName()), "n"); const uint16 length = seq.GetLengthTailTrimmed(); ssb.WriteItem(length, "l"); ssb.WriteItem(seq, "a", srlztn::VectorWriter(length)); @@ -577,15 +593,17 @@ void WriteModSequence(std::ostream& oStrm, const ModSequence& seq) #endif // MODPLUG_NO_FILESAVE -void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t) +void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t, mpt::Charset defaultCharset) { srlztn::SsbRead ssb(iStrm); ssb.BeginRead(FileIdSequence, Version::Current().GetRawVersion()); if ((ssb.GetStatus() & srlztn::SNT_FAILURE) != 0) return; + int8 useUTF8 = 0; + ssb.ReadItem(useUTF8, "u"); std::string str; ssb.ReadItem(str, "n"); - seq.SetName(str); + seq.SetName(mpt::ToUnicode(useUTF8 ? mpt::Charset::UTF8 : defaultCharset, str)); ORDERINDEX nSize = 0; ssb.ReadItem(nSize, "l"); LimitMax(nSize, ModSpecs::mptm.ordersMax); @@ -615,7 +633,7 @@ void WriteModSequences(std::ostream& oStrm, const ModSequenceSet& seq) #endif // MODPLUG_NO_FILESAVE -void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t) +void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t, mpt::Charset defaultCharset) { srlztn::SsbRead ssb(iStrm); ssb.BeginRead(FileIdSequences, Version::Current().GetRawVersion()); @@ -637,7 +655,7 @@ void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t) for(SEQUENCEINDEX i = 0; i < seqs; i++) { seq(i).SetRestartPos(legacyRestartPos); - ssb.ReadItem(seq(i), srlztn::ID::FromInt(i), &ReadModSequence); + ssb.ReadItem(seq(i), srlztn::ID::FromInt(i), [defaultCharset](std::istream &iStrm, ModSequence &seq, std::size_t dummy) { return ReadModSequence(iStrm, seq, dummy, defaultCharset); }); } seq.m_currentSeq = (currentSeq < seq.GetNumSequences()) ? currentSeq : 0; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.h b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.h index 09e0454ce..48f8aedb0 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/ModSequence.h @@ -26,9 +26,9 @@ class ModSequence: public std::vector friend class ModSequenceSet; protected: - std::string m_name; // Sequence name. - CSoundFile &m_sndFile; // Associated CSoundFile. - ORDERINDEX m_restartPos; // Restart position when playback of this order ended + mpt::ustring m_name; // Sequence name + CSoundFile &m_sndFile; // Associated CSoundFile + ORDERINDEX m_restartPos = 0; // Restart position when playback of this order ended public: ModSequence(CSoundFile &sndFile); @@ -80,9 +80,9 @@ public: void AdjustToNewModType(const MODTYPE oldtype); // Returns the internal representation of a stop '---' index - static PATTERNINDEX GetInvalidPatIndex() { return uint16_max; } + static constexpr PATTERNINDEX GetInvalidPatIndex() { return uint16_max; } // Returns the internal representation of an ignore '+++' index - static PATTERNINDEX GetIgnoreIndex() { return uint16_max - 1; } + static constexpr PATTERNINDEX GetIgnoreIndex() { return uint16_max - 1; } // Returns the previous/next order ignoring skip indices (+++). // If no previous/next order exists, return first/last order, and zero @@ -108,11 +108,13 @@ public: #ifdef MODPLUG_TRACKER // Check if a playback position is currently locked (inaccessible) bool IsPositionLocked(ORDERINDEX position) const; + // Check if this sequence has subsongs separated by invalid ("---" or non-existing) patterns + bool HasSubsongs() const; #endif // MODPLUG_TRACKER // Sequence name setter / getter - inline void SetName(const std::string &newName) { m_name = newName;} - inline std::string GetName() const { return m_name; } + inline void SetName(const mpt::ustring &newName) { m_name = newName;} + inline mpt::ustring GetName() const { return m_name; } // Restart position setter / getter inline void SetRestartPos(ORDERINDEX restartPos) { m_restartPos = restartPos; } @@ -123,12 +125,12 @@ public: class ModSequenceSet { friend void ReadModSequenceOld(std::istream& iStrm, ModSequenceSet& seq, const size_t); - friend void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t); + friend void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t, mpt::Charset defaultCharset); protected: - std::vector m_Sequences; // Array of sequences. + std::vector m_Sequences; // Array of sequences CSoundFile &m_sndFile; - SEQUENCEINDEX m_currentSeq; // Index of current sequence. + SEQUENCEINDEX m_currentSeq = 0; // Index of current sequence. public: ModSequenceSet(CSoundFile &sndFile); @@ -149,26 +151,31 @@ public: // Sets working sequence. void SetSequence(SEQUENCEINDEX); - // Add new sequence. - // If duplicate is true, new sequence is a duplicate of the current sequence. + // Add new empty sequence. // Returns the ID of the new sequence, or SEQUENCEINDEX_INVALID on failure. - SEQUENCEINDEX AddSequence(bool duplicate = true); + SEQUENCEINDEX AddSequence(); // Removes given sequence. void RemoveSequence(SEQUENCEINDEX); // Returns the internal representation of a stop '---' index - static PATTERNINDEX GetInvalidPatIndex() { return ModSequence::GetInvalidPatIndex(); } + static constexpr PATTERNINDEX GetInvalidPatIndex() { return ModSequence::GetInvalidPatIndex(); } // Returns the internal representation of an ignore '+++' index - static PATTERNINDEX GetIgnoreIndex() { return ModSequence::GetIgnoreIndex(); } + static constexpr PATTERNINDEX GetIgnoreIndex() { return ModSequence::GetIgnoreIndex(); } #ifdef MODPLUG_TRACKER + // Assigns a new set of sequences. The vector contents indicate which existing sequences to keep / duplicate or if a new sequences should be inserted (SEQUENCEINDEX_INVALID) + // The function fails if the vector is empty or contains too many sequences. + bool Rearrange(const std::vector &newOrder); + // Adjust sequence when converting between module formats void OnModTypeChanged(MODTYPE oldType); + // Check if there is a single sequences that qualifies for subsong splitting + bool CanSplitSubsongs() const; // If there are subsongs (separated by "---" patterns) in the module, // asks user whether to convert these into multiple sequences (given that the // modformat supports multiple sequences). // Returns true if sequences were modified, false otherwise. - bool ConvertSubsongsToMultipleSequences(); + bool SplitSubsongsToMultipleSequences(); // Convert the sequence's restart position information to a pattern command. bool RestartPosToPattern(SEQUENCEINDEX seq); @@ -192,12 +199,12 @@ const char FileIdSequence[] = "mptSeq"; #ifndef MODPLUG_NO_FILESAVE void WriteModSequences(std::ostream& oStrm, const ModSequenceSet& seq); #endif // MODPLUG_NO_FILESAVE -void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t nSize = 0); +void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t nSize, mpt::Charset defaultCharset); #ifndef MODPLUG_NO_FILESAVE void WriteModSequence(std::ostream& oStrm, const ModSequence& seq); #endif // MODPLUG_NO_FILESAVE -void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t); +void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t, mpt::Charset defaultCharset); #ifndef MODPLUG_NO_FILESAVE void WriteModSequenceOld(std::ostream& oStrm, const ModSequenceSet& seq); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.cpp index 4f949528e..297ba14af 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.cpp @@ -15,11 +15,9 @@ OPENMPT_NAMESPACE_BEGIN -OPL::OPL() +OPL::OPL(uint32 samplerate) { - m_KeyOnBlock.fill(0); - m_OPLtoChan.fill(CHANNELINDEX_INVALID); - m_ChanToOPL.fill(OPL_CHANNEL_INVALID); + Initialize(samplerate); } @@ -32,7 +30,7 @@ OPL::~OPL() void OPL::Initialize(uint32 samplerate) { if(m_opl == nullptr) - m_opl = mpt::make_unique(samplerate); + m_opl = std::make_unique(samplerate); else m_opl->SetSampleRate(samplerate); Reset(); @@ -45,7 +43,7 @@ void OPL::Mix(int32 *target, size_t count, uint32 volumeFactorQ16) return; // This factor causes a sample voice to be more or less as loud as an OPL voice - const int32 factor = (volumeFactorQ16 * 6169) / (1 << 16); + const int32 factor = Util::muldiv_unsigned(volumeFactorQ16, 6169, (1 << 16)); while(count--) { int16 l, r; @@ -69,7 +67,7 @@ uint16 OPL::ChannelToRegister(uint8 oplCh) // Translate a channel's first operator address into a register uint16 OPL::OperatorToRegister(uint8 oplCh) { - static const uint8 OPLChannelToOperator[] = { 0, 1, 2, 8, 9, 10, 16, 17, 18 }; + static constexpr uint8 OPLChannelToOperator[] = { 0, 1, 2, 8, 9, 10, 16, 17, 18 }; if(oplCh < 9) return OPLChannelToOperator[oplCh]; else @@ -79,33 +77,49 @@ uint16 OPL::OperatorToRegister(uint8 oplCh) uint8 OPL::GetVoice(CHANNELINDEX c) const { - return m_ChanToOPL[c]; + if((m_ChanToOPL[c] & OPL_CHANNEL_CUT) || m_ChanToOPL[c] == OPL_CHANNEL_INVALID) + return OPL_CHANNEL_INVALID; + return m_ChanToOPL[c] & OPL_CHANNEL_MASK; } uint8 OPL::AllocateVoice(CHANNELINDEX c) { // Can we re-use a previous channel? - if(m_ChanToOPL[c] != OPL_CHANNEL_INVALID) + if(auto oplCh = m_ChanToOPL[c]; oplCh != OPL_CHANNEL_INVALID) { - return GetVoice(c); + if(!(m_ChanToOPL[c] & OPL_CHANNEL_CUT)) + return oplCh; + // Check re-use hint + oplCh &= OPL_CHANNEL_MASK; + if(m_OPLtoChan[oplCh] == CHANNELINDEX_INVALID || m_OPLtoChan[oplCh] == c) + { + m_OPLtoChan[oplCh] = c; + m_ChanToOPL[c] = oplCh; + return oplCh; + } } // Search for unused channel or channel with released note - uint8 releasedChn = OPL_CHANNEL_INVALID; + uint8 releasedChn = OPL_CHANNEL_INVALID, releasedCutChn = OPL_CHANNEL_INVALID; for(uint8 oplCh = 0; oplCh < OPL_CHANNELS; oplCh++) { if(m_OPLtoChan[oplCh] == CHANNELINDEX_INVALID) { m_OPLtoChan[oplCh] = c; m_ChanToOPL[c] = oplCh; - return GetVoice(c); + return oplCh; } else if(!(m_KeyOnBlock[oplCh] & KEYON_BIT)) { releasedChn = oplCh; + if(m_ChanToOPL[m_OPLtoChan[oplCh]] & OPL_CHANNEL_CUT) + releasedCutChn = oplCh; } } if(releasedChn != OPL_CHANNEL_INVALID) { + // Prefer channel that has been marked as cut over channel that has just been released + if(releasedCutChn != OPL_CHANNEL_INVALID) + releasedChn = releasedCutChn; m_ChanToOPL[m_OPLtoChan[releasedChn]] = OPL_CHANNEL_INVALID; m_OPLtoChan[releasedChn] = c; m_ChanToOPL[c] = releasedChn; @@ -116,7 +130,7 @@ uint8 OPL::AllocateVoice(CHANNELINDEX c) void OPL::MoveChannel(CHANNELINDEX from, CHANNELINDEX to) { - uint8 oplCh = m_ChanToOPL[from]; + uint8 oplCh = GetVoice(from); if(oplCh == OPL_CHANNEL_INVALID) return; m_OPLtoChan[oplCh] = to; @@ -135,10 +149,15 @@ void OPL::NoteOff(CHANNELINDEX c) } -void OPL::NoteCut(CHANNELINDEX c) +void OPL::NoteCut(CHANNELINDEX c, bool unassign) { + uint8 oplCh = GetVoice(c); + if(oplCh == OPL_CHANNEL_INVALID) + return; NoteOff(c); - Volume(c, 0, false); + Volume(c, 0, false); // Note that a volume of 0 is not complete silence; the release portion of the sound will still be heard at -48dB + if(unassign) + m_ChanToOPL[c] |= OPL_CHANNEL_CUT; } @@ -169,7 +188,7 @@ void OPL::Frequency(CHANNELINDEX c, uint32 milliHertz, bool keyOff, bool beating // We allocate our OPL channels dynamically, which would result in slightly different beating characteristics, // but we can just take the pattern channel number instead, as the pattern channel layout is always identical. if(beatingOscillators) - fnum = std::min(fnum + (c & 3), 1023); + fnum = std::min(static_cast(fnum + (c & 3)), uint16(1023)); fnum |= (block << 10); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.h b/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.h index 331be872b..1d4ee6fb0 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/OPL.h @@ -67,21 +67,21 @@ public: STEREO_BITS = VOICE_TO_LEFT | VOICE_TO_RIGHT, }; - OPL(); + OPL(uint32 samplerate); ~OPL(); void Initialize(uint32 samplerate); void Mix(int32 *buffer, size_t count, uint32 volumeFactorQ16); void NoteOff(CHANNELINDEX c); - void NoteCut(CHANNELINDEX c); + void NoteCut(CHANNELINDEX c, bool unassign = true); void Frequency(CHANNELINDEX c, uint32 milliHertz, bool keyOff, bool beatingOscillators); void Volume(CHANNELINDEX c, uint8 vol, bool applyToModulator); int8 Pan(CHANNELINDEX c, int32 pan); void Patch(CHANNELINDEX c, const OPLPatch &patch); - void Reset(); - bool IsActive(CHANNELINDEX c) { return GetVoice(c) != OPL_CHANNEL_INVALID; } + bool IsActive(CHANNELINDEX c) const { return GetVoice(c) != OPL_CHANNEL_INVALID; } void MoveChannel(CHANNELINDEX from, CHANNELINDEX to); + void Reset(); protected: static uint16 ChannelToRegister(uint8 oplCh); @@ -92,7 +92,9 @@ protected: enum { - OPL_CHANNELS = 18, // 9 for OPL2 or 18 for OPL3 + OPL_CHANNELS = 18, // 9 for OPL2 or 18 for OPL3 + OPL_CHANNEL_CUT = 0x80, // Indicates that the channel has been cut and used as a hint to re-use the channel for the same tracker channel if possible + OPL_CHANNEL_MASK = 0x7F, OPL_CHANNEL_INVALID = 0xFF, OPL_BASERATE = 49716, }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.cpp index b42a5c83b..7f05f6380 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.cpp @@ -34,6 +34,15 @@ uint16 PageInfo::GetPagePhysicalSize() const } +uint16 PageInfo::GetPageHeaderSize() const +{ + uint16 size = 0; + size += sizeof(PageHeader); + size += header.page_segments; + return size; +} + + uint16 PageInfo::GetPageDataSize() const { uint16 size = 0; @@ -70,10 +79,13 @@ bool AdvanceToPageMagic(FileReader &file) } -bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector &pageData) +bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector *pageData) { pageInfo = PageInfo(); - pageData.clear(); + if(pageData) + { + (*pageData).clear(); + } if(!file.ReadMagic("OggS")) { return false; @@ -98,7 +110,13 @@ bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector &pageData { return false; } - filePageReader.ReadVector(pageData, pageDataSize); + if(pageData) + { + filePageReader.ReadVector(*pageData, pageDataSize); + } else + { + filePageReader.Skip(pageDataSize); + } filePageReader.SkipBack(pageInfo.GetPagePhysicalSize()); { mpt::crc32_ogg calculatedCRC; @@ -107,8 +125,18 @@ bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector &pageData filePageReader.ReadArray(rawHeader); std::memset(rawHeader + 22, 0, 4); // clear out old crc calculatedCRC.process(rawHeader, rawHeader + sizeof(rawHeader)); + filePageReader.Skip(pageInfo.header.page_segments); calculatedCRC.process(pageInfo.segment_table, pageInfo.segment_table + pageInfo.header.page_segments); - calculatedCRC.process(pageData); + if(pageData) + { + filePageReader.Skip(pageDataSize); + calculatedCRC.process(*pageData); + } else + { + FileReader pageDataReader = filePageReader.ReadChunk(pageDataSize); + auto pageDataView = pageDataReader.GetPinnedRawDataView(); + calculatedCRC.process(pageDataView.GetSpan()); + } if(calculatedCRC != pageInfo.header.CRC_checksum) { return false; @@ -119,6 +147,19 @@ bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector &pageData } +bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector &pageData) +{ + return ReadPage(file, pageInfo, &pageData); +} + + +bool ReadPage(FileReader &file) +{ + PageInfo pageInfo; + return ReadPage(file, pageInfo); +} + + bool ReadPageAndSkipJunk(FileReader &file, PageInfo &pageInfo, std::vector &pageData) { pageInfo = PageInfo(); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.h b/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.h index 4d97f9240..c11b4b92d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/OggStream.h @@ -51,6 +51,7 @@ struct PageInfo MemsetZero(segment_table); } uint16 GetPagePhysicalSize() const; + uint16 GetPageHeaderSize() const; uint16 GetPageDataSize() const; }; @@ -58,7 +59,9 @@ struct PageInfo // returns false on EOF bool AdvanceToPageMagic(FileReader &file); +bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector *pageData = nullptr); bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector &pageData); +bool ReadPage(FileReader &file); bool ReadPageAndSkipJunk(FileReader &file, PageInfo &pageInfo, std::vector &pageData); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.cpp index 4b357d6e9..619bc21fd 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.cpp @@ -2,7 +2,7 @@ * Paula.cpp * --------- * Purpose: Emulating the Amiga's sound chip, Paula, by implementing resampling using band-limited steps (BLEPs) -* Notes : (currently none) +* Notes : The BLEP table generator code is a translation of Antti S. Lankila's original Python code. * Authors: OpenMPT Devs * Antti S. Lankila * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. @@ -10,266 +10,257 @@ #include "stdafx.h" #include "Paula.h" +#include "TinyFFT.h" + +#include +#include OPENMPT_NAMESPACE_BEGIN +// Compute Bessel function Izero(y) using a series approximation +double Izero(double y); + namespace Paula { -// Tables are: A500 (filter off), A500 (filter on) -static constexpr int32 WinSincIntegral[2][2048] = +namespace { + +MPT_NOINLINE std::vector KaiserFIR(int numTaps, double cutoff, double beta) +{ + const double izeroBeta = Izero(beta); + const double kPi = 4.0 * std::atan(1.0) * cutoff; + const double xDiv = 1.0 / ((numTaps / 2) * (numTaps / 2)); + const int numTapsDiv2 = numTaps / 2; + std::vector result(numTaps); + for(int i = 0; i < numTaps; i++) { - 131072,131072,131072,131072,131072,131072,131072,131072,131072,131072,131072, - 131072,131072,131072,131072,131072,131072,131072,131072,131072,131072,131071,131071, - 131071,131071,131071,131071,131071,131071,131071,131071,131071,131070,131070,131070, - 131070,131070,131069,131069,131069,131068,131068,131068,131067,131067,131066,131066, - 131065,131065,131064,131063,131063,131062,131061,131060,131059,131058,131056,131055, - 131054,131052,131050,131049,131047,131045,131043,131040,131038,131035,131033,131030, - 131026,131023,131020,131016,131012,131008,131003,130998,130993,130988,130982,130976, - 130970,130963,130956,130949,130941,130932,130924,130914,130905,130895,130884,130872, - 130861,130848,130835,130821,130807,130792,130776,130759,130742,130724,130705,130685, - 130664,130642,130620,130596,130571,130545,130518,130490,130461,130430,130398,130365, - 130331,130295,130257,130219,130178,130136,130093,130047,130000,129951,129901,129848, - 129794,129737,129679,129618,129555,129490,129423,129353,129281,129207,129130,129050, - 128968,128883,128795,128704,128611,128514,128415,128312,128206,128097,127985,127869, - 127750,127627,127501,127371,127237,127100,126959,126813,126664,126510,126353,126191, - 126025,125854,125679,125499,125315,125126,124933,124734,124531,124323,124110,123891, - 123668,123439,123205,122965,122720,122470,122214,121952,121685,121412,121133,120849, - 120558,120261,119959,119650,119335,119014,118687,118354,118014,117668,117315,116956, - 116591,116219,115840,115455,115063,114665,114260,113849,113430,113005,112574,112135, - 111690,111239,110780,110315,109843,109364,108879,108387,107888,107383,106871,106352, - 105827,105295,104757,104212,103661,103104,102540,101970,101394,100812,100223,99629, - 99028,98422,97810,97192,96568,95939,95305,94665,94020,93370,92714,92054,91389,90719, - 90045,89366,88682,87995,87303,86607,85908,85205,84498,83788,83075,82358,81639,80916, - 80191,79464,78734,78002,77268,76533,75795,75056,74316,73575,72833,72090,71346,70602, - 69858,69114,68370,67626,66883,66140,65399,64658,63919,63181,62445,61711,60979,60249, - 59521,58796,58074,57355,56639,55926,55217,54512,53810,53113,52419,51731,51046,50367, - 49693,49023,48359,47701,47048,46400,45759,45124,44495,43872,43256,42646,42043,41447, - 40858,40276,39702,39134,38575,38023,37478,36941,36413,35892,35379,34874,34378,33890, - 33410,32938,32475,32020,31574,31137,30708,30288,29876,29473,29079,28693,28317,27948, - 27589,27238,26896,26562,26238,25921,25613,25314,25023,24740,24466,24200,23942,23692, - 23451,23217,22991,22773,22562,22359,22164,21975,21794,21621,21454,21294,21140,20994, - 20853,20719,20592,20470,20354,20244,20139,20040,19946,19857,19774,19694,19620,19550, - 19484,19422,19364,19310,19260,19213,19169,19128,19090,19054,19022,18991,18963,18936, - 18912,18889,18867,18847,18828,18810,18792,18776,18759,18743,18727,18711,18695,18679, - 18662,18644,18626,18607,18587,18565,18542,18518,18492,18465,18436,18404,18371,18336, - 18298,18259,18216,18172,18124,18074,18022,17966,17908,17847,17783,17716,17646,17572, - 17496,17416,17334,17248,17159,17066,16971,16872,16770,16664,16556,16444,16329,16211, - 16090,15966,15839,15709,15576,15440,15301,15159,15015,14868,14718,14566,14412,14255, - 14096,13935,13771,13606,13439,13270,13099,12927,12753,12578,12401,12224,12045,11866, - 11685,11504,11322,11140,10958,10775,10592,10409,10226,10044,9862,9680,9499,9319,9139, - 8961,8783,8607,8432,8258,8086,7915,7747,7580,7415,7252,7091,6932,6776,6622,6471, - 6322,6176,6032,5892,5754,5619,5488,5359,5234,5111,4992,4877,4764,4655,4550,4448, - 4349,4254,4163,4075,3990,3910,3832,3759,3689,3622,3560,3500,3445,3393,3344,3299, - 3257,3219,3184,3153,3124,3099,3078,3059,3044,3031,3022,3015,3011,3010,3012,3016, - 3023,3033,3044,3058,3075,3093,3113,3136,3160,3186,3213,3242,3273,3305,3338,3372, - 3408,3444,3481,3520,3558,3597,3637,3677,3718,3758,3799,3839,3880,3920,3960,4000, - 4039,4077,4115,4152,4188,4224,4258,4291,4323,4354,4384,4412,4439,4464,4488,4510, - 4530,4549,4566,4581,4594,4606,4615,4623,4628,4631,4633,4632,4629,4624,4617,4608, - 4597,4583,4568,4550,4530,4508,4484,4458,4429,4399,4366,4332,4296,4257,4217,4175, - 4130,4085,4037,3988,3937,3884,3830,3774,3717,3658,3598,3537,3475,3411,3347,3281, - 3215,3147,3079,3010,2940,2870,2799,2728,2657,2585,2513,2440,2368,2296,2224,2151, - 2080,2008,1937,1866,1796,1726,1657,1589,1521,1454,1389,1324,1260,1197,1135,1075, - 1016,958,901,846,792,740,689,640,592,546,502,459,419,379,342,307,273,241,211,183, - 156,132,109,88,69,52,37,24,12,2,-5,-11,-16,-18,-19,-18,-16,-11,-6,2,11,21,33,47,61, - 77,95,113,133,154,176,200,224,249,275,302,329,358,387,416,447,477,508,540,572,604, - 636,669,702,734,767,800,832,864,896,928,960,991,1021,1051,1081,1110,1138,1166,1193, - 1219,1245,1270,1293,1316,1338,1359,1379,1398,1416,1433,1448,1463,1476,1488,1499, - 1509,1518,1525,1531,1536,1540,1542,1543,1543,1542,1539,1536,1530,1524,1517,1508, - 1498,1487,1475,1462,1447,1432,1415,1397,1379,1359,1338,1317,1294,1271,1247,1222, - 1196,1170,1143,1115,1086,1057,1028,998,967,936,905,874,842,809,777,744,712,679,646, - 613,581,548,515,483,450,418,387,355,324,293,263,233,204,175,147,119,92,66,40,15, - -10,-33,-56,-78,-99,-120,-139,-158,-176,-193,-209,-224,-238,-252,-264,-275,-286, - -295,-304,-311,-318,-324,-329,-332,-335,-337,-338,-338,-338,-336,-333,-330,-326, - -321,-315,-308,-301,-293,-284,-274,-264,-253,-242,-229,-217,-203,-190,-175,-161, - -145,-130,-114,-98,-81,-64,-47,-29,-11,6,24,42,61,79,97,115,134,152,170,188,206,223, - 241,258,275,291,308,324,340,355,370,384,399,412,425,438,450,462,473,484,494,503, - 512,521,529,536,542,548,553,558,562,566,568,570,572,573,573,573,572,570,568,565, - 562,558,553,548,543,537,530,523,515,507,498,489,479,469,459,448,437,426,414,402, - 389,377,364,351,337,324,310,296,282,268,254,239,225,211,196,182,168,153,139,125, - 111,97,83,70,56,43,30,17,5,-7,-19,-31,-42,-53,-64,-75,-85,-94,-104,-113,-121,-129, - -137,-144,-151,-158,-164,-170,-175,-180,-184,-188,-192,-195,-198,-200,-202,-203, - -204,-205,-205,-204,-204,-203,-201,-199,-197,-195,-192,-188,-185,-181,-176,-172, - -167,-162,-156,-151,-145,-139,-132,-126,-119,-112,-104,-97,-90,-82,-74,-66,-59,-51, - -42,-34,-26,-18,-10,-2,7,15,23,31,39,47,54,62,70,77,85,92,99,106,112,119,125,131, - 137,143,148,154,159,163,168,172,176,180,183,187,190,192,195,197,199,201,202,203, - 204,205,205,205,205,204,203,202,201,200,198,196,194,192,189,186,183,180,177,173, - 169,165,161,157,153,148,143,139,134,129,124,119,113,108,103,97,92,86,81,75,70,64, - 59,53,48,42,37,32,26,21,16,11,6,1,-4,-9,-13,-18,-22,-26,-30,-34,-38,-42,-46,-49, - -52,-55,-58,-61,-64,-66,-69,-71,-73,-74,-76,-78,-79,-80,-81,-82,-82,-83,-83,-83, - -83,-83,-83,-82,-82,-81,-80,-79,-78,-77,-75,-74,-72,-70,-68,-66,-64,-62,-60,-57, - -55,-52,-50,-47,-44,-42,-39,-36,-33,-30,-27,-24,-21,-18,-15,-12,-9,-6,-3,0,3,6,8, - 11,14,17,20,22,25,27,30,32,35,37,39,41,43,45,47,49,50,52,54,55,56,58,59,60,61,61, - 62,63,63,64,64,65,65,65,65,65,64,64,64,63,63,62,61,61,60,59,58,57,56,55,53,52,51, - 49,48,46,45,43,41,40,38,36,35,33,31,29,28,26,24,22,20,19,17,15,13,11,10,8,6,5,3,1, - 0,-2,-3,-5,-6,-8,-9,-10,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-21,-22,-23,-23, - -24,-24,-25,-25,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-26,-25,-25,-25,-24, - -24,-23,-23,-22,-21,-21,-20,-19,-19,-18,-17,-16,-16,-15,-14,-13,-12,-11,-10,-10,-9, - -8,-7,-6,-5,-4,-3,-2,-1,0,1,1,2,3,4,5,6,6,7,8,9,9,10,11,11,12,12,13,14,14,15,15, - 15,16,16,16,17,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,17,17,17,17,16,16, - 16,16,15,15,14,14,14,13,13,12,12,11,11,11,10,10,9,9,8,8,7,7,6,6,5,5,4,4,3,3,2,2,1, - 1,0,0,-1,-1,-1,-2,-2,-3,-3,-3,-4,-4,-4,-4,-5,-5,-5,-5,-6,-6,-6,-6,-6,-6,-6,-7,-7, - -7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-6,-6,-6,-6,-6,-6,-6,-6,-5,-5,-5,-5,-5,-4,-4,-4, - -4,-4,-3,-3,-3,-3,-2,-2,-2,-2,-1,-1,-1,-1,-1,0,0,0,0,1,1,1,1,1,2,2,2,2,2,2,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,3,3,3,3,3,3, - 3,3,3,3,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - }, + double fsinc; + if(i == numTapsDiv2) + { + fsinc = 1.0; + } else + { + const double x = i - numTapsDiv2; + const double xPi = x * kPi; + // - sinc - - Kaiser window - -sinc- + fsinc = std::sin(xPi) * Izero(beta * std::sqrt(1 - x * x * xDiv)) / (izeroBeta * xPi); + } + + result[i] = fsinc * cutoff; + } + return result; +} + + +MPT_NOINLINE void FIR_MinPhase(std::vector &table, const TinyFFT &fft) +{ + std::vector> cepstrum(fft.Size()); + MPT_ASSERT(cepstrum.size() >= table.size()); + for(size_t i = 0; i < table.size(); i++) + cepstrum[i] = table[i]; + // Compute the real cepstrum: fft -> abs + ln -> ifft -> real + fft.FFT(cepstrum); + for(auto &v : cepstrum) + v = std::log(std::abs(v)); + fft.IFFT(cepstrum); + fft.Normalize(cepstrum); + + // Window the cepstrum in such a way that anticausal components become rejected + for(size_t i = 1; i < cepstrum.size() / 2; i++) { - 131072,131072,131072,131072,131072,131072,131072,131072,131072,131072,131072, - 131072,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071, - 131071,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071,131071, - 131070,131070,131070,131070,131070,131070,131070,131070,131070,131070,131070,131070, - 131070,131070,131070,131070,131070,131070,131070,131070,131070,131070,131069,131069, - 131069,131069,131069,131069,131069,131069,131069,131069,131069,131069,131069,131069, - 131069,131068,131068,131068,131068,131068,131068,131068,131068,131068,131068,131067, - 131067,131067,131067,131067,131067,131067,131066,131066,131066,131066,131066,131065, - 131065,131065,131065,131064,131064,131064,131064,131063,131063,131062,131062,131062, - 131061,131061,131060,131060,131059,131059,131058,131058,131057,131056,131056,131055, - 131054,131053,131052,131052,131051,131050,131049,131048,131046,131045,131044,131043, - 131041,131040,131038,131037,131035,131034,131032,131030,131028,131026,131024,131022, - 131020,131017,131015,131012,131010,131007,131004,131001,130998,130994,130991,130987, - 130984,130980,130976,130972,130968,130963,130959,130954,130949,130944,130938,130933, - 130927,130921,130915,130909,130902,130895,130888,130881,130873,130865,130857,130849, - 130840,130831,130822,130813,130803,130793,130782,130771,130760,130749,130737,130725, - 130712,130699,130685,130672,130657,130643,130628,130612,130596,130580,130563,130545, - 130528,130509,130490,130471,130451,130430,130409,130388,130365,130343,130319,130295, - 130270,130245,130219,130193,130165,130137,130109,130079,130049,130018,129987,129954, - 129921,129887,129853,129817,129781,129744,129706,129667,129627,129587,129545,129503, - 129459,129415,129370,129324,129276,129228,129179,129129,129078,129026,128972,128918, - 128862,128806,128748,128690,128630,128569,128507,128443,128379,128313,128246,128178, - 128109,128038,127966,127893,127819,127743,127667,127588,127509,127428,127346,127262, - 127177,127091,127004,126915,126824,126732,126639,126545,126449,126351,126252,126152, - 126050,125947,125842,125736,125628,125519,125408,125296,125182,125067,124950,124832, - 124712,124591,124468,124344,124218,124090,123961,123830,123698,123564,123429,123292, - 123154,123014,122872,122729,122585,122438,122291,122141,121990,121838,121684,121528, - 121371,121212,121052,120891,120727,120562,120396,120228,120059,119888,119715,119541, - 119366,119189,119011,118831,118649,118467,118282,118097,117909,117721,117531,117339, - 117146,116952,116757,116560,116361,116161,115960,115758,115554,115349,115143,114935, - 114726,114516,114305,114092,113878,113663,113447,113229,113011,112791,112570,112348, - 112124,111900,111675,111448,111220,110992,110762,110531,110300,110067,109833,109598, - 109363,109126,108889,108650,108411,108171,107930,107688,107445,107201,106957,106712, - 106466,106219,105972,105723,105474,105225,104974,104723,104471,104219,103966,103712, - 103458,103203,102948,102692,102435,102178,101920,101662,101403,101144,100885,100624, - 100364,100103,99841,99579,99317,99054,98791,98527,98264,97999,97735,97470,97204,96939, - 96673,96407,96140,95873,95606,95339,95071,94804,94536,94267,93999,93730,93461,93192, - 92923,92653,92383,92114,91844,91573,91303,91033,90762,90491,90220,89949,89678,89407, - 89136,88865,88593,88321,88050,87778,87506,87234,86962,86690,86418,86146,85874,85602, - 85330,85057,84785,84513,84240,83968,83696,83423,83151,82878,82606,82333,82061,81789, - 81516,81244,80971,80699,80427,80154,79882,79610,79337,79065,78793,78521,78249,77977, - 77705,77433,77161,76889,76617,76345,76074,75802,75531,75259,74988,74717,74446,74175, - 73904,73633,73362,73092,72821,72551,72280,72010,71740,71470,71201,70931,70661,70392, - 70123,69854,69585,69317,69048,68780,68512,68244,67976,67709,67441,67174,66907,66641, - 66374,66108,65842,65576,65311,65046,64781,64516,64252,63988,63724,63460,63197,62934, - 62672,62409,62147,61886,61624,61363,61103,60843,60583,60323,60064,59805,59547,59289, - 59031,58774,58517,58261,58005,57749,57494,57239,56985,56731,56478,56225,55973,55721, - 55470,55219,54968,54718,54469,54220,53971,53724,53476,53229,52983,52737,52492,52247, - 52003,51759,51516,51274,51032,50791,50550,50310,50070,49831,49593,49355,49118,48881, - 48645,48410,48175,47941,47707,47474,47242,47010,46779,46549,46319,46090,45861,45633, - 45406,45179,44953,44728,44503,44279,44056,43833,43611,43389,43168,42948,42728,42510, - 42291,42074,41857,41640,41425,41210,40995,40782,40568,40356,40144,39933,39723,39513, - 39304,39095,38887,38680,38473,38267,38062,37857,37653,37449,37247,37044,36843,36642, - 36442,36242,36043,35845,35647,35450,35253,35057,34862,34667,34473,34280,34087,33894, - 33703,33512,33321,33132,32942,32754,32566,32378,32192,32006,31820,31635,31451,31267, - 31084,30901,30719,30538,30357,30177,29997,29818,29640,29462,29285,29108,28932,28756, - 28581,28407,28233,28060,27888,27716,27545,27374,27204,27034,26865,26697,26529,26362, - 26195,26029,25863,25699,25534,25371,25207,25045,24883,24722,24561,24401,24241,24082, - 23924,23766,23609,23453,23297,23142,22987,22833,22679,22526,22374,22222,22071,21921, - 21771,21622,21473,21325,21177,21031,20884,20739,20594,20449,20306,20162,20020,19878, - 19737,19596,19456,19316,19178,19039,18902,18765,18628,18493,18358,18223,18089,17956, - 17823,17691,17560,17429,17299,17169,17040,16912,16784,16657,16531,16405,16280,16155, - 16031,15908,15785,15663,15541,15420,15300,15180,15061,14942,14824,14707,14590,14474, - 14358,14243,14129,14015,13901,13789,13677,13565,13454,13344,13234,13125,13016,12908, - 12801,12694,12588,12482,12377,12272,12168,12064,11961,11859,11757,11655,11554,11454, - 11354,11255,11156,11058,10960,10863,10766,10670,10575,10480,10385,10291,10197,10104, - 10012,9920,9828,9737,9646,9556,9466,9377,9289,9200,9113,9025,8939,8852,8766,8681, - 8596,8512,8428,8344,8261,8178,8096,8014,7933,7852,7772,7692,7612,7533,7455,7376, - 7299,7221,7144,7068,6992,6916,6841,6766,6692,6618,6544,6471,6398,6326,6254,6182, - 6111,6041,5970,5901,5831,5762,5693,5625,5557,5490,5423,5356,5290,5224,5159,5093, - 5029,4965,4901,4837,4774,4711,4649,4587,4525,4464,4404,4343,4283,4224,4164,4105, - 4047,3989,3931,3874,3817,3760,3704,3648,3593,3538,3483,3429,3375,3321,3268,3215, - 3163,3111,3059,3008,2957,2906,2856,2806,2756,2707,2658,2610,2562,2514,2466,2419, - 2373,2326,2280,2235,2189,2144,2100,2056,2012,1968,1925,1882,1839,1797,1755,1714, - 1672,1631,1591,1551,1511,1471,1432,1393,1354,1316,1278,1240,1203,1165,1129,1092, - 1056,1020,984,949,914,879,845,811,777,743,710,677,644,612,580,548,516,485,454,423, - 393,362,332,303,273,244,215,186,158,129,101,74,46,19,-8,-35,-61,-88,-114,-140,-165, - -191,-216,-241,-265,-290,-314,-338,-362,-386,-409,-432,-455,-478,-501,-523,-545, - -567,-589,-610,-632,-653,-674,-694,-715,-735,-756,-775,-795,-815,-834,-853,-872, - -891,-910,-928,-947,-965,-983,-1000,-1018,-1035,-1052,-1069,-1086,-1103,-1119,-1136, - -1152,-1168,-1184,-1199,-1215,-1230,-1245,-1260,-1275,-1289,-1304,-1318,-1332,-1346, - -1360,-1373,-1387,-1400,-1413,-1426,-1439,-1452,-1464,-1477,-1489,-1501,-1513,-1524, - -1536,-1547,-1558,-1570,-1581,-1591,-1602,-1612,-1623,-1633,-1643,-1653,-1663,-1672, - -1682,-1691,-1700,-1709,-1718,-1727,-1735,-1744,-1752,-1760,-1768,-1776,-1784,-1792, - -1799,-1807,-1814,-1821,-1828,-1835,-1842,-1848,-1855,-1861,-1867,-1873,-1879,-1885, - -1891,-1896,-1902,-1907,-1913,-1918,-1923,-1928,-1932,-1937,-1942,-1946,-1950,-1955, - -1959,-1963,-1967,-1971,-1974,-1978,-1981,-1985,-1988,-1991,-1994,-1997,-2000,-2003, - -2006,-2009,-2011,-2013,-2016,-2018,-2020,-2022,-2024,-2026,-2028,-2030,-2032,-2033, - -2035,-2036,-2037,-2039,-2040,-2041,-2042,-2043,-2044,-2045,-2045,-2046,-2047,-2047, - -2048,-2048,-2048,-2049,-2049,-2049,-2049,-2049,-2049,-2049,-2049,-2048,-2048,-2048, - -2047,-2047,-2046,-2046,-2045,-2044,-2043,-2043,-2042,-2041,-2040,-2039,-2038,-2036, - -2035,-2034,-2033,-2031,-2030,-2028,-2027,-2025,-2024,-2022,-2020,-2019,-2017,-2015, - -2013,-2011,-2009,-2007,-2005,-2003,-2001,-1998,-1996,-1994,-1992,-1989,-1987,-1984, - -1982,-1979,-1977,-1974,-1971,-1969,-1966,-1963,-1960,-1957,-1954,-1952,-1949,-1946, - -1942,-1939,-1936,-1933,-1930,-1927,-1923,-1920,-1917,-1913,-1910,-1906,-1903,-1899, - -1896,-1892,-1889,-1885,-1881,-1878,-1874,-1870,-1866,-1863,-1859,-1855,-1851,-1847, - -1843,-1839,-1835,-1831,-1827,-1823,-1819,-1814,-1810,-1806,-1802,-1798,-1793,-1789, - -1785,-1780,-1776,-1771,-1767,-1763,-1758,-1754,-1749,-1745,-1740,-1736,-1731,-1726, - -1722,-1717,-1713,-1708,-1703,-1698,-1694,-1689,-1684,-1680,-1675,-1670,-1665,-1660, - -1656,-1651,-1646,-1641,-1636,-1631,-1626,-1622,-1617,-1612,-1607,-1602,-1597,-1592, - -1587,-1582,-1577,-1572,-1567,-1562,-1557,-1552,-1547,-1542,-1537,-1532,-1527,-1522, - -1517,-1512,-1507,-1502,-1497,-1492,-1487,-1482,-1477,-1471,-1466,-1461,-1456,-1451, - -1446,-1441,-1436,-1431,-1426,-1421,-1416,-1411,-1406,-1401,-1395,-1390,-1385,-1380, - -1375,-1370,-1365,-1360,-1355,-1350,-1345,-1340,-1335,-1330,-1325,-1320,-1315,-1310, - -1305,-1300,-1295,-1290,-1285,-1280,-1275,-1270,-1265,-1260,-1255,-1250,-1245,-1240, - -1235,-1230,-1225,-1220,-1215,-1210,-1205,-1200,-1195,-1190,-1185,-1180,-1175,-1171, - -1166,-1161,-1156,-1151,-1146,-1141,-1136,-1131,-1127,-1122,-1117,-1112,-1107,-1102, - -1098,-1093,-1088,-1083,-1078,-1074,-1069,-1064,-1059,-1055,-1050,-1045,-1040,-1036, - -1031,-1026,-1022,-1017,-1012,-1007,-1003,-998,-994,-989,-984,-980,-975,-970,-966, - -961,-957,-952,-948,-943,-938,-934,-929,-925,-920,-916,-911,-907,-902,-898,-894, - -889,-885,-880,-876,-872,-867,-863,-858,-854,-850,-845,-841,-837,-833,-828,-824, - -820,-816,-811,-807,-803,-799,-795,-790,-786,-782,-778,-774,-770,-766,-762,-757, - -753,-749,-745,-741,-737,-733,-729,-725,-721,-717,-714,-710,-706,-702,-698,-694, - -690,-686,-683,-679,-675,-671,-667,-664,-660,-656,-652,-649,-645,-641,-638,-634, - -630,-627,-623,-620,-616,-612,-609,-605,-602,-598,-595,-591,-588,-584,-581,-577, - -574,-571,-567,-564,-560,-557,-554,-550,-547,-544,-540,-537,-534,-530,-527,-524, - -521,-518,-514,-511,-508,-505,-502,-499,-495,-492,-489,-486,-483,-480,-477,-474, - -471,-468,-465,-462,-459,-456,-453,-450,-447,-444,-441,-438,-435,-433,-430,-427, - -424,-421,-418,-416,-413,-410,-407,-405,-402,-399,-396,-394,-391,-388,-386,-383, - -380,-378,-375,-373,-370,-367,-365,-362,-360,-357,-355,-352,-350,-347,-345,-342, - -340,-337,-335,-333,-330,-328,-325,-323,-321,-318,-316,-314,-311,-309,-307,-305, - -302,-300,-298,-296,-293,-291,-289,-287,-285,-282,-280,-278,-276,-274,-272,-270, - -268,-266,-264,-261,-259,-257,-255,-253,-251,-249,-247,-246,-244,-242,-240,-238, - -236,-234,-232,-230,-228,-227,-225,-223,-221,-219,-217,-216,-214,-212,-210,-209, - -207,-205,-203,-202,-200,-198,-197,-195,-193,-192,-190,-188,-187,-185,-184,-182, - -180,-179,-177,-176,-174,-173,-171,-170,-168,-167,-165,-164,-162,-161,-159,-158, - -156,-155,-154,-152,-151,-149,-148,-147,-145,-144,-143,-141,-140,-139,-137,-136, - -135,-133,-132,-131,-130,-128,-127,-126,-125,-123,-122,-121,-120,-119,-117,-116, - -115,-114,-113,-112,-111,-109,-108,-107,-106,-105,-104,-103,-102,-101,-100,-99,-98, - -97,-96,-95,-94,-93,-92,-91,-90,-89,-88,-87,-86,-85,-84,-83,-82,-81,-80,-79,-78, - -78,-77,-76,-75,-74,-73,-72,-71,-71,-70,-69,-68,-67,-67,-66,-65,-64,-63,-63,-62, - -61,-60,-60,-59,-58,-58,-57,-56,-55,-55,-54,-53,-53,-52,-51,-51,-50,-49,-49,-48, - -47,-47,-46,-45,-45,-44,-44,-43,-42,-42,-41,-41,-40,-40,-39,-38,-38,-37,-37,-36, - -36,-35,-35,-34,-34,-33,-33,-32,-32,-31,-31,-30,-30,-29,-29,-28,-28,-28,-27,-27, - -26,-26,-25,-25,-25,-24,-24,-23,-23,-23,-22,-22,-21,-21,-21,-20,-20,-20,-19,-19, - -19,-18,-18,-17,-17,-17,-17,-16,-16,-16,-15,-15,-15,-14,-14,-14,-13,-13,-13,-13, - -12,-12,-12,-12,-11,-11,-11,-11,-10,-10,-10,-10,-9,-9,-9,-9,-9,-8,-8,-8,-8,-7,-7, - -7,-7,-7,-7,-6,-6,-6,-6,-6,-5,-5,-5,-5,-5,-5,-5,-4,-4,-4,-4,-4,-4,-4,-3,-3,-3,-3, - -3,-3,-3,-3,-3,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + cepstrum[i] *= 2; + cepstrum[i + cepstrum.size() / 2] *= 0; + } + + // Now cancel the previous steps: fft -> exp -> ifft -> real + fft.FFT(cepstrum); + for(auto &v : cepstrum) + v = std::exp(v); + fft.IFFT(cepstrum); + fft.Normalize(cepstrum); + for(size_t i = 0; i < table.size(); i++) + table[i] = cepstrum[i].real(); +} + + +class BiquadFilter +{ + double b0, b1, b2, a1, a2, x1 = 0.0, x2 = 0.0, y1 = 0.0, y2 = 0.0; + + double Filter(double x0) + { + double y0 = b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2; + x2 = x1; + x1 = x0; + y2 = y1; + y1 = y0; + return y0; + } + +public: + BiquadFilter(double b0_, double b1_, double b2_, double a1_, double a2_) + : b0(b0_), b1(b1_), b2(b2_), a1(a1_), a2(a2_) + { } + + std::vector Run(std::vector table) + { + x1 = 0.0; + x2 = 0.0; + y1 = 0.0; + y2 = 0.0; + + // Initialize filter to stable state + for(int i = 0; i < 10000; i++) + Filter(table[0]); + // Now run the filter + for(auto &v : table) + v = Filter(v); + + return table; } }; +// Observe: a and b are reversed here. To be absolutely clear: +// a is the nominator and b is the denominator. :-/ +BiquadFilter ZTransform(double a0, double a1, double a2, double b0, double b1, double b2, double fc, double fs) +{ + // Prewarp s - domain coefficients + const double wp = 2.0 * fs * std::tan(M_PI * fc / fs); + a2 /= wp * wp; + a1 /= wp; + b2 /= wp * wp; + b1 /= wp; + + // Compute bilinear transform and return it + const double bd = 4 * b2 * fs * fs + 2 * b1 * fs + b0; + return BiquadFilter( + (4 * a2 * fs * fs + 2 * a1 * fs + a0) / bd, + (2 * a0 - 8 * a2 * fs * fs) / bd, + (4 * a2 * fs * fs - 2 * a1 * fs + a0) / bd, + (2 * b0 - 8 * b2 * fs * fs) / bd, + (4 * b2 * fs * fs - 2 * b1 * fs + b0) / bd); +} + + +BiquadFilter MakeRCLowpass(double sampleRate, double freq) +{ + const double omega = 2 * M_PI * freq / sampleRate; + const double term = 1 + 1 / omega; + return BiquadFilter(1 / term, 0.0, 0.0, -1.0 + 1.0 / term, 0.0); +} + + +BiquadFilter MakeButterworth(double fs, double fc, double res_dB = 0) +{ + // 2nd-order Butterworth s-domain coefficients are: + // + // b0 = 1.0 b1 = 0 b2 = 0 + // a0 = 1 a1 = sqrt(2) a2 = 1 + // + // by tweaking the a1 parameter, some resonance can be produced. + + const double res = std::pow(10.0, (-res_dB / 10.0 / 2.0)); + return ZTransform(1, 0, 0, 1, std::sqrt(2) * res, 1, fc, fs); +} + + +MPT_NOINLINE void Integrate(std::vector &table) +{ + const double total = std::accumulate(table.begin(), table.end(), 0.0); + double startVal = -total; + + for(auto &v : table) + { + startVal += v; + v = startVal; + } +} + + +MPT_NOINLINE void Quantize(const std::vector &in, Paula::BlepArray &quantized) +{ + MPT_ASSERT(in.size() == Paula::BLEP_SIZE); + constexpr int fact = 1 << Paula::BLEP_SCALE; + const double cv = fact / (in.back() - in.front()); + + for(int i = 0; i < Paula::BLEP_SIZE; i++) + { + double val = in[i] * cv; +#ifdef MPT_INTMIXER + val = mpt::round(val); +#endif + quantized[i] = static_cast(-val); + } +} + +} // namespace + +void BlepTables::InitTables() +{ + constexpr double sampleRate = Paula::PAULA_HZ; + + // Because Amiga only has 84 dB SNR, the noise floor is low enough with -90 dB. + // A500 model uses slightly lower-quality kaiser window to obtain slightly + // steeper stopband attenuation. The fixed filters attenuates the sidelobes by + // 12 dB, compensating for the worse performance of the kaiser window. + + // 21 kHz stopband is not fully attenuated by 22 kHz. If the sampling frequency + // is 44.1 kHz, all frequencies above 22 kHz will alias over 20 kHz, thus inaudible. + // The output should be aliasingless for 48 kHz sampling frequency. + auto unfilteredA500 = KaiserFIR(Paula::BLEP_SIZE, 21000.0 / sampleRate * 2.0, 8.0); + auto unfilteredA1200 = KaiserFIR(Paula::BLEP_SIZE, 21000.0 / sampleRate * 2.0, 9.0); + // Move filtering effects to start to allow IIRs more time to settle + constexpr size_t padSize = 8; + constexpr int fftSize = static_cast(mpt::bit_width(size_t(Paula::BLEP_SIZE)) + mpt::bit_width(padSize) - 2); + const TinyFFT fft(fftSize); + FIR_MinPhase(unfilteredA500, fft); + FIR_MinPhase(unfilteredA1200, fft); + + // Make digital models for the filters on Amiga 500 and 1200. + auto filterFixed5kHz = MakeRCLowpass(sampleRate, 4900.0); + // The leakage filter seems to reduce treble in both models a bit + // The A500 filter seems to be well modelled only with a 4.9 kHz + // filter although the component values would suggest 5 kHz filter. + auto filterLeakage = MakeRCLowpass(sampleRate, 32000.0); + auto filterLED = MakeButterworth(sampleRate, 3275.0, -0.70); + + // Apply fixed filter to A500 + auto amiga500Off = filterFixed5kHz.Run(unfilteredA500); + // Produce the filtered outputs + auto amiga1200Off = filterLeakage.Run(unfilteredA1200); + + // Produce LED filters + auto amiga500On = filterLED.Run(amiga500Off); + auto amiga1200On = filterLED.Run(amiga1200Off); + + // Integrate to produce blep + Integrate(amiga500Off); + Integrate(amiga500On); + Integrate(amiga1200Off); + Integrate(amiga1200On); + Integrate(unfilteredA1200); + + // Quantize and scale + Quantize(amiga500Off, WinSincIntegral[A500Off]); + Quantize(amiga500On, WinSincIntegral[A500On]); + Quantize(amiga1200Off, WinSincIntegral[A1200Off]); + Quantize(amiga1200On, WinSincIntegral[A1200On]); + Quantize(unfilteredA1200, WinSincIntegral[Unfiltered]); +} + + +const Paula::BlepArray &BlepTables::GetAmigaTable(Resampling::AmigaFilter amigaType, bool enableFilter) const +{ + if(amigaType == Resampling::AmigaFilter::A500) + return enableFilter ? WinSincIntegral[A500On] : WinSincIntegral[A500Off]; + if(amigaType == Resampling::AmigaFilter::A1200) + return enableFilter ? WinSincIntegral[A1200On] : WinSincIntegral[A1200Off]; + return WinSincIntegral[Unfiltered]; +} + + // we do not initialize blepState here // cppcheck-suppress uninitMemberVar State::State(uint32 sampleRate) @@ -278,8 +269,6 @@ State::State(uint32 sampleRate) numSteps = static_cast(amigaClocksPerSample / MINIMUM_INTERVAL); stepRemainder = SamplePosition::FromDouble(amigaClocksPerSample - numSteps * MINIMUM_INTERVAL); remainder = SamplePosition(0); - activeBleps = 0; - globalOutputLevel = 0; } @@ -287,6 +276,7 @@ void State::Reset() { remainder = SamplePosition(0); activeBleps = 0; + firstBlep = MAX_BLEPS / 2u; globalOutputLevel = 0; } @@ -295,28 +285,26 @@ void State::InputSample(int16 sample) { if(sample != globalOutputLevel) { - LimitMax(activeBleps, static_cast(mpt::size(blepState) - 1)); - - // Make room for new blep in the sorted list - std::move_backward(blepState, blepState + activeBleps, blepState + activeBleps + 1); - // Start a new blep: level is the difference, age (or phase) is 0 clocks. - activeBleps++; - blepState[0].age = 0; - blepState[0].level = sample - globalOutputLevel; + firstBlep = (firstBlep - 1u) % MAX_BLEPS; + if(activeBleps < std::size(blepState)) + activeBleps++; + blepState[firstBlep].age = 0; + blepState[firstBlep].level = sample - globalOutputLevel; globalOutputLevel = sample; } } // Return output simulated as series of bleps -int State::OutputSample(bool filter) +int State::OutputSample(const BlepArray &WinSincIntegral) { int output = globalOutputLevel * (1 << Paula::BLEP_SCALE); - for(uint16 i = 0; i < activeBleps; i++) + uint32 lastBlep = firstBlep + activeBleps; + for(uint32 i = firstBlep; i != lastBlep; i++) { - const auto &blep = blepState[i]; - output -= WinSincIntegral[filter][blep.age] * blep.level; + const auto &blep = blepState[i % MAX_BLEPS]; + output -= WinSincIntegral[blep.age] * blep.level; } output /= (1 << (Paula::BLEP_SCALE - 2)); // - 2 to compensate for the fact that we reduced the input sample bit depth @@ -327,12 +315,14 @@ int State::OutputSample(bool filter) // Advance the simulation by given number of clock ticks void State::Clock(int cycles) { - for(uint16 i = 0; i < activeBleps; i++) + uint32 lastBlep = firstBlep + activeBleps; + for(uint32 i = firstBlep; i != lastBlep; i++) { - blepState[i].age += static_cast(cycles); - if(blepState[i].age >= mpt::size(WinSincIntegral[0])) + auto &blep = blepState[i % MAX_BLEPS]; + blep.age += static_cast(cycles); + if(blep.age >= Paula::BLEP_SIZE) { - activeBleps = i; + activeBleps = static_cast(i - firstBlep); return; } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.h index 5de16a192..d790ee1bf 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Paula.h @@ -13,20 +13,51 @@ #include "BuildSettings.h" #include "Snd_defs.h" +#include "Mixer.h" OPENMPT_NAMESPACE_BEGIN namespace Paula { -const int PAULA_HZ = 3546895; -const int MINIMUM_INTERVAL = 16; -const int BLEP_SCALE = 17; -const int BLEP_SIZE = 2048; -const int MAX_BLEPS = (BLEP_SIZE / MINIMUM_INTERVAL); +constexpr int PAULA_HZ = 3546895; +constexpr int MINIMUM_INTERVAL = 4; // Tradeoff between quality and speed (lower = less aliasing) +constexpr int BLEP_SCALE = 17; // TODO: Should be 1 for float mixer +constexpr int BLEP_SIZE = 2048; + +using BlepArray = std::array; + + +class BlepTables +{ + enum AmigaFilter + { + A500Off = 0, + A500On, + A1200Off, + A1200On, + Unfiltered, + NumFilterTypes + }; + + std::array WinSincIntegral; + +public: + void InitTables(); + const Paula::BlepArray &GetAmigaTable(Resampling::AmigaFilter amigaType, bool enableFilter) const; +}; + class State { + // MAX_BLEPS configures how many BLEPs (volume steps) are being kept track of per channel, + // and thus directly influences how much memory this feature wastes per virtual channel. + // Paula::BLEP_SIZE / Paula::MINIMUM_INTERVAL would be a safe maximum, + // but even a sample (alternating at +1/-1, thus causing a step on every frame) played at 200 kHz, + // which is way out of spec for the Amiga, will only get close to 128 active BLEPs with Paula::MINIMUM_INTERVAL == 4. + // Hence 128 is chosen as a tradeoff between quality and memory consumption. + static constexpr uint16 MAX_BLEPS = 128; + struct Blep { int16 level; @@ -35,10 +66,10 @@ class State public: SamplePosition remainder, stepRemainder; - int numSteps; // Number of full-length steps + int numSteps; // Number of full-length steps private: - uint16 activeBleps; // Count of simultaneous bleps to keep track of - int16 globalOutputLevel; // The instantenous value of Paula output + uint16 activeBleps = 0, firstBlep = 0; // Count of simultaneous bleps to keep track of + int16 globalOutputLevel = 0; // The instantenous value of Paula output Blep blepState[MAX_BLEPS]; public: @@ -46,7 +77,7 @@ public: void Reset(); void InputSample(int16 sample); - int OutputSample(bool filter); + int OutputSample(const BlepArray &WinSincIntegral); void Clock(int cycles); }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Resampler.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Resampler.h index 5c4b290b1..65d9a686c 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Resampler.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Resampler.h @@ -15,6 +15,7 @@ #include "WindowedFIR.h" #include "Mixer.h" #include "MixerSettings.h" +#include "Paula.h" OPENMPT_NAMESPACE_BEGIN @@ -50,24 +51,18 @@ typedef mixsample_t SINC_TYPE; #endif // MPT_INTMIXER #define SINC_MASK (SINC_PHASES-1) -STATIC_ASSERT((SINC_MASK & 0xffff) == SINC_MASK); // exceeding fractional freq +static_assert((SINC_MASK & 0xffff) == SINC_MASK); // exceeding fractional freq class CResamplerSettings { public: - ResamplingMode SrcMode; - double gdWFIRCutoff; - uint8 gbWFIRType; - bool emulateAmiga; + ResamplingMode SrcMode = Resampling::Default(); + double gdWFIRCutoff = 0.97; + uint8 gbWFIRType = WFIR_KAISER4T; + Resampling::AmigaFilter emulateAmiga = Resampling::AmigaFilter::Off; public: - MPT_CONSTEXPR11_FUN CResamplerSettings() - : SrcMode(Resampling::Default()) - , gdWFIRCutoff(0.97) - , gbWFIRType(WFIR_KAISER4T) - , emulateAmiga(false) - { - } + constexpr CResamplerSettings() = default; bool operator == (const CResamplerSettings &cmp) const { return SrcMode == cmp.SrcMode && gdWFIRCutoff == cmp.gdWFIRCutoff && gbWFIRType == cmp.gbWFIRType && emulateAmiga == cmp.emulateAmiga; @@ -94,6 +89,7 @@ public: RESAMPLER_TABLE SINC_TYPE gKaiserSinc[SINC_PHASES * 8]; // Upsampling RESAMPLER_TABLE SINC_TYPE gDownsample13x[SINC_PHASES * 8]; // Downsample 1.333x RESAMPLER_TABLE SINC_TYPE gDownsample2x[SINC_PHASES * 8]; // Downsample 2x + RESAMPLER_TABLE Paula::BlepTables blepTables; // Amiga BLEP resampler #ifndef MPT_INTMIXER RESAMPLER_TABLE mixsample_t FastSincTablef[256 * 4]; // Cubic spline LUT @@ -127,6 +123,7 @@ public: { InitializeTablesFromScratch(false); } + private: void InitFloatmixerTables(); void InitializeTablesFromScratch(bool force=false); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.cpp index c670b5d7f..d3c810798 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.cpp @@ -35,10 +35,9 @@ RowVisitor::RowVisitor(const CSoundFile &sf, SEQUENCEINDEX sequence) } -RowVisitor& RowVisitor::operator=(RowVisitor &&other) +void RowVisitor::MoveVisitedRowsFrom(RowVisitor &other) { m_visitedRows = std::move(other.m_visitedRows); - return *this; } @@ -254,4 +253,13 @@ void RowVisitor::AddVisitedRow(ORDERINDEX ord, ROWINDEX row) } +// Returns the last visited row index of the current pattern, or ROWINDEX_INVALID if there is none. +ROWINDEX RowVisitor::GetLastVisitedRow() const +{ + if(m_visitOrder.empty()) + return ROWINDEX_INVALID; + return m_visitOrder.back(); +} + + OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.h b/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.h index 6a72c948b..f8df2c9dd 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/RowVisitor.h @@ -34,7 +34,8 @@ protected: public: RowVisitor(const CSoundFile &sf, SEQUENCEINDEX sequence = SEQUENCEINDEX_INVALID); - RowVisitor& operator=(RowVisitor &&other); + + void MoveVisitedRowsFrom(RowVisitor &other); // Resize / Clear the row vector. // If reset is true, the vector is not only resized to the required dimensions, but also completely cleared (i.e. all visited rows are unset). @@ -69,6 +70,9 @@ public: // Set all rows of a previous pattern loop as unvisited. void ResetPatternLoop(ORDERINDEX ord, ROWINDEX startRow); + // Returns the last visited row index of the current pattern, or ROWINDEX_INVALID if there is none. + ROWINDEX GetLastVisitedRow() const; + protected: // (Un)sets a given row as visited. diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.cpp index e69f5723b..d7305412d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.cpp @@ -20,7 +20,7 @@ OPENMPT_NAMESPACE_BEGIN void S3MSampleHeader::ConvertToMPT(ModSample &mptSmp) const { mptSmp.Initialize(MOD_TYPE_S3M); - mpt::String::Read(mptSmp.filename, filename); + mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename); if(sampleType == typePCM || sampleType == typeNone) { @@ -28,8 +28,8 @@ void S3MSampleHeader::ConvertToMPT(ModSample &mptSmp) const if(sampleType == typePCM) { mptSmp.nLength = length; - mptSmp.nLoopStart = MIN(loopStart, mptSmp.nLength - 1); - mptSmp.nLoopEnd = MIN(loopEnd, mptSmp.nLength); + mptSmp.nLoopStart = std::min(static_cast(loopStart), mptSmp.nLength - 1); + mptSmp.nLoopEnd = std::min(static_cast(loopEnd), mptSmp.nLength); mptSmp.uFlags.set(CHN_LOOP, (flags & smpLoop) != 0); } @@ -48,7 +48,7 @@ void S3MSampleHeader::ConvertToMPT(ModSample &mptSmp) const } // Volume / Panning - mptSmp.nVolume = std::min(defaultVolume, 64) * 4; + mptSmp.nVolume = std::min(static_cast(defaultVolume), uint8(64)) * 4; // C-5 frequency mptSmp.nC5Speed = c5speed; @@ -67,7 +67,7 @@ void S3MSampleHeader::ConvertToMPT(ModSample &mptSmp) const SmpLength S3MSampleHeader::ConvertToS3M(const ModSample &mptSmp) { SmpLength smpLength = 0; - mpt::String::Write(filename, mptSmp.filename); + mpt::String::WriteBuf(mpt::String::maybeNullTerminated, filename) = mptSmp.filename; memcpy(magic, "SCRS", 4); if(mptSmp.uFlags[CHN_ADLIB]) @@ -100,7 +100,7 @@ SmpLength S3MSampleHeader::ConvertToS3M(const ModSample &mptSmp) sampleType = typeNone; } - defaultVolume = static_cast(MIN(mptSmp.nVolume / 4, 64)); + defaultVolume = static_cast(std::min(static_cast(mptSmp.nVolume / 4), uint16(64))); if(mptSmp.nC5Speed != 0) { c5speed = mptSmp.nC5Speed; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.h b/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.h index c27e70797..a78ad2281 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/S3MTools.h @@ -24,70 +24,71 @@ struct S3MFileHeader // Magic Bytes enum S3MMagic { - idEOF = 0x1A, - idS3MType = 0x10, - idPanning = 0xFC, + idEOF = 0x1A, + idS3MType = 0x10, + idPanning = 0xFC, }; // Tracker Versions in the cwtv field enum S3MTrackerVersions { - trackerMask = 0xF000, - versionMask = 0x0FFF, + trackerMask = 0xF000, + versionMask = 0x0FFF, - trkScreamTracker = 0x1000, - trkImagoOrpheus = 0x2000, - trkImpulseTracker = 0x3000, - trkSchismTracker = 0x4000, - trkOpenMPT = 0x5000, - trkBeRoTracker = 0x6000, - trkCreamTracker = 0x7000, + trkScreamTracker = 0x1000, + trkImagoOrpheus = 0x2000, + trkImpulseTracker = 0x3000, + trkSchismTracker = 0x4000, + trkOpenMPT = 0x5000, + trkBeRoTracker = 0x6000, + trkCreamTracker = 0x7000, - trkST3_20 = 0x1320, - trkIT2_07 = 0x3207, - trkIT2_14 = 0x3214, - trkBeRoTrackerOld = 0x4100, // Used from 2004 to 2012 - trkCamoto = 0xCA00, + trkST3_00 = 0x1300, + trkST3_20 = 0x1320, + trkIT2_07 = 0x3207, + trkIT2_14 = 0x3214, + trkBeRoTrackerOld = 0x4100, // Used from 2004 to 2012 + trkCamoto = 0xCA00, }; // Flags enum S3MHeaderFlags { - st2Vibrato = 0x01, // Vibrato is twice as deep. Cannot be enabled from UI. - zeroVolOptim = 0x08, // Volume 0 optimisations - amigaLimits = 0x10, // Enforce Amiga limits - fastVolumeSlides = 0x40, // Fast volume slides (like in ST3.00) + st2Vibrato = 0x01, // Vibrato is twice as deep. Cannot be enabled from UI. + zeroVolOptim = 0x08, // Volume 0 optimisations + amigaLimits = 0x10, // Enforce Amiga limits + fastVolumeSlides = 0x40, // Fast volume slides (like in ST3.00) }; // S3M Format Versions enum S3MFormatVersion { - oldVersion = 0x01, // Old Version, signed samples - newVersion = 0x02, // New Version, unsigned samples + oldVersion = 0x01, // Old Version, signed samples + newVersion = 0x02, // New Version, unsigned samples }; - char name[28]; // Song Title - uint8le dosEof; // Supposed to be 0x1A, but even ST3 seems to ignore this sometimes (see STRSHINE.S3M by Purple Motion) - uint8le fileType; // File Type, 0x10 = ST3 module - char reserved1[2]; // Reserved - uint16le ordNum; // Number of order items - uint16le smpNum; // Number of sample parapointers - uint16le patNum; // Number of pattern parapointers - uint16le flags; // Flags, see S3MHeaderFlags - uint16le cwtv; // "Made With" Tracker ID, see S3MTrackerVersions - uint16le formatVersion; // Format Version, see S3MFormatVersion - char magic[4]; // "SCRM" magic bytes - uint8le globalVol; // Default Global Volume (0...64) - uint8le speed; // Default Speed (1...254) - uint8le tempo; // Default Tempo (33...255) - uint8le masterVolume; // Sample Volume (0...127, stereo if high bit is set) - uint8le ultraClicks; // Number of channels used for ultra click removal - uint8le usePanningTable; // 0xFC => read extended panning table - uint16le reserved2; // Schism Tracker uses this for its extended version information - uint32le reserved3; // Impulse Tracker hides its edit timer here + char name[28]; // Song Title + uint8le dosEof; // Supposed to be 0x1A, but even ST3 seems to ignore this sometimes (see STRSHINE.S3M by Purple Motion) + uint8le fileType; // File Type, 0x10 = ST3 module + char reserved1[2]; // Reserved + uint16le ordNum; // Number of order items + uint16le smpNum; // Number of sample parapointers + uint16le patNum; // Number of pattern parapointers + uint16le flags; // Flags, see S3MHeaderFlags + uint16le cwtv; // "Made With" Tracker ID, see S3MTrackerVersions + uint16le formatVersion; // Format Version, see S3MFormatVersion + char magic[4]; // "SCRM" magic bytes + uint8le globalVol; // Default Global Volume (0...64) + uint8le speed; // Default Speed (1...254) + uint8le tempo; // Default Tempo (33...255) + uint8le masterVolume; // Sample Volume (0...127, stereo if high bit is set) + uint8le ultraClicks; // Number of channels used for ultra click removal + uint8le usePanningTable; // 0xFC => read extended panning table + uint16le reserved2; // Schism Tracker uses this for its extended version information + uint32le reserved3; // Impulse Tracker hides its edit timer here uint16le reserved4; - uint16le special; // Pointer to special custom data (unused) - uint8le channels[32]; // Channel setup + uint16le special; // Pointer to special custom data (unused) + uint8le channels[32]; // Channel setup }; MPT_BINARY_STRUCT(S3MFileHeader, 96) @@ -98,39 +99,39 @@ struct S3MSampleHeader { enum SampleType { - typeNone = 0, - typePCM = 1, - typeAdMel = 2, + typeNone = 0, + typePCM = 1, + typeAdMel = 2, }; enum SampleFlags { - smpLoop = 0x01, - smpStereo = 0x02, - smp16Bit = 0x04, + smpLoop = 0x01, + smpStereo = 0x02, + smp16Bit = 0x04, }; enum SamplePacking { - pUnpacked = 0x00, // PCM - pDP30ADPCM = 0x01, // Unused packing type - pADPCM = 0x04, // MODPlugin ADPCM :( + pUnpacked = 0x00, // PCM + pDP30ADPCM = 0x01, // Unused packing type + pADPCM = 0x04, // MODPlugin ADPCM :( }; - uint8le sampleType; // Sample type, see SampleType - char filename[12]; // Sample filename - uint8le dataPointer[3]; // Pointer to sample data (divided by 16) - uint32le length; // Sample length, in samples - uint32le loopStart; // Loop start, in samples - uint32le loopEnd; // Loop end, in samples - uint8le defaultVolume; // Default volume (0...64) - char reserved1; // Reserved - uint8le pack; // Packing algorithm, SamplePacking - uint8le flags; // Sample flags - uint32le c5speed; // Middle-C frequency - char reserved2[12]; // Reserved + Internal ST3 stuff - char name[28]; // Sample name - char magic[4]; // "SCRS" magic bytes ("SCRI" for Adlib instruments) + uint8le sampleType; // Sample type, see SampleType + char filename[12]; // Sample filename + uint8le dataPointer[3]; // Pointer to sample data (divided by 16) + uint32le length; // Sample length, in samples + uint32le loopStart; // Loop start, in samples + uint32le loopEnd; // Loop end, in samples + uint8le defaultVolume; // Default volume (0...64) + char reserved1; // Reserved + uint8le pack; // Packing algorithm, SamplePacking + uint8le flags; // Sample flags + uint32le c5speed; // Middle-C frequency + char reserved2[12]; // Reserved + Internal ST3 stuff + char name[28]; // Sample name + char magic[4]; // "SCRS" magic bytes ("SCRI" for Adlib instruments) // Convert an S3M sample header to OpenMPT's internal sample header. void ConvertToMPT(ModSample &mptSmp) const; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatBRR.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatBRR.cpp new file mode 100644 index 000000000..c7bc9fc91 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatBRR.cpp @@ -0,0 +1,134 @@ +/* + * SampleFormatBRR.cpp + * ------------------- + * Purpose: BRR (SNES Bit Rate Reduction) sample format import. + * Notes : This format has no magic bytes, so frame headers are thoroughly validated. + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "Sndfile.h" +#include "../common/FileReader.h" + + +OPENMPT_NAMESPACE_BEGIN + + +static void ProcessBRRSample(int32 sample, int16 *output, uint8 range, uint8 filter) +{ + if(sample >= 8) + sample -= 16; + + if(range <= 12) + sample = mpt::rshift_signed(mpt::lshift_signed(sample, range), 1); + else + sample = (sample < 0) ? -2048 : 0; // Implementations do not fully agree on what to do in this case. This is what bsnes does. + + // Apply prediction filter + // Note 1: It is okay that we may access data before the first sample point because this memory is reserved for interpolation + // Note 2: The use of signed shift arithmetic is crucial for some samples (e.g. killer lead.brr, Mac2.brr) + // Note 3: Divisors are twice of what is written in the respective comments, as all sample data is divided by 2 (again crucial for accuracy) + static_assert(InterpolationMaxLookahead >= 2); + switch(filter) + { + case 1: // y(n) = x(n) + x(n-1) * 15/16 + sample += mpt::rshift_signed(output[-1] * 15, 5); + break; + case 2: // y(n) = x(n) + x(n-1) * 61/32 - x(n-2) * 15/16 + sample += mpt::rshift_signed(output[-1] * 61, 6) + mpt::rshift_signed(output[-2] * -15, 5); + break; + case 3: // y(n) = x(n) + x(n-1) * 115/64 - x(n-2) * 13/16 + sample += mpt::rshift_signed(output[-1] * 115, 7) + mpt::rshift_signed(output[-2] * -13, 5); + break; + } + + sample = std::clamp(sample, int32(-32768), int32(32767)) * 2; + if(sample > 32767) + sample -= 65536; + else if(sample < -32768) + sample += 65536; + output[0] = static_cast(sample); +} + + +bool CSoundFile::ReadBRRSample(SAMPLEINDEX sample, FileReader &file) +{ + const auto fileSize = file.GetLength(); + if(fileSize < 9 || fileSize > uint16_max) + return false; + const bool hasLoopInfo = (fileSize % 9) == 2; + if((fileSize % 9) != 0 && !hasLoopInfo) + return false; + + file.Rewind(); + + SmpLength loopStart = 0; + if(hasLoopInfo) + { + loopStart = file.ReadUint16LE(); + if(loopStart >= fileSize) + return false; + if((loopStart % 9) != 0) + return false; + } + + // First scan the file for validity and consistency + // Note: There are some files with loop start set but ultimately the loop is never enabled. Cannot use this as a consistency check. + // Very few files also have a filter set on the first block, so we cannot reject those either. + bool enableLoop = false, first = true; + while(!file.EndOfFile()) + { + const auto block = file.ReadArray(); + const bool isLast = (block[0] & 0x01) != 0; + const bool isLoop = (block[0] & 0x02) != 0; + const uint8 range = block[0] >> 4u; + if(isLast != file.EndOfFile()) + return false; + if(!first && enableLoop != isLoop) + return false; + // While a range of 13 is technically invalid as well, it can be found in the wild. + if(range > 13) + return false; + enableLoop = isLoop; + first = false; + } + + file.Seek(hasLoopInfo ? 2 : 0); + + DestroySampleThreadsafe(sample); + ModSample &mptSmp = Samples[sample]; + mptSmp.Initialize(); + mptSmp.uFlags = CHN_16BIT; + mptSmp.nLength = mpt::saturate_cast((fileSize - (hasLoopInfo ? 2 : 0)) * 16 / 9); + if(enableLoop) + mptSmp.SetLoop(loopStart * 16 / 9, mptSmp.nLength, true, false, *this); + mptSmp.nC5Speed = 32000; + m_szNames[sample] = ""; + + if(!mptSmp.AllocateSample()) + return false; + + int16 *output = mptSmp.sample16(); + while(!file.EndOfFile()) + { + const auto block = file.ReadArray(); + const uint8 range = block[0] >> 4u; + const uint8 filter = (block[0] >> 2) & 0x03; + + for(int i = 0; i < 8; i++) + { + ProcessBRRSample(block[i + 1] >> 4u, output, range, filter); + ProcessBRRSample(block[i + 1] & 0x0F, output + 1, range, filter); + output += 2; + } + } + mptSmp.Convert(MOD_TYPE_IT, GetType()); + mptSmp.PrecomputeLoops(*this, false); + + return true; +} + + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatFLAC.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatFLAC.cpp index 63de71cd3..24e8c4c2e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatFLAC.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatFLAC.cpp @@ -28,12 +28,26 @@ //#include "../common/mptCRC.h" #include "OggStream.h" #ifdef MPT_WITH_OGG +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG #include +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG #endif // MPT_WITH_OGG #ifdef MPT_WITH_FLAC +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG #include #include #include +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG #endif // MPT_WITH_FLAC @@ -167,7 +181,7 @@ struct FLACDecoder { // Init sample information client.sndFile.DestroySampleThreadsafe(client.sample); - strcpy(client.sndFile.m_szNames[client.sample], ""); + client.sndFile.m_szNames[client.sample] = ""; sample.Initialize(); sample.uFlags.set(CHN_16BIT, metadata->data.stream_info.bits_per_sample > 8); sample.uFlags.set(CHN_STEREO, metadata->data.stream_info.channels > 1); @@ -195,9 +209,7 @@ struct FLACDecoder const FLAC__uint32 length = metadata->data.vorbis_comment.comments[i].length; if(length > 6 && !mpt::CompareNoCaseAscii(tag, "TITLE=", 6)) { - mpt::ustring sampleName; - mpt::String::Read(sampleName, mpt::CharsetUTF8, tag + 6, length - 6); - mpt::String::Copy(client.sndFile.m_szNames[client.sample], mpt::ToCharset(client.sndFile.GetCharsetInternal(), sampleName)); + client.sndFile.m_szNames[client.sample] = mpt::ToCharset(client.sndFile.GetCharsetInternal(), mpt::Charset::UTF8, mpt::String::ReadBuf(mpt::String::maybeNullTerminated, tag + 6, length - 6)); } else if(length > 11 && !mpt::CompareNoCaseAscii(tag, "SAMPLERATE=", 11)) { uint32 sampleRate = ConvertStrTo(tag + 11); @@ -246,7 +258,7 @@ bool CSoundFile::ReadFLACSample(SAMPLEINDEX sample, FileReader &file) file.Rewind(); bool oggOK = false; bool needMoreData = true; - static const long bufsize = 65536; + constexpr long bufsize = 65536; std::size_t readSize = 0; char *buf = nullptr; ogg_sync_state oy; @@ -539,13 +551,14 @@ public: bool CSoundFile::SaveFLACSample(SAMPLEINDEX nSample, std::ostream &f) const { #ifdef MPT_WITH_FLAC + const ModSample &sample = Samples[nSample]; + if(sample.uFlags[CHN_ADLIB]) + return false; + FLAC__StreamEncoder_RAII encoder(f); if(encoder == nullptr) - { return false; - } - const ModSample &sample = Samples[nSample]; uint32 sampleRate = sample.GetSampleRate(GetType()); // First off, set up all the metadata... @@ -562,9 +575,9 @@ bool CSoundFile::SaveFLACSample(SAMPLEINDEX nSample, std::ostream &f) const { // Store sample name FLAC__StreamMetadata_VorbisComment_Entry entry; - FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "TITLE", mpt::ToCharset(mpt::CharsetUTF8, GetCharsetInternal(), m_szNames[nSample]).c_str()); + FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "TITLE", mpt::ToCharset(mpt::Charset::UTF8, GetCharsetInternal(), m_szNames[nSample]).c_str()); FLAC__metadata_object_vorbiscomment_append_comment(metadata[0], entry, false); - FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "ENCODER", mpt::ToCharset(mpt::CharsetUTF8, Version::Current().GetOpenMPTVersionString()).c_str()); + FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "ENCODER", mpt::ToCharset(mpt::Charset::UTF8, Version::Current().GetOpenMPTVersionString()).c_str()); FLAC__metadata_object_vorbiscomment_append_comment(metadata[0], entry, false); if(sampleRate > FLAC__MAX_SAMPLE_RATE) { @@ -679,7 +692,7 @@ bool CSoundFile::SaveFLACSample(SAMPLEINDEX nSample, std::ostream &f) const FLAC__int32 buffer[mpt::IO::BUFFERSIZE_TINY]; while(framesRemain && success) { - const SmpLength copyFrames = std::min(framesRemain, mpt::saturate_cast(mpt::size(buffer) / numChannels)); + const SmpLength copyFrames = std::min(framesRemain, mpt::saturate_cast(std::size(buffer) / numChannels)); // First, convert to a 32-bit integer buffer switch(sample.GetElementarySampleSize()) @@ -690,7 +703,7 @@ bool CSoundFile::SaveFLACSample(SAMPLEINDEX nSample, std::ostream &f) const } // Now do the actual encoding - success = FLAC__stream_encoder_process_interleaved(encoder, buffer, copyFrames) != false; + success = FLAC__stream_encoder_process_interleaved(encoder, buffer, copyFrames) != static_cast(false); framesRead += copyFrames; framesRemain -= copyFrames; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMP3.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMP3.cpp index f43743d8e..b33e3f8e6 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMP3.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMP3.cpp @@ -56,11 +56,7 @@ typedef size_t mpg123_size_t; typedef ssize_t mpg123_ssize_t; class ComponentMPG123 -#if defined(MPT_ENABLE_MPG123_DELAYLOAD) - : public ComponentBundledDLL -#else : public ComponentBuiltin -#endif { MPT_DECLARE_COMPONENT_MEMBERS @@ -90,22 +86,12 @@ public: public: ComponentMPG123() -#if defined(MPT_ENABLE_MPG123_DELAYLOAD) - : ComponentBundledDLL(P_("openmpt-mpg123")) -#else : ComponentBuiltin() -#endif { return; } bool DoInitialize() override { -#if defined(MPT_ENABLE_MPG123_DELAYLOAD) - if(!ComponentBundledDLL::DoInitialize()) - { - return false; - } -#endif if(mpg123_init() != 0) { return false; @@ -133,7 +119,7 @@ static mpt::ustring ReadMPG123String(const mpg123_string &str) { return result; } - result = mpt::ToUnicode(mpt::CharsetUTF8, std::string(str.p, str.p + str.fill - 1)); + result = mpt::ToUnicode(mpt::Charset::UTF8, std::string(str.p, str.p + str.fill - 1)); return result; } @@ -151,7 +137,7 @@ static mpt::ustring ReadMPG123String(const mpg123_string *str) template static mpt::ustring ReadMPG123String(const char (&str)[N]) { - return mpt::ToUnicode(mpt::CharsetISO8859_1, mpt::String::ReadBuf(mpt::String::spacePadded, str)); + return mpt::ToUnicode(mpt::Charset::ISO8859_1, mpt::String::ReadBuf(mpt::String::spacePadded, str)); } #endif // MPT_WITH_MPG123 @@ -500,7 +486,7 @@ bool CSoundFile::ReadMP3Sample(SAMPLEINDEX sample, FileReader &file, bool raw, b } } - std::vector buf_bytes; + std::vector buf_bytes; std::vector buf_samples; bool decode_error = false; bool decode_done = false; @@ -564,7 +550,7 @@ bool CSoundFile::ReadMP3Sample(SAMPLEINDEX sample, FileReader &file, bool raw, b DestroySampleThreadsafe(sample); if(!mo3Decode) { - mpt::String::Copy(m_szNames[sample], mpt::ToCharset(GetCharsetInternal(), sampleName)); + m_szNames[sample] = mpt::ToCharset(GetCharsetInternal(), sampleName); Samples[sample].Initialize(); Samples[sample].nC5Speed = rate; } @@ -623,8 +609,8 @@ bool CSoundFile::ReadMP3Sample(SAMPLEINDEX sample, FileReader &file, bool raw, b channels = info.channels; if(rate <= 0) break; // broken stream if(channels != 1 && channels != 2) break; // broken stream - stream_pos += mpt::clamp(info.frame_bytes, 0, mpt::saturate_cast(bytes_left)); - bytes_left -= mpt::clamp(info.frame_bytes, 0, mpt::saturate_cast(bytes_left)); + stream_pos += std::clamp(info.frame_bytes, 0, mpt::saturate_cast(bytes_left)); + bytes_left -= std::clamp(info.frame_bytes, 0, mpt::saturate_cast(bytes_left)); if(frame_samples > 0) { try @@ -656,7 +642,7 @@ bool CSoundFile::ReadMP3Sample(SAMPLEINDEX sample, FileReader &file, bool raw, b DestroySampleThreadsafe(sample); if(!mo3Decode) { - strcpy(m_szNames[sample], ""); + m_szNames[sample] = ""; Samples[sample].Initialize(); Samples[sample].nC5Speed = rate; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMediaFoundation.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMediaFoundation.cpp index b40a13248..8cc9c3d2a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMediaFoundation.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatMediaFoundation.cpp @@ -263,7 +263,7 @@ std::vector CSoundFile::GetMediaFoundationFileTypes() guidMap[guid].AddExtension(mpt::PathString::FromWide(handlerType.substr(1))); } else { - guidMap[guid].AddMimeType(mpt::ToCharset(mpt::CharsetASCII, handlerType)); + guidMap[guid].AddMimeType(mpt::ToCharset(mpt::Charset::ASCII, handlerType)); } } @@ -408,7 +408,7 @@ bool CSoundFile::ReadMediaFoundationSample(SAMPLEINDEX sample, FileReader &file, DestroySampleThreadsafe(sample); if(!mo3Decode) { - mpt::String::Copy(m_szNames[sample], sampleName); + m_szNames[sample] = sampleName; Samples[sample].Initialize(); Samples[sample].nC5Speed = samplesPerSecond; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatOpus.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatOpus.cpp index 83c97a73a..9918a64c6 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatOpus.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatOpus.cpp @@ -24,11 +24,18 @@ //#include "../common/mptCRC.h" #include "OggStream.h" #ifdef MPT_WITH_OGG +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG #include +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG #endif // MPT_WITH_OGG #if defined(MPT_WITH_OPUSFILE) #include -#endif +#endif // MPT_WITH_OPUSFILE OPENMPT_NAMESPACE_BEGIN @@ -41,7 +48,7 @@ OPENMPT_NAMESPACE_BEGIN static mpt::ustring UStringFromOpus(const char *str) { - return str ? mpt::ToUnicode(mpt::CharsetUTF8, str) : mpt::ustring(); + return str ? mpt::ToUnicode(mpt::Charset::UTF8, str) : mpt::ustring(); } static FileTags GetOpusFileTags(OggOpusFile *of) @@ -156,7 +163,7 @@ bool CSoundFile::ReadOpusSample(SAMPLEINDEX sample, FileReader &file) } DestroySampleThreadsafe(sample); - mpt::String::Copy(m_szNames[sample], sampleName); + m_szNames[sample] = sampleName; Samples[sample].Initialize(); Samples[sample].nC5Speed = rate; Samples[sample].nLength = mpt::saturate_cast(raw_sample_data.size() / channels); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatSFZ.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatSFZ.cpp new file mode 100644 index 000000000..b0f870129 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatSFZ.cpp @@ -0,0 +1,1252 @@ +/* + * SampleFormatSFZ.cpp + * ------------------- + * Purpose: Loading and saving SFZ instruments. + * 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 "Sndfile.h" +#ifdef MODPLUG_TRACKER +#include "../mptrack/TrackerSettings.h" +#endif // MODPLUG_TRACKER +#ifndef MODPLUG_NO_FILESAVE +#include "../common/mptFileIO.h" +#endif // !MODPLUG_NO_FILESAVE +#include "modsmp_ctrl.h" + +#include + +OPENMPT_NAMESPACE_BEGIN + +#ifdef MPT_EXTERNAL_SAMPLES + +template +static bool SFZStartsWith(const std::string_view &l, const char(&r)[N]) +{ + return l.substr(0, N - 1) == r; +} + +static bool SFZIsNumeric(const std::string_view &str) +{ + return std::find_if(str.begin(), str.end(), [](char c) { return c < '0' || c > '9'; }) == str.end(); +} + +struct SFZControl +{ + std::string defaultPath; + int8 octaveOffset = 0, noteOffset = 0; + + void Parse(const std::string_view key, const std::string &value) + { + if(key == "default_path") + defaultPath = value; + else if(key == "octave_offset") + octaveOffset = ConvertStrTo(value); + else if(key == "note_offset") + noteOffset = ConvertStrTo(value); + } +}; + +struct SFZFlexEG +{ + using PointIndex = decltype(InstrumentEnvelope().nLoopStart); + + std::vector> points; + double amplitude = 0; // percentage (100 = full volume range) + double pan = 0; // percentage (100 = full pan range) + double pitch = 0; // in cents + double cutoff = 0; // in cents + PointIndex sustain = 0; + + void Parse(std::string_view key, const std::string &value) + { + key = key.substr(key.find('_') + 1); + const double v = ConvertStrTo(value); + + const bool isTime = SFZStartsWith(key, "time"), isLevel = SFZStartsWith(key, "level"); + std::string_view pointStr; + if(isTime) + pointStr = key.substr(4); + else if(isLevel) + pointStr = key.substr(5); + + if(!pointStr.empty() && SFZIsNumeric(pointStr)) + { + PointIndex point = ConvertStrTo(std::string(pointStr)); + if(point >= points.size() && point < MAX_ENVPOINTS) + points.resize(point + 1); + + if(point < points.size()) + { + if(isTime) + points[point].first = v; + else + points[point].second = v; + } + return; + } + + if(key == "points") + points.resize(std::min(static_cast(v), static_cast(MAX_ENVPOINTS))); + else if(key == "sustain") + sustain = mpt::saturate_round(v); + else if(key == "amplitude" || key == "ampeg") + amplitude = v; + else if(key == "pan") + pan = v; + else if(key == "pitch") + pitch = v; + else if(key == "cutoff") + cutoff = v; + } + + void ConvertToMPT(ModInstrument *ins, const CSoundFile &sndFile) const + { + if(amplitude) + ConvertToMPT(ins, sndFile, ENV_VOLUME, amplitude / 100.0, 0.0, 1.0); + if(pan) + ConvertToMPT(ins, sndFile, ENV_PANNING, pan / 100.0, -1.0, 1.0); + if(pitch) + ConvertToMPT(ins, sndFile, ENV_PITCH, pitch / 1600.0, -1.0, 1.0); + if(cutoff) + ConvertToMPT(ins, sndFile, ENV_PITCH, cutoff, 0.0, 1.0, true); + } + + void ConvertToMPT(ModInstrument *ins, const CSoundFile &sndFile, EnvelopeType envType, double scale, double minVal, double maxVal, bool forceFilter = false) const + { + const double tickDuration = sndFile.m_PlayState.m_nSamplesPerTick / static_cast(sndFile.GetSampleRate()); + if(tickDuration <= 0 || points.empty() || scale == 0.0) + return; + + auto &env = ins->GetEnvelope(envType); + std::function conversionFunc = Identity; + if(forceFilter && envType == ENV_PITCH) + { + env.dwFlags.set(ENV_FILTER); + conversionFunc = FilterConversionFunc(*ins, sndFile); + } + + env.clear(); + env.reserve(points.size()); + + const auto ToValue = std::bind(SFZFlexEG::ToValue, std::placeholders::_1, scale, minVal, maxVal, conversionFunc); + + int32 prevTick = -1; + // If the first envelope point's time is greater than 0, we fade in from a neutral value + if(points.front().first > 0) + { + env.push_back({0, ToValue(0.0)}); + prevTick = 0; + } + + for(const auto &point : points) + { + const auto tick = mpt::saturate_cast(prevTick + ToTicks(point.first, tickDuration)); + const auto value = ToValue(point.second); + env.push_back({tick, value}); + prevTick = tick; + if(tick == Util::MaxValueOfType(tick)) + break; + } + + if(sustain < env.size()) + { + env.nSustainStart = env.nSustainEnd = sustain; + env.dwFlags.set(ENV_SUSTAIN); + } else + { + env.dwFlags.reset(ENV_SUSTAIN); + } + env.dwFlags.set(ENV_ENABLED); + + if(envType == ENV_VOLUME && env.nSustainEnd > 0) + env.nReleaseNode = env.nSustainEnd; + } + +protected: + static EnvelopeNode::tick_t ToTicks(double duration, double tickDuration) + { + return std::max(EnvelopeNode::tick_t(1), mpt::saturate_round(duration / tickDuration)); + } + + static EnvelopeNode::value_t ToValue(double value, double scale, double minVal, double maxVal, const std::function &conversionFunc) + { + value = conversionFunc((value * scale - minVal) / (maxVal - minVal)) * ENVELOPE_MAX + ENVELOPE_MIN; + Limit(value, ENVELOPE_MIN, ENVELOPE_MAX); + return mpt::saturate_round(value); + } + + static double Identity(double v) noexcept { return v; } + + static double CentsToFilterCutoff(double v, const CSoundFile &sndFile, int envBaseCutoff, uint32 envBaseFreq) + { + const auto freq = envBaseFreq * std::pow(2.0, v / 1200.0); + return Util::muldivr(sndFile.FrequencyToCutOff(freq), 127, envBaseCutoff) / 127.0; + } + + static std::function FilterConversionFunc(const ModInstrument &ins, const CSoundFile &sndFile) + { + const auto envBaseCutoff = ins.IsCutoffEnabled() ? ins.GetCutoff() : 127; + const auto envBaseFreq = sndFile.CutOffToFrequency(envBaseCutoff); + return std::bind(CentsToFilterCutoff, std::placeholders::_1, std::cref(sndFile), envBaseCutoff, envBaseFreq); + } +}; + +struct SFZEnvelope +{ + double startLevel = 0, delay = 0, attack = 0, hold = 0; + double decay = 0, sustainLevel = 100, release = 0, depth = 0; + + void Parse(std::string_view key, const std::string &value) + { + key = key.substr(key.find('_') + 1); + double v = ConvertStrTo(value); + if(key == "depth") + Limit(v, -12000.0, 12000.0); + else if(key == "start" || key == "sustain") + Limit(v, -100.0, 100.0); + else + Limit(v, 0.0, 100.0); + + if(key == "start") + startLevel = v; + else if(key == "delay") + delay = v; + else if(key == "attack") + attack = v; + else if(key == "hold") + hold = v; + else if(key == "decay") + decay = v; + else if(key == "sustain") + sustainLevel = v; + else if(key == "release") + release = v; + else if(key == "depth") + depth = v; + } + + void ConvertToMPT(ModInstrument *ins, const CSoundFile &sndFile, EnvelopeType envType, bool forceFilter = false) const + { + SFZFlexEG eg; + if(envType == ENV_VOLUME) + eg.amplitude = 1.0; + else if(envType == ENV_PITCH && !forceFilter) + eg.pitch = depth / 100.0; + else if(envType == ENV_PITCH && forceFilter) + eg.cutoff = depth / 100.0; + + auto &env = eg.points; + if(attack > 0 || delay > 0) + { + env.push_back({0, startLevel}); + if(delay > 0) + env.push_back({delay, env.back().second}); + env.push_back({attack, 100.0}); + } + if(hold > 0) + { + if(env.empty()) + env.push_back({0, 100.0}); + env.push_back({hold, env.back().second}); + } + if(env.empty()) + env.push_back({0, 100.0}); + if(env.back().second != sustainLevel) + env.push_back({decay, sustainLevel}); + if(sustainLevel != 0) + { + eg.sustain = static_cast(env.size() - 1); + env.push_back({release, 0.0}); + } else + { + eg.sustain = std::numeric_limits::max(); + } + + eg.ConvertToMPT(ins, sndFile); + } +}; + + +struct SFZRegion +{ + enum class LoopMode + { + kUnspecified, + kContinuous, + kOneShot, + kSustain, + kNoLoop + }; + + enum class LoopType + { + kUnspecified, + kForward, + kBackward, + kAlternate, + }; + + size_t filenameOffset = 0; + std::string filename, name; + SFZEnvelope ampEnv, pitchEnv, filterEnv; + std::vector flexEGs; + SmpLength loopStart = 0, loopEnd = 0; + SmpLength end = MAX_SAMPLE_LENGTH, offset = 0; + LoopMode loopMode = LoopMode::kUnspecified; + LoopType loopType = LoopType::kUnspecified; + double loopCrossfade = 0.0; + double cutoff = 0; // in Hz + double resonance = 0; // 0...40dB + double filterRandom = 0; // 0...9600 cents + double volume = 0; // -144dB...+6dB + double amplitude = 100.0; // 0...100 + double pitchBend = 200; // -9600...9600 cents + double pitchLfoFade = 0; // 0...100 seconds + double pitchLfoDepth = 0; // -1200...12000 + double pitchLfoFreq = 0; // 0...20 Hz + double panning = -128; // -100...+100 + double finetune = 0; // in cents + int8 transpose = 0; + uint8 keyLo = 0, keyHi = 127, keyRoot = 60; + FilterMode filterType = FilterMode::Unchanged; + uint8 polyphony = 255; + bool useSampleKeyRoot = false; + bool invertPhase = false; + + template + static void Read(const std::string &valueStr, T &value, Tc valueMin = std::numeric_limits::min(), Tc valueMax = std::numeric_limits::max()) + { + double valueF = ConvertStrTo(valueStr); + if constexpr(std::numeric_limits::is_integer) + { + valueF = mpt::round(valueF); + } + Limit(valueF, static_cast(valueMin), static_cast(valueMax)); + value = static_cast(valueF); + } + + static uint8 ReadKey(const std::string &value, const SFZControl &control) + { + if(value.empty()) + return 0; + + int key = 0; + if(value[0] >= '0' && value[0] <= '9') + { + // MIDI key + key = ConvertStrTo(value); + } else if(value.length() < 2) + { + return 0; + } else + { + // Scientific pitch + static constexpr int8 keys[] = { 9, 11, 0, 2, 4, 5, 7 }; + static_assert(std::size(keys) == 'g' - 'a' + 1); + auto keyC = value[0]; + if(keyC >= 'A' && keyC <= 'G') + key = keys[keyC - 'A']; + if(keyC >= 'a' && keyC <= 'g') + key = keys[keyC - 'a']; + else + return 0; + + uint8 octaveOffset = 1; + if(value[1] == '#') + { + key++; + octaveOffset = 2; + } else if(value[1] == 'b' || value[1] == 'B') + { + key--; + octaveOffset = 2; + } + if(octaveOffset >= value.length()) + return 0; + + int8 octave = ConvertStrTo(value.c_str() + octaveOffset); + key += (octave + 1) * 12; + } + key += control.octaveOffset * 12 + control.noteOffset; + return static_cast(Clamp(key, 0, 127)); + } + + void Parse(const std::string_view key, const std::string &value, const SFZControl &control) + { + if(key == "sample") + { + filename = control.defaultPath + value; + filenameOffset = control.defaultPath.size(); + } + else if(key == "region_label") + name = value; + else if(key == "lokey") + keyLo = ReadKey(value, control); + else if(key == "hikey") + keyHi = ReadKey(value, control); + else if(key == "pitch_keycenter") + { + keyRoot = ReadKey(value, control); + useSampleKeyRoot = (value == "sample"); + } + else if(key == "key") + { + keyLo = keyHi = keyRoot = ReadKey(value, control); + useSampleKeyRoot = false; + } + else if(key == "bend_up" || key == "bendup") + Read(value, pitchBend, -9600.0, 9600.0); + else if(key == "pitchlfo_fade") + Read(value, pitchLfoFade, 0.0, 100.0); + else if(key == "pitchlfo_depth") + Read(value, pitchLfoDepth, -12000.0, 12000.0); + else if(key == "pitchlfo_freq") + Read(value, pitchLfoFreq, 0.0, 20.0); + else if(key == "volume") + Read(value, volume, -144.0, 6.0); + else if(key == "amplitude") + Read(value, amplitude, 0.0, 100.0); + else if(key == "pan") + Read(value, panning, -100.0, 100.0); + else if(key == "transpose") + Read(value, transpose, -127, 127); + else if(key == "tune") + Read(value, finetune, -100.0, 100.0); + else if(key == "end") + Read(value, end, SmpLength(0), MAX_SAMPLE_LENGTH); + else if(key == "offset") + Read(value, offset, SmpLength(0), MAX_SAMPLE_LENGTH); + else if(key == "loop_start" || key == "loopstart") + Read(value, loopStart, SmpLength(0), MAX_SAMPLE_LENGTH); + else if(key == "loop_end" || key == "loopend") + Read(value, loopEnd, SmpLength(0), MAX_SAMPLE_LENGTH); + else if(key == "loop_crossfade") + Read(value, loopCrossfade, 0.0, DBL_MAX); + else if(key == "loop_mode" || key == "loopmode") + { + if(value == "loop_continuous") + loopMode = LoopMode::kContinuous; + else if(value == "one_shot") + loopMode = LoopMode::kOneShot; + else if(value == "loop_sustain") + loopMode = LoopMode::kSustain; + else if(value == "no_loop") + loopMode = LoopMode::kNoLoop; + } + else if(key == "loop_type" || key == "looptype") + { + if(value == "forward") + loopType = LoopType::kForward; + else if(value == "backward") + loopType = LoopType::kBackward; + else if(value == "alternate") + loopType = LoopType::kAlternate; + } + else if(key == "cutoff") + Read(value, cutoff, 0.0, 96000.0); + else if(key == "fil_random") + Read(value, filterRandom, 0.0, 9600.0); + else if(key == "resonance") + Read(value, resonance, 0.0, 40.0); + else if(key == "polyphony") + Read(value, polyphony, 0, 255); + else if(key == "phase") + invertPhase = (value == "invert"); + else if(key == "fil_type" || key == "filtype") + { + if(value == "lpf_1p" || value == "lpf_2p" || value == "lpf_4p" || value == "lpf_6p") + filterType = FilterMode::LowPass; + else if(value == "hpf_1p" || value == "hpf_2p" || value == "hpf_4p" || value == "hpf_6p") + filterType = FilterMode::HighPass; + // Alternatives: bpf_2p, brf_2p + } + else if(SFZStartsWith(key, "ampeg_")) + ampEnv.Parse(key, value); + else if(SFZStartsWith(key, "fileg_")) + filterEnv.Parse(key, value); + else if(SFZStartsWith(key, "pitcheg_")) + pitchEnv.Parse(key, value); + else if(SFZStartsWith(key, "eg") && SFZIsNumeric(key.substr(2, 2)) && key.substr(4, 1) == "_") + { + uint8 eg = ConvertStrTo(std::string(key.substr(2, 2))); + if(eg >= flexEGs.size()) + flexEGs.resize(eg + 1); + flexEGs[eg].Parse(key, value); + } + } +}; + +struct SFZInputFile +{ + FileReader file; + std::unique_ptr inputFile; // FileReader has pointers into this so its address must not change + std::string remain; + + SFZInputFile(FileReader f = {}, std::unique_ptr i = {}, std::string r = {}) + : file{std::move(f)}, inputFile{std::move(i)}, remain{std::move(r)} {} + SFZInputFile(SFZInputFile &&) = default; +}; + +bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) +{ + file.Rewind(); + + enum { kNone, kGlobal, kMaster, kGroup, kRegion, kControl, kCurve, kEffect, kUnknown } section = kNone; + bool inMultiLineComment = false; + SFZControl control; + SFZRegion group, master, globals; + std::vector regions; + std::map macros; + std::vector files; + files.emplace_back(file); + + std::string s; + while(!files.empty()) + { + if(!files.back().file.ReadLine(s, 1024)) + { + // Finished reading file, so back to remaining characters of the #include line from the previous file + s = std::move(files.back().remain); + files.pop_back(); + } + + if(inMultiLineComment) + { + if(auto commentEnd = s.find("*/"); commentEnd != std::string::npos) + { + s.erase(0, commentEnd + 2); + inMultiLineComment = false; + } else + { + continue; + } + } + + // First, terminate line at the start of a comment block + if(auto commentPos = s.find("//"); commentPos != std::string::npos) + { + s.resize(commentPos); + } + + // Now, read the tokens. + // This format is so funky that no general tokenizer approach seems to work here... + // Consider this jolly good example found at https://stackoverflow.com/questions/5923895/tokenizing-a-custom-text-file-format-file-using-c-sharp + // sample=piano C3.wav key=48 ampeg_release=0.7 // a comment here + // key = 49 sample = piano Db3.wav + // + // group=1 + // key = 48 + // sample = piano D3.ogg + // The original sfz specification claims that spaces around = are not allowed, but a quick look into the real world tells us otherwise. + + while(!s.empty()) + { + s.erase(0, s.find_first_not_of(" \t")); + + const bool isDefine = SFZStartsWith(s, "#define ") || SFZStartsWith(s, "#define\t"); + + // Replace macros (unless this is a #define statement, to allow for macro re-definition) + if(!isDefine) + { + for(const auto &[oldStr, newStr] : macros) + { + std::string::size_type pos = 0; + while((pos = s.find(oldStr, pos)) != std::string::npos) + { + s.replace(pos, oldStr.length(), newStr); + pos += newStr.length(); + } + } + } + + if(s.empty()) + break; + + std::string::size_type charsRead = 0; + + if(s[0] == '<' && (charsRead = s.find('>')) != std::string::npos) + { + // Section header + const auto sec = std::string_view(s).substr(1, charsRead - 1); + section = kUnknown; + if(sec == "global") + { + section = kGlobal; + // Reset global parameters + globals = SFZRegion(); + } else if(sec == "master") + { + section = kMaster; + // Reset master parameters + master = globals; + } else if(sec == "group") + { + section = kGroup; + // Reset group parameters + group = master; + } else if(sec == "region") + { + section = kRegion; + regions.push_back(group); + } else if(sec == "control") + { + section = kControl; + } else if(sec == "curve") + { + section = kCurve; + } else if(sec == "effect") + { + section = kEffect; + } + charsRead++; + } else if(isDefine) + { + // Macro definition + charsRead += 8; + auto keyStart = s.find_first_not_of(" \t", 8); + auto keyEnd = s.find_first_of(" \t", keyStart); + auto valueStart = s.find_first_not_of(" \t", keyEnd); + if(keyStart != std::string::npos && valueStart != std::string::npos) + { + charsRead = s.find_first_of(" \t", valueStart); + const auto key = s.substr(keyStart, keyEnd - keyStart); + if(key.length() > 1 && key[0] == '$') + macros[std::move(key)] = s.substr(valueStart, charsRead - valueStart); + } else + { + break; + } + } else if(SFZStartsWith(s, "#include ") || SFZStartsWith(s, "#include\t")) + { + // Include other sfz file + auto fileStart = s.find("\"", 9); // Yes, there can be arbitrary characters before the opening quote, at least that's how sforzando does it. + auto fileEnd = s.find("\"", fileStart + 1); + if(fileStart != std::string::npos && fileEnd != std::string::npos) + { + charsRead = fileEnd + 1; + fileStart++; + } else + { + break; + } + + std::string filenameU8 = s.substr(fileStart, fileEnd - fileStart); + mpt::PathString filename = mpt::PathString::FromUTF8(filenameU8); + if(!filename.empty()) + { + if(filenameU8.find(':') == std::string::npos) + filename = file.GetFileName().GetPath() + filename; + filename = filename.Simplify(); + // Avoid recursive #include + if(std::find_if(files.begin(), files.end(), [&filename](const SFZInputFile &f) { return f.file.GetFileName() == filename; }) == files.end()) + { + auto f = std::make_unique(filename); + if(f->IsValid()) + { + s.erase(0, charsRead); + files.emplace_back(GetFileReader(*f), std::move(f), std::move(s)); + break; + } else + { + AddToLog(LogWarning, U_("Unable to load include file: ") + filename.ToUnicode()); + } + } else + { + AddToLog(LogWarning, U_("Recursive include file ignored: ") + filename.ToUnicode()); + } + } + } else if(SFZStartsWith(s, "/*")) + { + // Multi-line comment + if(auto commentEnd = s.find("*/", charsRead + 2); commentEnd != std::string::npos) + { + charsRead = commentEnd; + } else + { + inMultiLineComment = true; + charsRead = s.length(); + } + } else if(section == kNone) + { + // Garbage before any section, probably not an sfz file + return false; + } else if(s.find('=') != std::string::npos) + { + // Read key=value pair + auto keyEnd = s.find_first_of(" \t="); + auto valueStart = s.find_first_not_of(" \t=", keyEnd); + if(valueStart == std::string::npos) + { + break; + } + const std::string key = mpt::ToLowerCaseAscii(s.substr(0, keyEnd)); + if(key == "sample" || key == "default_path" || SFZStartsWith(key, "label_cc") || SFZStartsWith(key, "region_label")) + { + // Sample / CC name may contain spaces... + charsRead = s.find_first_of("=\t<", valueStart); + if(charsRead != std::string::npos && s[charsRead] == '=') + { + // Backtrack to end of key + while(charsRead > valueStart && s[charsRead] == ' ') + charsRead--; + // Backtrack to start of key + while(charsRead > valueStart && s[charsRead] != ' ') + charsRead--; + } + } else + { + charsRead = s.find_first_of(" \t<", valueStart); + } + const std::string value = s.substr(valueStart, charsRead - valueStart); + + switch(section) + { + case kGlobal: + globals.Parse(key, value, control); + [[fallthrough]]; + case kMaster: + master.Parse(key, value, control); + [[fallthrough]]; + case kGroup: + group.Parse(key, value, control); + break; + case kRegion: + regions.back().Parse(key, value, control); + break; + case kControl: + control.Parse(key, value); + break; + } + } else + { + // Garbage, probably not an sfz file + return false; + } + + // Remove the token(s) we just read + s.erase(0, charsRead); + } + } + + if(regions.empty()) + return false; + + + ModInstrument *pIns = new (std::nothrow) ModInstrument(); + if(pIns == nullptr) + return false; + + RecalculateSamplesPerTick(); + DestroyInstrument(nInstr, deleteAssociatedSamples); + if(nInstr > m_nInstruments) m_nInstruments = nInstr; + Instruments[nInstr] = pIns; + + SAMPLEINDEX prevSmp = 0; + for(auto ®ion : regions) + { + uint8 keyLo = region.keyLo, keyHi = region.keyHi; + if(keyLo > keyHi) + continue; + Clamp(keyLo, 0, NOTE_MAX - NOTE_MIN); + Clamp(keyHi, 0, NOTE_MAX - NOTE_MIN); + SAMPLEINDEX smp = GetNextFreeSample(nInstr, prevSmp + 1); + if(smp == SAMPLEINDEX_INVALID) + break; + prevSmp = smp; + + ModSample &sample = Samples[smp]; + sample.Initialize(MOD_TYPE_MPT); + if(const auto synthSample = std::string_view(region.filename).substr(region.filenameOffset); SFZStartsWith(synthSample, "*")) + { + sample.nLength = 256; + sample.nC5Speed = mpt::saturate_round(sample.nLength * 261.6255653); + sample.uFlags.set(CHN_16BIT); + std::function generator; + if(synthSample == "*sine") + generator = [](int32 i) { return mpt::saturate_round(std::sin(i * 2.0 * M_PI / 256.0) * int16_max); }; + else if(synthSample == "*square") + generator = [](int32 i) { return i < 128 ? int16_max : int16_min; }; + else if(synthSample == "*triangle" || synthSample == "*tri") + generator = [](int32 i) { return static_cast(i < 128 ? ((63 - i) * 512) : ((i - 192) * 512)); }; + else if(synthSample == "*saw") + generator = [](int32 i) { return static_cast((i - 128) * 256); }; + else if(synthSample == "*silence") + generator = [](int32) { return int16(0); }; + else if(synthSample == "*noise") + { + sample.nLength = sample.nC5Speed; + generator = [this](int32) { return mpt::random(AccessPRNG()); }; + } else + { + AddToLog(LogWarning, U_("Unknown sample type: ") + mpt::ToUnicode(mpt::Charset::UTF8, std::string(synthSample))); + prevSmp--; + continue; + } + if(sample.AllocateSample()) + { + for(SmpLength i = 0; i < sample.nLength; i++) + { + sample.sample16()[i] = generator(static_cast(i)); + } + if(smp > m_nSamples) + m_nSamples = smp; + region.offset = 0; + region.loopMode = SFZRegion::LoopMode::kContinuous; + region.loopStart = 0; + region.loopEnd = sample.nLength - 1; + region.loopCrossfade = 0; + region.keyRoot = 60; + } + } else if(auto filename = mpt::PathString::FromUTF8(region.filename); !filename.empty()) + { + if(region.filename.find(':') == std::string::npos) + { + filename = file.GetFileName().GetPath() + filename; + } + SetSamplePath(smp, filename); + InputFile f(filename, SettingCacheCompleteFileBeforeLoading()); + FileReader smpFile = GetFileReader(f); + if(!ReadSampleFromFile(smp, smpFile, false)) + { + AddToLog(LogWarning, U_("Unable to load sample: ") + filename.ToUnicode()); + prevSmp--; + continue; + } + + if(UseFinetuneAndTranspose()) + sample.TransposeToFrequency(); + + sample.uFlags.set(SMP_KEEPONDISK, sample.HasSampleData()); + } + + if(!region.name.empty()) + m_szNames[smp] = mpt::ToCharset(GetCharsetInternal(), mpt::Charset::UTF8, region.name); + if(!m_szNames[smp][0]) + m_szNames[smp] = mpt::ToCharset(GetCharsetInternal(), mpt::PathString::FromUTF8(region.filename).GetFileName().ToUnicode()); + + if(region.useSampleKeyRoot) + { + if(sample.rootNote != NOTE_NONE) + region.keyRoot = sample.rootNote - NOTE_MIN; + else + region.keyRoot = 60; + } + + const auto origSampleRate = sample.GetSampleRate(GetType()); + int8 transp = region.transpose + (60 - region.keyRoot); + for(uint8 i = keyLo; i <= keyHi; i++) + { + pIns->Keyboard[i] = smp; + if(GetType() != MOD_TYPE_XM) + pIns->NoteMap[i] = NOTE_MIN + i + transp; + } + if(GetType() == MOD_TYPE_XM) + sample.Transpose(transp / 12.0); + + pIns->filterMode = region.filterType; + if(region.cutoff != 0) + pIns->SetCutoff(FrequencyToCutOff(region.cutoff), true); + if(region.resonance != 0) + pIns->SetResonance(mpt::saturate_round(region.resonance * 128.0 / 24.0), true); + pIns->nCutSwing = mpt::saturate_round(region.filterRandom * (m_SongFlags[SONG_EXFILTERRANGE] ? 20 : 24) / 1200.0); + pIns->midiPWD = mpt::saturate_round(region.pitchBend / 100.0); + + pIns->nNNA = NNA_NOTEOFF; + if(region.polyphony == 1) + { + pIns->nDNA = DNA_NOTECUT; + pIns->nDCT = DCT_SAMPLE; + } + region.ampEnv.ConvertToMPT(pIns, *this, ENV_VOLUME); + if(region.pitchEnv.depth) + region.pitchEnv.ConvertToMPT(pIns, *this, ENV_PITCH); + else if(region.filterEnv.depth) + region.filterEnv.ConvertToMPT(pIns, *this, ENV_PITCH, true); + + for(const auto &flexEG : region.flexEGs) + { + flexEG.ConvertToMPT(pIns, *this); + } + + if(region.ampEnv.release > 0) + { + const double tickDuration = m_PlayState.m_nSamplesPerTick / static_cast(GetSampleRate()); + pIns->nFadeOut = std::min(mpt::saturate_cast(32768.0 * tickDuration / region.ampEnv.release), uint32(32767)); + if(GetType() == MOD_TYPE_IT) + pIns->nFadeOut = std::min((pIns->nFadeOut + 16u) & ~31u, uint32(8192)); + } + + sample.rootNote = region.keyRoot + NOTE_MIN; + sample.nGlobalVol = mpt::saturate_round(64.0 * Clamp(std::pow(10.0, region.volume / 20.0) * region.amplitude / 100.0, 0.0, 1.0)); + if(region.panning != -128) + { + sample.nPan = mpt::saturate_round((region.panning + 100) * 256.0 / 200.0); + sample.uFlags.set(CHN_PANNING); + } + sample.Transpose(region.finetune / 1200.0); + + if(region.pitchLfoDepth && region.pitchLfoFreq) + { + sample.nVibSweep = 255; + if(region.pitchLfoFade > 0) + sample.nVibSweep = mpt::saturate_round(255.0 / region.pitchLfoFade); + sample.nVibDepth = mpt::saturate_round(region.pitchLfoDepth * 32.0 / 100.0); + sample.nVibRate = mpt::saturate_round(region.pitchLfoFreq * 4.0); + } + + if(region.loopMode != SFZRegion::LoopMode::kUnspecified) + { + switch(region.loopMode) + { + case SFZRegion::LoopMode::kContinuous: + case SFZRegion::LoopMode::kOneShot: + sample.uFlags.set(CHN_LOOP); + break; + case SFZRegion::LoopMode::kSustain: + sample.uFlags.set(CHN_SUSTAINLOOP); + break; + case SFZRegion::LoopMode::kNoLoop: + sample.uFlags.reset(CHN_LOOP | CHN_SUSTAINLOOP); + } + } + if(region.loopEnd > region.loopStart) + { + // Loop may also be defined in file, in which case loopStart and loopEnd are unset. + if(region.loopMode == SFZRegion::LoopMode::kSustain) + { + sample.nSustainStart = region.loopStart; + sample.nSustainEnd = region.loopEnd + 1; + } else if(region.loopMode == SFZRegion::LoopMode::kContinuous || region.loopMode == SFZRegion::LoopMode::kOneShot) + { + sample.nLoopStart = region.loopStart; + sample.nLoopEnd = region.loopEnd + 1; + } + } else if(sample.nLoopEnd <= sample.nLoopStart && region.loopMode != SFZRegion::LoopMode::kUnspecified && region.loopMode != SFZRegion::LoopMode::kNoLoop) + { + sample.nLoopEnd = sample.nLength; + } + switch(region.loopType) + { + case SFZRegion::LoopType::kUnspecified: + break; + case SFZRegion::LoopType::kForward: + sample.uFlags.reset(CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN | CHN_REVERSE); + break; + case SFZRegion::LoopType::kBackward: + sample.uFlags.set(CHN_REVERSE); + break; + case SFZRegion::LoopType::kAlternate: + sample.uFlags.set(CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN); + break; + default: + break; + } + if(sample.nSustainEnd <= sample.nSustainStart && sample.nLoopEnd > sample.nLoopStart && region.loopMode == SFZRegion::LoopMode::kSustain) + { + // Turn normal loop (imported from sample) into sustain loop + std::swap(sample.nSustainStart, sample.nLoopStart); + std::swap(sample.nSustainEnd, sample.nLoopEnd); + sample.uFlags.set(CHN_SUSTAINLOOP); + sample.uFlags.set(CHN_PINGPONGSUSTAIN, sample.uFlags[CHN_PINGPONGLOOP]); + sample.uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP); + } + + mpt::PathString filenameModifier; + + // Loop cross-fade + SmpLength fadeSamples = mpt::saturate_round(region.loopCrossfade * origSampleRate); + LimitMax(fadeSamples, sample.uFlags[CHN_SUSTAINLOOP] ? sample.nSustainStart : sample.nLoopStart); + if(fadeSamples > 0) + { + ctrlSmp::XFadeSample(sample, fadeSamples, 50000, true, sample.uFlags[CHN_SUSTAINLOOP], *this); + sample.uFlags.set(SMP_MODIFIED); + filenameModifier += P_(" (cross-fade)"); + } + + // Sample offset + if(region.offset && region.offset < sample.nLength) + { + auto offset = region.offset * sample.GetBytesPerSample(); + memmove(sample.sampleb(), sample.sampleb() + offset, sample.nLength * sample.GetBytesPerSample() - offset); + if(region.end > region.offset) + region.end -= region.offset; + sample.nLength -= region.offset; + sample.nLoopStart -= region.offset; + sample.nLoopEnd -= region.offset; + sample.uFlags.set(SMP_MODIFIED); + filenameModifier += P_(" (offset)"); + } + LimitMax(sample.nLength, region.end); + + if(region.invertPhase) + { + ctrlSmp::InvertSample(sample, 0, sample.nLength, *this); + sample.uFlags.set(SMP_MODIFIED); + filenameModifier += P_(" (inverted)"); + } + + if(sample.uFlags.test_all(SMP_KEEPONDISK | SMP_MODIFIED)) + { + // Avoid ruining the original samples + if(auto filename = GetSamplePath(smp); !filename.empty()) + { + filename = filename.GetPath() + filename.GetFileName() + filenameModifier + filename.GetFileExt(); + SetSamplePath(smp, filename); + } + } + + sample.PrecomputeLoops(*this, false); + sample.Convert(MOD_TYPE_MPT, GetType()); + } + + pIns->Sanitize(MOD_TYPE_MPT); + pIns->Convert(MOD_TYPE_MPT, GetType()); + return true; +} + +#ifndef MODPLUG_NO_FILESAVE + +static double SFZLinear2dB(double volume) +{ + return (volume > 0.0 ? 20.0 * std::log10(volume) : -144.0); +} + +static void WriteSFZEnvelope(std::ostream &f, double tickDuration, int index, const InstrumentEnvelope &env, const char *type, double scale, std::function convFunc) +{ + if(!env.dwFlags[ENV_ENABLED] || env.empty()) + return; + + const bool sustainAtEnd = (!env.dwFlags[ENV_SUSTAIN] || env.nSustainStart == (env.size() - 1)) && convFunc(env.back().value) != 0.0; + + const auto prefix = mpt::format("\neg%1_")(mpt::fmt::dec0<2>(index)); + f << "\n" << prefix << type << "=" << scale; + f << prefix << "points=" << (env.size() + (sustainAtEnd ? 1 : 0)); + EnvelopeNode::tick_t lastTick = 0; + int nodeIndex = 0; + for(const auto &node : env) + { + const double time = (node.tick - lastTick) * tickDuration; + lastTick = node.tick; + f << prefix << "time" << nodeIndex << "=" << time; + f << prefix << "level" << nodeIndex << "=" << convFunc(node.value); + nodeIndex++; + } + if(sustainAtEnd) + { + // Prevent envelope from going back to neutral + f << prefix << "time" << nodeIndex << "=0"; + f << prefix << "level" << nodeIndex << "=" << convFunc(env.back().value); + } + // We always must write a sustain point, or the envelope will be sustained on the first point of the envelope + f << prefix << "sustain=" << (env.dwFlags[ENV_SUSTAIN] ? env.nSustainStart : (env.size() - 1)); + + if(env.dwFlags[ENV_LOOP]) + f << "\n// Loop: " << static_cast(env.nLoopStart) << "-" << static_cast(env.nLoopEnd); + if(env.dwFlags[ENV_SUSTAIN] && env.nSustainEnd > env.nSustainStart) + f << "\n// Sustain Loop: " << static_cast(env.nSustainStart) << "-" << static_cast(env.nSustainEnd); + if(env.nReleaseNode != ENV_RELEASE_NODE_UNSET) + f << "\n// Release Node: " << static_cast(env.nReleaseNode); +} + +bool CSoundFile::SaveSFZInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, const mpt::PathString &filename, bool useFLACsamples) const +{ +#ifdef MODPLUG_TRACKER + const mpt::FlushMode flushMode = mpt::FlushModeFromBool(TrackerSettings::Instance().MiscFlushFileBuffersOnSave); +#else + const mpt::FlushMode flushMode = mpt::FlushMode::Full; +#endif + const ModInstrument *ins = Instruments[nInstr]; + if(ins == nullptr) + return false; + + const mpt::PathString sampleBaseName = filename.GetFileName(); + const mpt::PathString sampleDirName = sampleBaseName + P_("/"); + const mpt::PathString sampleBasePath = filename.GetPath() + sampleDirName; + if(!sampleBasePath.IsDirectory() && !::CreateDirectory(sampleBasePath.AsNative().c_str(), nullptr)) + return false; + + const double tickDuration = m_PlayState.m_nSamplesPerTick / static_cast(m_MixerSettings.gdwMixingFreq); + + f << std::setprecision(10); + if(!ins->name.empty()) + { + f << "// Name: " << mpt::ToCharset(mpt::Charset::UTF8, GetCharsetInternal(), ins->name) << "\n"; + } + f << "// Created with " << mpt::ToCharset(mpt::Charset::UTF8, Version::Current().GetOpenMPTVersionString()) << "\n"; + f << "// Envelope tempo base: tempo " << m_PlayState.m_nMusicTempo.ToDouble(); + switch(m_nTempoMode) + { + case tempoModeClassic: + f << " (classic tempo mode)"; + break; + case tempoModeAlternative: + f << " (alternative tempo mode)"; + break; + case tempoModeModern: + f << ", " << m_PlayState.m_nMusicSpeed << " ticks per row, " << m_PlayState.m_nCurrentRowsPerBeat << " rows per beat (modern tempo mode)"; + break; + default: + MPT_ASSERT_NOTREACHED(); + break; + } + + f << "\n\n\ndefault_path=" << sampleDirName.ToUTF8() << "\n\n"; + f << ""; + f << "\nbend_up=" << ins->midiPWD * 100; + f << "\nbend_down=" << -ins->midiPWD * 100; + const uint32 cutoff = ins->IsCutoffEnabled() ? ins->GetCutoff() : 127; + // If filter envelope is active but cutoff is not set, we still need to set the base cutoff frequency to be modulated by the envelope. + if(ins->IsCutoffEnabled() || ins->PitchEnv.dwFlags[ENV_FILTER]) + f << "\ncutoff=" << CSoundFile::CutOffToFrequency(cutoff) << " // " << cutoff; + if(ins->IsResonanceEnabled()) + f << "\nresonance=" << Util::muldivr_unsigned(ins->GetResonance(), 24, 128) << " // " << static_cast(ins->GetResonance()); + if(ins->IsCutoffEnabled() || ins->IsResonanceEnabled()) + f << "\nfil_type=" << (ins->filterMode == FilterMode::HighPass ? "hpf_2p" : "lpf_2p"); + if(ins->dwFlags[INS_SETPANNING]) + f << "\npan=" << (Util::muldivr_unsigned(ins->nPan, 200, 256) - 100) << " // " << ins->nPan; + if(ins->nGlobalVol != 64) + f << "\nvolume=" << SFZLinear2dB(ins->nGlobalVol / 64.0) << " // " << ins->nGlobalVol; + if(ins->nFadeOut) + { + f << "\nampeg_release=" << (32768.0 * tickDuration / ins->nFadeOut) << " // " << ins->nFadeOut; + f << "\nampeg_release_shape=0"; + } + + if(ins->nDNA == DNA_NOTECUT && ins->nDCT != DCT_NONE) + f << "\npolyphony=1"; + + WriteSFZEnvelope(f, tickDuration, 1, ins->VolEnv, "amplitude", 100.0, [](int32 val) { return val / static_cast(ENVELOPE_MAX); }); + WriteSFZEnvelope(f, tickDuration, 2, ins->PanEnv, "pan", 100.0, [](int32 val) { return 2.0 * (val - ENVELOPE_MID) / (ENVELOPE_MAX - ENVELOPE_MIN); }); + if(ins->PitchEnv.dwFlags[ENV_FILTER]) + { + const auto envScale = 1200.0 * std::log(CutOffToFrequency(127, 256) / static_cast(CutOffToFrequency(0, -256))) / M_LN2; + const auto cutoffNormal = CutOffToFrequency(cutoff); + WriteSFZEnvelope(f, tickDuration, 3, ins->PitchEnv, "cutoff", envScale, [this, cutoff, cutoffNormal, envScale](int32 val) { + // Convert interval between center frequency and envelope into cents + const auto freq = CutOffToFrequency(cutoff, (val - ENVELOPE_MID) * 256 / (ENVELOPE_MAX - ENVELOPE_MID)); + return 1200.0 * std::log(freq / static_cast(cutoffNormal)) / M_LN2 / envScale; + }); + } else + { + WriteSFZEnvelope(f, tickDuration, 3, ins->PitchEnv, "pitch", 1600.0, [](int32 val) { return 2.0 * (val - ENVELOPE_MID) / (ENVELOPE_MAX - ENVELOPE_MIN); }); + } + + size_t numSamples = 0; + for(size_t i = 0; i < std::size(ins->Keyboard); i++) + { + if(ins->Keyboard[i] < 1 || ins->Keyboard[i] > GetNumSamples()) + continue; + + size_t endOfRegion = i + 1; + while(endOfRegion < std::size(ins->Keyboard)) + { + if(ins->Keyboard[endOfRegion] != ins->Keyboard[i] || ins->NoteMap[endOfRegion] != (ins->NoteMap[i] + endOfRegion - i)) + break; + endOfRegion++; + } + endOfRegion--; + + const ModSample &sample = Samples[ins->Keyboard[i]]; + const bool isAdlib = sample.uFlags[CHN_ADLIB]; + + if(!sample.HasSampleData()) + { + i = endOfRegion; + continue; + } + + numSamples++; + mpt::PathString sampleName = sampleBasePath + sampleBaseName + P_(" ") + mpt::PathString::FromUnicode(mpt::ufmt::val(numSamples)); + if(isAdlib) + sampleName += P_(".s3i"); + else if(useFLACsamples) + sampleName += P_(".flac"); + else + sampleName += P_(".wav"); + + try + { + mpt::SafeOutputFile fSmp(sampleName, std::ios::binary, flushMode); + if(fSmp) + { + //fSmp.exceptions(fSmp.exceptions() | std::ios::badbit | std::ios::failbit); + + if(isAdlib) + SaveS3ISample(ins->Keyboard[i], fSmp); + else if(useFLACsamples) + SaveFLACSample(ins->Keyboard[i], fSmp); + else + SaveWAVSample(ins->Keyboard[i], fSmp); + } + } catch(const std::exception &) + { + AddToLog(LogError, MPT_USTRING("Unable to save sample: ") + sampleName.ToUnicode()); + } + + f << "\n\n"; + if(!m_szNames[ins->Keyboard[i]].empty()) + { + f << "\nregion_label=" << mpt::ToCharset(mpt::Charset::UTF8, GetCharsetInternal(), m_szNames[ins->Keyboard[i]]); + } + f << "\nsample=" << sampleName.GetFullFileName().ToUTF8(); + f << "\nlokey=" << i; + f << "\nhikey=" << endOfRegion; + if(sample.rootNote != NOTE_NONE) + f << "\npitch_keycenter=" << sample.rootNote - NOTE_MIN; + else + f << "\npitch_keycenter=" << NOTE_MIDDLEC + i - ins->NoteMap[i]; + if(sample.uFlags[CHN_PANNING]) + f << "\npan=" << (Util::muldivr_unsigned(sample.nPan, 200, 256) - 100) << " // " << sample.nPan; + if(sample.nGlobalVol != 64) + f << "\nvolume=" << SFZLinear2dB((ins->nGlobalVol * sample.nGlobalVol) / 4096.0) << " // " << sample.nGlobalVol; + const char *loopMode = "no_loop", *loopType = "forward"; + SmpLength loopStart = 0, loopEnd = 0; + if(sample.uFlags[CHN_SUSTAINLOOP]) + { + loopMode = "loop_sustain"; + loopStart = sample.nSustainStart; + loopEnd = sample.nSustainEnd; + if(sample.uFlags[CHN_PINGPONGSUSTAIN]) + loopType = "alternate"; + } else if(sample.uFlags[CHN_LOOP]) + { + loopMode = "loop_continuous"; + loopStart = sample.nLoopStart; + loopEnd = sample.nLoopEnd; + if(sample.uFlags[CHN_PINGPONGLOOP]) + loopType = "alternate"; + else if(sample.uFlags[CHN_REVERSE]) + loopType = "backward"; + } + f << "\nloop_mode=" << loopMode; + if(loopStart < loopEnd) + { + f << "\nloop_start=" << loopStart; + f << "\nloop_end=" << (loopEnd - 1); + f << "\nloop_type=" << loopType; + } + if(sample.uFlags.test_all(CHN_SUSTAINLOOP | CHN_LOOP)) + { + f << "\n// Warning: Only sustain loop was exported!"; + } + i = endOfRegion; + } + + return true; +} + +#endif // MODPLUG_NO_FILESAVE + +#else +bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX, FileReader &) +{ + return false; +} +#endif // MPT_EXTERNAL_SAMPLES + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatVorbis.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatVorbis.cpp index d0ba12ce2..e467cfb5d 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatVorbis.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormatVorbis.cpp @@ -24,14 +24,35 @@ //#include "../common/mptCRC.h" #include "OggStream.h" #ifdef MPT_WITH_OGG +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG #include +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG #endif // MPT_WITH_OGG #if defined(MPT_WITH_VORBIS) +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG #include -#endif +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG +#endif // MPT_WITH_VORBIS #if defined(MPT_WITH_VORBISFILE) +#if MPT_COMPILER_CLANG +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif // MPT_COMPILER_CLANG #include -#endif +#if MPT_COMPILER_CLANG +#pragma clang diagnostic pop +#endif // MPT_COMPILER_CLANG +#endif // MPT_WITH_VORBISFILE #ifdef MPT_WITH_STBVORBIS #include #endif // MPT_WITH_STBVORBIS @@ -48,7 +69,7 @@ OPENMPT_NAMESPACE_BEGIN static size_t VorbisfileFilereaderRead(void *ptr, size_t size, size_t nmemb, void *datasource) { FileReader &file = *reinterpret_cast(datasource); - return file.ReadRaw(mpt::void_cast(ptr), size * nmemb) / size; + return file.ReadRaw(mpt::void_cast(ptr), size * nmemb) / size; } static int VorbisfileFilereaderSeek(void *datasource, ogg_int64_t offset, int whence) @@ -119,7 +140,7 @@ static long VorbisfileFilereaderTell(void *datasource) #if defined(MPT_WITH_VORBIS) static mpt::ustring UStringFromVorbis(const char *str) { - return str ? mpt::ToUnicode(mpt::CharsetUTF8, str) : mpt::ustring(); + return str ? mpt::ToUnicode(mpt::Charset::UTF8, str) : mpt::ustring(); } #endif // MPT_WITH_VORBIS @@ -247,7 +268,7 @@ bool CSoundFile::ReadVorbisSample(SAMPLEINDEX sample, FileReader &file) // files, stb_vorbis will include superfluous samples at the beginning. FileReader::PinnedRawDataView fileView = file.GetPinnedRawDataView(); - const mpt::byte* data = fileView.data(); + const std::byte* data = fileView.data(); std::size_t dataLeft = fileView.size(); std::size_t offset = 0; @@ -309,7 +330,7 @@ bool CSoundFile::ReadVorbisSample(SAMPLEINDEX sample, FileReader &file) } DestroySampleThreadsafe(sample); - mpt::String::Copy(m_szNames[sample], sampleName); + m_szNames[sample] = sampleName; Samples[sample].Initialize(); Samples[sample].nC5Speed = rate; Samples[sample].nLength = mpt::saturate_cast(raw_sample_data.size() / channels); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp index 968848465..3bd949b5e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleFormats.cpp @@ -13,13 +13,12 @@ #include "mod_specifications.h" #ifdef MODPLUG_TRACKER #include "../mptrack/Moddoc.h" -#include "../mptrack/TrackerSettings.h" #include "Dlsbank.h" -#endif //MODPLUG_TRACKER +#endif // MODPLUG_TRACKER #include "../soundlib/AudioCriticalSection.h" #ifndef MODPLUG_NO_FILESAVE #include "../common/mptFileIO.h" -#endif +#endif // !MODPLUG_NO_FILESAVE #include "../common/misc_util.h" #include "Tagging.h" #include "ITTools.h" @@ -29,10 +28,10 @@ #include "../common/version.h" #include "Loaders.h" #include "ChunkReader.h" -#include "modsmp_ctrl.h" #include "../soundbase/SampleFormatConverters.h" #include "../soundbase/SampleFormatCopy.h" #include "../soundlib/ModSampleCopy.h" +#include #include @@ -53,6 +52,7 @@ bool CSoundFile::ReadSampleFromFile(SAMPLEINDEX nSample, FileReader &file, bool && !ReadS3ISample(nSample, file) && !ReadSBISample(nSample, file) && !ReadAUSample(nSample, file, mayNormalize) + && !ReadBRRSample(nSample, file) && !ReadFLACSample(nSample, file) && !ReadOpusSample(nSample, file) && !ReadVorbisSample(nSample, file) @@ -261,9 +261,9 @@ bool CSoundFile::ReadInstrumentFromSong(INSTRUMENTINDEX targetInstr, const CSoun } #ifdef MODPLUG_TRACKER - if(!strcmp(pIns->filename, "") && srcSong.GetpModDoc() != nullptr && &srcSong != this) + if(pIns->filename.empty() && srcSong.GetpModDoc() != nullptr && &srcSong != this) { - mpt::String::Copy(pIns->filename, srcSong.GetpModDoc()->GetPathNameMpt().GetFullFileName().ToLocale()); + pIns->filename = srcSong.GetpModDoc()->GetPathNameMpt().GetFullFileName().ToLocale(); } #endif pIns->Convert(srcSong.GetType(), GetType()); @@ -294,7 +294,7 @@ bool CSoundFile::ReadSampleFromSong(SAMPLEINDEX targetSample, const CSoundFile & if(GetNumSamples() < targetSample) m_nSamples = targetSample; targetSmp = sourceSmp; - strcpy(m_szNames[targetSample], srcSong.m_szNames[sourceSample]); + m_szNames[targetSample] = srcSong.m_szNames[sourceSample]; if(sourceSmp.HasSampleData()) { @@ -314,9 +314,9 @@ bool CSoundFile::ReadSampleFromSong(SAMPLEINDEX targetSample, const CSoundFile & } #ifdef MODPLUG_TRACKER - if(!strcmp(targetSmp.filename, "") && srcSong.GetpModDoc() != nullptr && &srcSong != this) + if((targetSmp.filename.empty()) && srcSong.GetpModDoc() != nullptr && &srcSong != this) { - mpt::String::Copy(targetSmp.filename, mpt::ToCharset(GetCharsetInternal(), srcSong.GetpModDoc()->GetTitle())); + targetSmp.filename = mpt::ToCharset(GetCharsetInternal(), srcSong.GetpModDoc()->GetTitle()); } #endif @@ -339,8 +339,8 @@ bool CSoundFile::ReadSampleFromSong(SAMPLEINDEX targetSample, const CSoundFile & static bool IMAADPCMUnpack16(int16 *target, SmpLength sampleLen, FileReader file, uint16 blockAlign, uint32 numChannels) { - static const int32 IMAIndexTab[8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; - static const int32 IMAUnpackTable[90] = + static constexpr int8 IMAIndexTab[8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; + static constexpr int16 IMAUnpackTable[90] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, @@ -365,7 +365,7 @@ static bool IMAADPCMUnpack16(int16 *target, SmpLength sampleLen, FileReader file { FileReader block = file.ReadChunk(blockAlign); FileReader::PinnedRawDataView blockView = block.GetPinnedRawDataView(); - const mpt::byte *data = blockView.data(); + const std::byte *data = blockView.data(); const uint32 blockSize = static_cast(blockView.size()); for(uint32 chn = 0; chn < numChannels; chn++) @@ -431,7 +431,7 @@ bool CSoundFile::ReadWAVSample(SAMPLEINDEX nSample, FileReader &file, bool mayNo } DestroySampleThreadsafe(nSample); - strcpy(m_szNames[nSample], ""); + m_szNames[nSample] = ""; ModSample &sample = Samples[nSample]; sample.Initialize(); sample.nLength = wavFile.GetSampleLength(); @@ -538,14 +538,15 @@ bool CSoundFile::ReadWAVSample(SAMPLEINDEX nSample, FileReader &file, bool mayNo #ifndef MODPLUG_NO_FILESAVE bool CSoundFile::SaveWAVSample(SAMPLEINDEX nSample, std::ostream &f) const { + const ModSample &sample = Samples[nSample]; + if(sample.uFlags[CHN_ADLIB]) + return false; + WAVWriter file(&f); if(!file.IsValid()) - { return false; - } - const ModSample &sample = Samples[nSample]; file.WriteFormat(sample.GetSampleRate(GetType()), sample.GetElementarySampleSize() * 8, sample.GetNumChannels(), WAVFormatChunk::fmtPCM); // Write sample data @@ -640,7 +641,7 @@ static void Wave64TagFromLISTINFO(mpt::ustring & dst, uint16 codePage, const Chu textChunk.ReadString(str, textChunk.GetLength()); str = mpt::String::Replace(str, std::string("\r\n"), std::string("\n")); str = mpt::String::Replace(str, std::string("\r"), std::string("\n")); - dst = mpt::ToUnicode(codePage, mpt::CharsetWindows1252, str); + dst = mpt::ToUnicode(codePage, mpt::Charset::Windows1252, str); } @@ -755,7 +756,7 @@ bool CSoundFile::ReadW64Sample(SAMPLEINDEX nSample, FileReader &file, bool mayNo FileTags tags; - uint16 codePage = 28591; // mpt::CharsetISO8859_1 + uint16 codePage = 28591; // mpt::Charset::ISO8859_1 FileReader csetChunk = chunkList.GetChunk(guidCSET); if(csetChunk.IsValid()) { @@ -804,7 +805,7 @@ bool CSoundFile::ReadW64Sample(SAMPLEINDEX nSample, FileReader &file, bool mayNo sampleIO.ReadSample(mptSample, audioData); - mpt::String::Copy(m_szNames[nSample], mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags))); + m_szNames[nSample] = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags)); mptSample.Convert(MOD_TYPE_IT, GetType()); mptSample.PrecomputeLoops(*this, false); @@ -989,7 +990,7 @@ static void PatchToSample(CSoundFile *that, SAMPLEINDEX nSample, GF1SampleHeader sample.Convert(MOD_TYPE_IT, that->GetType()); sample.PrecomputeLoops(*that, false); - mpt::String::Read(that->m_szNames[nSample], sampleHeader.name); + that->m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); } @@ -1018,7 +1019,7 @@ bool CSoundFile::ReadPATSample(SAMPLEINDEX nSample, FileReader &file) if(instrHeader.name[0] > ' ') { - mpt::String::Read(m_szNames[nSample], instrHeader.name); + m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name); } return true; } @@ -1054,7 +1055,7 @@ bool CSoundFile::ReadPATInstrument(INSTRUMENTINDEX nInstr, FileReader &file) if (nInstr > m_nInstruments) m_nInstruments = nInstr; Instruments[nInstr] = pIns; - mpt::String::Read(pIns->name, instrHeader.name); + pIns->name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name); pIns->nFadeOut = 2048; if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) { @@ -1076,7 +1077,7 @@ bool CSoundFile::ReadPATInstrument(INSTRUMENTINDEX nInstr, FileReader &file) GF1SampleHeader sampleHeader; PatchToSample(this, nextSample, sampleHeader, file); int32 nMinNote = (sampleHeader.low_freq > 100) ? PatchFreqToNoteInt(sampleHeader.low_freq) : 0; - int32 nMaxNote = (sampleHeader.high_freq > 100) ? PatchFreqToNoteInt(sampleHeader.high_freq) : NOTE_MAX; + int32 nMaxNote = (sampleHeader.high_freq > 100) ? PatchFreqToNoteInt(sampleHeader.high_freq) : static_cast(NOTE_MAX); int32 nBaseNote = (sampleHeader.root_freq > 100) ? PatchFreqToNoteInt(sampleHeader.root_freq) : -1; if(!sampleHeader.scale_factor && layerHeader.samples == 1) { nMinNote = 0; nMaxNote = NOTE_MAX; } // Fill Note Map @@ -1139,7 +1140,7 @@ bool CSoundFile::ReadS3ISample(SAMPLEINDEX nSample, FileReader &file) ModSample &sample = Samples[nSample]; sampleHeader.ConvertToMPT(sample); - mpt::String::Read(m_szNames[nSample], sampleHeader.name); + m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name); if(sampleHeader.sampleType < S3MSampleHeader::typeAdMel) sampleHeader.GetSampleFormat(false).ReadSample(sample, file); @@ -1161,8 +1162,8 @@ bool CSoundFile::SaveS3ISample(SAMPLEINDEX smp, std::ostream &f) const S3MSampleHeader sampleHeader; MemsetZero(sampleHeader); SmpLength length = sampleHeader.ConvertToS3M(sample); - mpt::String::Write(sampleHeader.name, m_szNames[smp]); - mpt::String::Write(sampleHeader.reserved2, mpt::ToCharset(mpt::CharsetUTF8, Version::Current().GetOpenMPTVersionString())); + mpt::String::WriteBuf(mpt::String::nullTerminated, sampleHeader.name) = m_szNames[smp]; + mpt::String::WriteBuf(mpt::String::maybeNullTerminated, sampleHeader.reserved2) = mpt::ToCharset(mpt::Charset::UTF8, Version::Current().GetOpenMPTVersionString()); if(length) sampleHeader.dataPointer[1] = sizeof(S3MSampleHeader) >> 4; mpt::IO::Write(f, sampleHeader); @@ -1290,8 +1291,8 @@ bool CSoundFile::ReadXIInstrument(INSTRUMENTINDEX nInstr, FileReader &file) mptSample.uFlags &= ~CHN_PANNING; } - mpt::String::Read(mptSample.filename, sampleHeader.name); - mpt::String::Read(m_szNames[sampleMap[i]], sampleHeader.name); + mptSample.filename = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); + m_szNames[sampleMap[i]] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); sampleFlags[i] = sampleHeader.GetSampleFormat(); } @@ -1352,7 +1353,7 @@ bool CSoundFile::SaveXIInstrument(INSTRUMENTINDEX nInstr, std::ostream &f) const } sampleFlags[i] = xmSample.GetSampleFormat(); - mpt::String::Write(xmSample.name, m_szNames[samples[i]]); + mpt::String::WriteBuf(mpt::String::spacePadded, xmSample.name) = m_szNames[samples[i]]; mpt::IO::Write(f, xmSample); } @@ -1425,8 +1426,8 @@ bool CSoundFile::ReadXISample(SAMPLEINDEX nSample, FileReader &file) fileHeader.instrument.ApplyAutoVibratoToMPT(mptSample); mptSample.Convert(MOD_TYPE_XM, GetType()); - mpt::String::Read(mptSample.filename, sampleHeader.name); - mpt::String::Read(m_szNames[nSample], sampleHeader.name); + mptSample.filename = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); + m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name); // Read sample data sampleHeader.GetSampleFormat().ReadSample(Samples[nSample], file); @@ -1436,860 +1437,6 @@ bool CSoundFile::ReadXISample(SAMPLEINDEX nSample, FileReader &file) } -///////////////////////////////////////////////////////////////////////////////////////// -// SFZ Instrument - -#ifdef MPT_EXTERNAL_SAMPLES - -struct SFZControl -{ - std::string defaultPath; - int8 octaveOffset = 0, noteOffset = 0; - - void Parse(const std::string &key, const std::string &value) - { - if(key == "default_path") - defaultPath = value; - else if(key == "octave_offset") - octaveOffset = ConvertStrTo(value); - else if(key == "note_offset") - noteOffset = ConvertStrTo(value); - } -}; - -struct SFZEnvelope -{ - float startLevel = 0, delay = 0, attack = 0, hold = 0, - decay = 0, sustainLevel = 100, release = 0, depth = 0; - - void Parse(std::string key, const std::string &value) - { - key.erase(0, key.find('_') + 1); - float v = ConvertStrTo(value); - if(key == "depth") - Limit(v, -12000.0f, 12000.0f); - else if(key == "start" || key == "sustain") - Limit(v, -100.0f, 100.0f); - else - Limit(v, 0.0f, 100.0f); - - if(key == "start") - startLevel = v; - else if(key == "delay") - delay = v; - else if(key == "attack") - attack = v; - else if(key == "hold") - hold = v; - else if(key == "decay") - decay = v; - else if(key == "sustain") - sustainLevel = v; - else if(key == "release") - release = v; - else if(key == "depth") - depth = v; - } - - static EnvelopeNode::tick_t ToTicks(float duration, float tickDuration) - { - return std::max(EnvelopeNode::tick_t(1), mpt::saturate_round(duration / tickDuration)); - } - - EnvelopeNode::value_t ToValue(float value, EnvelopeType envType) const - { - value *= (ENVELOPE_MAX / 100.0f); - if(envType == ENV_PITCH) - { - value *= depth / 3200.0f; - value += ENVELOPE_MID; - } - Limit(value, ENVELOPE_MIN, ENVELOPE_MAX); - return mpt::saturate_round(value); - } - - void ConvertToMPT(ModInstrument *ins, const CSoundFile &sndFile, EnvelopeType envType) const - { - auto &env = ins->GetEnvelope(envType); - float tickDuration = sndFile.m_PlayState.m_nSamplesPerTick / static_cast(sndFile.GetSampleRate()); - if(tickDuration <= 0) - return; - env.clear(); - if(envType != ENV_VOLUME && attack == 0 && delay == 0 && hold == 0 && decay == 0 && sustainLevel == 100 && release == 0 && depth == 0) - { - env.dwFlags.reset(ENV_SUSTAIN | ENV_ENABLED); - return; - } - if(attack > 0 || delay > 0) - { - env.push_back(0, ToValue(startLevel, envType)); - if(delay > 0) - env.push_back(ToTicks(delay, tickDuration), env.back().value); - env.push_back(env.back().tick + ToTicks(attack, tickDuration), ToValue(100, envType)); - } - if(hold > 0) - { - if(env.empty()) - env.push_back(0, ToValue(100, envType)); - env.push_back(env.back().tick + ToTicks(hold, tickDuration), env.back().value); - } - if(env.empty()) - env.push_back(0, ToValue(100, envType)); - auto sustain = ToValue(sustainLevel, envType); - if(env.back().value != sustain) - env.push_back(env.back().tick + ToTicks(decay, tickDuration), sustain); - env.nSustainStart = env.nSustainEnd = static_cast(env.size() - 1); - if(sustainLevel != 0) - { - if(envType == ENV_VOLUME && env.nSustainEnd > 0) - env.nReleaseNode = env.nSustainEnd; - env.push_back(env.back().tick + ToTicks(release, tickDuration), ToValue(0, envType)); - env.dwFlags.set(ENV_SUSTAIN); - } - env.dwFlags.set(ENV_ENABLED); - } -}; - -struct SFZRegion -{ - enum class LoopMode - { - kUnspecified, - kContinuous, - kOneShot, - kSustain, - kNoLoop - }; - - enum class LoopType - { - kUnspecified, - kForward, - kBackward, - kAlternate, - }; - - std::string filename, name; - SFZEnvelope ampEnv, pitchEnv, filterEnv; - SmpLength loopStart = 0, loopEnd = 0; - SmpLength end = MAX_SAMPLE_LENGTH, offset = 0; - double loopCrossfade = 0.0; - LoopMode loopMode = LoopMode::kUnspecified; - LoopType loopType = LoopType::kUnspecified; - int32 cutoff = 0; // in Hz - int32 filterRandom = 0; // 0...9600 cents - int16 volume = 0; // -144dB...+6dB - int16 pitchBend = 200; // -9600...9600 cents - float pitchLfoFade = 0; // 0...100 seconds - int16 pitchLfoDepth = 0; // -1200...12000 - uint8 pitchLfoFreq = 0; // 0...20 Hz - int8 panning = -128; // -100...+100 - int8 transpose = 0; - int8 finetune = 0; - uint8 keyLo = 0, keyHi = 127, keyRoot = 60; - uint8 resonance = 0; // 0...40dB - InstrFilterMode filterType = FLTMODE_UNCHANGED; - uint8 polyphony = 255; - bool useSampleKeyRoot = false; - bool invertPhase = false; - - template - static void Read(const std::string &valueStr, T &value, Tc valueMin = std::numeric_limits::min(), Tc valueMax = std::numeric_limits::max()) - { - double valueF = ConvertStrTo(valueStr); - MPT_CONSTANT_IF(std::numeric_limits::is_integer) - { - valueF = mpt::round(valueF); - } - Limit(valueF, static_cast(valueMin), static_cast(valueMax)); - value = static_cast(valueF); - } - - static uint8 ReadKey(const std::string &value, const SFZControl &control) - { - if(value.empty()) - return 0; - - int key = 0; - if(value[0] >= '0' && value[0] <= '9') - { - // MIDI key - key = ConvertStrTo(value); - } else if(value.length() < 2) - { - return 0; - } else - { - // Scientific pitch - static const int8 keys[] = { 9, 11, 0, 2, 4, 5, 7 }; - STATIC_ASSERT(CountOf(keys) == 'g' - 'a' + 1); - auto keyC = value[0]; - if(keyC >= 'A' && keyC <= 'G') - key = keys[keyC - 'A']; - if(keyC >= 'a' && keyC <= 'g') - key = keys[keyC - 'a']; - else - return 0; - - uint8 octaveOffset = 1; - if(value[1] == '#') - { - key++; - octaveOffset = 2; - } else if(value[1] == 'b' || value[1] == 'B') - { - key--; - octaveOffset = 2; - } - if(octaveOffset >= value.length()) - return 0; - - int8 octave = ConvertStrTo(value.c_str() + octaveOffset); - key += (octave + 1) * 12; - } - key += control.octaveOffset * 12 + control.noteOffset; - return static_cast(Clamp(key, 0, 127)); -} - - void Parse(const std::string &key, const std::string &value, const SFZControl &control) - { - if(key == "sample") - filename = control.defaultPath + value; - else if(key == "region_label") - name = value; - else if(key == "lokey") - keyLo = ReadKey(value, control); - else if(key == "hikey") - keyHi = ReadKey(value, control); - else if(key == "pitch_keycenter") - { - keyRoot = ReadKey(value, control); - useSampleKeyRoot = (value == "sample"); - } - else if(key == "key") - { - keyLo = keyHi = keyRoot = ReadKey(value, control); - useSampleKeyRoot = false; - } - else if(key == "bend_up" || key == "bendup") - Read(value, pitchBend, -9600, 9600); - else if(key == "pitchlfo_fade") - Read(value, pitchLfoFade, 0.0f, 100.0f); - else if(key == "pitchlfo_depth") - Read(value, pitchLfoDepth, -12000, 12000); - else if(key == "pitchlfo_freq") - Read(value, pitchLfoFreq, 0, 20); - else if(key == "volume") - Read(value, volume, -144, 6); - else if(key == "pan") - Read(value, panning, -100, 100); - else if(key == "transpose") - Read(value, transpose, -127, 127); - else if(key == "tune") - Read(value, finetune, -100, 100); - else if(key == "end") - Read(value, end, SmpLength(0), MAX_SAMPLE_LENGTH); - else if(key == "offset") - Read(value, offset, SmpLength(0), MAX_SAMPLE_LENGTH); - else if(key == "loop_start" || key == "loopstart") - Read(value, loopStart, SmpLength(0), MAX_SAMPLE_LENGTH); - else if(key == "loop_end" || key == "loopend") - Read(value, loopEnd, SmpLength(0), MAX_SAMPLE_LENGTH); - else if(key == "loop_crossfade") - Read(value, loopCrossfade, 0.0, DBL_MAX); - else if(key == "loop_mode" || key == "loopmode") - { - if(value == "loop_continuous") - loopMode = LoopMode::kContinuous; - else if(value == "one_shot") - loopMode = LoopMode::kOneShot; - else if(value == "loop_sustain") - loopMode = LoopMode::kSustain; - else if(value == "no_loop") - loopMode = LoopMode::kNoLoop; - } - else if(key == "loop_type" || key == "looptype") - { - if(value == "forward") - loopType = LoopType::kForward; - else if(value == "backward") - loopType = LoopType::kBackward; - else if(value == "alternate") - loopType = LoopType::kAlternate; - } - else if(key == "cutoff") - Read(value, cutoff, 0, 96000); - else if(key == "fil_random") - Read(value, filterRandom, 0, 9600); - else if(key == "resonance") - Read(value, resonance, 0u, 40u); - else if(key == "polyphony") - Read(value, polyphony, 0u, 255u); - else if(key == "phase") - invertPhase = (value == "invert"); - else if(key == "fil_type" || key == "filtype") - { - if(value == "lpf_1p" || value == "lpf_2p" || value == "lpf_4p" || value == "lpf_6p") - filterType = FLTMODE_LOWPASS; - else if(value == "hpf_1p" || value == "hpf_2p" || value == "hpf_4p" || value == "hpf_6p") - filterType = FLTMODE_HIGHPASS; - // Alternatives: bpf_2p, brf_2p - } - else if(key.substr(0, 6) == "ampeg_") - ampEnv.Parse(key, value); - else if(key.substr(0, 6) == "fileg_") - filterEnv.Parse(key, value); - else if(key.substr(0, 8) == "pitcheg_") - pitchEnv.Parse(key, value); - } -}; - -bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX nInstr, FileReader &file) -{ - file.Rewind(); - - enum { kNone, kGlobal, kMaster, kGroup, kRegion, kControl, kCurve, kUnknown } section = kNone; - SFZControl control; - SFZRegion group, master, globals; - std::vector regions; - std::map macros; - - std::string s; - while(file.ReadLine(s, 1024)) - { - // First, terminate line at the start of a comment block - auto commentPos = s.find("//"); - if(commentPos != std::string::npos) - { - s.resize(commentPos); - } - - // Now, read the tokens. - // This format is so funky that no general tokenizer approach seems to work here... - // Consider this jolly good example found at https://stackoverflow.com/questions/5923895/tokenizing-a-custom-text-file-format-file-using-c-sharp - // sample=piano C3.wav key=48 ampeg_release=0.7 // a comment here - // key = 49 sample = piano Db3.wav - // - // group=1 - // key = 48 - // sample = piano D3.ogg - // The original sfz specification claims that spaces around = are not allowed, but a quick look into the real world tells us otherwise. - - while(!s.empty()) - { - s.erase(0, s.find_first_not_of(" \t")); - - // Replace macros - for(const auto &m : macros) - { - auto &oldStr = m.first; - auto &newStr = m.second; - std::string::size_type pos = 0; - while((pos = s.find(oldStr, pos)) != std::string::npos) - { - s.replace(pos, oldStr.length(), newStr); - pos += newStr.length(); - } - } - - if(s.empty()) - { - break; - } - - std::string::size_type charsRead = 0; - - if(s[0] == '<' && (charsRead = s.find('>')) != std::string::npos) - { - // Section header - std::string sec = s.substr(1, charsRead - 1); - section = kUnknown; - if(sec == "global") - { - section = kGlobal; - // Reset global parameters - globals = SFZRegion(); - } else if(sec == "master") - { - section = kMaster; - // Reset master parameters - master = globals; - } else if(sec == "group") - { - section = kGroup; - // Reset group parameters - group = master; - } else if(sec == "region") - { - section = kRegion; - regions.push_back(group); - } else if(sec == "control") - { - section = kControl; - } else if(sec == "curve") - { - section = kCurve; - } - charsRead++; - } else if(s.substr(0, 8) == "#define " || s.substr(0, 8) == "#define\t") - { - // Macro definition - auto keyStart = s.find_first_not_of(" \t", 8); - auto keyEnd = s.find_first_of(" \t", keyStart); - auto valueStart = s.find_first_not_of(" \t", keyEnd); - std::string key = s.substr(keyStart, keyEnd - keyStart); - if(valueStart != std::string::npos && key.length() > 1 && key[0] == '$') - { - charsRead = s.find_first_of(" \t", valueStart); - macros[key] = s.substr(valueStart, charsRead - valueStart); - } - } else if(s.substr(0, 9) == "#include " || s.substr(0, 9) == "#include\t") - { - AddToLog(LogWarning, U_("#include directive is not supported.")); - auto fileStart = s.find("\"", 9); // Yes, there can be arbitrary characters before the opening quote, at least that's how sforzando does it. - auto fileEnd = s.find("\"", fileStart + 1); - if(fileStart != std::string::npos && fileEnd != std::string::npos) - { - charsRead = fileEnd + 1; - } else - { - return false; - } - } else if(section == kNone) - { - // Garbage before any section, probably not an sfz file - return false; - } else if(s.find('=') != std::string::npos) - { - // Read key=value pair - auto keyEnd = s.find_first_of(" \t="); - auto valueStart = s.find_first_not_of(" \t=", keyEnd); - std::string key = mpt::ToLowerCaseAscii(s.substr(0, keyEnd)); - if(key == "sample" || key == "default_path" || key.substr(0, 8) == "label_cc" || key.substr(0, 12) == "region_label") - { - // Sample / CC name may contain spaces... - charsRead = s.find_first_of("=\t<", valueStart); - if(charsRead != std::string::npos && s[charsRead] == '=') - { - // Backtrack to end of key - while(charsRead > valueStart && s[charsRead] == ' ') - charsRead--; - // Backtrack to start of key - while(charsRead > valueStart && s[charsRead] != ' ') - charsRead--; - } - } else - { - charsRead = s.find_first_of(" \t<", valueStart); - } - std::string value = s.substr(valueStart, charsRead - valueStart); - - switch(section) - { - case kGlobal: - globals.Parse(key, value, control); - MPT_FALLTHROUGH; - case kMaster: - master.Parse(key, value, control); - MPT_FALLTHROUGH; - case kGroup: - group.Parse(key, value, control); - break; - case kRegion: - regions.back().Parse(key, value, control); - break; - case kControl: - control.Parse(key, value); - break; - } - } else - { - // Garbage, probably not an sfz file - MPT_ASSERT(false); - return false; - } - - // Remove the token(s) we just read - s.erase(0, charsRead); - } - } - - if(regions.empty()) - { - return false; - } - - - ModInstrument *pIns = new (std::nothrow) ModInstrument(); - if(pIns == nullptr) - { - return false; - } - - RecalculateSamplesPerTick(); - DestroyInstrument(nInstr, deleteAssociatedSamples); - if(nInstr > m_nInstruments) m_nInstruments = nInstr; - Instruments[nInstr] = pIns; - - SAMPLEINDEX prevSmp = 0; - for(auto ®ion : regions) - { - uint8 keyLo = region.keyLo, keyHi = region.keyHi; - if(keyLo > keyHi) - continue; - Clamp(keyLo, 0, NOTE_MAX - NOTE_MIN); - Clamp(keyHi, 0, NOTE_MAX - NOTE_MIN); - SAMPLEINDEX smp = GetNextFreeSample(nInstr, prevSmp + 1); - if(smp == SAMPLEINDEX_INVALID) - break; - prevSmp = smp; - - ModSample &sample = Samples[smp]; - mpt::PathString filename = mpt::PathString::FromUTF8(region.filename); - if(!filename.empty()) - { - if(region.filename.find(':') == std::string::npos) - { - filename = file.GetFileName().GetPath() + filename; - } - SetSamplePath(smp, filename); - InputFile f(filename); - FileReader smpFile = GetFileReader(f); - if(!ReadSampleFromFile(smp, smpFile, false)) - { - AddToLog(LogWarning, U_("Unable to load sample: ") + filename.ToUnicode()); - prevSmp--; - continue; - } - if(!region.name.empty()) - { - mpt::String::Copy(m_szNames[smp], mpt::ToCharset(GetCharsetInternal(), mpt::CharsetUTF8, region.name)); - } - if(!m_szNames[smp][0]) - { - mpt::String::Copy(m_szNames[smp], filename.GetFileName().ToLocale()); - } - } - sample.uFlags.set(SMP_KEEPONDISK, sample.HasSampleData()); - - if(region.useSampleKeyRoot) - { - if(sample.rootNote != NOTE_NONE) - region.keyRoot = sample.rootNote - NOTE_MIN; - else - region.keyRoot = 60; - } - - const auto origSampleRate = sample.GetSampleRate(GetType()); - int8 transp = region.transpose + (60 - region.keyRoot); - for(uint8 i = keyLo; i <= keyHi; i++) - { - pIns->Keyboard[i] = smp; - if(GetType() != MOD_TYPE_XM) - pIns->NoteMap[i] = NOTE_MIN + i + transp; - } - if(GetType() == MOD_TYPE_XM) - sample.Transpose(transp / 12.0); - - pIns->nFilterMode = region.filterType; - if(region.cutoff != 0) - pIns->SetCutoff(FrequencyToCutOff(region.cutoff), true); - if(region.resonance != 0) - pIns->SetResonance(mpt::saturate_cast(Util::muldivr(region.resonance, 128, 24)), true); - pIns->nCutSwing = mpt::saturate_cast(Util::muldivr(region.filterRandom, m_SongFlags[SONG_EXFILTERRANGE] ? 20 : 24, 1200)); - pIns->midiPWD = static_cast(region.pitchBend / 100); - - pIns->nNNA = NNA_NOTEOFF; - if(region.polyphony == 1) - { - pIns->nDNA = DNA_NOTECUT; - pIns->nDCT = DCT_SAMPLE; - } - region.ampEnv.ConvertToMPT(pIns, *this, ENV_VOLUME); - region.pitchEnv.ConvertToMPT(pIns, *this, ENV_PITCH); - //region.filterEnv.ConvertToMPT(pIns, *this, ENV_PITCH); - - sample.rootNote = region.keyRoot + NOTE_MIN; - sample.nGlobalVol = mpt::saturate_round(64 * std::pow(10.0, region.volume / 20.0)); - if(region.panning != -128) - { - sample.nPan = static_cast(Util::muldivr_unsigned(region.panning + 100, 256, 200)); - sample.uFlags.set(CHN_PANNING); - } - sample.Transpose(region.finetune / 1200.0); - - if(region.pitchLfoDepth && region.pitchLfoFreq) - { - sample.nVibSweep = 255; - if(region.pitchLfoFade > 0) - sample.nVibSweep = mpt::saturate_round(255 / region.pitchLfoFade); - sample.nVibDepth = static_cast(Util::muldivr(region.pitchLfoDepth, 32, 100)); - sample.nVibRate = region.pitchLfoFreq * 4; - } - - if(region.loopMode != SFZRegion::LoopMode::kUnspecified) - { - switch(region.loopMode) - { - case SFZRegion::LoopMode::kContinuous: - case SFZRegion::LoopMode::kOneShot: - sample.uFlags.set(CHN_LOOP); - break; - case SFZRegion::LoopMode::kSustain: - sample.uFlags.set(CHN_SUSTAINLOOP); - break; - case SFZRegion::LoopMode::kNoLoop: - sample.uFlags.reset(CHN_LOOP | CHN_SUSTAINLOOP); - } - } - if(region.loopEnd > region.loopStart) - { - // Loop may also be defined in file, in which case loopStart and loopEnd are unset. - if(region.loopMode == SFZRegion::LoopMode::kSustain) - { - sample.nSustainStart = region.loopStart; - sample.nSustainEnd = region.loopEnd + 1; - } else if(region.loopMode == SFZRegion::LoopMode::kContinuous || region.loopMode == SFZRegion::LoopMode::kOneShot) - { - sample.nLoopStart = region.loopStart; - sample.nLoopEnd = region.loopEnd + 1; - } - } else if(sample.nLoopEnd <= sample.nLoopStart && region.loopMode != SFZRegion::LoopMode::kUnspecified && region.loopMode != SFZRegion::LoopMode::kNoLoop) - { - sample.nLoopEnd = sample.nLength; - } - switch(region.loopType) - { - case SFZRegion::LoopType::kUnspecified: - break; - case SFZRegion::LoopType::kForward: - sample.uFlags.reset(CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN | CHN_REVERSE); - break; - case SFZRegion::LoopType::kBackward: - sample.uFlags.set(CHN_REVERSE); - break; - case SFZRegion::LoopType::kAlternate: - sample.uFlags.set(CHN_PINGPONGLOOP | CHN_PINGPONGSUSTAIN); - break; - default: - break; - } - if(sample.nSustainEnd <= sample.nSustainStart && sample.nLoopEnd > sample.nLoopStart && region.loopMode == SFZRegion::LoopMode::kSustain) - { - // Turn normal loop (imported from sample) into sustain loop - std::swap(sample.nSustainStart, sample.nLoopStart); - std::swap(sample.nSustainEnd, sample.nLoopEnd); - sample.uFlags.set(CHN_SUSTAINLOOP); - sample.uFlags.set(CHN_PINGPONGSUSTAIN, sample.uFlags[CHN_PINGPONGLOOP]); - sample.uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP); - } - - // Loop cross-fade - SmpLength fadeSamples = mpt::saturate_round(region.loopCrossfade * origSampleRate); - LimitMax(fadeSamples, sample.uFlags[CHN_SUSTAINLOOP] ? sample.nSustainStart : sample.nLoopStart); - if(fadeSamples > 0) - { - ctrlSmp::XFadeSample(sample, fadeSamples, 50000, true, sample.uFlags[CHN_SUSTAINLOOP], *this); - sample.uFlags.set(SMP_MODIFIED); - } - - // Sample offset - if(region.offset && region.offset < sample.nLength) - { - auto offset = region.offset * sample.GetBytesPerSample(); - memmove(sample.sampleb(), sample.sampleb() + offset, sample.nLength * sample.GetBytesPerSample() - offset); - if(region.end > region.offset) - region.end -= region.offset; - sample.nLength -= region.offset; - sample.nLoopStart -= region.offset; - sample.nLoopEnd -= region.offset; - sample.uFlags.set(SMP_MODIFIED); - } - LimitMax(sample.nLength, region.end); - - if(region.invertPhase) - { - ctrlSmp::InvertSample(sample, 0, sample.nLength, *this); - sample.uFlags.set(SMP_MODIFIED); - } - - sample.PrecomputeLoops(*this, false); - sample.Convert(MOD_TYPE_MPT, GetType()); - } - - pIns->Sanitize(MOD_TYPE_MPT); - pIns->Convert(MOD_TYPE_MPT, GetType()); - return true; -} - -#ifndef MODPLUG_NO_FILESAVE - -static double SFZLinear2dB(double volume) -{ - return (volume > 0.0 ? 20.0 * std::log10(volume) : -144.0); -} - -bool CSoundFile::SaveSFZInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, const mpt::PathString &filename, bool useFLACsamples) const -{ -#ifdef MODPLUG_TRACKER - const mpt::FlushMode flushMode = mpt::FlushModeFromBool(TrackerSettings::Instance().MiscFlushFileBuffersOnSave); -#else - const mpt::FlushMode flushMode = mpt::FlushMode::Full; -#endif - const ModInstrument *ins = Instruments[nInstr]; - if(ins == nullptr) - { - return false; - } - const mpt::PathString sampleBaseName = filename.GetFileName(); - const mpt::PathString sampleDirName = sampleBaseName + P_("/"); - const mpt::PathString sampleBasePath = filename.GetPath() + sampleDirName; - if(!sampleBasePath.IsDirectory() && !::CreateDirectory(sampleBasePath.AsNative().c_str(), nullptr)) - { - return false; - } - - if(strcmp(ins->name, "")) - { - f << "// Name: " << mpt::ToCharset(mpt::CharsetUTF8, GetCharsetInternal(), ins->name) << "\n"; - } - f << "// Created with " << mpt::ToCharset(mpt::CharsetUTF8, Version::Current().GetOpenMPTVersionString()) << "\n\n"; - - f << "\ndefault_path=" << sampleDirName.ToUTF8() << "\n\n"; - f << ""; - f << "\nbend_up=" << ins->midiPWD * 100; - if(ins->IsCutoffEnabled()) - { - f << "\ncutoff=" << CSoundFile::CutOffToFrequency(ins->GetCutoff()); - } - if(ins->IsResonanceEnabled()) - { - f << "\nresonance=" << Util::muldivr_unsigned(ins->GetResonance(), 24, 128); - } - if(ins->IsCutoffEnabled() || ins->IsResonanceEnabled()) - { - f << "\nfil_type=" << (ins->nFilterMode == FLTMODE_HIGHPASS ? "hpf_2p" : "lpf_2p"); - } - if(ins->dwFlags[INS_SETPANNING]) - { - f << "\npan=" << (Util::muldivr_unsigned(ins->nPan, 200, 256) - 100); - } - if(ins->nGlobalVol != 64) - { - f << "\nvolume=" << SFZLinear2dB(ins->nGlobalVol / 64.0); - } - - size_t numSamples = 0; - for(size_t i = 0; i < mpt::size(ins->Keyboard); i++) - { - if(ins->Keyboard[i] < 1 || ins->Keyboard[i] > GetNumSamples()) - continue; - - numSamples++; - size_t endOfRegion = i + 1; - while(endOfRegion < mpt::size(ins->Keyboard)) - { - if(ins->Keyboard[endOfRegion] != ins->Keyboard[i] || ins->NoteMap[endOfRegion] != (ins->NoteMap[i] + endOfRegion - i)) - break; - endOfRegion++; - } - endOfRegion--; - - mpt::PathString sampleName = sampleBasePath + sampleBaseName + P_(" ") + mpt::PathString::FromUnicode(mpt::ufmt::val(numSamples)); - if(useFLACsamples) - sampleName += P_(".flac"); - else - sampleName += P_(".wav"); - - try - { - mpt::SafeOutputFile fSmp(sampleName, std::ios::binary, flushMode); - if(fSmp) - { - //fSmp.exceptions(fSmp.exceptions() | std::ios::badbit | std::ios::failbit); - - if(useFLACsamples) - SaveFLACSample(ins->Keyboard[i], fSmp); - else - SaveWAVSample(ins->Keyboard[i], fSmp); - } - } catch(const std::exception &) - { - AddToLog(LogError, MPT_USTRING("Unable to save sample: ") + sampleName.ToUnicode()); - } - - const ModSample &sample = Samples[ins->Keyboard[i]]; - f << "\n\n"; - if(strcmp(m_szNames[ins->Keyboard[i]], "")) - { - f << "\nregion_label=" << mpt::ToCharset(mpt::CharsetUTF8, GetCharsetInternal(), m_szNames[ins->Keyboard[i]]); - } - f << "\nsample=" << sampleName.GetFullFileName().ToUTF8(); - f << "\nlokey=" << i; - f << "\nhikey=" << endOfRegion; - if(sample.rootNote != NOTE_NONE) - { - f << "\npitch_keycenter=" << sample.rootNote - NOTE_MIN; - } else - { - f << "\npitch_keycenter=" << NOTE_MIDDLEC + i - ins->NoteMap[i]; - } - if(sample.uFlags[CHN_PANNING]) - { - f << "\npan=" << (Util::muldivr_unsigned(sample.nPan, 200, 256) - 100); - } - if(sample.nGlobalVol != 64) - { - f << "\nvolume=" << SFZLinear2dB((ins->nGlobalVol * sample.nGlobalVol) / 4096.0); - } - const char *loopMode; - SmpLength loopStart = 0, loopEnd = 0; - bool loopType = false; - if(sample.uFlags[CHN_SUSTAINLOOP]) - { - loopMode = "loop_sustain"; - loopStart = sample.nSustainStart; - loopEnd = sample.nSustainEnd; - loopType = sample.uFlags[CHN_PINGPONGSUSTAIN]; - } else if(sample.uFlags[CHN_LOOP]) - { - loopMode = "loop_continuous"; - loopStart = sample.nLoopStart; - loopEnd = sample.nLoopEnd; - loopType = sample.uFlags[CHN_SUSTAINLOOP]; - // TODO backward loop (supported by engine but no UI) - } else - { - loopMode = "no_loop"; - } - f << "\nloop_mode=" << loopMode; - if(loopStart < loopEnd) - { - f << "\nloop_start=" << loopStart; - f << "\nloop_end=" << loopEnd; - f << "\nloop_type=" << (loopType ? "alternate" : "forward"); - } - if(sample.uFlags.test_all(CHN_SUSTAINLOOP | CHN_LOOP)) - { - f << "\n// Warning: Only sustain loop was exported!"; - } - i = endOfRegion; - } - - return true; -} - -#endif // MODPLUG_NO_FILESAVE - -#else -bool CSoundFile::ReadSFZInstrument(INSTRUMENTINDEX, FileReader &) -{ - return false; -} -#endif // MPT_EXTERNAL_SAMPLES - - - /////////////// // Apple CAF // @@ -2394,7 +1541,7 @@ static void CAFSetTagFromInfoKey(mpt::ustring & dst, const std::mapsecond); + dst = mpt::ToUnicode(mpt::Charset::UTF8, item->second); } @@ -2591,7 +1738,7 @@ bool CSoundFile::ReadCAFSample(SAMPLEINDEX nSample, FileReader &file, bool mayNo sampleIO.ReadSample(mptSample, audioData); - mpt::String::Copy(m_szNames[nSample], mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags))); + m_szNames[nSample] = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags)); mptSample.Convert(MOD_TYPE_IT, GetType()); mptSample.PrecomputeLoops(*this, false); @@ -2883,7 +2030,7 @@ bool CSoundFile::ReadAIFFSample(SAMPLEINDEX nSample, FileReader &file, bool mayN nameChunk.ReadString(m_szNames[nSample], nameChunk.GetLength()); } else { - strcpy(m_szNames[nSample], ""); + m_szNames[nSample] = ""; } mptSample.Convert(MOD_TYPE_IT, GetType()); @@ -2892,7 +2039,7 @@ bool CSoundFile::ReadAIFFSample(SAMPLEINDEX nSample, FileReader &file, bool mayN } -static bool AUIsAnnotationLineWithField(const std::string & line) +static bool AUIsAnnotationLineWithField(const std::string &line) { std::size_t pos = line.find('='); if(pos == std::string::npos) @@ -2903,23 +2050,19 @@ static bool AUIsAnnotationLineWithField(const std::string & line) { return false; } - std::string field = line.substr(0, pos); - bool invalidChars = false; + const auto field = std::string_view(line).substr(0, pos); + // Scan for invalid chars for(auto c : field) { if(!IsInRange(c, 'a', 'z') && !IsInRange(c, 'A', 'Z') && !IsInRange(c, '0', '9') && c != '-' && c != '_') { - invalidChars = true; + return false; } } - if(invalidChars) - { - return false; - } return true; } -static std::string AUTrimFieldFromAnnotationLine(const std::string & line) +static std::string AUTrimFieldFromAnnotationLine(const std::string &line) { if(!AUIsAnnotationLineWithField(line)) { @@ -2929,7 +2072,7 @@ static std::string AUTrimFieldFromAnnotationLine(const std::string & line) return line.substr(pos + 1); } -static std::string AUGetAnnotationFieldFromLine(const std::string & line) +static std::string AUGetAnnotationFieldFromLine(const std::string &line) { if(!AUIsAnnotationLineWithField(line)) { @@ -2944,14 +2087,19 @@ bool CSoundFile::ReadAUSample(SAMPLEINDEX nSample, FileReader &file, bool mayNor file.Rewind(); // Verify header - if(!file.ReadMagic(".snd")) + const auto magic = file.ReadArray(); + const bool bigEndian = !std::memcmp(magic.data(), ".snd", 4); + const bool littleEndian = !std::memcmp(magic.data(), "dns.", 4); + if(!bigEndian && !littleEndian) return false; - uint32 dataOffset = file.ReadUint32BE(); // must be divisible by 8 according to spec, however, there are files that ignore this requirement - uint32 dataSize = file.ReadUint32BE(); - uint32 encoding = file.ReadUint32BE(); - uint32 sampleRate = file.ReadUint32BE(); - uint32 channels = file.ReadUint32BE(); + auto readUint32 = std::bind(bigEndian ? &FileReader::ReadUint32BE : &FileReader::ReadUint32LE, file); + + uint32 dataOffset = readUint32(); // must be divisible by 8 according to spec, however, there are files that ignore this requirement + uint32 dataSize = readUint32(); + uint32 encoding = readUint32(); + uint32 sampleRate = readUint32(); + uint32 channels = readUint32(); // According to spec, a minimum 8 byte annotation field after the header fields is required, // however, there are files in the wild that violate this requirement. @@ -2964,22 +2112,22 @@ bool CSoundFile::ReadAUSample(SAMPLEINDEX nSample, FileReader &file, bool mayNor if(channels < 1 || channels > 2) return false; - SampleIO sampleIO(SampleIO::_8bit, channels == 1 ? SampleIO::mono : SampleIO::stereoInterleaved, SampleIO::bigEndian, SampleIO::signedPCM); + SampleIO sampleIO(SampleIO::_8bit, channels == 1 ? SampleIO::mono : SampleIO::stereoInterleaved, bigEndian ? SampleIO::bigEndian : SampleIO::littleEndian, SampleIO::signedPCM); switch(encoding) { - case 1: sampleIO |= SampleIO::_16bit; // u-law + case 1: sampleIO |= SampleIO::_16bit; // u-law sampleIO |= SampleIO::uLaw; break; - case 2: break; // 8-bit linear PCM - case 3: sampleIO |= SampleIO::_16bit; break; // 16-bit linear PCM - case 4: sampleIO |= SampleIO::_24bit; break; // 24-bit linear PCM - case 5: sampleIO |= SampleIO::_32bit; break; // 32-bit linear PCM - case 6: sampleIO |= SampleIO::_32bit; // 32-bit IEEE floating point + case 2: break; // 8-bit linear PCM + case 3: sampleIO |= SampleIO::_16bit; break; // 16-bit linear PCM + case 4: sampleIO |= SampleIO::_24bit; break; // 24-bit linear PCM + case 5: sampleIO |= SampleIO::_32bit; break; // 32-bit linear PCM + case 6: sampleIO |= SampleIO::_32bit; // 32-bit IEEE floating point sampleIO |= SampleIO::floatPCM; break; - case 7: sampleIO |= SampleIO::_64bit; // 64-bit IEEE floating point + case 7: sampleIO |= SampleIO::_64bit; // 64-bit IEEE floating point sampleIO |= SampleIO::floatPCM; break; - case 27: sampleIO |= SampleIO::_16bit; // a-law + case 27: sampleIO |= SampleIO::_16bit; // a-law sampleIO |= SampleIO::aLaw; break; default: return false; } @@ -2993,46 +2141,41 @@ bool CSoundFile::ReadAUSample(SAMPLEINDEX nSample, FileReader &file, bool mayNor // This reads annotation metadata as written by OpenMPT, sox, ffmpeg. // Additionally, we fall back to just reading the whole field as a single comment. + // We only read up to the first \0 byte. file.Seek(24); - std::vector annotationData; - file.ReadVector(annotationData, dataOffset - 24); - std::string annotation(annotationData.begin(), annotationData.end()); - annotation = mpt::String::RTrim(annotation, std::string(1, '\0')); - std::size_t term = annotation.find(std::string(1, '\0')); - if(term != std::string::npos) - { // only up to first \0 byte - annotation = annotation.substr(0, term); - } + std::string annotation; + file.ReadString(annotation, dataOffset - 24); annotation = mpt::String::Replace(annotation, "\r\n", "\n"); annotation = mpt::String::Replace(annotation, "\r", "\n"); - mpt::Charset charset = mpt::IsUTF8(annotation) ? mpt::CharsetUTF8 : mpt::CharsetISO8859_1; - std::vector lines = mpt::String::Split(annotation, "\n"); - bool has_fields = false; - for(const auto & line : lines) + mpt::Charset charset = mpt::IsUTF8(annotation) ? mpt::Charset::UTF8 : mpt::Charset::ISO8859_1; + const auto lines = mpt::String::Split(annotation, "\n"); + bool hasFields = false; + for(const auto &line : lines) { if(AUIsAnnotationLineWithField(line)) { - has_fields = true; + hasFields = true; + break; } } - if(has_fields) + if(hasFields) { - std::map> lines_per_field; - std::string last_field = "comment"; - for(const auto & line : lines) + std::map> linesPerField; + std::string lastField = "comment"; + for(const auto &line : lines) { if(AUIsAnnotationLineWithField(line)) { - last_field = mpt::ToLowerCaseAscii(mpt::String::Trim(AUGetAnnotationFieldFromLine(line))); + lastField = mpt::ToLowerCaseAscii(mpt::String::Trim(AUGetAnnotationFieldFromLine(line))); } - lines_per_field[last_field].push_back(AUTrimFieldFromAnnotationLine(line)); + linesPerField[lastField].push_back(AUTrimFieldFromAnnotationLine(line)); } - tags.title = mpt::ToUnicode(charset, mpt::String::Combine(lines_per_field["title" ], std::string("\n"))); - tags.artist = mpt::ToUnicode(charset, mpt::String::Combine(lines_per_field["artist" ], std::string("\n"))); - tags.album = mpt::ToUnicode(charset, mpt::String::Combine(lines_per_field["album" ], std::string("\n"))); - tags.trackno = mpt::ToUnicode(charset, mpt::String::Combine(lines_per_field["track" ], std::string("\n"))); - tags.genre = mpt::ToUnicode(charset, mpt::String::Combine(lines_per_field["genre" ], std::string("\n"))); - tags.comments = mpt::ToUnicode(charset, mpt::String::Combine(lines_per_field["comment"], std::string("\n"))); + tags.title = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["title" ], std::string("\n"))); + tags.artist = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["artist" ], std::string("\n"))); + tags.album = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["album" ], std::string("\n"))); + tags.trackno = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["track" ], std::string("\n"))); + tags.genre = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["genre" ], std::string("\n"))); + tags.comments = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["comment"], std::string("\n"))); } else { // Most applications tend to write their own name here, @@ -3051,7 +2194,7 @@ bool CSoundFile::ReadAUSample(SAMPLEINDEX nSample, FileReader &file, bool mayNor LimitMax(length, dataSize); mptSample.nLength = (length * 8u) / (sampleIO.GetEncodedBitsPerSample() * channels); mptSample.nC5Speed = sampleRate; - mpt::String::Copy(m_szNames[nSample], mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags))); + m_szNames[nSample] = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags)); if(mayNormalize) { @@ -3087,7 +2230,7 @@ bool CSoundFile::ReadITSSample(SAMPLEINDEX nSample, FileReader &file, bool rewin ModSample &sample = Samples[nSample]; file.Seek(sampleHeader.ConvertToMPT(sample)); - mpt::String::Read(m_szNames[nSample], sampleHeader.name); + m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::spacePaddedNull, sampleHeader.name); if(sample.uFlags[CHN_ADLIB]) { @@ -3282,7 +2425,7 @@ bool CSoundFile::SaveITIInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, cons itss.ConvertToIT(Samples[smp], GetType(), compress, compress, allowExternal); const bool isExternal = itss.cvt == ITSample::cvtExternalSample; - mpt::String::Write(itss.name, m_szNames[smp]); + mpt::String::WriteBuf(mpt::String::nullTerminated, itss.name) = m_szNames[smp]; itss.samplepointer = filePos; mpt::IO::Write(f, itss); @@ -3423,7 +2566,7 @@ bool CSoundFile::ReadIFFSample(SAMPLEINDEX nSample, FileReader &file) nameChunk.ReadString(m_szNames[nSample], nameChunk.GetLength()); } else { - strcpy(m_szNames[nSample], ""); + m_szNames[nSample] = ""; } sample.nLength = mpt::saturate_cast(bodyChunk.GetLength() / bytesPerSample); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp index f7cf942f7..671cad111 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.cpp @@ -44,7 +44,7 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const FileReader::off_t bytesRead = 0; // Amount of memory that has been read from file FileReader::off_t filePosition = file.GetPosition(); - const mpt::byte * sourceBuf = nullptr; + const std::byte * sourceBuf = nullptr; FileReader::PinnedRawDataView restrictedSampleDataView; FileReader::off_t fileSize = 0; if(UsesFileReaderForDecoding()) @@ -232,7 +232,7 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const } else if((GetEncoding() == uLaw || GetEncoding() == aLaw) && GetBitDepth() == 16 && (GetChannelFormat() == mono || GetChannelFormat() == stereoInterleaved)) { // 8-to-16 bit G.711 u-law / a-law - static const int16 uLawTable[256] = + static constexpr int16 uLawTable[256] = { -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, @@ -268,7 +268,7 @@ size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const 56, 48, 40, 32, 24, 16, 8, 0, }; - static const int16 aLawTable[256] = + static constexpr int16 aLawTable[256] = { -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, @@ -861,7 +861,7 @@ size_t SampleIO::WriteSample(std::ostream &f, const ModSample &sample, SmpLength return 0; } - std::array writeBuffer; + std::array writeBuffer; mpt::IO::WriteBuffer fb{f, mpt::as_span(writeBuffer)}; SmpLength numSamples = sample.nLength; @@ -962,7 +962,7 @@ size_t SampleIO::WriteSample(std::ostream &f, const ModSample &sample, SmpLength // Stereo signed interleaved MPT_ASSERT(len == numSamples * 2); const int8 *const pSample8 = sample.sample8(); - mpt::IO::WriteRaw(f, reinterpret_cast(pSample8), len); + mpt::IO::WriteRaw(f, reinterpret_cast(pSample8), len); } else if(GetBitDepth() == 16 && GetChannelFormat() == stereoInterleaved && GetEncoding() == signedPCM && GetEndianness() == littleEndian) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.h b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.h index 48014021b..b5412afe5 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/SampleIO.h @@ -119,7 +119,7 @@ public: void MayNormalize() { - if(GetBitDepth() == 24 || GetBitDepth() == 32) + if(GetBitDepth() >= 24) { if(GetEncoding() == SampleIO::signedPCM) { diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h index 02f21d07f..0dbb9db52 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_defs.h @@ -19,25 +19,25 @@ OPENMPT_NAMESPACE_BEGIN -typedef uint32 ROWINDEX; +using ROWINDEX = uint32; const ROWINDEX ROWINDEX_INVALID = uint32_max; -typedef uint16 CHANNELINDEX; +using CHANNELINDEX = uint16; const CHANNELINDEX CHANNELINDEX_INVALID = uint16_max; -typedef uint16 ORDERINDEX; +using ORDERINDEX = uint16; const ORDERINDEX ORDERINDEX_INVALID = uint16_max; const ORDERINDEX ORDERINDEX_MAX = uint16_max - 1; -typedef uint16 PATTERNINDEX; +using PATTERNINDEX = uint16; const PATTERNINDEX PATTERNINDEX_INVALID = uint16_max; -typedef uint8 PLUGINDEX; +using PLUGINDEX = uint8; const PLUGINDEX PLUGINDEX_INVALID = uint8_max; -typedef uint16 SAMPLEINDEX; +using SAMPLEINDEX = uint16; const SAMPLEINDEX SAMPLEINDEX_INVALID = uint16_max; -typedef uint16 INSTRUMENTINDEX; +using INSTRUMENTINDEX = uint16; const INSTRUMENTINDEX INSTRUMENTINDEX_INVALID = uint16_max; -typedef uint8 SEQUENCEINDEX; +using SEQUENCEINDEX = uint8; const SEQUENCEINDEX SEQUENCEINDEX_INVALID = uint8_max; -typedef uint32 SmpLength; +using SmpLength = uint32; const SmpLength MAX_SAMPLE_LENGTH = 0x10000000; // Sample length in frames. Sample size in bytes can be more than this (= 256 MB). @@ -54,15 +54,18 @@ const SEQUENCEINDEX MAX_SEQUENCES = 50; const CHANNELINDEX MAX_BASECHANNELS = 127; // Maximum pattern channels. const CHANNELINDEX MAX_CHANNELS = 256; // Maximum number of mixing channels. -#define FREQ_FRACBITS 4 // Number of fractional bits in return value of CSoundFile::GetFreqFromPeriod() +enum { FREQ_FRACBITS = 4 }; // Number of fractional bits in return value of CSoundFile::GetFreqFromPeriod() // String lengths (including trailing null char) -#define MAX_SAMPLENAME 32 -#define MAX_SAMPLEFILENAME 22 -#define MAX_INSTRUMENTNAME 32 -#define MAX_INSTRUMENTFILENAME 32 -#define MAX_PATTERNNAME 32 -#define MAX_CHANNELNAME 20 +enum +{ + MAX_SAMPLENAME = 32, + MAX_SAMPLEFILENAME = 22, + MAX_INSTRUMENTNAME = 32, + MAX_INSTRUMENTFILENAME = 32, + MAX_PATTERNNAME = 32, + MAX_CHANNELNAME = 20, +}; enum MODTYPE { @@ -160,7 +163,7 @@ DECLARE_FLAGSET(ChannelFlags) #define CHN_CHANNELFLAGS (~CHN_SAMPLEFLAGS | CHN_SURROUND) // Sample flags fit into the first 16 bits, and with the current memory layout, storing them as a 16-bit integer packs struct ModSample nicely. -typedef FlagSet SampleFlags; +using SampleFlags = FlagSet; // Instrument envelope-specific flags @@ -202,11 +205,11 @@ enum EnvelopeType : uint8 }; // Filter Modes -enum InstrFilterMode : uint8 +enum class FilterMode : uint8 { - FLTMODE_UNCHANGED = 0xFF, - FLTMODE_LOWPASS = 0, - FLTMODE_HIGHPASS = 1, + Unchanged = 0xFF, + LowPass = 0, + HighPass = 1, }; @@ -274,6 +277,7 @@ DECLARE_FLAGSET(SongFlags) #ifndef NO_DSP #define SNDDSP_MEGABASS 0x02 // Bass expansion #define SNDDSP_SURROUND 0x08 // Surround mix +#define SNDDSP_BITCRUSH 0x01 #endif // NO_DSP #ifndef NO_REVERB #define SNDDSP_REVERB 0x20 // Apply reverb @@ -310,22 +314,30 @@ enum ResamplingMode : uint8 namespace Resampling { +enum class AmigaFilter +{ + Off = 0, + A500 = 1, + A1200 = 2, + Unfiltered = 3, +}; + static inline std::array AllModes() noexcept { return { { SRCMODE_NEAREST, SRCMODE_LINEAR, SRCMODE_CUBIC, SRCMODE_SINC8, SRCMODE_SINC8LP } }; } static inline std::array AllModesWithDefault() noexcept { return { { SRCMODE_NEAREST, SRCMODE_LINEAR, SRCMODE_CUBIC, SRCMODE_SINC8, SRCMODE_SINC8LP, SRCMODE_DEFAULT } }; } -static MPT_CONSTEXPR11_FUN ResamplingMode Default() noexcept { return SRCMODE_SINC8LP; } +static constexpr ResamplingMode Default() noexcept { return SRCMODE_SINC8LP; } -static MPT_CONSTEXPR11_FUN bool IsKnownMode(int mode) noexcept { return (mode >= 0) && (mode < SRCMODE_DEFAULT); } +static constexpr bool IsKnownMode(int mode) noexcept { return (mode >= 0) && (mode < SRCMODE_DEFAULT); } -static MPT_CONSTEXPR11_FUN ResamplingMode ToKnownMode(int mode) noexcept +static constexpr ResamplingMode ToKnownMode(int mode) noexcept { return Resampling::IsKnownMode(mode) ? static_cast(mode) : (mode == SRCMODE_AMIGA) ? SRCMODE_LINEAR : Resampling::Default(); } -static MPT_CONSTEXPR11_FUN int Length(ResamplingMode mode) noexcept +static constexpr int Length(ResamplingMode mode) noexcept { return mode == SRCMODE_NEAREST ? 1 : mode == SRCMODE_LINEAR ? 2 @@ -335,11 +347,11 @@ static MPT_CONSTEXPR11_FUN int Length(ResamplingMode mode) noexcept : 0; } -static MPT_CONSTEXPR11_FUN bool HasAA(ResamplingMode mode) noexcept { return (mode == SRCMODE_SINC8LP); } +static constexpr bool HasAA(ResamplingMode mode) noexcept { return (mode == SRCMODE_SINC8LP); } -static MPT_CONSTEXPR11_FUN ResamplingMode AddAA(ResamplingMode mode) noexcept { return (mode == SRCMODE_SINC8) ? SRCMODE_SINC8LP : mode; } +static constexpr ResamplingMode AddAA(ResamplingMode mode) noexcept { return (mode == SRCMODE_SINC8) ? SRCMODE_SINC8LP : mode; } -static MPT_CONSTEXPR11_FUN ResamplingMode RemoveAA(ResamplingMode mode) noexcept { return (mode == SRCMODE_SINC8LP) ? SRCMODE_SINC8 : mode; } +static constexpr ResamplingMode RemoveAA(ResamplingMode mode) noexcept { return (mode == SRCMODE_SINC8LP) ? SRCMODE_SINC8 : mode; } } @@ -348,7 +360,7 @@ static MPT_CONSTEXPR11_FUN ResamplingMode RemoveAA(ResamplingMode mode) noexcept // Release node defines #define ENV_RELEASE_NODE_UNSET 0xFF #define NOT_YET_RELEASED (-1) -STATIC_ASSERT(ENV_RELEASE_NODE_UNSET > MAX_ENVPOINTS); +static_assert(ENV_RELEASE_NODE_UNSET > MAX_ENVPOINTS); enum PluginPriority @@ -447,7 +459,7 @@ enum PlayBehaviour kITNoSurroundPan, // Panning and surround are mutually exclusive kITShortSampleRetrig, // Don't retrigger already stopped channels kITPortaNoNote, // Don't apply any portamento if no previous note is playing - kITDontResetNoteOffOnPorta, // Only reset note-off status on portamento in IT Compatible Gxx mode + kITFT2DontResetNoteOffOnPorta, // Only reset note-off status on portamento in IT Compatible Gxx mode kITVolColMemory, // IT volume column effects share their memory with the effect column kITPortamentoSwapResetsPos, // Portamento with sample swap plays the new sample from the beginning kITEmptyNoteMapSlot, // IT ignores instrument note map entries with no note completely @@ -456,7 +468,7 @@ enum PlayBehaviour kITClearPortaTarget, // New notes reset portamento target in IT kITPanbrelloHold, // Don't reset panbrello effect until next note or panning effect kITPanningReset, // Sample and instrument panning is only applied on note change, not instrument change - kITPatternLoopWithJumps, // Bxx on the same row as SBx terminates the loop in IT + kITPatternLoopWithJumpsOld, // Bxx on the same row as SBx terminates the loop in IT (old implementation of kITPatternLoopWithJumps) kITInstrWithNoteOff, // Instrument number with note-off recalls default volume kFT2Arpeggio, // FT2 arpeggio algorithm @@ -465,7 +477,7 @@ enum PlayBehaviour kFT2PortaNoNote, // Don't play portamento-ed note if no previous note is playing kFT2KeyOff, // FT2-style Kxx handling kFT2PanSlide, // Volume-column pan slides should be handled like fine slides - kFT2OffsetOutOfRange, // FT2-style 9xx edge case handling + kFT2ST3OffsetOutOfRange, // Offset past sample end stops the note kFT2RestrictXCommand, // Don't allow MPT extensions to Xxx command in XM kFT2RetrigWithNoteDelay, // Retrigger envelopes if there is a note delay with no note kFT2SetPanEnvPos, // Lxx only sets the pan env position if the volume envelope's sustain flag is set @@ -488,7 +500,7 @@ enum PlayBehaviour kST3NoMutedChannels, // Don't process any effects on muted S3M channels kST3EffectMemory, // Most effects share the same memory in ST3 - kST3PortaSampleChange, // Portamento plus instrument number applies the volume settings of the new sample, but not the new sample itself. + kST3PortaSampleChange, // Portamento plus instrument number applies the volume settings of the new sample, but not the new sample itself (GUS behaviour). kST3VibratoMemory, // Do not remember vibrato type in effect memory kST3LimitPeriod, // Cut note instead of limiting final period (ModPlug Tracker style) KST3PortaAfterArpeggio, // Portamento after arpeggio continues at the note where the arpeggio left off @@ -515,6 +527,14 @@ enum PlayBehaviour kOPLFlexibleNoteOff, // Full control after note-off over OPL voices, ^^^ sends note cut instead of just note-off kITInstrWithNoteOffOldEffects, // Instrument number with note-off recalls default volume - special cases with Old Effects enabled kMIDIVolumeOnNoteOffBug, // Update MIDI channel volume on note-off (legacy bug emulation) + kITDoNotOverrideChannelPan, // Sample / instrument pan does not override channel pan for following samples / instruments that are not panned + kITPatternLoopWithJumps, // Bxx right of SBx terminates the loop in IT + kITDCTBehaviour, // DCT="Sample" requires sample instrument, DCT="Note" checks old pattern note against new pattern note (previously was checking old pattern note against new translated note) + kOPLwithNNA, // NNA note-off / fade are applied to OPL channels + kST3RetrigAfterNoteCut, // Qxy does not retrigger note after it has been cut with ^^^ or SCx + kST3SampleSwap, // On-the-fly sample swapping (SoundBlaster behaviour) + kOPLRealRetrig, // Retrigger effect (Qxy) restarts OPL notes + kOPLNoResetAtEnvelopeEnd, // Do not reset OPL channel status at end of envelope (OpenMPT 1.28 inconsistency with samples) // Add new play behaviours here. @@ -539,8 +559,8 @@ public: // Sample position and sample position increment value struct SamplePosition { - typedef int64 value_t; - typedef uint64 unsigned_value_t; + using value_t = int64; + using unsigned_value_t = uint64; protected: value_t v = 0; @@ -548,40 +568,40 @@ protected: public: enum : uint32 { fractMax = 0xFFFFFFFFu }; - SamplePosition() { } - explicit SamplePosition(value_t pos) : v(pos) { } - SamplePosition(int32 intPart, uint32 fractPart) : v((static_cast(intPart) * (1ll<<32)) | fractPart) { } + MPT_CONSTEXPR11_FUN SamplePosition() { } + MPT_CONSTEXPR11_FUN explicit SamplePosition(value_t pos) : v(pos) { } + MPT_CONSTEXPR11_FUN SamplePosition(int32 intPart, uint32 fractPart) : v((static_cast(intPart) * (1ll << 32)) | fractPart) { } static SamplePosition Ratio(uint32 dividend, uint32 divisor) { return SamplePosition((static_cast(dividend) << 32) / divisor); } static SamplePosition FromDouble(double pos) { return SamplePosition(static_cast(pos * 4294967296.0)); } // Set integer and fractional part - MPT_FORCEINLINE SamplePosition &Set(int32 intPart, uint32 fractPart = 0) { v = (static_cast(intPart) << 32) | fractPart; return *this; } + MPT_CONSTEXPR14_FUN SamplePosition &Set(int32 intPart, uint32 fractPart = 0) { v = (static_cast(intPart) << 32) | fractPart; return *this; } // Set integer part, keep fractional part - MPT_FORCEINLINE SamplePosition &SetInt(int32 intPart) { v = (static_cast(intPart) << 32) | GetFract(); return *this; } + MPT_CONSTEXPR14_FUN SamplePosition &SetInt(int32 intPart) { v = (static_cast(intPart) << 32) | GetFract(); return *this; } // Get integer part (as sample length / position) - MPT_FORCEINLINE SmpLength GetUInt() const { return static_cast(static_cast(v) >> 32); } + MPT_CONSTEXPR11_FUN SmpLength GetUInt() const { return static_cast(static_cast(v) >> 32); } // Get integer part - MPT_FORCEINLINE int32 GetInt() const { return static_cast(static_cast(v) >> 32); } + MPT_CONSTEXPR11_FUN int32 GetInt() const { return static_cast(static_cast(v) >> 32); } // Get fractional part - MPT_FORCEINLINE uint32 GetFract() const { return static_cast(v); } + MPT_CONSTEXPR11_FUN uint32 GetFract() const { return static_cast(v); } // Get the inverted fractional part - MPT_FORCEINLINE SamplePosition GetInvertedFract() const { return SamplePosition(0x100000000ll - GetFract()); } + MPT_CONSTEXPR11_FUN SamplePosition GetInvertedFract() const { return SamplePosition(0x100000000ll - GetFract()); } // Get the raw fixed-point value - MPT_FORCEINLINE int64 GetRaw() const { return v; } + MPT_CONSTEXPR11_FUN int64 GetRaw() const { return v; } // Negate the current value - MPT_FORCEINLINE SamplePosition &Negate() { v = -v; return *this; } + MPT_CONSTEXPR14_FUN SamplePosition &Negate() { v = -v; return *this; } // Multiply and divide by given integer scalars - MPT_FORCEINLINE SamplePosition &MulDiv(uint32 mul, uint32 div) { v = (v * mul) / div; return *this; } + MPT_CONSTEXPR14_FUN SamplePosition &MulDiv(uint32 mul, uint32 div) { v = (v * mul) / div; return *this; } // Removes the integer part, only keeping fractions - MPT_FORCEINLINE SamplePosition &RemoveInt() { v &= fractMax; return *this; } + MPT_CONSTEXPR14_FUN SamplePosition &RemoveInt() { v &= fractMax; return *this; } // Check if value is 1.0 - MPT_FORCEINLINE bool IsUnity() const { return v == 0x100000000ll; } + MPT_CONSTEXPR11_FUN bool IsUnity() const { return v == 0x100000000ll; } // Check if value is 0 - MPT_FORCEINLINE bool IsZero() const { return v == 0; } + MPT_CONSTEXPR11_FUN bool IsZero() const { return v == 0; } // Check if value is > 0 - MPT_FORCEINLINE bool IsPositive() const { return v > 0; } + MPT_CONSTEXPR11_FUN bool IsPositive() const { return v > 0; } // Check if value is < 0 - MPT_FORCEINLINE bool IsNegative() const { return v < 0; } + MPT_CONSTEXPR11_FUN bool IsNegative() const { return v < 0; } // Addition / subtraction of another fixed-point number SamplePosition operator+ (const SamplePosition &other) const { return SamplePosition(v + other.v); } @@ -600,12 +620,12 @@ public: // Division by scalar; returns fractional point number SamplePosition operator/ (int div) const { return SamplePosition(v / div); } - bool operator== (const SamplePosition &other) const { return v == other.v; } - bool operator!= (const SamplePosition &other) const { return v != other.v; } - bool operator<= (const SamplePosition &other) const { return v <= other.v; } - bool operator>= (const SamplePosition &other) const { return v >= other.v; } - bool operator< (const SamplePosition &other) const { return v < other.v; } - bool operator> (const SamplePosition &other) const { return v > other.v; } + MPT_CONSTEXPR11_FUN bool operator==(const SamplePosition &other) const { return v == other.v; } + MPT_CONSTEXPR11_FUN bool operator!=(const SamplePosition &other) const { return v != other.v; } + MPT_CONSTEXPR11_FUN bool operator<=(const SamplePosition &other) const { return v <= other.v; } + MPT_CONSTEXPR11_FUN bool operator>=(const SamplePosition &other) const { return v >= other.v; } + MPT_CONSTEXPR11_FUN bool operator<(const SamplePosition &other) const { return v < other.v; } + MPT_CONSTEXPR11_FUN bool operator>(const SamplePosition &other) const { return v > other.v; } }; @@ -623,7 +643,7 @@ protected: public: enum : size_t { fractFact = FFact }; - typedef T store_t; + using store_t = T; MPT_CONSTEXPR11_FUN FPInt() : v(0) { } MPT_CONSTEXPR11_FUN FPInt(T intPart, T fractPart) : v((intPart * fractFact) + (fractPart % fractFact)) { } @@ -643,21 +663,21 @@ public: // Formats the stored value as a floating-point value MPT_CONSTEXPR11_FUN double ToDouble() const { return v / double(fractFact); } - MPT_CONSTEXPR11_FUN FPInt operator+ (const FPInt &other) const { return FPInt(v + other.v); } - MPT_CONSTEXPR11_FUN FPInt operator- (const FPInt &other) const { return FPInt(v - other.v); } - MPT_CONSTEXPR14_FUN FPInt operator+= (const FPInt &other) { v += other.v; return *this; } - MPT_CONSTEXPR14_FUN FPInt operator-= (const FPInt &other) { v -= other.v; return *this; } + MPT_CONSTEXPR11_FUN friend FPInt operator+ (const FPInt &a, const FPInt &b) noexcept { return FPInt(a.v + b.v); } + MPT_CONSTEXPR11_FUN friend FPInt operator- (const FPInt &a, const FPInt &b) noexcept { return FPInt(a.v - b.v); } + MPT_CONSTEXPR14_FUN FPInt operator+= (const FPInt &other) noexcept { v += other.v; return *this; } + MPT_CONSTEXPR14_FUN FPInt operator-= (const FPInt &other) noexcept { v -= other.v; return *this; } - MPT_CONSTEXPR11_FUN bool operator== (const FPInt &other) const { return v == other.v; } - MPT_CONSTEXPR11_FUN bool operator!= (const FPInt &other) const { return v != other.v; } - MPT_CONSTEXPR11_FUN bool operator<= (const FPInt &other) const { return v <= other.v; } - MPT_CONSTEXPR11_FUN bool operator>= (const FPInt &other) const { return v >= other.v; } - MPT_CONSTEXPR11_FUN bool operator< (const FPInt &other) const { return v < other.v; } - MPT_CONSTEXPR11_FUN bool operator> (const FPInt &other) const { return v > other.v; } + MPT_CONSTEXPR11_FUN friend bool operator== (const FPInt &a, const FPInt &b) noexcept { return a.v == b.v; } + MPT_CONSTEXPR11_FUN friend bool operator!= (const FPInt &a, const FPInt &b) noexcept { return a.v != b.v; } + MPT_CONSTEXPR11_FUN friend bool operator<= (const FPInt &a, const FPInt &b) noexcept { return a.v <= b.v; } + MPT_CONSTEXPR11_FUN friend bool operator>= (const FPInt &a, const FPInt &b) noexcept { return a.v >= b.v; } + MPT_CONSTEXPR11_FUN friend bool operator< (const FPInt &a, const FPInt &b) noexcept { return a.v < b.v; } + MPT_CONSTEXPR11_FUN friend bool operator> (const FPInt &a, const FPInt &b) noexcept { return a.v > b.v; } }; -typedef FPInt<10000, uint32> TEMPO; +using TEMPO = FPInt<10000, uint32>; -typedef std::array OPLPatch; +using OPLPatch = std::array; OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_flt.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_flt.cpp index 21f735259..6477a7921 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_flt.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_flt.cpp @@ -81,7 +81,8 @@ int CSoundFile::SetupChannelFilter(ModChannel &chn, bool bReset, int envModifier // Filtering is only ever done in IT if either cutoff is not full or if resonance is set. if(m_playBehaviour[kITFilterBehaviour] && resonance == 0 && computedCutoff >= 254) { - if(chn.rowCommand.IsNote() && !chn.rowCommand.IsPortamento() && !chn.nMasterChn && m_SongFlags[SONG_FIRSTTICK]) + if(chn.rowCommand.IsNote() && !chn.rowCommand.IsPortamento() && !chn.nMasterChn + && chn.position.IsZero() && !chn.dwFlags[CHN_WRAPPED_LOOP]) { // Z7F next to a note disables the filter, however in other cases this should not happen. // Test cases: filter-reset.it, filter-reset-carry.it, filter-nna.it @@ -124,7 +125,7 @@ int CSoundFile::SetupChannelFilter(ModChannel &chn, bool bReset, int envModifier switch(chn.nFilterMode) { - case FLTMODE_HIGHPASS: + case FilterMode::HighPass: chn.nFilter_A0 = FILTER_CONVERT(1.0f - fg); chn.nFilter_B0 = FILTER_CONVERT(fb0); chn.nFilter_B1 = FILTER_CONVERT(fb1); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp index 77f86ea1b..d389e14b3 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Snd_fx.cpp @@ -31,11 +31,11 @@ OPENMPT_NAMESPACE_BEGIN // Formats which have 7-bit (0...128) instead of 6-bit (0...64) global volume commands, or which are imported to this range (mostly formats which are converted to IT internally) #ifdef MODPLUG_TRACKER -#define GLOBALVOL_7BIT_FORMATS_EXT (MOD_TYPE_MT2) +static constexpr auto GLOBALVOL_7BIT_FORMATS_EXT = MOD_TYPE_MT2; #else -#define GLOBALVOL_7BIT_FORMATS_EXT (MOD_TYPE_NONE) +static constexpr auto GLOBALVOL_7BIT_FORMATS_EXT = MOD_TYPE_NONE; #endif // MODPLUG_TRACKER -#define GLOBALVOL_7BIT_FORMATS (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_IMF | MOD_TYPE_J2B | MOD_TYPE_MID | MOD_TYPE_AMS | MOD_TYPE_DBM | MOD_TYPE_PTM | MOD_TYPE_MDL | MOD_TYPE_DTM | GLOBALVOL_7BIT_FORMATS_EXT) +static constexpr auto GLOBALVOL_7BIT_FORMATS = MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_IMF | MOD_TYPE_J2B | MOD_TYPE_MID | MOD_TYPE_AMS | MOD_TYPE_DBM | MOD_TYPE_PTM | MOD_TYPE_MDL | MOD_TYPE_DTM | GLOBALVOL_7BIT_FORMATS_EXT; // Compensate frequency slide LUTs depending on whether we are handling periods or frequency - "up" and "down" in function name are seen from frequency perspective. @@ -73,11 +73,11 @@ public: #endif std::vector chnSettings; double elapsedTime; - static const uint32 IGNORE_CHANNEL = uint32_max; + static constexpr uint32 IGNORE_CHANNEL = uint32_max; GetLengthMemory(const CSoundFile &sf) : sndFile(sf) - , state(mpt::make_unique(sf.m_PlayState)) + , state(std::make_unique(sf.m_PlayState)) { Reset(); } @@ -127,22 +127,19 @@ public: if(i >= portaStart) { chn.isFirstTick = false; - const ModCommand &p = *sndFile.Patterns[state->m_nPattern].GetpModCommand(state->m_nRow, channel); - if(p.command == CMD_TONEPORTAMENTO) sndFile.TonePortamento(chn, p.param); - else if(p.command == CMD_TONEPORTAVOL) sndFile.TonePortamento(chn, 0); - if(p.volcmd == VOLCMD_TONEPORTAMENTO) + const ModCommand &m = *sndFile.Patterns[state->m_nPattern].GetpModCommand(state->m_nRow, channel); + auto command = m.command; + if(m.volcmd == VOLCMD_TONEPORTAMENTO) { - uint32 param = p.vol; - if(sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_AMS | MOD_TYPE_DMF | MOD_TYPE_DBM | MOD_TYPE_IMF | MOD_TYPE_PSM | MOD_TYPE_J2B | MOD_TYPE_ULT | MOD_TYPE_OKT | MOD_TYPE_MT2 | MOD_TYPE_MDL)) - { - param = ImpulseTrackerPortaVolCmd[param & 0x0F]; - } else - { - // Close enough. Do not bother with idiosyncratic FT2 behaviour here. - param <<= 4; - } - sndFile.TonePortamento(chn, param); + const auto [porta, clearEffectCommand] = sndFile.GetVolCmdTonePorta(m, 0); + sndFile.TonePortamento(chn, porta); + if(clearEffectCommand) + command = CMD_NONE; } + if(command == CMD_TONEPORTAMENTO) + sndFile.TonePortamento(chn, m.param); + else if(command == CMD_TONEPORTAVOL) + sndFile.TonePortamento(chn, 0); updateInc = true; } @@ -159,6 +156,14 @@ public: if(updateInc || chnSettings[channel].incChanged) { + if(chn.m_CalculateFreq || chn.m_ReCalculateFreqOnFirstTick) + { + chn.RecalcTuningFreq(1, 0, sndFile); + if(!chn.m_CalculateFreq) + chn.m_ReCalculateFreqOnFirstTick = false; + else + chn.m_CalculateFreq = false; + } chn.increment = sndFile.GetChannelIncrement(chn, period, 0); chnSettings[channel].incChanged = false; inc = chn.increment * tickDuration; @@ -239,7 +244,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod retval.startRow = target.startRow; // Are we trying to reach a certain pattern position? - const bool hasSearchTarget = target.mode != GetLengthTarget::NoTarget; + const bool hasSearchTarget = target.mode != GetLengthTarget::NoTarget && target.mode != GetLengthTarget::GetAllSubsongs; const bool adjustSamplePos = (adjustMode & eAdjustSamplePositions) == eAdjustSamplePositions; SEQUENCEINDEX sequence = target.sequence; @@ -277,7 +282,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod const PATTERNINDEX seekPat = orderList[target.pos.order]; if(Patterns.IsValidPat(seekPat) && Patterns[seekPat].IsValidRow(target.pos.row)) { - const ModCommand *m = Patterns[seekPat].GetRow(target.pos.row); + const ModCommand *m = Patterns[seekPat].GetpModCommand(target.pos.row, 0); for(CHANNELINDEX i = 0; i < GetNumChannels(); i++, m++) { if(m->note == NOTE_NOTECUT || m->note == NOTE_KEYOFF || (m->note == NOTE_FADE && GetNumInstruments()) @@ -295,14 +300,6 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod for (;;) { - // Time target reached. - if(target.mode == GetLengthTarget::SeekSeconds && memory.elapsedTime >= target.time) - { - retval.targetReached = true; - break; - } - - uint32 rowDelay = 0, tickDelay = 0; playState.m_nRow = playState.m_nNextRow; playState.m_nCurrentOrder = playState.m_nNextOrder; @@ -317,9 +314,16 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod playState.m_nCurrentOrder = ++playState.m_nNextOrder; } + // Time target reached. + if(target.mode == GetLengthTarget::SeekSeconds && memory.elapsedTime >= target.time) + { + retval.targetReached = true; + break; + } + // Check if pattern is valid playState.m_nPattern = playState.m_nCurrentOrder < orderList.size() ? orderList[playState.m_nCurrentOrder] : orderList.GetInvalidPatIndex(); - bool positionJumpOnThisRow = false; + bool positionJumpOnThisRow = false, positionJumpRightOfPatternLoop = false; bool patternBreakOnThisRow = false; bool patternLoopEndedOnThisRow = false, patternLoopStartedOnThisRow = false; @@ -347,7 +351,12 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod playState.m_nNextOrder = playState.m_nCurrentOrder; if((!Patterns.IsValidPat(playState.m_nPattern)) && visitedRows.IsVisited(playState.m_nCurrentOrder, 0, true)) { - if(!hasSearchTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true)) + if(!hasSearchTarget) + { + retval.lastOrder = playState.m_nCurrentOrder; + retval.lastRow = 0; + } + if(target.mode == GetLengthTarget::NoTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true)) { // We aren't searching for a specific row, or we couldn't find any more unvisited rows. break; @@ -379,7 +388,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod // If there isn't even a tune, we should probably stop here. if(playState.m_nCurrentOrder == orderList.GetRestartPos()) { - if(!hasSearchTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true)) + if(target.mode == GetLengthTarget::NoTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true)) { // We aren't searching for a specific row, or we couldn't find any more unvisited rows. break; @@ -411,7 +420,12 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod if(visitedRows.IsVisited(playState.m_nCurrentOrder, playState.m_nRow, true)) { - if(!hasSearchTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true)) + if(!hasSearchTarget) + { + retval.lastOrder = playState.m_nCurrentOrder; + retval.lastRow = playState.m_nRow; + } + if(target.mode == GetLengthTarget::NoTarget || !visitedRows.GetFirstUnvisitedRow(playState.m_nNextOrder, playState.m_nRow, true)) { // We aren't searching for a specific row, or we couldn't find any more unvisited rows. break; @@ -450,6 +464,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod } // For various effects, we need to know first how many ticks there are in this row. + uint32 rowDelay = 0, tickDelay = 0; const ModCommand *p = Patterns[playState.m_nPattern].GetpModCommand(playState.m_nRow, 0); for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++, p++) { @@ -541,7 +556,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod if(chn.nNewIns <= GetNumInstruments() && (pIns = Instruments[chn.nNewIns]) != nullptr) { if(pIns->dwFlags[INS_SETPANNING]) - chn.nPan = pIns->nPan; + chn.SetInstrumentPan(pIns->nPan, *this); if(ModCommand::IsNote(note)) smp = pIns->Keyboard[note - NOTE_MIN]; } @@ -552,7 +567,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod if(smp > 0 && smp <= GetNumSamples()) { if(Samples[smp].uFlags[CHN_PANNING]) - chn.nPan = Samples[smp].nPan; + chn.SetInstrumentPan(Samples[smp].nPan, *this); if(Samples[smp].uFlags[CHN_ADLIB]) { memory.state->Chn[nChn].Stop(); @@ -571,13 +586,22 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod if(chn.rowCommand.vol != 0) chn.nOldVolParam = chn.rowCommand.vol; break; + case VOLCMD_TONEPORTAMENTO: + if(chn.rowCommand.vol) + { + const auto [porta, clearEffectCommand] = GetVolCmdTonePorta(chn.rowCommand, 0); + chn.nPortamentoSlide = porta * 4; + if(clearEffectCommand) + command = CMD_NONE; + } + break; } switch(command) { // Position Jump case CMD_POSITIONJUMP: - positionJumpOnThisRow = true; + positionJumpOnThisRow = positionJumpRightOfPatternLoop = true; playState.m_nNextOrder = static_cast(CalculateXParam(playState.m_nPattern, playState.m_nRow, nChn)); playState.m_nNextPatStartRow = 0; // FT2 E60 bug // see https://forum.openmpt.org/index.php?topic=2769.0 - FastTracker resets Dxx if Bxx is called _after_ Dxx @@ -665,11 +689,8 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod case 0xB0: // Pattern Loop - if (param & 0x0F) - { - patternLoopEndedOnThisRow = true; - } else { + positionJumpRightOfPatternLoop = false; CHANNELINDEX firstChn = nChn, lastChn = nChn; if(GetType() == MOD_TYPE_S3M) { @@ -677,13 +698,28 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod firstChn = 0; lastChn = GetNumChannels() - 1; } - for(CHANNELINDEX c = firstChn; c <= lastChn; c++) + if(param & 0x0F) { - memory.chnSettings[c].patLoop = memory.elapsedTime; - memory.chnSettings[c].patLoopSmp = playState.m_lTotalSampleCount; - memory.chnSettings[c].patLoopStart = playState.m_nRow; + if(m_playBehaviour[kITPatternLoopTargetReset] || (GetType() == MOD_TYPE_S3M)) + { + for(CHANNELINDEX c = firstChn; c <= lastChn; c++) + { + playState.Chn[c].nPatternLoop = playState.m_nRow + 1; + } + } + patternLoopEndedOnThisRow = true; + } + else + { + for(CHANNELINDEX c = firstChn; c <= lastChn; c++) + { + memory.chnSettings[c].patLoop = memory.elapsedTime; + memory.chnSettings[c].patLoopSmp = playState.m_lTotalSampleCount; + memory.chnSettings[c].patLoopStart = playState.m_nRow; + playState.Chn[c].nPatternLoop = playState.m_nRow; + } + patternLoopStartedOnThisRow = true; } - patternLoopStartedOnThisRow = true; } break; @@ -709,6 +745,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod memory.chnSettings[nChn].patLoop = memory.elapsedTime; memory.chnSettings[nChn].patLoopSmp = playState.m_lTotalSampleCount; memory.chnSettings[nChn].patLoopStart = playState.m_nRow; + playState.Chn[nChn].nPatternLoop = playState.m_nRow; } break; @@ -844,7 +881,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod playState.Chn[channel].dwFlags.set(CHN_AMIGAFILTER, !(param & 1)); } } - MPT_FALLTHROUGH; + [[fallthrough]]; case CMD_S3MCMDEX: if((param & 0xF0) == 0x80) { @@ -855,7 +892,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod case CMD_VIBRATOVOL: if (param) chn.nOldVolumeSlide = param; param = 0; - MPT_FALLTHROUGH; + [[fallthrough]]; case CMD_VIBRATO: Vibrato(chn, param); break; @@ -1006,7 +1043,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod SampleOffset(chn, offset); } else if(m.command == CMD_OFFSETPERCENTAGE) { - SampleOffset(chn, Util::muldiv_unsigned(chn.nLength, m.param, 255)); + SampleOffset(chn, Util::muldiv_unsigned(chn.nLength, m.param, 256)); } else if(m.command == CMD_REVERSEOFFSET && chn.pModSample != nullptr) { memory.RenderChannel(nChn, oldTickDuration); // Re-sync what we've got so far @@ -1172,12 +1209,13 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod oldTickDuration = tickDuration; // Pattern loop is not executed in FT2 if there are any position jump or pattern break commands on the same row. - // Pattern loop is not executed in IT if there are any position jump commands on the same row. + // Pattern loop is not executed in IT if there are any position jump commands to the right (and to the left in older OpenMPT versions). // Test case for FT2 exception: PatLoop-Jumps.xm, PatLoop-Various.xm - // Test case for IT: exception: LoopBreak.it + // Test case for IT: exception: LoopBreak.it, sbx-priority.it if(patternLoopEndedOnThisRow - && (!m_playBehaviour[kFT2PatternLoopWithJumps] || !(positionJumpOnThisRow || patternBreakOnThisRow)) - && (!m_playBehaviour[kITPatternLoopWithJumps] || !positionJumpOnThisRow)) + && (!m_playBehaviour[kFT2PatternLoopWithJumps] || !(positionJumpOnThisRow || patternBreakOnThisRow)) + && (!m_playBehaviour[kITPatternLoopWithJumpsOld] || !positionJumpOnThisRow) + && (!m_playBehaviour[kITPatternLoopWithJumps] || !positionJumpRightOfPatternLoop)) { std::map startTimes; // This is really just a simple estimation for nested pattern loops. It should handle cases correctly where all parallel loops start and end on the same row. @@ -1193,17 +1231,18 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod { const double start = memory.chnSettings[nChn].patLoop; if(!startTimes[start]) startTimes[start] = 1; - startTimes[start] = mpt::lcm(startTimes[start], 1 + (param & 0x0F)); + startTimes[start] = std::lcm(startTimes[start], 1 + (param & 0x0F)); } } - for(const auto &i : startTimes) + for(const auto &[startTime, loopCount] : startTimes) { - memory.elapsedTime += (memory.elapsedTime - i.first) * (double)(i.second - 1); + memory.elapsedTime += (memory.elapsedTime - startTime) * (loopCount - 1); + //memory.elapsedBeats += 1.0 / playState.m_nCurrentRowsPerBeat; for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++) { - if(memory.chnSettings[nChn].patLoop == i.first) + if(memory.chnSettings[nChn].patLoop == startTime) { - playState.m_lTotalSampleCount += (playState.m_lTotalSampleCount - memory.chnSettings[nChn].patLoopSmp) * (i.second - 1); + playState.m_lTotalSampleCount += (playState.m_lTotalSampleCount - memory.chnSettings[nChn].patLoopSmp) * (loopCount - 1); if(m_playBehaviour[kITPatternLoopTargetReset] || (GetType() == MOD_TYPE_S3M)) { memory.chnSettings[nChn].patLoop = memory.elapsedTime; @@ -1242,7 +1281,7 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod } } - if(retval.targetReached || target.mode == GetLengthTarget::NoTarget) + if(retval.targetReached) { retval.lastOrder = playState.m_nCurrentOrder; retval.lastRow = playState.m_nRow; @@ -1316,11 +1355,11 @@ std::vector CSoundFile::GetLength(enmGetLengthResetMode adjustMod { Order.SetSequence(sequence); } - visitedSongRows = std::move(visitedRows); } + if(adjustMode & (eAdjust | eAdjustOnlyVisitedRows)) + visitedSongRows.MoveVisitedRowsFrom(visitedRows); return results; - } @@ -1389,11 +1428,11 @@ void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bo } // Special XM hack (also applies to MOD / S3M, except when playing IT-style S3Ms, such as k_vision.s3m) - // Test case: PortaSmpChange.mod, PortaSmpChange.s3m + // Test case: PortaSmpChange.mod, PortaSmpChange.s3m, PortaSwap.s3m if((!instrumentChanged && (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) && pIns) || (GetType() == MOD_TYPE_PLM) || (GetType() == MOD_TYPE_MOD && chn.IsSamplePlaying()) - || m_playBehaviour[kST3PortaSampleChange]) + || (m_playBehaviour[kST3PortaSampleChange] && chn.IsSamplePlaying())) { // FT2 doesn't change the sample in this case, // but still uses the sample info from the old one (bug?) @@ -1448,11 +1487,16 @@ void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bo } } - if(returnAfterVolumeAdjust && sampleChanged && m_playBehaviour[kMODSampleSwap] && pSmp != nullptr) + if(returnAfterVolumeAdjust && sampleChanged && pSmp != nullptr) { // ProTracker applies new instrument's finetune but keeps the old sample playing. // Test case: PortaSwapPT.mod - chn.nFineTune = pSmp->nFineTune; + if(m_playBehaviour[kMODSampleSwap]) + chn.nFineTune = pSmp->nFineTune; + // ST3 does it similarly for middle-C speed. + // Test case: PortaSwap.s3m, SampleSwap.s3m + if(GetType() == MOD_TYPE_S3M && pSmp->HasSampleData()) + chn.nC5Speed = pSmp->nC5Speed; } if(returnAfterVolumeAdjust) return; @@ -1496,12 +1540,9 @@ void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bo if(m_playBehaviour[kITEnvelopeReset]) { const bool insNumber = (instr != 0); - // IT compatibility: Note-off with instrument number + Old Effects retriggers envelopes. - // Test case: ResetEnvNoteOffOldFx.it - const bool isKeyOff = chn.dwFlags[CHN_NOTEFADE | CHN_KEYOFF] || (chn.rowCommand.note == NOTE_KEYOFF && m_playBehaviour[kITInstrWithNoteOffOldEffects]); reset = (!chn.nLength || (insNumber && bPorta && m_SongFlags[SONG_ITCOMPATGXX]) - || (insNumber && !bPorta && isKeyOff && m_SongFlags[SONG_ITOLDEFFECTS])); + || (insNumber && !bPorta && chn.dwFlags[CHN_NOTEFADE | CHN_KEYOFF] && m_SongFlags[SONG_ITOLDEFFECTS])); // NOTE: IT2.14 with SB/GUS/etc. output is different. We are going after IT's WAV writer here. // For SB/GUS/etc. emulation, envelope carry should only apply when the NNA isn't set to "Note Cut". // Test case: CarryNNA.it @@ -1558,15 +1599,18 @@ void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bo if(bPorta && pSmp == chn.pModSample && pSmp != nullptr) { // If channel length is 0, we cut a previous sample using SCx. In that case, we have to update sample length, loop points, etc... - if(GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT) && chn.nLength != 0) return; - chn.dwFlags.reset(CHN_KEYOFF | CHN_NOTEFADE); + if(GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT) && chn.nLength != 0) + return; + // FT2 compatibility: Do not reset key-off status on portamento without instrument number + // Test case: Off-Porta.xm + if(GetType() != MOD_TYPE_XM || !m_playBehaviour[kITFT2DontResetNoteOffOnPorta] || chn.rowCommand.instr != 0) + chn.dwFlags.reset(CHN_KEYOFF | CHN_NOTEFADE); chn.dwFlags = (chn.dwFlags & (CHN_CHANNELFLAGS | CHN_PINGPONGFLAG)); } else //if(!instrumentChanged || chn.rowCommand.instr != 0 || !IsCompatibleMode(TRK_FASTTRACKER2)) // SampleChange.xm? { chn.dwFlags.reset(CHN_KEYOFF | CHN_NOTEFADE); - // IT compatibility tentative fix: Don't change bidi loop direction when - // no sample nor instrument is changed. + // IT compatibility: Don't change bidi loop direction when no sample nor instrument is changed. if((m_playBehaviour[kITPingPongNoReset] || !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) && pSmp == chn.pModSample && !instrumentChanged) chn.dwFlags = (chn.dwFlags & (CHN_CHANNELFLAGS | CHN_PINGPONGFLAG)); else @@ -1616,7 +1660,7 @@ void CSoundFile::InstrumentChange(ModChannel &chn, uint32 instr, bool bPorta, bo } chn.nInsVol = oldInsVol; chn.nVolume = pSmp->nVolume; - if(pSmp->uFlags[CHN_PANNING]) chn.nPan = pSmp->nPan; + if(pSmp->uFlags[CHN_PANNING]) chn.SetInstrumentPan(pSmp->nPan, *this); return; } @@ -1693,7 +1737,7 @@ void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetE // save the note that's actually used, as it's necessary to properly calculate PPS and stuff const int realnote = note; - if((pIns) && (note - NOTE_MIN < (int)CountOf(pIns->Keyboard))) + if((pIns) && (note - NOTE_MIN < (int)std::size(pIns->Keyboard))) { uint32 n = pIns->Keyboard[note - NOTE_MIN]; if((n) && (n < MAX_SAMPLES)) @@ -1844,9 +1888,9 @@ void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetE if(pIns->nPanSwing) { chn.nPanSwing = static_cast(((mpt::random(AccessPRNG()) * pIns->nPanSwing * 4) / 128)); - if(!m_playBehaviour[kITSwingBehaviour]) + if(!m_playBehaviour[kITSwingBehaviour] && chn.nRestorePanOnNewNote == 0) { - chn.nRestorePanOnNewNote = static_cast(chn.nPan + 1); + chn.nRestorePanOnNewNote = static_cast(chn.nPan + 1); } } // Cutoff Swing @@ -1893,7 +1937,7 @@ void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetE chn.position.Set(0); if((m_SongFlags[SONG_PT_MODE] || m_playBehaviour[kST3OffsetWithoutInstrument]) && !chn.rowCommand.instr) { - chn.position.SetInt(std::min(chn.prevNoteOffset, chn.nLength - 1)); + chn.position.SetInt(std::min(chn.prevNoteOffset, chn.nLength - SmpLength(1))); } else { chn.prevNoteOffset = 0; @@ -1972,9 +2016,9 @@ void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetE } } - // IT compatibility: Don't reset key-off flag on porta notes unless Compat Gxx is enabled - // Test case: Off-Porta.it, Off-Porta-CompatGxx.it - if(m_playBehaviour[kITDontResetNoteOffOnPorta] && bPorta && (!m_SongFlags[SONG_ITCOMPATGXX] || chn.rowCommand.instr == 0)) + // IT compatibility: Don't reset key-off flag on porta notes unless Compat Gxx is enabled. + // Test case: Off-Porta.it, Off-Porta-CompatGxx.it, Off-Porta.xm + if(m_playBehaviour[kITFT2DontResetNoteOffOnPorta] && bPorta && (!m_SongFlags[SONG_ITCOMPATGXX] || chn.rowCommand.instr == 0)) chn.dwFlags.reset(CHN_EXTRALOUD); else chn.dwFlags.reset(CHN_EXTRALOUD | CHN_KEYOFF); @@ -2017,9 +2061,9 @@ void CSoundFile::NoteChange(ModChannel &chn, int note, bool bPorta, bool bResetE chn.nCutOff = pIns->GetCutoff(); useFilter = true; } - if(useFilter && (pIns->nFilterMode != FLTMODE_UNCHANGED)) + if(useFilter && (pIns->filterMode != FilterMode::Unchanged)) { - chn.nFilterMode = pIns->nFilterMode; + chn.nFilterMode = pIns->filterMode; } } else { @@ -2063,7 +2107,7 @@ void CSoundFile::ApplyInstrumentPanning(ModChannel &chn, const ModInstrument *in if(newPan != int32_min) { - chn.nPan = newPan; + chn.SetInstrumentPan(newPan, *this); // IT compatibility: Sample and instrument panning overrides channel surround status. // Test case: SmpInsPanSurround.it if(m_playBehaviour[kPanOverride] && !m_SongFlags[SONG_SURROUNDPAN]) @@ -2077,7 +2121,7 @@ void CSoundFile::ApplyInstrumentPanning(ModChannel &chn, const ModInstrument *in CHANNELINDEX CSoundFile::GetNNAChannel(CHANNELINDEX nChn) const { // Check for empty channel - for (CHANNELINDEX i = m_nChannels; i < MAX_CHANNELS; i++) + for(CHANNELINDEX i = m_nChannels; i < MAX_CHANNELS; i++) { const ModChannel &c = m_PlayState.Chn[i]; // No sample and no plugin playing @@ -2086,20 +2130,24 @@ CHANNELINDEX CSoundFile::GetNNAChannel(CHANNELINDEX nChn) const // Plugin channel with already released note if(!c.nLength && c.dwFlags[CHN_KEYOFF | CHN_NOTEFADE]) return i; + // Stopped OPL channel + if(c.dwFlags[CHN_ADLIB] && (!m_opl || !m_opl->IsActive(i))) + return i; } uint32 vol = 0x800000; if(nChn < MAX_CHANNELS) { const ModChannel &srcChn = m_PlayState.Chn[nChn]; - if(!srcChn.nFadeOutVol && srcChn.nLength) return 0; + if(!srcChn.nFadeOutVol && srcChn.nLength) + return CHANNELINDEX_INVALID; vol = (srcChn.nRealVolume << 9) | srcChn.nVolume; } // All channels are used: check for lowest volume - CHANNELINDEX result = 0; + CHANNELINDEX result = CHANNELINDEX_INVALID; uint32 envpos = 0; - for (CHANNELINDEX i = m_nChannels; i < MAX_CHANNELS; i++) + for(CHANNELINDEX i = m_nChannels; i < MAX_CHANNELS; i++) { const ModChannel &c = m_PlayState.Chn[i]; if(c.nLength && !c.nFadeOutVol) @@ -2108,7 +2156,8 @@ CHANNELINDEX CSoundFile::GetNNAChannel(CHANNELINDEX nChn) const // Rationale: We need volume envelopes in case e.g. all NNA channels are playing at full volume but are looping on a 0-volume envelope node. // But if global volume is not applied to master and the global volume temporarily drops to 0, we would kill arbitrary channels. Hence, add the note volume as well. uint32 v = (c.nRealVolume << 9) | c.nVolume; - if(c.dwFlags[CHN_LOOP]) v /= 2; + if(c.dwFlags[CHN_LOOP]) + v /= 2; if((v < vol) || ((v == vol) && (c.VolEnv.nEnvPosition > envpos))) { envpos = c.VolEnv.nEnvPosition; @@ -2122,23 +2171,20 @@ CHANNELINDEX CSoundFile::GetNNAChannel(CHANNELINDEX nChn) const CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, bool forceCut) { - CHANNELINDEX nnaChn = CHANNELINDEX_INVALID; ModChannel &srcChn = m_PlayState.Chn[nChn]; const ModInstrument *pIns = nullptr; if(!ModCommand::IsNote(static_cast(note))) - { - return nnaChn; - } + return CHANNELINDEX_INVALID; + // Always NNA cut - using if((!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_MT2)) || !m_nInstruments || forceCut) && !srcChn.HasMIDIOutput()) { if(!srcChn.nLength || srcChn.dwFlags[CHN_MUTE] || !(srcChn.rightVol | srcChn.leftVol)) - { return CHANNELINDEX_INVALID; - } - nnaChn = GetNNAChannel(nChn); - if(!nnaChn) return CHANNELINDEX_INVALID; + const CHANNELINDEX nnaChn = GetNNAChannel(nChn); + if(nnaChn == CHANNELINDEX_INVALID) + return CHANNELINDEX_INVALID; ModChannel &chn = m_PlayState.Chn[nnaChn]; // Copy Channel chn = srcChn; @@ -2161,18 +2207,24 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo } return nnaChn; } - if(instr > GetNumInstruments()) instr = 0; + if(instr > GetNumInstruments()) + instr = 0; const ModSample *pSample = srcChn.pModSample; // If no instrument is given, assume previous instrument to still be valid. // Test case: DNA-NoInstr.it pIns = instr > 0 ? Instruments[instr] : srcChn.pModInstrument; + auto dnaNote = note; if(pIns != nullptr) { - uint32 n = pIns->Keyboard[note - NOTE_MIN]; - note = pIns->NoteMap[note - NOTE_MIN]; - if ((n) && (n < MAX_SAMPLES)) + auto smp = pIns->Keyboard[note - NOTE_MIN]; + // IT compatibility: DCT = note uses pattern notes for comparison + // Note: This is not applied in case kITRealNoteMapping is not set to keep playback of legacy modules simple (chn.nNote is translated note in that case) + // Test case: dct_smp_note_test.it + if(!m_playBehaviour[kITDCTBehaviour] || !m_playBehaviour[kITRealNoteMapping]) + dnaNote = pIns->NoteMap[note - NOTE_MIN]; + if(smp > 0 && smp < MAX_SAMPLES) { - pSample = &Samples[n]; + pSample = &Samples[smp]; } else if(m_playBehaviour[kITEmptyNoteMapSlot] && !pIns->HasValidMIDIChannel()) { // Impulse Tracker ignores empty slots. @@ -2181,17 +2233,20 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo return CHANNELINDEX_INVALID; } } - if (srcChn.dwFlags[CHN_MUTE]) + if(srcChn.dwFlags[CHN_MUTE]) return CHANNELINDEX_INVALID; for(CHANNELINDEX i = nChn; i < MAX_CHANNELS; i++) - if(i >= m_nChannels || i == nChn) { + // Only apply to background channels, or the same pattern channel + if(i < m_nChannels && i != nChn) + continue; + ModChannel &chn = m_PlayState.Chn[i]; bool applyDNAtoPlug = false; if((chn.nMasterChn == nChn + 1 || i == nChn) && chn.pModInstrument != nullptr) { - bool bOk = false; + bool applyDNA = false; // Duplicate Check Type switch(chn.pModInstrument->nDCT) { @@ -2199,33 +2254,40 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo break; // Note case DCT_NOTE: - if(note && chn.nNote == note && pIns == chn.pModInstrument) bOk = true; - if(pIns && pIns->nMixPlug) applyDNAtoPlug = true; + if(dnaNote != NOTE_NONE && chn.nNote == dnaNote && pIns == chn.pModInstrument) + applyDNA = true; + if(pIns && pIns->nMixPlug) + applyDNAtoPlug = true; break; // Sample case DCT_SAMPLE: - if(pSample != nullptr && pSample == chn.pModSample) bOk = true; + // IT compatibility: DCT = sample only applies to same instrument + // Test case: dct_smp_note_test.it + if(pSample != nullptr && pSample == chn.pModSample && (pIns == chn.pModInstrument || !m_playBehaviour[kITDCTBehaviour])) + applyDNA = true; break; // Instrument case DCT_INSTRUMENT: - if(pIns == chn.pModInstrument) bOk = true; - if(pIns && pIns->nMixPlug) applyDNAtoPlug = true; + if(pIns == chn.pModInstrument) + applyDNA = true; + if(pIns && pIns->nMixPlug) + applyDNAtoPlug = true; break; // Plugin case DCT_PLUGIN: if(pIns && (pIns->nMixPlug) && (pIns->nMixPlug == chn.pModInstrument->nMixPlug)) { applyDNAtoPlug = true; - bOk = true; + applyDNA = true; } break; - } + // Duplicate Note Action - if (bOk) + if(applyDNA) { #ifndef NO_PLUGINS - if (applyDNAtoPlug && chn.nNote != NOTE_NONE) + if(applyDNAtoPlug && chn.nNote != NOTE_NONE) { switch(chn.pModInstrument->nDNA) { @@ -2258,7 +2320,7 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo // Note Fade case DNA_NOTEFADE: chn.dwFlags.set(CHN_NOTEFADE); - if(chn.dwFlags[CHN_ADLIB] && m_opl) + if(chn.dwFlags[CHN_ADLIB] && m_opl && !m_playBehaviour[kOPLwithNNA]) m_opl->NoteOff(i); break; } @@ -2293,72 +2355,84 @@ CHANNELINDEX CSoundFile::CheckNNA(CHANNELINDEX nChn, uint32 instr, int note, boo #endif // NO_PLUGINS // New Note Action - if(srcChn.IsSamplePlaying() || applyNNAtoPlug) - { - nnaChn = GetNNAChannel(nChn); - if(nnaChn != 0) - { - ModChannel &chn = m_PlayState.Chn[nnaChn]; - // Copy Channel - chn = srcChn; - chn.dwFlags.reset(CHN_VIBRATO | CHN_TREMOLO | CHN_PORTAMENTO); - chn.nPanbrelloOffset = 0; + if(!srcChn.IsSamplePlaying() && !applyNNAtoPlug) + return CHANNELINDEX_INVALID; - chn.nMasterChn = nChn < GetNumChannels() ? nChn + 1 : 0; - chn.nCommand = CMD_NONE; + CHANNELINDEX nnaChn = GetNNAChannel(nChn); + if(nnaChn == CHANNELINDEX_INVALID) + return CHANNELINDEX_INVALID; + + ModChannel &chn = m_PlayState.Chn[nnaChn]; + if(chn.dwFlags[CHN_ADLIB] && m_opl) + m_opl->NoteCut(nnaChn); + // Copy Channel + chn = srcChn; + chn.dwFlags.reset(CHN_VIBRATO | CHN_TREMOLO | CHN_PORTAMENTO); + chn.nPanbrelloOffset = 0; + + chn.nMasterChn = nChn < GetNumChannels() ? nChn + 1 : 0; + chn.nCommand = CMD_NONE; #ifndef NO_PLUGINS - if(applyNNAtoPlug && pPlugin) - { - switch(srcChn.nNNA) - { - case NNA_NOTEOFF: - case NNA_NOTECUT: - case NNA_NOTEFADE: - // Switch off note played on this plugin, on this tracker channel and midi channel - SendMIDINote(nChn, NOTE_KEYOFF, 0); - srcChn.nArpeggioLastNote = NOTE_NONE; - break; - case NNA_CONTINUE: - break; - } - } -#endif // NO_PLUGINS - - // Key Off the note - switch(srcChn.nNNA) - { - case NNA_NOTEOFF: - KeyOff(chn); - if(chn.dwFlags[CHN_ADLIB] && m_opl) - m_opl->NoteOff(nChn); - break; - case NNA_NOTECUT: - chn.nFadeOutVol = 0; - chn.dwFlags.set(CHN_NOTEFADE); - if(chn.dwFlags[CHN_ADLIB] && m_opl) - m_opl->NoteCut(nChn); - break; - case NNA_NOTEFADE: - chn.dwFlags.set(CHN_NOTEFADE); - if(chn.dwFlags[CHN_ADLIB] && m_opl) - m_opl->NoteOff(nChn); - break; - case NNA_CONTINUE: - if(chn.dwFlags[CHN_ADLIB] && m_opl) - m_opl->MoveChannel(nChn, nnaChn); - break; - } - if(!chn.nVolume) - { - chn.nFadeOutVol = 0; - chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP); - } - // Stop this channel - srcChn.nLength = 0; - srcChn.position.Set(0); - srcChn.nROfs = srcChn.nLOfs = 0; + if(applyNNAtoPlug && pPlugin) + { + switch(srcChn.nNNA) + { + case NNA_NOTEOFF: + case NNA_NOTECUT: + case NNA_NOTEFADE: + // Switch off note played on this plugin, on this tracker channel and midi channel + SendMIDINote(nChn, NOTE_KEYOFF, 0); + srcChn.nArpeggioLastNote = NOTE_NONE; + break; + case NNA_CONTINUE: + break; } } +#endif // NO_PLUGINS + + // Key Off the note + switch(srcChn.nNNA) + { + case NNA_NOTEOFF: + KeyOff(chn); + if(chn.dwFlags[CHN_ADLIB] && m_opl) + { + m_opl->NoteOff(nChn); + if(m_playBehaviour[kOPLwithNNA]) + m_opl->MoveChannel(nChn, nnaChn); + } + break; + case NNA_NOTECUT: + chn.nFadeOutVol = 0; + chn.dwFlags.set(CHN_NOTEFADE); + if(chn.dwFlags[CHN_ADLIB] && m_opl) + m_opl->NoteCut(nChn); + break; + case NNA_NOTEFADE: + chn.dwFlags.set(CHN_NOTEFADE); + if(chn.dwFlags[CHN_ADLIB] && m_opl) + { + if(m_playBehaviour[kOPLwithNNA]) + m_opl->MoveChannel(nChn, nnaChn); + else + m_opl->NoteOff(nChn); + } + break; + case NNA_CONTINUE: + if(chn.dwFlags[CHN_ADLIB] && m_opl) + m_opl->MoveChannel(nChn, nnaChn); + break; + } + if(!chn.nVolume) + { + chn.nFadeOutVol = 0; + chn.dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP); + } + // Stop this channel + srcChn.nLength = 0; + srcChn.position.Set(0); + srcChn.nROfs = srcChn.nLOfs = 0; + return nnaChn; } @@ -2419,7 +2493,7 @@ bool CSoundFile::ProcessEffects() { PlugParamValue targetvalue = ModCommand::GetValueEffectCol(chn.rowCommand.command, chn.rowCommand.param) / PlugParamValue(ModCommand::maxColumnValue); chn.m_plugParamTargetValue = targetvalue; - chn.m_plugParamValueStep = (targetvalue - m_MixPlugins[plugin-1].pMixPlugin->GetParameter(plugparam)) / float(GetNumTicksOnCurrentRow()); + chn.m_plugParamValueStep = (targetvalue - m_MixPlugins[plugin - 1].pMixPlugin->GetParameter(plugparam)) / PlugParamValue(GetNumTicksOnCurrentRow()); } if(m_PlayState.m_nTickCount + 1 == GetNumTicksOnCurrentRow()) { // On last tick, set parameter exactly to target value. @@ -2507,6 +2581,10 @@ bool CSoundFile::ProcessEffects() } nPatLoopRow = nloop; + // IT compatibility: SBx is prioritized over Position Jump (Bxx) effects that are located left of the SBx effect. + // Test case: sbx-priority.it, LoopBreak.it + if(m_playBehaviour[kITPatternLoopWithJumps]) + nPosJump = ORDERINDEX_INVALID; } if(GetType() == MOD_TYPE_S3M) @@ -2664,8 +2742,10 @@ bool CSoundFile::ProcessEffects() // Test cases: keyoff+instr.xm, delay.xm bool reloadSampleSettings = (m_playBehaviour[kFT2ReloadSampleSettings] && instr != 0); // ProTracker Compatibility: If a sample was stopped before, lone instrument numbers can retrigger it - // Test case: PTSwapEmpty.mod - bool keepInstr = (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || (m_playBehaviour[kMODSampleSwap] && !chn.IsSamplePlaying() && chn.pModSample != nullptr && !chn.pModSample->HasSampleData()); + // Test case: PTSwapEmpty.mod, PTInstrVolume.mod, SampleSwap.s3m + bool keepInstr = (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) + || m_playBehaviour[kST3SampleSwap] + || (m_playBehaviour[kMODSampleSwap] && !chn.IsSamplePlaying() && (chn.pModSample == nullptr || !chn.pModSample->HasSampleData())); // Now it's time for some FT2 crap... if (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) @@ -2735,12 +2815,12 @@ bool CSoundFile::ProcessEffects() if(oldSample != nullptr) { - if(!oldSample->uFlags[SMP_NODEFAULTVOLUME]) + if(!oldSample->uFlags[SMP_NODEFAULTVOLUME] && (GetType() != MOD_TYPE_S3M || oldSample->HasSampleData())) chn.nVolume = oldSample->nVolume; if(reloadSampleSettings) { // Also reload panning - chn.nPan = oldSample->nPan; + chn.SetInstrumentPan(oldSample->nPan, *this); } } } @@ -2752,6 +2832,26 @@ bool CSoundFile::ProcessEffects() chn.nTremorCount = 0x20; } + // IT compatibility: Envelope retriggering with instrument number based on Old Effects and Compatible Gxx flags: + // OldFX CompatGxx Env Behaviour + // ----- --------- ------------- + // off off never reset + // on off reset on instrument without portamento + // off on reset on instrument with portamento + // on on always reset + // Test case: ins-xx.it, ins-ox.it, ins-oc.it, ins-xc.it, ResetEnvNoteOffOldFx.it, ResetEnvNoteOffOldFx2.it, noteoff3.it + if(GetNumInstruments() && m_playBehaviour[kITInstrWithNoteOffOldEffects] + && instr && !ModCommand::IsNote(note)) + { + if((bPorta && m_SongFlags[SONG_ITCOMPATGXX]) + || (!bPorta && m_SongFlags[SONG_ITOLDEFFECTS])) + { + chn.ResetEnvelopes(); + chn.dwFlags.set(CHN_FASTVOLRAMP); + chn.nFadeOutVol = 65536; + } + } + if(retrigEnv) //Case: instrument with no note data. { //IT compatibility: Instrument with no note. @@ -2773,7 +2873,7 @@ bool CSoundFile::ProcessEffects() } } - if (GetNumInstruments() && (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))) + if(GetNumInstruments() && (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED))) { chn.ResetEnvelopes(); chn.dwFlags.set(CHN_FASTVOLRAMP); @@ -2829,7 +2929,9 @@ bool CSoundFile::ProcessEffects() { if(chn.nRestorePanOnNewNote > 0) { - chn.nPan = chn.nRestorePanOnNewNote - 1; + chn.nPan = (chn.nRestorePanOnNewNote & 0x7FFF) - 1; + if(chn.nRestorePanOnNewNote & 0x8000) + chn.dwFlags.set(CHN_SURROUND); chn.nRestorePanOnNewNote = 0; } if(chn.nRestoreResonanceOnNewNote > 0) @@ -2876,11 +2978,18 @@ bool CSoundFile::ProcessEffects() //const bool newInstrument = oldInstrument != chn.pModInstrument && chn.pModInstrument->Keyboard[chn.nNewNote - NOTE_MIN] != 0; chn.position.Set(0); } - } else if ((GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT) && oldSample != chn.pModSample && ModCommand::IsNote(note))) + } else if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && oldSample != chn.pModSample && ModCommand::IsNote(note)) { // Special IT case: portamento+note causes sample change -> ignore portamento bPorta = false; - } else if(m_playBehaviour[kMODSampleSwap] && chn.increment.IsZero()) + } else if(m_playBehaviour[kST3SampleSwap] && oldSample != chn.pModSample && (bPorta || !ModCommand::IsNote(note)) && chn.position.GetUInt() > chn.nLength) + { + // ST3 with SoundBlaster does sample swapping and continues playing the new sample where the old sample was stopped. + // If the new sample is shorter than that, it is stopped, even if it could be looped. + // This also applies to portamento between different samples. + // Test case: SampleSwap.s3m + chn.nLength = 0; + } else if(m_playBehaviour[kMODSampleSwap] && !chn.IsSamplePlaying()) { // If channel was paused and is resurrected by a lone instrument number, reset the sample position. // Test case: PTSwapEmpty.mod @@ -2958,30 +3067,10 @@ bool CSoundFile::ProcessEffects() { if (volcmd == VOLCMD_TONEPORTAMENTO) { - uint32 porta = 0; - if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_AMS | MOD_TYPE_DMF | MOD_TYPE_DBM | MOD_TYPE_IMF | MOD_TYPE_PSM | MOD_TYPE_J2B | MOD_TYPE_ULT | MOD_TYPE_OKT | MOD_TYPE_MT2 | MOD_TYPE_MDL)) - { - porta = ImpulseTrackerPortaVolCmd[vol & 0x0F]; - } else - { - if(cmd == CMD_TONEPORTAMENTO && GetType() == MOD_TYPE_XM) - { - // Yes, FT2 is *that* weird. If there is a Mx command in the volume column - // and a normal 3xx command, the 3xx command is ignored but the Mx command's - // effectiveness is doubled. - // Test case: TonePortamentoMemory.xm - cmd = CMD_NONE; - vol *= 2; - } - porta = vol << 4; + const auto [porta, clearEffectCommand] = GetVolCmdTonePorta(chn.rowCommand, nStartTick); + if(clearEffectCommand) + cmd = CMD_NONE; - // FT2 compatibility: If there's a portamento and a note delay, execute the portamento, but don't update the parameter - // Test case: PortaDelay.xm - if(m_playBehaviour[kFT2PortaDelay] && nStartTick != 0) - { - porta = 0; - } - } TonePortamento(chn, porta); } else { @@ -3001,7 +3090,7 @@ bool CSoundFile::ProcessEffects() { chn.nPan = 0; } - MPT_FALLTHROUGH; + [[fallthrough]]; default: // no memory here. volcmd = VOLCMD_NONE; @@ -3203,7 +3292,7 @@ bool CSoundFile::ProcessEffects() case CMD_OFFSETPERCENTAGE: if(triggerNote) { - SampleOffset(chn, Util::muldiv_unsigned(chn.nLength, param, 255)); + SampleOffset(chn, Util::muldiv_unsigned(chn.nLength, param, 256)); } break; @@ -3529,9 +3618,12 @@ bool CSoundFile::ProcessEffects() // Pattern Break / Position Jump only if no loop running // Exception: FastTracker 2 in all cases, Impulse Tracker in case of position jump // Test case for FT2 exception: PatLoop-Jumps.xm, PatLoop-Various.xm - // Test case for IT: exception: LoopBreak.it + // Test case for IT: exception: LoopBreak.it, sbx-priority.it if((doBreakRow || doPosJump) - && (!doPatternLoop || m_playBehaviour[kFT2PatternLoopWithJumps] || (m_playBehaviour[kITPatternLoopWithJumps] && doPosJump))) + && (!doPatternLoop + || m_playBehaviour[kFT2PatternLoopWithJumps] + || (m_playBehaviour[kITPatternLoopWithJumps] && doPosJump) + || (m_playBehaviour[kITPatternLoopWithJumpsOld] && doPosJump))) { if(!doPosJump) nPosJump = m_PlayState.m_nCurrentOrder + 1; if(!doBreakRow) nBreakRow = 0; @@ -3561,7 +3653,12 @@ bool CSoundFile::ProcessEffects() // Pattern Loop m_PlayState.m_nNextOrder = m_PlayState.m_nCurrentOrder; m_PlayState.m_nNextRow = nPatLoopRow; - if(m_PlayState.m_nPatternDelay) + // FT2 skips the first row of the pattern loop if there's a pattern delay, ProTracker sometimes does it too (didn't quite figure it out yet). + // But IT and ST3 don't do this. + // Test cases: PatLoopWithDelay.it, PatLoopWithDelay.s3m + if(m_PlayState.m_nPatternDelay + && (GetType() != MOD_TYPE_IT || !m_playBehaviour[kITPatternLoopWithJumps]) + && GetType() != MOD_TYPE_S3M) { m_PlayState.m_nNextRow++; } @@ -3723,7 +3820,10 @@ void CSoundFile::PortamentoUp(CHANNELINDEX nChn, ModCommand::PARAM param, const } } // Regular Slide - if(!chn.isFirstTick || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1]) || GetType() == MOD_TYPE_669) + if(!chn.isFirstTick + || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1]) + || GetType() == MOD_TYPE_669 + || (GetType() == MOD_TYPE_MED && m_SongFlags[SONG_FASTVOLSLIDES])) { DoFreqSlide(chn, -int(param) * 4); } @@ -3788,7 +3888,10 @@ void CSoundFile::PortamentoDown(CHANNELINDEX nChn, ModCommand::PARAM param, cons } } - if(!chn.isFirstTick || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1]) || GetType() == MOD_TYPE_669) + if(!chn.isFirstTick + || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1]) + || GetType() == MOD_TYPE_669 + || (GetType() == MOD_TYPE_MED && m_SongFlags[SONG_FASTVOLSLIDES])) { DoFreqSlide(chn, int(param) * 4); } @@ -3798,7 +3901,7 @@ void CSoundFile::PortamentoDown(CHANNELINDEX nChn, ModCommand::PARAM param, cons // Send portamento commands to plugins void CSoundFile::MidiPortamento(CHANNELINDEX nChn, int param, bool doFineSlides) { - int actualParam = mpt::abs(param); + int actualParam = std::abs(param); int pitchBend = 0; // Old MIDI Pitch Bends: @@ -3845,6 +3948,7 @@ void CSoundFile::MidiPortamento(CHANNELINDEX nChn, int param, bool doFineSlides) void CSoundFile::FinePortamentoUp(ModChannel &chn, ModCommand::PARAM param) const { + MPT_ASSERT(!chn.HasCustomTuning()); if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked @@ -3890,6 +3994,7 @@ void CSoundFile::FinePortamentoUp(ModChannel &chn, ModCommand::PARAM param) cons void CSoundFile::FinePortamentoDown(ModChannel &chn, ModCommand::PARAM param) const { + MPT_ASSERT(!chn.HasCustomTuning()); if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked @@ -3927,6 +4032,7 @@ void CSoundFile::FinePortamentoDown(ModChannel &chn, ModCommand::PARAM param) co void CSoundFile::ExtraFinePortamentoUp(ModChannel &chn, ModCommand::PARAM param) const { + MPT_ASSERT(!chn.HasCustomTuning()); if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked @@ -3966,6 +4072,7 @@ void CSoundFile::ExtraFinePortamentoUp(ModChannel &chn, ModCommand::PARAM param) void CSoundFile::ExtraFinePortamentoDown(ModChannel &chn, ModCommand::PARAM param) const { + MPT_ASSERT(!chn.HasCustomTuning()); if(GetType() == MOD_TYPE_XM) { // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked @@ -4014,8 +4121,11 @@ void CSoundFile::NoteSlide(ModChannel &chn, uint32 param, bool slideUp, bool ret { chn.nNoteSlideCounter = chn.nNoteSlideSpeed; // update it - chn.nPeriod = GetPeriodFromNote - ((slideUp ? 1 : -1) * chn.nNoteSlideStep + GetNoteFromPeriod(chn.nPeriod), 8363, 0); + const int32 delta = (slideUp ? 1 : -1) * chn.nNoteSlideStep; + if(chn.HasCustomTuning()) + chn.m_PortamentoFineSteps += delta * chn.pModInstrument->pTuning->GetFineStepCount(); + else + chn.nPeriod = GetPeriodFromNote(delta + GetNoteFromPeriod(chn.nPeriod), 8363, 0); if(retrig) { @@ -4025,6 +4135,36 @@ void CSoundFile::NoteSlide(ModChannel &chn, uint32 param, bool slideUp, bool ret } } + +std::pair CSoundFile::GetVolCmdTonePorta(const ModCommand &m, uint32 startTick) const +{ + if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_AMS | MOD_TYPE_DMF | MOD_TYPE_DBM | MOD_TYPE_IMF | MOD_TYPE_PSM | MOD_TYPE_J2B | MOD_TYPE_ULT | MOD_TYPE_OKT | MOD_TYPE_MT2 | MOD_TYPE_MDL)) + { + return {ImpulseTrackerPortaVolCmd[m.vol & 0x0F], false}; + } else + { + bool clearEffectColumn = false; + uint32 vol = m.vol; + if(m.command == CMD_TONEPORTAMENTO && GetType() == MOD_TYPE_XM) + { + // Yes, FT2 is *that* weird. If there is a Mx command in the volume column + // and a normal 3xx command, the 3xx command is ignored but the Mx command's + // effectiveness is doubled. + // Test case: TonePortamentoMemory.xm + clearEffectColumn = true; + vol *= 2; + } + + // FT2 compatibility: If there's a portamento and a note delay, execute the portamento, but don't update the parameter + // Test case: PortaDelay.xm + if(m_playBehaviour[kFT2PortaDelay] && startTick != 0) + return {0, clearEffectColumn}; + else + return {vol * 16, clearEffectColumn}; + } +} + + // Portamento Slide void CSoundFile::TonePortamento(ModChannel &chn, uint32 param) const { @@ -4037,11 +4177,11 @@ void CSoundFile::TonePortamento(ModChannel &chn, uint32 param) const chn.nOldPortaUp = chn.nOldPortaDown = static_cast(param); } - if(GetType() == MOD_TYPE_MPT && chn.pModInstrument && chn.pModInstrument->pTuning) + if(chn.HasCustomTuning()) { //Behavior: Param tells number of finesteps(or 'fullsteps'(notes) with glissando) //to slide per row(not per tick). - const int32 old_PortamentoTickSlide = (m_PlayState.m_nTickCount != 0) ? chn.m_PortamentoTickSlide : 0; + const int32 oldPortamentoTickSlide = (m_PlayState.m_nTickCount != 0) ? chn.m_PortamentoTickSlide : 0; if(param) chn.nPortamentoSlide = param; @@ -4062,9 +4202,9 @@ void CSoundFile::TonePortamento(ModChannel &chn, uint32 param) const //With glissando interpreting param as notes instead of finesteps. } - const int32 slide = chn.m_PortamentoTickSlide - old_PortamentoTickSlide; + const int32 slide = chn.m_PortamentoTickSlide - oldPortamentoTickSlide; - if(mpt::abs(chn.nPortamentoDest) <= mpt::abs(slide)) + if(std::abs(chn.nPortamentoDest) <= std::abs(slide)) { if(chn.nPortamentoDest != 0) { @@ -4080,9 +4220,12 @@ void CSoundFile::TonePortamento(ModChannel &chn, uint32 param) const } return; - } //End candidate MPT behavior. + } - bool doPorta = !chn.isFirstTick || (GetType() & (MOD_TYPE_DBM | MOD_TYPE_669)) || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1]); + bool doPorta = !chn.isFirstTick + || (GetType() & (MOD_TYPE_DBM | MOD_TYPE_669)) + || (m_PlayState.m_nMusicSpeed == 1 && m_playBehaviour[kSlidesAtSpeed1]) + || (GetType() == MOD_TYPE_MED && m_SongFlags[SONG_FASTVOLSLIDES]); if(GetType() == MOD_TYPE_PLM && param >= 0xF0) { param -= 0xF0; @@ -4484,9 +4627,7 @@ void CSoundFile::ExtendedMODCommands(CHANNELINDEX nChn, ModCommand::PARAM param) case 0x40: chn.nVibratoType = param & 0x07; break; // E5x: Set FineTune case 0x50: if(!m_SongFlags[SONG_FIRSTTICK]) - { break; - } if(GetType() & (MOD_TYPE_MOD | MOD_TYPE_DIGI | MOD_TYPE_AMF0 | MOD_TYPE_MED)) { chn.nFineTune = MOD2XMFineTune(param); @@ -4551,12 +4692,14 @@ void CSoundFile::ExtendedS3MCommands(CHANNELINDEX nChn, ModCommand::PARAM param) // S1x: Set Glissando Control case 0x10: chn.dwFlags.set(CHN_GLISSANDO, param != 0); break; // S2x: Set FineTune - case 0x20: if(!m_SongFlags[SONG_FIRSTTICK]) break; + case 0x20: if(!m_SongFlags[SONG_FIRSTTICK]) + break; if(GetType() != MOD_TYPE_669) { chn.nC5Speed = S3MFineTuneTable[param]; chn.nFineTune = MOD2XMFineTune(param); - if (chn.nPeriod) chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed); + if(chn.nPeriod) + chn.nPeriod = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed); } else if(chn.pModSample != nullptr) { chn.nC5Speed = chn.pModSample->nC5Speed + param * 80; @@ -4800,7 +4943,7 @@ void CSoundFile::InvertLoop(ModChannel &chn) // TRASH IT!!! (Yes, the sample!) uint8 &sample = mpt::byte_cast(pModSample->sampleb())[pModSample->nLoopStart + chn.nEFxOffset]; sample = ~sample; - ctrlSmp::PrecomputeLoops(*pModSample, *this, false); + pModSample->PrecomputeLoops(*this, false); } @@ -4855,22 +4998,22 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char * const int swing = (m_playBehaviour[kITSwingBehaviour] || m_playBehaviour[kMPTOldSwingBehaviour]) ? chn.nVolSwing : 0; const int vol = Util::muldiv((chn.nVolume + swing) * m_PlayState.m_nGlobalVolume, chn.nGlobalVol * chn.nInsVol, 1 << 20); data = static_cast(Clamp(vol / 2, 1, 127)); - //data = (unsigned char)MIN((chn.nVolume * chn.nGlobalVol * m_nGlobalVolume) >> (1 + 6 + 8), 127); + //data = (unsigned char)std::min((chn.nVolume * chn.nGlobalVol * m_nGlobalVolume) >> (1 + 6 + 8), 127); } else if(macro[pos] == 'u') { // Calculated volume // Same note as with velocity applies here, but apparently also for instrument / sample volumes? const int vol = Util::muldiv(chn.nCalcVolume * m_PlayState.m_nGlobalVolume, chn.nGlobalVol * chn.nInsVol, 1 << 26); data = static_cast(Clamp(vol / 2, 1, 127)); - //data = (unsigned char)MIN((chn.nCalcVolume * chn.nGlobalVol * m_nGlobalVolume) >> (7 + 6 + 8), 127); + //data = (unsigned char)std::min((chn.nCalcVolume * chn.nGlobalVol * m_nGlobalVolume) >> (7 + 6 + 8), 127); } else if(macro[pos] == 'x') { // Pan set - data = static_cast(std::min(chn.nPan / 2, 127)); + data = static_cast(std::min(static_cast(chn.nPan / 2), 127)); } else if(macro[pos] == 'y') { // Calculated pan - data = static_cast(std::min(chn.nRealPan / 2, 127)); + data = static_cast(std::min(static_cast(chn.nRealPan / 2), 127)); } else if(macro[pos] == 'a') { // High byte of bank select @@ -5012,7 +5155,7 @@ void CSoundFile::ProcessMIDIMacro(CHANNELINDEX nChn, bool isSmooth, const char * } else { // Other MIDI messages - sendLen = std::min(MIDIEvents::GetEventLength(out[sendPos]), outPos - sendPos); + sendLen = std::min(static_cast(MIDIEvents::GetEventLength(out[sendPos])), outPos - sendPos); } if(sendLen == 0) @@ -5124,7 +5267,7 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned // F0.F0.02.xx: Set filter mode (high nibble determines filter mode) if(param < 0x20) { - chn.nFilterMode = (param >> 4); + chn.nFilterMode = static_cast(param >> 4); SetupChannelFilter(chn, !chn.dwFlags[CHN_FILTER]); } @@ -5194,10 +5337,10 @@ uint32 CSoundFile::SendMIDIData(CHANNELINDEX nChn, bool isSmooth, const unsigned { if(macro[0] == 0xF0) { - pPlugin->MidiSysexSend(mpt::as_span(mpt::byte_cast(macro), macroLen)); + pPlugin->MidiSysexSend(mpt::as_span(mpt::byte_cast(macro), macroLen)); } else { - uint32 len = std::min(MIDIEvents::GetEventLength(macro[0]), macroLen); + uint32 len = std::min(static_cast(MIDIEvents::GetEventLength(macro[0])), macroLen); uint32 curData = 0; memcpy(&curData, macro, len); pPlugin->MidiSend(curData); @@ -5251,9 +5394,11 @@ void CSoundFile::SampleOffset(ModChannel &chn, SmpLength param) const chn.prevNoteOffset += param; - if(param >= chn.nLoopEnd && GetType() == MOD_TYPE_MTM && chn.dwFlags[CHN_LOOP] && chn.nLoopEnd > 0) + if(param >= chn.nLoopEnd && (GetType() & (MOD_TYPE_S3M | MOD_TYPE_MTM)) && chn.dwFlags[CHN_LOOP] && chn.nLoopEnd > 0) { // Offset wrap-around + // Note that ST3 only does this in GUS mode. SoundBlaster stops the sample entirely instead. + // Test case: OffsetLoopWraparound.s3m param = (param - chn.nLoopStart) % (chn.nLoopEnd - chn.nLoopStart) + chn.nLoopStart; } @@ -5288,7 +5433,14 @@ void CSoundFile::SampleOffset(ModChannel &chn, SmpLength param) const if (chn.position.GetUInt() >= chn.nLength || (chn.dwFlags[CHN_LOOP] && chn.position.GetUInt() >= chn.nLoopEnd)) { // Offset beyond sample size - if (!(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MOD | MOD_TYPE_MTM))) + if(m_playBehaviour[kFT2ST3OffsetOutOfRange] || GetType() == MOD_TYPE_MTM) + { + // FT2 Compatibility: Don't play note if offset is beyond sample length + // ST3 Compatibility: Don't play note if offset is beyond sample length (non-looped samples only) + // Test cases: 3xx-no-old-samp.xm, OffsetPastSampleEnd.s3m + chn.dwFlags.set(CHN_FASTVOLRAMP); + chn.nPeriod = 0; + } else if(!(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MOD))) { // IT Compatibility: Offset if(m_playBehaviour[kITOffset]) @@ -5305,12 +5457,6 @@ void CSoundFile::SampleOffset(ModChannel &chn, SmpLength param) const chn.position.Set(chn.nLength - 2); } } - } else if(m_playBehaviour[kFT2OffsetOutOfRange] || GetType() == MOD_TYPE_MTM) - { - // FT2 Compatibility: Don't play note if offset is beyond sample length - // Test case: 3xx-no-old-samp.xm - chn.dwFlags.set(CHN_FASTVOLRAMP); - chn.nPeriod = 0; } else if(GetType() == MOD_TYPE_MOD && chn.dwFlags[CHN_LOOP]) { chn.position.Set(chn.nLoopStart); @@ -5332,7 +5478,7 @@ void CSoundFile::ReverseSampleOffset(ModChannel &chn, ModCommand::PARAM param) c chn.dwFlags.set(CHN_PINGPONGFLAG); chn.dwFlags.reset(CHN_LOOP); chn.nLength = chn.pModSample->nLength; // If there was a loop, extend sample to whole length. - chn.position.Set((chn.nLength - 1) - std::min(SmpLength(param) << 8, chn.nLength - 1), 0); + chn.position.Set((chn.nLength - 1) - std::min(SmpLength(param) << 8, chn.nLength - SmpLength(1)), 0); } } @@ -5364,7 +5510,8 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) { // Here are some really stupid things FT2 does on the first tick. // Test case: RetrigTick0.xm - if(chn.rowCommand.instr > 0 && chn.rowCommand.IsNoteOrEmpty()) retrigCount = 1; + if(chn.rowCommand.instr > 0 && chn.rowCommand.IsNoteOrEmpty()) + retrigCount = 1; if(chn.rowCommand.volcmd == VOLCMD_VOLUME && chn.rowCommand.vol != 0) { // I guess this condition simply checked if the volume byte was != 0 in FT2. @@ -5385,8 +5532,10 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) // old routines if (GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT)) { - if (!retrigSpeed) retrigSpeed = 1; - if ((retrigCount) && (!(retrigCount % retrigSpeed))) doRetrig = true; + if(!retrigSpeed) + retrigSpeed = 1; + if(retrigCount && !(retrigCount % retrigSpeed)) + doRetrig = true; retrigCount++; } else if(GetType() == MOD_TYPE_MTM) { @@ -5396,16 +5545,21 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) { int realspeed = retrigSpeed; // FT2 bug: if a retrig (Rxy) occurs together with a volume command, the first retrig interval is increased by one tick - if ((param & 0x100) && (chn.rowCommand.volcmd == VOLCMD_VOLUME) && (chn.rowCommand.param & 0xF0)) realspeed++; + if((param & 0x100) && (chn.rowCommand.volcmd == VOLCMD_VOLUME) && (chn.rowCommand.param & 0xF0)) + realspeed++; if(!m_SongFlags[SONG_FIRSTTICK] || (param & 0x100)) { - if (!realspeed) realspeed = 1; - if ((!(param & 0x100)) && (m_PlayState.m_nMusicSpeed) && (!(m_PlayState.m_nTickCount % realspeed))) doRetrig = true; + if(!realspeed) + realspeed = 1; + if(!(param & 0x100) && m_PlayState.m_nMusicSpeed && !(m_PlayState.m_nTickCount % realspeed)) + doRetrig = true; retrigCount++; - } else if (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2)) retrigCount = 0; + } else if(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) + retrigCount = 0; if (retrigCount >= realspeed) { - if ((m_PlayState.m_nTickCount) || ((param & 0x100) && (!chn.rowCommand.note))) doRetrig = true; + if(m_PlayState.m_nTickCount || ((param & 0x100) && !chn.rowCommand.note)) + doRetrig = true; } if(m_playBehaviour[kFT2Retrigger] && param == 0) { @@ -5418,21 +5572,23 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) // IT compatibility: If a sample is shorter than the retrig time (i.e. it stops before the retrig counter hits zero), it is not retriggered. // Test case: retrig-short.it if(chn.nLength == 0 && m_playBehaviour[kITShortSampleRetrig] && !chn.HasMIDIOutput()) - { return; - } + // ST3 compatibility: No retrig after Note Cut + // Test case: RetrigAfterNoteCut.s3m + if(m_playBehaviour[kST3RetrigAfterNoteCut] && !chn.nFadeOutVol) + return; if(doRetrig) { uint32 dv = (param >> 4) & 0x0F; int vol = chn.nVolume; - if (dv) + if(dv) { // FT2 compatibility: Retrig + volume will not change volume of retrigged notes if(!m_playBehaviour[kFT2Retrigger] || !(chn.rowCommand.volcmd == VOLCMD_VOLUME)) { - if (retrigTable1[dv]) + if(retrigTable1[dv]) vol = (vol * retrigTable1[dv]) >> 4; else vol += ((int)retrigTable2[dv]) << 2; @@ -5443,22 +5599,34 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) } uint32 note = chn.nNewNote; int32 oldPeriod = chn.nPeriod; - if (note >= NOTE_MIN && note <= NOTE_MAX && chn.nLength) + const bool retrigAdlib = chn.dwFlags[CHN_ADLIB] && m_playBehaviour[kOPLRealRetrig]; + if(note >= NOTE_MIN && note <= NOTE_MAX && chn.nLength && retrigAdlib) CheckNNA(nChn, 0, note, true); bool resetEnv = false; if(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) { - if((chn.rowCommand.instr) && (param < 0x100)) + if(chn.rowCommand.instr && param < 0x100) { InstrumentChange(chn, chn.rowCommand.instr, false, false); resetEnv = true; } - if (param < 0x100) resetEnv = true; + if(param < 0x100) + resetEnv = true; } - bool fading = chn.dwFlags[CHN_NOTEFADE]; + if(retrigAdlib && chn.pModSample && m_opl) + { + m_opl->NoteCut(nChn); + m_opl->Patch(nChn, chn.pModSample->adlib); + } + + const bool fading = chn.dwFlags[CHN_NOTEFADE]; + const auto oldPrevNoteOffset = chn.prevNoteOffset; + chn.prevNoteOffset = 0; // Retriggered notes should not use previous offset (test case: OxxMemoryWithRetrig.s3m) // IT compatibility: Really weird combination of envelopes and retrigger (see Storlek's q.it testcase) // Test case: retrig.it NoteChange(chn, note, m_playBehaviour[kITRetrigger], resetEnv, false, nChn); + if(!chn.rowCommand.instr) + chn.prevNoteOffset = oldPrevNoteOffset; // XM compatibility: Prevent NoteChange from resetting the fade flag in case an instrument number + note-off is present. // Test case: RetrigFade.xm if(fading && GetType() == MOD_TYPE_XM) @@ -5471,22 +5639,27 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) ProcessMidiOut(nChn); //Send retrig to Midi #endif // NO_PLUGINS } - if ((GetType() & (MOD_TYPE_IT|MOD_TYPE_MPT)) && (!chn.rowCommand.note) && (oldPeriod)) chn.nPeriod = oldPeriod; - if (!(GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT))) retrigCount = 0; + if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && chn.rowCommand.note == NOTE_NONE && oldPeriod != 0) + chn.nPeriod = oldPeriod; + if(!(GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT))) + retrigCount = 0; // IT compatibility: see previous IT compatibility comment =) if(m_playBehaviour[kITRetrigger]) chn.position.Set(0); offset--; if(offset >= 0 && offset <= static_cast(CountOf(chn.pModSample->cues)) && chn.pModSample != nullptr) { - if(offset == 0) offset = chn.oldOffset; - else offset = chn.oldOffset = chn.pModSample->cues[offset - 1]; + if(offset == 0) + offset = chn.oldOffset; + else + offset = chn.oldOffset = chn.pModSample->cues[offset - 1]; SampleOffset(chn, offset); } } // buggy-like-hell FT2 Rxy retrig! - if(m_playBehaviour[kFT2Retrigger] && (param & 0x100)) retrigCount++; + if(m_playBehaviour[kFT2Retrigger] && (param & 0x100)) + retrigCount++; // Now we can also store the retrig value for IT... if(!m_playBehaviour[kITRetrigger]) @@ -5496,7 +5669,10 @@ void CSoundFile::RetrigNote(CHANNELINDEX nChn, int param, int offset) void CSoundFile::DoFreqSlide(ModChannel &chn, int32 nFreqSlide) const { - if(!chn.nPeriod) return; + if(!chn.nPeriod) + return; + MPT_ASSERT(!chn.HasCustomTuning()); + if(GetType() == MOD_TYPE_669) { // Like other oldskool trackers, Composer 669 doesn't have linear slides... @@ -5508,7 +5684,7 @@ void CSoundFile::DoFreqSlide(ModChannel &chn, int32 nFreqSlide) const { // IT Linear slides const auto nOldPeriod = chn.nPeriod; - uint32 n = mpt::abs(nFreqSlide) / 4u; + uint32 n = std::abs(nFreqSlide) / 4u; LimitMax(n, 255u); if(n != 0) { @@ -5559,7 +5735,7 @@ void CSoundFile::NoteCut(CHANNELINDEX nChn, uint32 nTick, bool cutSample) if(chn.dwFlags[CHN_ADLIB] && m_opl) { - m_opl->NoteCut(nChn); + m_opl->NoteCut(nChn, false); } } } @@ -5634,7 +5810,7 @@ void CSoundFile::SetSpeed(PlayState &playState, uint32 param) const if(param > 0) playState.m_nMusicSpeed = param; if(GetType() == MOD_TYPE_STM && param > 0) { - playState.m_nMusicSpeed = std::max(param >> 4u, 1); + playState.m_nMusicSpeed = std::max(param >> 4, uint32(1)); playState.m_nMusicTempo = ConvertST2Tempo(static_cast(param)); } } @@ -5643,11 +5819,12 @@ void CSoundFile::SetSpeed(PlayState &playState, uint32 param) const // Convert a ST2 tempo byte to classic tempo and speed combination TEMPO CSoundFile::ConvertST2Tempo(uint8 tempo) { - static const uint8 ST2TempoFactor[] = { 140, 50, 25, 15, 10, 7, 6, 4, 3, 3, 2, 2, 2, 2, 1, 1 }; - static const uint32 st2MixingRate = 23863; // Highest possible setting in ST2 + static constexpr uint8 ST2TempoFactor[] = { 140, 50, 25, 15, 10, 7, 6, 4, 3, 3, 2, 2, 2, 2, 1, 1 }; + static constexpr uint32 st2MixingRate = 23863; // Highest possible setting in ST2 // This underflows at tempo 06...0F, and the resulting tick lengths depend on the mixing rate. - int32 samplesPerTick = st2MixingRate / (49 - ((ST2TempoFactor[tempo >> 4u] * (tempo & 0x0F)) >> 4u)); + // Note: ST2.3 uses the constant 50 below, earlier versions use 49 but they also play samples at a different speed. + int32 samplesPerTick = st2MixingRate / (50 - ((ST2TempoFactor[tempo >> 4u] * (tempo & 0x0F)) >> 4u)); if(samplesPerTick <= 0) samplesPerTick += 65536; return TEMPO().SetRaw(Util::muldivrfloor(st2MixingRate, 5 * TEMPO::fractFact, samplesPerTick * 2)); @@ -5670,12 +5847,7 @@ void CSoundFile::SetTempo(TEMPO param, bool setFromUI) // ProTracker sets the tempo after the first tick. // Note: The case of one tick per row is handled in ProcessRow() instead. // Test case: TempoChange.mod -#if MPT_MSVC_AT_LEAST(2017,8) && MPT_MSVC_BEFORE(2019,0) - // Work-around MSVC getting confused about deduced const input type in noexcept operator inside noexcept condition. - m_PlayState.m_nMusicTempo.SetRaw(std::min(param.GetRaw(), specs.GetTempoMax().GetRaw())); -#else m_PlayState.m_nMusicTempo = std::min(param, specs.GetTempoMax()); -#endif } else if(param < minTempo && !m_SongFlags[SONG_FIRSTTICK]) { // Tempo Slide @@ -5896,7 +6068,7 @@ uint32 CSoundFile::GetPeriodFromNote(uint32 note, int32 nFineTune, uint32 nC5Spe } else { nFineTune = XM2MODFineTune(nFineTune); - if ((nFineTune) || (note < 24) || (note >= 24 + mpt::size(ProTrackerPeriodTable))) + if ((nFineTune) || (note < 24) || (note >= 24 + std::size(ProTrackerPeriodTable))) return (ProTrackerTunedPeriods[nFineTune * 12u + note % 12u] << 5) >> (note / 12u); else return (ProTrackerPeriodTable[note - 24] << 2); @@ -6016,7 +6188,7 @@ PLUGINDEX CSoundFile::GetChannelPlugin(CHANNELINDEX nChn, PluginMutePriority res const ModChannel &channel = m_PlayState.Chn[nChn]; PLUGINDEX plugin; - if((respectMutes == RespectMutes && channel.dwFlags[CHN_MUTE]) || channel.dwFlags[CHN_NOFX]) + if((respectMutes == RespectMutes && channel.dwFlags[CHN_MUTE | CHN_SYNCMUTE]) || channel.dwFlags[CHN_NOFX]) { plugin = 0; } else @@ -6094,7 +6266,7 @@ IMixPlugin *CSoundFile::GetChannelInstrumentPlugin(CHANNELINDEX chn) const // Get the MIDI channel currently associated with a given tracker channel uint8 CSoundFile::GetBestMidiChannel(CHANNELINDEX trackerChn) const { - if(trackerChn >= mpt::size(m_PlayState.Chn)) + if(trackerChn >= std::size(m_PlayState.Chn)) { return 0; } @@ -6183,9 +6355,9 @@ void CSoundFile::PortamentoFineMPT(ModChannel &chn, int param) const int tickParam = static_cast((m_PlayState.m_nTickCount + 1.0) * param / m_PlayState.m_nMusicSpeed); chn.m_PortamentoFineSteps += (param >= 0) ? tickParam - chn.nOldFinePortaUpDown : tickParam + chn.nOldFinePortaUpDown; if(m_PlayState.m_nTickCount + 1 == m_PlayState.m_nMusicSpeed) - chn.nOldFinePortaUpDown = static_cast(mpt::abs(param)); + chn.nOldFinePortaUpDown = static_cast(std::abs(param)); else - chn.nOldFinePortaUpDown = static_cast(mpt::abs(tickParam)); + chn.nOldFinePortaUpDown = static_cast(std::abs(tickParam)); chn.m_CalculateFreq = true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp index cce5e503b..89f3db9b1 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.cpp @@ -43,6 +43,16 @@ OPENMPT_NAMESPACE_BEGIN +bool SettingCacheCompleteFileBeforeLoading() +{ + #ifdef MODPLUG_TRACKER + return TrackerSettings::Instance().MiscCacheCompleteFileBeforeLoading; + #else + return false; + #endif +} + + mpt::ustring FileHistory::AsISO8601() const { tm date = loadDate; @@ -73,10 +83,10 @@ CSoundFile::CSoundFile() : m_pModSpecs(&ModSpecs::itEx), m_nType(MOD_TYPE_NONE), Patterns(*this), - Order(*this), #ifdef MODPLUG_TRACKER m_MIDIMapper(*this), #endif + Order(*this), m_PRNG(mpt::make_prng(mpt::global_prng())), visitedSongRows(*this) { @@ -95,7 +105,7 @@ CSoundFile::CSoundFile() : #endif // MODPLUG_TRACKER MemsetZero(Instruments); - MemsetZero(m_szNames); + Clear(m_szNames); m_pTuningsTuneSpecific = new CTuningCollection(); } @@ -170,6 +180,9 @@ void CSoundFile::InitializeGlobals(MODTYPE type) m_modFormat = ModFormatDetails(); m_FileHistory.clear(); m_tempoSwing.clear(); +#ifdef MPT_EXTERNAL_SAMPLES + m_samplePaths.clear(); +#endif // MPT_EXTERNAL_SAMPLES // Note: we do not use the Amiga resampler for DBM as it's a multichannel format and can make use of higher-quality Amiga soundcards instead of Paula. if(GetType() & (/*MOD_TYPE_DBM | */MOD_TYPE_DIGI | MOD_TYPE_MED | MOD_TYPE_MOD | MOD_TYPE_OKT | MOD_TYPE_SFX | MOD_TYPE_STP)) @@ -260,12 +273,12 @@ CSoundFile::ProbeResult CSoundFile::ProbeAdditionalSize(MemoryFileReader &file, { const uint64 availableFileSize = file.GetLength(); const uint64 fileSize = (pfilesize ? *pfilesize : file.GetLength()); - //const uint64 validFileSize = std::min(fileSize, ProbeRecommendedSize); + //const uint64 validFileSize = std::min(fileSize, static_cast(ProbeRecommendedSize)); const uint64 goalSize = file.GetPosition() + minimumAdditionalSize; - //const uint64 goalMinimumSize = std::min(goalSize, ProbeRecommendedSize); + //const uint64 goalMinimumSize = std::min(goalSize, static_cast(ProbeRecommendedSize)); if(pfilesize) { - if(availableFileSize < std::min(fileSize, ProbeRecommendedSize)) + if(availableFileSize < std::min(fileSize, static_cast(ProbeRecommendedSize))) { if(availableFileSize < goalSize) { @@ -284,9 +297,6 @@ CSoundFile::ProbeResult CSoundFile::ProbeAdditionalSize(MemoryFileReader &file, } -const std::size_t CSoundFile::ProbeRecommendedSize = PROBE_RECOMMENDED_SIZE; - - #define MPT_DO_PROBE( storedResult , call ) \ MPT_DO { \ ProbeResult lastResult = call ; \ @@ -299,7 +309,7 @@ const std::size_t CSoundFile::ProbeRecommendedSize = PROBE_RECOMMENDED_SIZE; /**/ -CSoundFile::ProbeResult CSoundFile::Probe(ProbeFlags flags, mpt::span data, const uint64 *pfilesize) +CSoundFile::ProbeResult CSoundFile::Probe(ProbeFlags flags, mpt::span data, const uint64 *pfilesize) { ProbeResult result = ProbeFailure; if(pfilesize && (*pfilesize < data.size())) @@ -364,7 +374,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) #endif MemsetZero(Instruments); - MemsetZero(m_szNames); + Clear(m_szNames); #ifndef NO_PLUGINS std::fill(std::begin(m_MixPlugins), std::end(m_MixPlugins), SNDMIXPLUGIN()); #endif // NO_PLUGINS @@ -442,7 +452,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) // Read archive comment if there is no song comment if(m_songMessage.empty()) { - m_songMessage.assign(mpt::ToCharset(mpt::CharsetLocale, unarchiver.GetComment())); + m_songMessage.assign(mpt::ToCharset(mpt::Charset::Locale, unarchiver.GetComment())); } #endif } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) @@ -473,8 +483,6 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) // Checking samples, load external samples for(SAMPLEINDEX nSmp = 1; nSmp <= m_nSamples; nSmp++) { - // Sanitize sample names - mpt::String::SetNullTerminator(m_szNames[nSmp]); ModSample &sample = Samples[nSmp]; #ifdef MPT_EXTERNAL_SAMPLES @@ -531,6 +539,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) // Set default play state values if (!m_nDefaultTempo.GetInt()) m_nDefaultTempo.Set(125); if (!m_nDefaultSpeed) m_nDefaultSpeed = 6; + if (m_nDefaultRowsPerMeasure < m_nDefaultRowsPerBeat) m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat; m_PlayState.m_nMusicSpeed = m_nDefaultSpeed; m_PlayState.m_nMusicTempo = m_nDefaultTempo; m_PlayState.m_nCurrentRowsPerBeat = m_nDefaultRowsPerBeat; @@ -583,8 +592,8 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) { mpt::ustring s = mpt::format(U_("Loading Plugin FX%1: %2 (%3)"))( mpt::ufmt::dec0<2>(plug + 1), - mpt::ToUnicode(mpt::CharsetUTF8, plugin.Info.szLibraryName), - mpt::ToUnicode(mpt::CharsetLocale, plugin.Info.szName)); + mpt::ToUnicode(mpt::Charset::UTF8, plugin.Info.szLibraryName), + mpt::ToUnicode(mpt::Charset::Locale, plugin.Info.szName)); CMainFrame::GetMainFrame()->SetHelpText(mpt::ToCString(s)); } #endif // MODPLUG_TRACKER @@ -606,7 +615,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) notFoundText.append(plugin.GetLibraryName()); notFoundText.append("\n"); #else - AddToLog(LogWarning, U_("Plugin not found: ") + mpt::ToUnicode(mpt::CharsetUTF8, plugin.GetLibraryName())); + AddToLog(LogWarning, U_("Plugin not found: ") + mpt::ToUnicode(mpt::Charset::UTF8, plugin.GetLibraryName())); #endif // MODPLUG_TRACKER } } @@ -626,7 +635,7 @@ bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags) { notFoundText = "The following plugins have not been found:\n\n" + notFoundText + "\nDo you want to search for them online?"; } - if(Reporting::Confirm(mpt::ToUnicode(mpt::CharsetUTF8, notFoundText), U_("OpenMPT - Plugins missing"), false, true) == cnfYes) + if(Reporting::Confirm(mpt::ToUnicode(mpt::Charset::UTF8, notFoundText), U_("OpenMPT - Plugins missing"), false, true) == cnfYes) { std::string url = "https://resources.openmpt.org/plugins/search.php?p="; for(const auto &id : notFoundIDs) @@ -676,6 +685,9 @@ bool CSoundFile::Destroy() m_songArtist.clear(); m_songMessage.clear(); m_FileHistory.clear(); +#ifdef MPT_EXTERNAL_SAMPLES + m_samplePaths.clear(); +#endif // MPT_EXTERNAL_SAMPLES for(auto &smp : Samples) { @@ -706,11 +718,6 @@ bool CSoundFile::Destroy() void CSoundFile::SetDspEffects(uint32 DSPMask) { -#ifdef ENABLE_ASM -#ifndef NO_REVERB - if(!(GetRealProcSupport() & PROCSUPPORT_MMX)) DSPMask &= ~SNDDSP_REVERB; -#endif -#endif m_MixerSettings.DSPMask = DSPMask; InitPlayer(false); } @@ -1018,7 +1025,7 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type) playBehaviour.set(kITNoSurroundPan); playBehaviour.set(kITShortSampleRetrig); playBehaviour.set(kITPortaNoNote); - playBehaviour.set(kITDontResetNoteOffOnPorta); + playBehaviour.set(kITFT2DontResetNoteOffOnPorta); playBehaviour.set(kITVolColMemory); playBehaviour.set(kITPortamentoSwapResetsPos); playBehaviour.set(kITEmptyNoteMapSlot); @@ -1032,9 +1039,13 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type) playBehaviour.set(kITMultiSampleInstrumentNumber); playBehaviour.set(kRowDelayWithNoteDelay); playBehaviour.set(kITInstrWithNoteOffOldEffects); + playBehaviour.set(kITDoNotOverrideChannelPan); + playBehaviour.set(kITDCTBehaviour); if(type == MOD_TYPE_MPT) { playBehaviour.set(kOPLFlexibleNoteOff); + playBehaviour.set(kOPLwithNNA); + playBehaviour.set(kOPLRealRetrig); } break; @@ -1045,13 +1056,14 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type) playBehaviour.set(kPerChannelGlobalVolSlide); playBehaviour.set(kPanOverride); playBehaviour.set(kITFT2PatternLoop); + playBehaviour.set(kITFT2DontResetNoteOffOnPorta); playBehaviour.set(kFT2Arpeggio); playBehaviour.set(kFT2Retrigger); playBehaviour.set(kFT2VolColVibrato); playBehaviour.set(kFT2PortaNoNote); playBehaviour.set(kFT2KeyOff); playBehaviour.set(kFT2PanSlide); - playBehaviour.set(kFT2OffsetOutOfRange); + playBehaviour.set(kFT2ST3OffsetOutOfRange); playBehaviour.set(kFT2RestrictXCommand); playBehaviour.set(kFT2RetrigWithNoteDelay); playBehaviour.set(kFT2SetPanEnvPos); @@ -1084,6 +1096,7 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type) playBehaviour.set(kTempoClamp); playBehaviour.set(kPanOverride); playBehaviour.set(kITPanbrelloHold); + playBehaviour.set(kFT2ST3OffsetOutOfRange); playBehaviour.set(kST3NoMutedChannels); playBehaviour.set(kST3PortaSampleChange); playBehaviour.set(kST3EffectMemory); @@ -1091,6 +1104,8 @@ PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type) playBehaviour.set(KST3PortaAfterArpeggio); playBehaviour.set(kRowDelayWithNoteDelay); playBehaviour.set(kST3OffsetWithoutInstrument); + playBehaviour.set(kST3RetrigAfterNoteCut); + playBehaviour.set(kST3SampleSwap); break; case MOD_TYPE_MOD: @@ -1147,6 +1162,17 @@ PlayBehaviourSet CSoundFile::GetDefaultPlaybackBehaviour(MODTYPE type) playBehaviour.set(kITPanningReset); playBehaviour.set(kITInstrWithNoteOff); playBehaviour.set(kOPLFlexibleNoteOff); + playBehaviour.set(kITDoNotOverrideChannelPan); + playBehaviour.set(kITDCTBehaviour); + playBehaviour.set(kOPLwithNNA); + playBehaviour.set(kOPLRealRetrig); + break; + + case MOD_TYPE_S3M: + playBehaviour = GetSupportedPlaybackBehaviour(type); + // Default behaviour was chosen to follow GUS, so kST3PortaSampleChange is enabled and kST3SampleSwap is disabled. + // For SoundBlaster behaviour, those two flags would need to be swapped. + playBehaviour.reset(kST3SampleSwap); break; case MOD_TYPE_XM: @@ -1183,7 +1209,7 @@ MODTYPE CSoundFile::GetBestSaveFormat() const case MOD_TYPE_STP: return MOD_TYPE_MOD; case MOD_TYPE_MED: - if(m_nDefaultTempo == TEMPO(125, 0) && m_nDefaultSpeed == 6 && !m_nInstruments) + if(!m_nInstruments) { for(const auto &pat : Patterns) { @@ -1236,7 +1262,7 @@ const char *CSoundFile::GetSampleName(SAMPLEINDEX nSample) const MPT_ASSERT(nSample <= GetNumSamples()); if (nSample < MAX_SAMPLES) { - return m_szNames[nSample]; + return m_szNames[nSample].buf; } else { return ""; @@ -1250,7 +1276,7 @@ const char *CSoundFile::GetInstrumentName(INSTRUMENTINDEX nInstr) const return ""; MPT_ASSERT(nInstr <= GetNumInstruments()); - return Instruments[nInstr]->name; + return Instruments[nInstr]->name.buf; } @@ -1264,8 +1290,7 @@ bool CSoundFile::InitChannel(CHANNELINDEX nChn) #ifdef MODPLUG_TRACKER if(GetpModDoc() != nullptr) { - GetpModDoc()->Record1Channel(nChn, false); - GetpModDoc()->Record2Channel(nChn, false); + GetpModDoc()->SetChannelRecordGroup(nChn, RecordGroup::NoGroup); } #endif // MODPLUG_TRACKER @@ -1279,7 +1304,7 @@ bool CSoundFile::InitChannel(CHANNELINDEX nChn) void CSoundFile::InitAmigaResampler() { - if(m_SongFlags[SONG_ISAMIGA] && m_Resampler.m_Settings.emulateAmiga) + if(m_SongFlags[SONG_ISAMIGA] && m_Resampler.m_Settings.emulateAmiga != Resampling::AmigaFilter::Off) { const Paula::State defaultState(GetSampleRate()); for(auto &chn : m_PlayState.Chn) @@ -1292,7 +1317,8 @@ void CSoundFile::InitAmigaResampler() void CSoundFile::InitOPL() { - if(!m_opl) m_opl = mpt::make_unique(); + if(!m_opl) + m_opl = std::make_unique(m_MixerSettings.gdwMixingFreq); } @@ -1383,7 +1409,7 @@ SAMPLEINDEX CSoundFile::RemoveSelectedSamples(const std::vector &keepSampl if(DestroySample(nSmp)) { - strcpy(m_szNames[nSmp], ""); + m_szNames[nSmp] = ""; nRemoved++; } if((nSmp == GetNumSamples()) && (nSmp > 1)) m_nSamples--; @@ -1435,12 +1461,12 @@ bool CSoundFile::DestroySampleThreadsafe(SAMPLEINDEX nSample) } -CTuning* CSoundFile::CreateTuning12TET(const std::string &name) +std::unique_ptr CSoundFile::CreateTuning12TET(const mpt::ustring &name) { - CTuning* pT = CTuning::CreateGeometric(name, 12, 2, 15); + std::unique_ptr pT = CTuning::CreateGeometric(name, 12, 2, 15); for(ModCommand::NOTE note = 0; note < 12; ++note) { - pT->SetNoteName(note, mpt::ToCharset(mpt::CharsetASCII, mpt::ustring(NoteNamesSharp[note]))); + pT->SetNoteName(note, mpt::ustring(NoteNamesSharp[note])); } return pT; } @@ -1451,7 +1477,7 @@ mpt::ustring CSoundFile::GetNoteName(const ModCommand::NOTE note, const INSTRUME // For MPTM instruments with custom tuning, find the appropriate note name. Else, use default note names. if(ModCommand::IsNote(note) && GetType() == MOD_TYPE_MPT && inst >= 1 && inst <= GetNumInstruments() && Instruments[inst] && Instruments[inst]->pTuning) { - return mpt::ToUnicode(GetCharsetInternal(), Instruments[inst]->pTuning->GetNoteName(note - NOTE_MIDDLEC)); + return Instruments[inst]->pTuning->GetNoteName(note - NOTE_MIDDLEC); } else { return GetNoteName(note); @@ -1471,8 +1497,8 @@ mpt::ustring CSoundFile::GetNoteName(const ModCommand::NOTE note, const NoteName { // cppcheck false-positive // cppcheck-suppress constStatement - const MPT_UCHAR_TYPE specialNoteNames[][4] = { UL_("PCs"), UL_("PC "), UL_("~~~"), UL_("^^^"), UL_("===") }; - STATIC_ASSERT(CountOf(specialNoteNames) == NOTE_MAX_SPECIAL - NOTE_MIN_SPECIAL + 1); + const mpt::uchar specialNoteNames[][4] = { UL_("PCs"), UL_("PC "), UL_("~~~"), UL_("^^^"), UL_("===") }; + static_assert(CountOf(specialNoteNames) == NOTE_MAX_SPECIAL - NOTE_MIN_SPECIAL + 1); return specialNoteNames[note - NOTE_MIN_SPECIAL]; } else if(ModCommand::IsNote(note)) { @@ -1570,12 +1596,40 @@ void CSoundFile::ChangeModTypeTo(const MODTYPE newType) Order.OnModTypeChanged(oldType); Patterns.OnModTypeChanged(oldType); - m_modFormat.type = mpt::ToUnicode(mpt::CharsetUTF8, GetModSpecifications().fileExtension); + m_modFormat.type = mpt::ToUnicode(mpt::Charset::UTF8, GetModSpecifications().fileExtension); } #endif // MODPLUG_TRACKER +ModMessageHeuristicOrder CSoundFile::GetMessageHeuristic() const +{ + ModMessageHeuristicOrder result = ModMessageHeuristicOrder::Default; + switch(GetType()) + { + case MOD_TYPE_MPT: + result = ModMessageHeuristicOrder::Samples; + break; + case MOD_TYPE_IT: + result = ModMessageHeuristicOrder::Samples; + break; + case MOD_TYPE_XM: + result = ModMessageHeuristicOrder::InstrumentsSamples; + break; + case MOD_TYPE_MDL: + result = ModMessageHeuristicOrder::InstrumentsSamples; + break; + case MOD_TYPE_IMF: + result = ModMessageHeuristicOrder::InstrumentsSamples; + break; + default: + result = ModMessageHeuristicOrder::Default; + break; + } + return result; +} + + bool CSoundFile::SetTitle(const std::string &newTitle) { if(m_songName != newTitle) @@ -1666,7 +1720,7 @@ uint32 CSoundFile::GetTickDuration(PlayState &playState) const bufferCount--; playState.m_dBufferDiff++; } - MPT_ASSERT(mpt::abs(playState.m_dBufferDiff) < 1.0); + MPT_ASSERT(std::abs(playState.m_dBufferDiff) < 1.0); retval = bufferCount; } break; @@ -1836,13 +1890,13 @@ void CSoundFile::PrecomputeSampleLoops(bool updateChannels) bool CSoundFile::LoadExternalSample(SAMPLEINDEX smp, const mpt::PathString &filename) { bool ok = false; - InputFile f(filename); + InputFile f(filename, SettingCacheCompleteFileBeforeLoading()); if(f.IsValid()) { const ModSample origSample = Samples[smp]; - char origName[MAX_SAMPLENAME]; - mpt::String::Copy(origName, m_szNames[smp]); + mpt::charbuf origName; + origName = m_szNames[smp]; FileReader file = GetFileReader(f); ok = ReadSampleFromFile(smp, file, false); @@ -1862,7 +1916,7 @@ bool CSoundFile::LoadExternalSample(SAMPLEINDEX smp, const mpt::PathString &file sample.uFlags.reset(SMP_MODIFIED); sample.SanitizeLoops(); } - mpt::String::Copy(m_szNames[smp], origName); + m_szNames[smp] = origName; } SetSamplePath(smp, filename); return ok; @@ -1925,7 +1979,7 @@ void TempoSwing::Normalize() i = Util::muldivr_unsigned(i, Unity, static_cast(sum)); remain -= i; } - //MPT_ASSERT(static_cast(mpt::abs(static_cast(remain))) <= size()); + //MPT_ASSERT(static_cast(std::abs(static_cast(remain))) <= size()); at(0) += static_cast(remain); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h index 26ffafe7b..739b013cf 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndfile.h @@ -60,6 +60,9 @@ OPENMPT_NAMESPACE_BEGIN +bool SettingCacheCompleteFileBeforeLoading(); + + // ----------------------------------------------------------------------------- // MODULAR ModInstrument FIELD ACCESS : body content in InstrumentExtensions.cpp // ----------------------------------------------------------------------------- @@ -70,8 +73,6 @@ bool ReadInstrumentHeaderField(ModInstrument * input, uint32 fcode, uint16 fsize // -------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------- -typedef void (* LPSNDMIXHOOKPROC)(int *, unsigned long, unsigned long); // buffer, samples, channels - #ifdef LIBOPENMPT_BUILD #ifndef NO_PLUGINS @@ -80,7 +81,7 @@ class CVstPluginManager; #endif -typedef std::bitset PlayBehaviourSet; +using PlayBehaviourSet = std::bitset; #ifdef MODPLUG_TRACKER @@ -196,6 +197,8 @@ enum enmGetLengthResetMode eAdjustOnSuccess = 0x02 | eAdjust, // Same as previous option, but will also try to emulate sample playback so that voices from previous patterns will sound when continuing playback at the target position. eAdjustSamplePositions = 0x04 | eAdjustOnSuccess, + // Only adjust the visited rows state + eAdjustOnlyVisitedRows = 0x08, }; @@ -210,7 +213,7 @@ enum deleteInstrumentSamples namespace Tuning { class CTuningCollection; } // namespace Tuning -typedef Tuning::CTuningCollection CTuningCollection; +using CTuningCollection = Tuning::CTuningCollection; struct CModSpecifications; class OPL; #ifdef MODPLUG_TRACKER @@ -225,9 +228,8 @@ class CModDoc; struct FileHistory { - FileHistory() { MemsetZero(loadDate); } // Date when the file was loaded in the the tracker or created. - tm loadDate; + tm loadDate = {}; // Time the file was open in the editor, in 1/18.2th seconds (frequency of a standard DOS timer, to keep compatibility with Impulse Tracker easy). uint32 openTime = 0; // Return the date as a (possibly truncated if not enough precision is available) ISO 8601 formatted date. @@ -247,6 +249,17 @@ struct TimingInfo }; +enum class ModMessageHeuristicOrder +{ + Instruments, + Samples, + InstrumentsSamples, + SamplesInstruments, + BothInstrumentsSamples, + BothSamplesInstruments, + Default = InstrumentsSamples, +}; + struct ModFormatDetails { mpt::ustring formatName; // "FastTracker 2" @@ -254,7 +267,7 @@ struct ModFormatDetails mpt::ustring madeWithTracker; // "OpenMPT 1.28.01.00" mpt::ustring originalFormatName; // "FastTracker 2" in the case of converted formats like MO3 or GDM mpt::ustring originalType; // "xm" in the case of converted formats like MO3 or GDM - mpt::Charset charset = mpt::CharsetUTF8; + mpt::Charset charset = mpt::Charset::UTF8; }; @@ -263,7 +276,8 @@ class IAudioReadTarget protected: virtual ~IAudioReadTarget() = default; public: - virtual void DataCallback(int32 *MixSoundBuffer, std::size_t channels, std::size_t countChunk) = 0; + virtual void DataCallback(MixSampleInt *MixSoundBuffer, std::size_t channels, std::size_t countChunk) = 0; + virtual void DataCallback(MixSampleFloat *MixSoundBuffer, std::size_t channels, std::size_t countChunk) = 0; }; @@ -272,7 +286,8 @@ class IAudioSource public: virtual ~IAudioSource() = default; public: - virtual void FillCallback(int32 * const *MixSoundBuffers, std::size_t channels, std::size_t countChunk) = 0; + virtual void FillCallback(MixSampleInt * const *MixSoundBuffers, std::size_t channels, std::size_t countChunk) = 0; + virtual void FillCallback(MixSampleFloat * const *MixSoundBuffers, std::size_t channels, std::size_t countChunk) = 0; }; @@ -280,7 +295,7 @@ class AudioSourceNone : public IAudioSource { public: - void FillCallback(int32 * const *MixSoundBuffers, std::size_t channels, std::size_t countChunk) override + void FillCallback(MixSampleInt * const *MixSoundBuffers, std::size_t channels, std::size_t countChunk) override { for(std::size_t channel = 0; channel < channels; ++channel) { @@ -290,10 +305,20 @@ public: } } } + void FillCallback(MixSampleFloat * const *MixSoundBuffers, std::size_t channels, std::size_t countChunk) override + { + for(std::size_t channel = 0; channel < channels; ++channel) + { + for(std::size_t frame = 0; frame < countChunk; ++frame) + { + MixSoundBuffers[channel][frame] = MixSampleFloat(0.0); + } + } + } }; -typedef MPT_UCHAR_TYPE NoteName[4]; +using NoteName = mpt::uchar[4]; class CSoundFile @@ -312,7 +337,7 @@ public: //Tuning--> public: - static CTuning* CreateTuning12TET(const std::string &name); + static std::unique_ptr CreateTuning12TET(const mpt::ustring &name); static CTuning *GetDefaultTuning() {return nullptr;} CTuningCollection& GetTuneSpecificTunings() {return *m_pTuningsTuneSpecific;} @@ -358,10 +383,12 @@ private: mixsample_t MixRearBuffer[MIXBUFFERSIZE * 2]; // Non-interleaved plugin processing buffer float MixFloatBuffer[2][MIXBUFFERSIZE]; - mixsample_t gnDryLOfsVol = 0; - mixsample_t gnDryROfsVol = 0; mixsample_t MixInputBuffer[NUMMIXINPUTBUFFERS][MIXBUFFERSIZE]; + // End-of-sample pop reduction tail level + mixsample_t m_dryLOfsVol = 0, m_dryROfsVol = 0; + mixsample_t m_surroundLOfsVol = 0, m_surroundROfsVol = 0; + public: MixerSettings m_MixerSettings; CResampler m_Resampler; @@ -378,8 +405,11 @@ public: #ifndef NO_AGC CAGC m_AGC; #endif +#ifndef NO_DSP + BitCrush m_BitCrush; +#endif - typedef uint32 samplecount_t; // Number of rendered samples + using samplecount_t = uint32; // Number of rendered samples public: // for Editing #ifdef MODPLUG_TRACKER @@ -411,7 +441,7 @@ public: uint32 m_nSamplePreAmp, m_nVSTiVolume; uint32 m_OPLVolumeFactor; // 16.16 - enum : uint32 { m_OPLVolumeFactorScale = (1 << 16) }; + static constexpr uint32 m_OPLVolumeFactorScale = 1 << 16; bool IsGlobalVolumeUnset() const { return IsFirstTick(); } #ifndef MODPLUG_TRACKER @@ -441,7 +471,7 @@ public: #ifndef NO_PLUGINS SNDMIXPLUGIN m_MixPlugins[MAX_MIXPLUGINS]; // Mix plugins #endif - char m_szNames[MAX_SAMPLES][MAX_SAMPLENAME]; // Sample names + mpt::charbuf m_szNames[MAX_SAMPLES]; // Sample names Version m_dwCreatedWithVersion; Version m_dwLastSavedWithVersion; @@ -463,44 +493,49 @@ public: struct PlayState { friend class CSoundFile; - protected: - samplecount_t m_nBufferCount; - double m_dBufferDiff; - public: - samplecount_t m_lTotalSampleCount = 0; public: - uint32 m_nTickCount; + samplecount_t m_lTotalSampleCount = 0; // Total number of rendered samples protected: - uint32 m_nPatternDelay, m_nFrameDelay; // m_nPatternDelay = pattern delay (rows), m_nFrameDelay = fine pattern delay (ticks) + samplecount_t m_nBufferCount = 0; // Remaining number samples to render for this tick + double m_dBufferDiff = 0.0; // Modern tempo rounding error compensation + public: - uint32 m_nSamplesPerTick; - ROWINDEX m_nCurrentRowsPerBeat, m_nCurrentRowsPerMeasure; // current rows per beat and measure for this module - uint32 m_nMusicSpeed; // Current speed - TEMPO m_nMusicTempo; // Current tempo + uint32 m_nTickCount = 0; // Current tick being processed + protected: + uint32 m_nPatternDelay = 0; // Pattern delay (rows) + uint32 m_nFrameDelay = 0; // Fine pattern delay (ticks) + public: + uint32 m_nSamplesPerTick = 0; + ROWINDEX m_nCurrentRowsPerBeat = 0; // Current time signature + ROWINDEX m_nCurrentRowsPerMeasure = 0; // Current time signature + uint32 m_nMusicSpeed = 0; // Current speed + TEMPO m_nMusicTempo; // Current tempo // Playback position - ROWINDEX m_nRow; - ROWINDEX m_nNextRow; + ROWINDEX m_nRow = 0; // Current row being processed + ROWINDEX m_nNextRow = 0; // Next row to process protected: - ROWINDEX m_nNextPatStartRow; // for FT2's E60 bug + ROWINDEX m_nNextPatStartRow = 0; // For FT2's E60 bug public: - PATTERNINDEX m_nPattern; - ORDERINDEX m_nCurrentOrder, m_nNextOrder, m_nSeqOverride = ORDERINDEX_INVALID; + PATTERNINDEX m_nPattern = 0; // Current pattern being processed + ORDERINDEX m_nCurrentOrder = 0; // Current order being processed + ORDERINDEX m_nNextOrder = 0; // Next order to process + ORDERINDEX m_nSeqOverride = ORDERINDEX_INVALID; // Queued order to be processed next, regardless of what order would normally follow // Global volume public: - int32 m_nGlobalVolume; + int32 m_nGlobalVolume = MAX_GLOBAL_VOLUME; // Current global volume (0...MAX_GLOBAL_VOLUME) protected: - int32 m_nSamplesToGlobalVolRampDest, m_nGlobalVolumeRampAmount, - m_nGlobalVolumeDestination; - int32 m_lHighResRampingGlobalVolume; + int32 m_nSamplesToGlobalVolRampDest = 0, m_nGlobalVolumeRampAmount = 0, + m_nGlobalVolumeDestination = 0; // Global volume ramping + int32 m_lHighResRampingGlobalVolume = 0; // Global volume ramping public: bool m_bPositionChanged = true; // Report to plugins that we jumped around in the module public: - CHANNELINDEX ChnMix[MAX_CHANNELS]; // Channels to be mixed + CHANNELINDEX ChnMix[MAX_CHANNELS]; // Index of channels in Chn to be actually mixed ModChannel Chn[MAX_CHANNELS]; // Mixing channels... First m_nChannels channels are master channels (i.e. they are never NNA channels)! public: @@ -577,13 +612,15 @@ private: public: CSoundFile(); + CSoundFile(const CSoundFile &) = delete; + CSoundFile & operator=(const CSoundFile &) = delete; ~CSoundFile(); public: // logging void SetCustomLog(ILog *pLog) { m_pCustomLog = pLog; } void AddToLog(LogLevel level, const mpt::ustring &text) const; - /*MPT_DEPRECATED*/ void AddToLog(const AnyStringLocale &text) const { AddToLog(LogInformation, mpt::ToUnicode(text)); } + /*[[deprecated]]*/ void AddToLog(const AnyStringLocale &text) const { AddToLog(LogInformation, mpt::ToUnicode(text)); } public: @@ -605,7 +642,7 @@ public: #define PROBE_RECOMMENDED_SIZE 2048u - static const std::size_t ProbeRecommendedSize; + static constexpr std::size_t ProbeRecommendedSize = PROBE_RECOMMENDED_SIZE; enum ProbeFlags { @@ -625,7 +662,7 @@ public: static ProbeResult ProbeAdditionalSize(MemoryFileReader &file, const uint64 *pfilesize, uint64 minimumAdditionalSize); - static ProbeResult Probe(ProbeFlags flags, mpt::span data, const uint64 *pfilesize); + static ProbeResult Probe(ProbeFlags flags, mpt::span data, const uint64 *pfilesize); public: @@ -651,12 +688,14 @@ public: mpt::Charset GetCharsetInternal() const // 8bit string encoding of strings internal in CSoundFile { #if defined(MODPLUG_TRACKER) - return mpt::CharsetLocale; + return mpt::Charset::Locale; #else // MODPLUG_TRACKER return GetCharsetFile(); #endif // MODPLUG_TRACKER } + ModMessageHeuristicOrder GetMessageHeuristic() const; + void SetPreAmp(uint32 vol); uint32 GetPreAmp() const { return m_MixerSettings.m_nPreAmp; } @@ -670,8 +709,11 @@ public: ORDERINDEX GetCurrentOrder() const { return m_PlayState.m_nCurrentOrder; } CHANNELINDEX GetNumChannels() const { return m_nChannels; } + constexpr bool CanAddMoreSamples(SAMPLEINDEX amount = 1) const noexcept { return (amount < MAX_SAMPLES) && m_nSamples < (MAX_SAMPLES - amount); } + constexpr bool CanAddMoreInstruments(INSTRUMENTINDEX amount = 1) const noexcept { return (amount < MAX_INSTRUMENTS) && m_nInstruments < (MAX_INSTRUMENTS - amount); } + #ifndef NO_PLUGINS - IMixPlugin* GetInstrumentPlugin(INSTRUMENTINDEX instr); + IMixPlugin* GetInstrumentPlugin(INSTRUMENTINDEX instr) const; #endif const CModSpecifications& GetModSpecifications() const {return *m_pModSpecs;} static const CModSpecifications& GetModSpecifications(const MODTYPE type); @@ -679,8 +721,12 @@ public: #ifdef MODPLUG_TRACKER void PatternTranstionChnSolo(const CHANNELINDEX chnIndex); void PatternTransitionChnUnmuteAll(); -#endif // MODPLUG_TRACKER +protected: + void HandlePatternTransitionEvents(); +#endif // MODPLUG_TRACKER + +public: double GetCurrentBPM() const; void DontLoopPattern(PATTERNINDEX nPat, ROWINDEX nRow = 0); CHANNELINDEX GetMixStat() const { return m_nMixStat; } @@ -809,7 +855,7 @@ public: bool ReadWAV(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule); static std::vector GetSupportedExtensions(bool otherFormats); - static bool IsExtensionSupported(const char *ext); // UTF8, casing of ext is ignored + static bool IsExtensionSupported(std::string_view ext); // UTF8, casing of ext is ignored static mpt::ustring ModContainerTypeToString(MODCONTAINERTYPE containertype); static mpt::ustring ModContainerTypeToTracker(MODCONTAINERTYPE containertype); @@ -847,7 +893,7 @@ public: void S3MSaveConvert(uint8 &command, uint8 ¶m, bool toIT, bool compatibilityExport = false) const; void ModSaveCommand(uint8 &command, uint8 ¶m, const bool toXM, const bool compatibilityExport = false) const; static void ReadMODPatternEntry(FileReader &file, ModCommand &m); - static void ReadMODPatternEntry(const uint8 (&data)[4], ModCommand &m); + static void ReadMODPatternEntry(const std::array data, ModCommand &m); void SetupMODPanning(bool bForceSetup = false); // Setup LRRL panning, max channel volume @@ -959,6 +1005,7 @@ protected: void PortamentoFineMPT(ModChannel &chn, int); void PortamentoExtraFineMPT(ModChannel &chn, int); void NoteSlide(ModChannel &chn, uint32 param, bool slideUp, bool retrig) const; + std::pair GetVolCmdTonePorta(const ModCommand &m, uint32 startTick) const; void TonePortamento(ModChannel &chn, uint32 param) const; void Vibrato(ModChannel &chn, uint32 param) const; void FineVibrato(ModChannel &chn, uint32 param) const; @@ -1057,7 +1104,8 @@ protected: bool ReadXISample(SAMPLEINDEX nSample, FileReader &file); bool ReadITSSample(SAMPLEINDEX nSample, FileReader &file, bool rewind = true); bool ReadITISample(SAMPLEINDEX nSample, FileReader &file); - bool ReadIFFSample(SAMPLEINDEX nInstr, FileReader &file); + bool ReadIFFSample(SAMPLEINDEX sample, FileReader &file); + bool ReadBRRSample(SAMPLEINDEX sample, FileReader& file); bool ReadFLACSample(SAMPLEINDEX sample, FileReader &file); bool ReadOpusSample(SAMPLEINDEX sample, FileReader &file); bool ReadVorbisSample(SAMPLEINDEX sample, FileReader &file); @@ -1120,8 +1168,6 @@ private: PLUGINDEX GetActiveInstrumentPlugin(CHANNELINDEX, PluginMutePriority respectMutes) const; IMixPlugin *GetChannelInstrumentPlugin(CHANNELINDEX chn) const; - void HandlePatternTransitionEvents(); - public: PLUGINDEX GetBestPlugin(CHANNELINDEX nChn, PluginPriority priority, PluginMutePriority respectMutes) const; uint8 GetBestMidiChannel(CHANNELINDEX nChn) const; @@ -1130,7 +1176,7 @@ public: #ifndef NO_PLUGINS -inline IMixPlugin* CSoundFile::GetInstrumentPlugin(INSTRUMENTINDEX instr) +inline IMixPlugin* CSoundFile::GetInstrumentPlugin(INSTRUMENTINDEX instr) const { if(instr > 0 && instr <= GetNumInstruments() && Instruments[instr] && Instruments[instr]->nMixPlug && Instruments[instr]->nMixPlug <= MAX_MIXPLUGINS) return m_MixPlugins[Instruments[instr]->nMixPlug - 1].pMixPlugin; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp index 603e923e9..e41b46ea4 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Sndmix.cpp @@ -14,7 +14,6 @@ #include "Sndfile.h" #include "MixerLoops.h" #include "MIDIEvents.h" -#include "tuning.h" #include "Tables.h" #ifdef MODPLUG_TRACKER #include "../mptrack/TrackerSettings.h" @@ -26,13 +25,10 @@ OPENMPT_NAMESPACE_BEGIN -// VU-Meter -#define VUMETER_DECAY 4 - // Log tables for pre-amp // Pre-amp (or more precisely: Pre-attenuation) depends on the number of channels, // Which this table takes care of. -static const uint8 PreAmpTable[16] = +static constexpr uint8 PreAmpTable[16] = { 0x60, 0x60, 0x60, 0x70, // 0-7 0x80, 0x88, 0x90, 0x98, // 8-15 @@ -41,7 +37,7 @@ static const uint8 PreAmpTable[16] = }; #ifndef NO_AGC -static const uint8 PreAmpAGCTable[16] = +static constexpr uint8 PreAmpAGCTable[16] = { 0x60, 0x60, 0x60, 0x64, 0x68, 0x70, 0x78, 0x80, @@ -87,8 +83,8 @@ void CSoundFile::InitPlayer(bool bReset) if(bReset) { ResetMixStat(); - gnDryLOfsVol = 0; - gnDryROfsVol = 0; + m_dryLOfsVol = m_dryROfsVol = 0; + m_surroundLOfsVol = m_surroundROfsVol = 0; InitAmigaResampler(); } m_Resampler.UpdateTables(); @@ -106,6 +102,9 @@ void CSoundFile::InitPlayer(bool bReset) #endif #ifndef NO_AGC m_AGC.Initialize(bReset, m_MixerSettings.gdwMixingFreq); +#endif +#ifndef NO_DSP + m_BitCrush.Initialize(bReset, m_MixerSettings.gdwMixingFreq); #endif if(m_opl) { @@ -290,7 +289,7 @@ CSoundFile::samplecount_t CSoundFile::Read(samplecount_t count, IAudioReadTarget MPT_ASSERT(m_PlayState.m_nBufferCount > 0); // assert that we have actually something to do - const samplecount_t countChunk = std::min({ MIXBUFFERSIZE, m_PlayState.m_nBufferCount, countToRender }); + const samplecount_t countChunk = std::min({ static_cast(MIXBUFFERSIZE), static_cast(m_PlayState.m_nBufferCount), static_cast(countToRender) }); if(m_MixerSettings.NumInputChannels > 0) { @@ -399,6 +398,14 @@ void CSoundFile::ProcessDSP(uint32 countChunk) m_AGC.Process(MixSoundBuffer, MixRearBuffer, countChunk, m_MixerSettings.gnChannels); } #endif // NO_AGC + + #ifndef NO_DSP + if(m_MixerSettings.DSPMask & SNDDSP_BITCRUSH) + { + m_BitCrush.Process(MixSoundBuffer, MixRearBuffer, countChunk, m_MixerSettings.gnChannels); + } + #endif // NO_DSP + #if defined(NO_DSP) && defined(NO_EQ) && defined(NO_AGC) MPT_UNREFERENCED_PARAMETER(countChunk); #endif @@ -502,28 +509,33 @@ bool CSoundFile::ProcessRow() m_PlayState.m_nGlobalVolume = m_nDefaultGlobalVolume; for(CHANNELINDEX i = 0; i < MAX_CHANNELS; i++) { - m_PlayState.Chn[i].dwFlags.set(CHN_NOTEFADE | CHN_KEYOFF); - m_PlayState.Chn[i].nFadeOutVol = 0; - - if (i < m_nChannels) + auto &chn = m_PlayState.Chn[i]; + if(chn.dwFlags[CHN_ADLIB] && m_opl) { - m_PlayState.Chn[i].nGlobalVol = ChnSettings[i].nVolume; - m_PlayState.Chn[i].nVolume = ChnSettings[i].nVolume; - m_PlayState.Chn[i].nPan = ChnSettings[i].nPan; - m_PlayState.Chn[i].nPanSwing = m_PlayState.Chn[i].nVolSwing = 0; - m_PlayState.Chn[i].nCutSwing = m_PlayState.Chn[i].nResSwing = 0; - m_PlayState.Chn[i].nOldVolParam = 0; - m_PlayState.Chn[i].oldOffset = 0; - m_PlayState.Chn[i].nOldHiOffset = 0; - m_PlayState.Chn[i].nPortamentoDest = 0; + m_opl->NoteCut(i); + } + chn.dwFlags.set(CHN_NOTEFADE | CHN_KEYOFF); + chn.nFadeOutVol = 0; - if (!m_PlayState.Chn[i].nLength) + if(i < m_nChannels) + { + chn.nGlobalVol = ChnSettings[i].nVolume; + chn.nVolume = ChnSettings[i].nVolume; + chn.nPan = ChnSettings[i].nPan; + chn.nPanSwing = chn.nVolSwing = 0; + chn.nCutSwing = chn.nResSwing = 0; + chn.nOldVolParam = 0; + chn.oldOffset = 0; + chn.nOldHiOffset = 0; + chn.nPortamentoDest = 0; + + if(!chn.nLength) { - m_PlayState.Chn[i].dwFlags = ChnSettings[i].dwFlags; - m_PlayState.Chn[i].nLoopStart = 0; - m_PlayState.Chn[i].nLoopEnd = 0; - m_PlayState.Chn[i].pModInstrument = nullptr; - m_PlayState.Chn[i].pModSample = nullptr; + chn.dwFlags = ChnSettings[i].dwFlags; + chn.nLoopStart = 0; + chn.nLoopEnd = 0; + chn.pModInstrument = nullptr; + chn.pModSample = nullptr; } } } @@ -576,7 +588,9 @@ bool CSoundFile::ProcessRow() // the pattern loop (editor flag, not to be confused with the pattern loop effect) // flag is set - because in that case, the module would stop after the first pattern loop... const bool overrideLoopCheck = (m_nRepeatCount != -1) && m_SongFlags[SONG_PATTERNLOOP]; - if(!overrideLoopCheck && visitedSongRows.IsVisited(m_PlayState.m_nCurrentOrder, m_PlayState.m_nRow, true)) + // If a row is jumped over due to a ProTracker quirk, don't add it to the visited rows (unless we notice that we are stuck in a loop by repeatedly playing that row) + const bool checkIgnoreRow = !ignoreRow || visitedSongRows.GetLastVisitedRow() == m_PlayState.m_nRow; + if(!overrideLoopCheck && checkIgnoreRow && visitedSongRows.IsVisited(m_PlayState.m_nCurrentOrder, m_PlayState.m_nRow, true)) { if(m_nRepeatCount) { @@ -594,8 +608,23 @@ bool CSoundFile::ProcessRow() // Let's check again if this really is the end of the song. // The visited rows vector might have been screwed up while editing... // This is of course not possible during rendering to WAV, so we ignore that case. - GetLengthType t = GetLength(eNoAdjust).back(); - if(IsRenderingToDisc() || (t.lastOrder == m_PlayState.m_nCurrentOrder && t.lastRow == m_PlayState.m_nRow)) + bool isReallyAtEnd = false; + if(IsRenderingToDisc()) + { + isReallyAtEnd = true; + } else + { + for(const auto &t : GetLength(eNoAdjust, GetLengthTarget(true))) + { + if(t.lastOrder == m_PlayState.m_nCurrentOrder && t.lastRow == m_PlayState.m_nRow) + { + isReallyAtEnd = true; + break; + } + } + } + + if(isReallyAtEnd) { // This is really the song's end! visitedSongRows.Initialize(true); @@ -603,7 +632,7 @@ bool CSoundFile::ProcessRow() } else { // Ok, this is really dirty, but we have to update the visited rows vector... - GetLength(eAdjustOnSuccess, GetLengthTarget(m_PlayState.m_nCurrentOrder, m_PlayState.m_nRow)); + GetLength(eAdjustOnlyVisitedRows, GetLengthTarget(m_PlayState.m_nCurrentOrder, m_PlayState.m_nRow)); } #else if(m_SongFlags[SONG_PLAYALLSONGS]) @@ -664,7 +693,7 @@ bool CSoundFile::ProcessRow() } // Reset channel values - ModCommand *m = Patterns[m_PlayState.m_nPattern].GetRow(m_PlayState.m_nRow); + ModCommand *m = Patterns[m_PlayState.m_nPattern].GetpModCommand(m_PlayState.m_nRow, 0); for (ModChannel *pChn = m_PlayState.Chn, *pEnd = pChn + m_nChannels; pChn != pEnd; pChn++, m++) { // First, handle some quirks that happen after the last tick of the previous row... @@ -733,7 +762,9 @@ bool CSoundFile::ProcessRow() if (m_PlayState.m_nTickCount) { m_SongFlags.reset(SONG_FIRSTTICK); - if(!(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) && m_PlayState.m_nTickCount < GetNumTicksOnCurrentRow()) + if(!(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2)) + && (GetType() != MOD_TYPE_MOD || m_SongFlags[SONG_PT_MODE]) // Fix infinite loop in "GamerMan " by MrGamer, which was made with FT2 + && m_PlayState.m_nTickCount < GetNumTicksOnCurrentRow()) { // Emulate first tick behaviour if Row Delay is set. // Test cases: PatternDelaysRetrig.it, PatternDelaysRetrig.s3m, PatternDelaysRetrig.xm, PatternDelaysRetrig.mod @@ -779,7 +810,7 @@ int CSoundFile::GetVibratoDelta(int type, int position) const } else if(GetType() & (MOD_TYPE_DIGI | MOD_TYPE_DBM)) { // Other waveforms are not supported. - static const int8 DBMSinus[] = + static constexpr int8 DBMSinus[] = { 33, 52, 69, 84, 96, 107, 116, 122, 125, 127, 125, 122, 116, 107, 96, 84, 69, 52, 33, 13, -8, -31, -54, -79, -104,-128, -104, -79, -54, -31, -8, 13, @@ -1136,7 +1167,7 @@ int CSoundFile::ProcessPitchFilterEnvelope(ModChannel &chn, int &period) const } else { // Pitch Envelope - if(GetType() == MOD_TYPE_MPT && chn.pModInstrument && chn.pModInstrument->pTuning) + if(chn.HasCustomTuning()) { if(chn.nFineTune != envval) { @@ -1292,6 +1323,8 @@ void CSoundFile::IncrementEnvelopePosition(ModChannel &chn, EnvelopeType envType void CSoundFile::IncrementEnvelopePositions(ModChannel &chn) const { + if (chn.isFirstTick && GetType() == MOD_TYPE_MED) + return; IncrementEnvelopePosition(chn, ENV_VOLUME); IncrementEnvelopePosition(chn, ENV_PANNING); IncrementEnvelopePosition(chn, ENV_PITCH); @@ -1393,7 +1426,7 @@ void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int &period, Tuning::NOTEIND { uint8 step = 0; const bool arpOnRow = (chn.rowCommand.command == CMD_ARPEGGIO); - const ModCommand::NOTE lastNote = ModCommand::IsNote(chn.nLastNote) ? pIns->NoteMap[chn.nLastNote - NOTE_MIN] : NOTE_NONE; + const ModCommand::NOTE lastNote = ModCommand::IsNote(chn.nLastNote) ? static_cast(pIns->NoteMap[chn.nLastNote - NOTE_MIN]) : static_cast(NOTE_NONE); if(arpOnRow) { switch(m_PlayState.m_nTickCount % 3) @@ -1432,7 +1465,7 @@ void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int &period, Tuning::NOTEIND if(chn.nCommand == CMD_ARPEGGIO) { - if((GetType() & MOD_TYPE_MPT) && chn.pModInstrument && chn.pModInstrument->pTuning) + if(chn.HasCustomTuning()) { switch(m_PlayState.m_nTickCount % 3) { @@ -1477,30 +1510,40 @@ void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int &period, Tuning::NOTEIND // Arpeggio is added on top of current note, but cannot do it the IT way because of // the behaviour in ArpeggioClamp.xm. // Test case: ArpSlide.xm - auto note = GetNoteFromPeriod(period, chn.nFineTune, chn.nC5Speed); + uint32 note = 0; // The fact that arpeggio behaves in a totally fucked up way at 16 ticks/row or more is that the arpeggio offset LUT only has 16 entries in FT2. // At more than 16 ticks/row, FT2 reads into the vibrato table, which is placed right after the arpeggio table. // Test case: Arpeggio.xm int arpPos = m_PlayState.m_nMusicSpeed - (m_PlayState.m_nTickCount % m_PlayState.m_nMusicSpeed); - if(arpPos > 16) arpPos = 2; - else if(arpPos == 16) arpPos = 0; - else arpPos %= 3; + if(arpPos > 16) + arpPos = 2; + else if(arpPos == 16) + arpPos = 0; + else + arpPos %= 3; switch(arpPos) { - case 1: note += (chn.nArpeggio >> 4); break; - case 2: note += (chn.nArpeggio & 0x0F); break; + case 1: note = (chn.nArpeggio >> 4); break; + case 2: note = (chn.nArpeggio & 0x0F); break; } - period = GetPeriodFromNote(note, chn.nFineTune, chn.nC5Speed); - - // FT2 compatibility: FT2 has a different note limit for Arpeggio. - // Test case: ArpeggioClamp.xm - if(note >= 108 + NOTE_MIN && arpPos != 0) + if(arpPos != 0) { - period = std::max(period, GetPeriodFromNote(108 + NOTE_MIN, 0, chn.nC5Speed)); - } + // Arpeggio is added on top of current note, but cannot do it the IT way because of + // the behaviour in ArpeggioClamp.xm. + // Test case: ArpSlide.xm + note += GetNoteFromPeriod(period, chn.nFineTune, chn.nC5Speed); + period = GetPeriodFromNote(note, chn.nFineTune, chn.nC5Speed); + + // FT2 compatibility: FT2 has a different note limit for Arpeggio. + // Test case: ArpeggioClamp.xm + if(note >= 108 + NOTE_MIN) + { + period = std::max(static_cast(period), GetPeriodFromNote(108 + NOTE_MIN, 0, chn.nC5Speed)); + } + } } } // Other trackers @@ -1558,6 +1601,8 @@ void CSoundFile::ProcessVibrato(CHANNELINDEX nChn, int &period, Tuning::RATIOTYP if(chn.dwFlags[CHN_VIBRATO]) { + const bool advancePosition = !m_SongFlags[SONG_FIRSTTICK] || ((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !(m_SongFlags[SONG_ITOLDEFFECTS])); + if(GetType() == MOD_TYPE_669) { if(chn.nVibratoPos % 2u) @@ -1569,12 +1614,12 @@ void CSoundFile::ProcessVibrato(CHANNELINDEX nChn, int &period, Tuning::RATIOTYP } // IT compatibility: IT has its own, more precise tables and pre-increments the vibrato position - if(m_playBehaviour[kITVibratoTremoloPanbrello]) + if(advancePosition && m_playBehaviour[kITVibratoTremoloPanbrello]) chn.nVibratoPos += 4 * chn.nVibratoSpeed; int vdelta = GetVibratoDelta(chn.nVibratoType, chn.nVibratoPos); - if(GetType() == MOD_TYPE_MPT && chn.pModInstrument && chn.pModInstrument->pTuning) + if(chn.HasCustomTuning()) { //Hack implementation: Scaling vibratofactor to [0.95; 1.05] //using figure from above tables and vibratodepth parameter @@ -1592,7 +1637,7 @@ void CSoundFile::ProcessVibrato(CHANNELINDEX nChn, int &period, Tuning::RATIOTYP // ProTracker doesn't apply vibrato nor advance on the first tick. // Test case: VibratoReset.mod return; - } else if((GetType() & MOD_TYPE_XM) && (chn.nVibratoType & 0x03) == 1) + } else if((GetType() & (MOD_TYPE_XM | MOD_TYPE_MOD)) && (chn.nVibratoType & 0x03) == 1) { // FT2 compatibility: Vibrato ramp down table is upside down. // Test case: VibratoWaveforms.xm @@ -1672,12 +1717,9 @@ void CSoundFile::ProcessVibrato(CHANNELINDEX nChn, int &period, Tuning::RATIOTYP } // Advance vibrato position - IT updates on every tick, unless "old effects" are enabled (in this case it only updates on non-first ticks like other trackers) - if(!m_SongFlags[SONG_FIRSTTICK] || ((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !(m_SongFlags[SONG_ITOLDEFFECTS]))) - { - // IT compatibility: IT has its own, more precise tables and pre-increments the vibrato position - if(!m_playBehaviour[kITVibratoTremoloPanbrello]) - chn.nVibratoPos += chn.nVibratoSpeed; - } + // IT compatibility: IT has its own, more precise tables and pre-increments the vibrato position + if(advancePosition && !m_playBehaviour[kITVibratoTremoloPanbrello]) + chn.nVibratoPos += chn.nVibratoSpeed; } else if(chn.dwOldFlags[CHN_VIBRATO]) { // Stop MIDI vibrato for plugins: @@ -1698,7 +1740,7 @@ void CSoundFile::ProcessSampleAutoVibrato(ModChannel &chn, int &period, Tuning:: if(chn.pModSample != nullptr && chn.pModSample->nVibDepth) { const ModSample *pSmp = chn.pModSample; - const bool alternativeTuning = chn.pModInstrument && chn.pModInstrument->pTuning; + const bool hasTuning = chn.HasCustomTuning(); // In IT linear slide mode, we use frequencies, otherwise we use periods, which are upside down. // In this context, the "up" tables refer to the tables that increase frequency, and the down tables are the ones that decrease frequency. @@ -1709,7 +1751,7 @@ void CSoundFile::ProcessSampleAutoVibrato(ModChannel &chn, int &period, Tuning:: const uint32 (&fineDownTable)[16] = useFreq ? FineLinearSlideDownTable : FineLinearSlideUpTable; // IT compatibility: Autovibrato is so much different in IT that I just put this in a separate code block, to get rid of a dozen IsCompatibilityMode() calls. - if(m_playBehaviour[kITVibratoTremoloPanbrello] && !alternativeTuning && GetType() != MOD_TYPE_MT2) + if(m_playBehaviour[kITVibratoTremoloPanbrello] && !hasTuning && GetType() != MOD_TYPE_MT2) { if(!pSmp->nVibRate) return; @@ -1755,7 +1797,7 @@ void CSoundFile::ProcessSampleAutoVibrato(ModChannel &chn, int &period, Tuning:: } vdelta = (vdelta * adepth) / 64; - uint32 l = mpt::abs(vdelta); + uint32 l = std::abs(vdelta); LimitMax(period, Util::MaxValueOfType(period) / 256); period *= 256; if(vdelta < 0) @@ -1828,7 +1870,7 @@ void CSoundFile::ProcessSampleAutoVibrato(ModChannel &chn, int &period, Tuning:: } int n = (vdelta * chn.nAutoVibDepth) / 256; - if(alternativeTuning) + if(hasTuning) { //Vib sweep is not taken into account here. vibratoFactor += 0.05F * pSmp->nVibDepth * vdelta / 4096.0f; //4096 == 64^2 @@ -1941,20 +1983,16 @@ void CSoundFile::ProcessRamping(ModChannel &chn) const SamplePosition CSoundFile::GetChannelIncrement(const ModChannel &chn, uint32 period, int periodFrac) const { uint32 freq; - - const ModInstrument *pIns = chn.pModInstrument; - if(GetType() != MOD_TYPE_MPT || pIns == nullptr || pIns->pTuning == nullptr) - { + if(!chn.HasCustomTuning()) freq = GetFreqFromPeriod(period, chn.nC5Speed, periodFrac); - } else - { - freq = chn.m_Freq; - } + else + freq = chn.nPeriod; - // Applying Pitch/Tempo lock. - if(pIns && pIns->pitchToTempoLock.GetRaw()) + // Applying Pitch/Tempo lock + const ModInstrument *ins = chn.pModInstrument; + if(ins && ins->pitchToTempoLock.GetRaw()) { - freq = Util::muldivr(freq, m_PlayState.m_nMusicTempo.GetRaw(), pIns->pitchToTempoLock.GetRaw()); + freq = Util::muldivr(freq, m_PlayState.m_nMusicTempo.GetRaw(), ins->pitchToTempoLock.GetRaw()); } // Avoid increment to overflow and become negative with unrealisticly high frequencies. @@ -2236,6 +2274,19 @@ bool CSoundFile::ReadNote() period = m_nMinPeriod; } + const bool hasTuning = chn.HasCustomTuning(); + if(hasTuning) + { + if(chn.m_CalculateFreq || (chn.m_ReCalculateFreqOnFirstTick && m_PlayState.m_nTickCount == 0)) + { + chn.RecalcTuningFreq(vibratoFactor, arpeggioSteps, *this); + if(!chn.m_CalculateFreq) + chn.m_ReCalculateFreqOnFirstTick = false; + else + chn.m_CalculateFreq = false; + } + } + if((chn.dwFlags & (CHN_ADLIB | CHN_MUTE | CHN_SYNCMUTE)) == CHN_ADLIB && m_opl) { const bool doProcess = m_playBehaviour[kOPLFlexibleNoteOff] || !chn.dwFlags[CHN_NOTEFADE] || GetType() == MOD_TYPE_S3M; @@ -2243,10 +2294,10 @@ bool CSoundFile::ReadNote() { // In ST3, a sample rate of 8363 Hz is mapped to middle-C, which is 261.625 Hz in a tempered scale at A4 = 440. // Hence, we have to translate our "sample rate" into pitch. - const auto freq = GetFreqFromPeriod(period, chn.nC5Speed, nPeriodFrac); - const auto oplmilliHertz = Util::muldivr_unsigned(freq, 261625, 8363 << FREQ_FRACBITS); + const auto freq = hasTuning ? chn.nPeriod : GetFreqFromPeriod(period, chn.nC5Speed, nPeriodFrac); + const auto milliHertz = Util::muldivr_unsigned(freq, 261625, 8363 << FREQ_FRACBITS); const bool keyOff = chn.dwFlags[CHN_KEYOFF] || (chn.dwFlags[CHN_NOTEFADE] && chn.nFadeOutVol == 0); - m_opl->Frequency(nChn, oplmilliHertz, keyOff, m_playBehaviour[kOPLBeatingOscillators]); + m_opl->Frequency(nChn, milliHertz, keyOff, m_playBehaviour[kOPLBeatingOscillators]); } if(doProcess) { @@ -2256,33 +2307,21 @@ bool CSoundFile::ReadNote() } // Deallocate OPL channels for notes that are most definitely never going to play again. - const auto *ins = chn.pModInstrument; - if(ins != nullptr + if(const auto *ins = chn.pModInstrument; ins != nullptr && (ins->VolEnv.dwFlags & (ENV_ENABLED | ENV_LOOP | ENV_SUSTAIN)) == ENV_ENABLED && !ins->VolEnv.empty() && chn.GetEnvelope(ENV_VOLUME).nEnvPosition >= ins->VolEnv.back().tick && ins->VolEnv.back().value == 0) { m_opl->NoteCut(nChn); + if(!m_playBehaviour[kOPLNoResetAtEnvelopeEnd]) + chn.dwFlags.reset(CHN_ADLIB); chn.dwFlags.set(CHN_NOTEFADE); chn.nFadeOutVol = 0; - } - } - - if(GetType() == MOD_TYPE_MPT && pIns != nullptr && pIns->pTuning != nullptr) - { - // In this case: GetType() == MOD_TYPE_MPT and using custom tunings. - if(chn.m_CalculateFreq || (chn.m_ReCalculateFreqOnFirstTick && m_PlayState.m_nTickCount == 0)) + } else if(m_playBehaviour[kOPLFlexibleNoteOff] && chn.dwFlags[CHN_NOTEFADE] && chn.nFadeOutVol == 0) { - ModCommand::NOTE note = chn.nNote; - if(!ModCommand::IsNote(note)) note = chn.nLastNote; - if(m_playBehaviour[kITRealNoteMapping] && note >= NOTE_MIN && note <= NOTE_MAX) - note = pIns->NoteMap[note - NOTE_MIN]; - chn.m_Freq = mpt::saturate_round((chn.nC5Speed << FREQ_FRACBITS) * vibratoFactor * pIns->pTuning->GetRatio(note - NOTE_MIDDLEC + arpeggioSteps, chn.nFineTune+chn.m_PortamentoFineSteps)); - if(!chn.m_CalculateFreq) - chn.m_ReCalculateFreqOnFirstTick = false; - else - chn.m_CalculateFreq = false; + m_opl->NoteCut(nChn); + chn.dwFlags.reset(CHN_ADLIB); } } @@ -2308,8 +2347,9 @@ bool CSoundFile::ReadNote() // Volume ramping chn.dwFlags.set(CHN_VOLUMERAMP, (chn.nRealVolume | chn.rightVol | chn.leftVol) != 0); - if (chn.nLeftVU > VUMETER_DECAY) chn.nLeftVU -= VUMETER_DECAY; else chn.nLeftVU = 0; - if (chn.nRightVU > VUMETER_DECAY) chn.nRightVU -= VUMETER_DECAY; else chn.nRightVU = 0; + constexpr uint8 VUMETER_DECAY = 4; + chn.nLeftVU = (chn.nLeftVU > VUMETER_DECAY) ? (chn.nLeftVU - VUMETER_DECAY) : 0; + chn.nRightVU = (chn.nRightVU > VUMETER_DECAY) ? (chn.nRightVU - VUMETER_DECAY) : 0; chn.newLeftVol = chn.newRightVol = 0; chn.pCurrentSample = (chn.pModSample && chn.pModSample->HasSampleData() && chn.nLength && chn.IsSamplePlaying()) ? chn.pModSample->samplev() : nullptr; @@ -2402,7 +2442,7 @@ bool CSoundFile::ReadNote() } else if(Resampling::IsKnownMode(m_nResampling)) { chn.resamplingMode = m_nResampling; - } else if(m_SongFlags[SONG_ISAMIGA] && m_Resampler.m_Settings.emulateAmiga) + } else if(m_SongFlags[SONG_ISAMIGA] && m_Resampler.m_Settings.emulateAmiga != Resampling::AmigaFilter::Off) { // Enforce Amiga resampler for Amiga modules chn.resamplingMode = SRCMODE_AMIGA; @@ -2443,6 +2483,9 @@ bool CSoundFile::ReadNote() { chn.rightVol = chn.leftVol = 0; chn.nLength = 0; + // Put the channel back into the mixer for end-of-sample pop reduction + if(chn.nLOfs || chn.nROfs) + m_PlayState.ChnMix[m_nMixChannels++] = nChn; } chn.dwOldFlags = chn.dwFlags; @@ -2575,8 +2618,8 @@ void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn) else pPlugin->SetDryRatio(2 * defaultVolume); break; case PLUGIN_VOLUMEHANDLING_MIDI: - if(hasVolCommand) pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, std::min(127u, 2u * vol), nChn); - else pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, static_cast(std::min(127u, 2u * defaultVolume)), nChn); + if(hasVolCommand) pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, std::min(uint8(127), static_cast(2 * vol)), nChn); + else pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, static_cast(std::min(uint32(127), static_cast(2 * defaultVolume))), nChn); break; default: break; @@ -2599,20 +2642,20 @@ MPT_FORCEINLINE void ApplyGlobalVolumeWithRamping(int32 *SoundBuffer, int32 *Rea // Ramping required m_lHighResRampingGlobalVolume += step; SoundBuffer[0] = Util::muldiv(SoundBuffer[0], m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); - MPT_CONSTANT_IF(isStereo) SoundBuffer[1] = Util::muldiv(SoundBuffer[1], m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); - MPT_CONSTANT_IF(hasRear) RearBuffer[0] = Util::muldiv(RearBuffer[0] , m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); else MPT_UNUSED_VARIABLE(RearBuffer); - MPT_CONSTANT_IF(hasRear) RearBuffer[1] = Util::muldiv(RearBuffer[1] , m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); else MPT_UNUSED_VARIABLE(RearBuffer); + if constexpr(isStereo) SoundBuffer[1] = Util::muldiv(SoundBuffer[1], m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); + if constexpr(hasRear) RearBuffer[0] = Util::muldiv(RearBuffer[0] , m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); else MPT_UNUSED_VARIABLE(RearBuffer); + if constexpr(hasRear) RearBuffer[1] = Util::muldiv(RearBuffer[1] , m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); else MPT_UNUSED_VARIABLE(RearBuffer); m_nSamplesToGlobalVolRampDest--; } else { SoundBuffer[0] = Util::muldiv(SoundBuffer[0], m_nGlobalVolume, MAX_GLOBAL_VOLUME); - MPT_CONSTANT_IF(isStereo) SoundBuffer[1] = Util::muldiv(SoundBuffer[1], m_nGlobalVolume, MAX_GLOBAL_VOLUME); - MPT_CONSTANT_IF(hasRear) RearBuffer[0] = Util::muldiv(RearBuffer[0] , m_nGlobalVolume, MAX_GLOBAL_VOLUME); else MPT_UNUSED_VARIABLE(RearBuffer); - MPT_CONSTANT_IF(hasRear) RearBuffer[1] = Util::muldiv(RearBuffer[1] , m_nGlobalVolume, MAX_GLOBAL_VOLUME); else MPT_UNUSED_VARIABLE(RearBuffer); + if constexpr(isStereo) SoundBuffer[1] = Util::muldiv(SoundBuffer[1], m_nGlobalVolume, MAX_GLOBAL_VOLUME); + if constexpr(hasRear) RearBuffer[0] = Util::muldiv(RearBuffer[0] , m_nGlobalVolume, MAX_GLOBAL_VOLUME); else MPT_UNUSED_VARIABLE(RearBuffer); + if constexpr(hasRear) RearBuffer[1] = Util::muldiv(RearBuffer[1] , m_nGlobalVolume, MAX_GLOBAL_VOLUME); else MPT_UNUSED_VARIABLE(RearBuffer); m_lHighResRampingGlobalVolume = m_nGlobalVolume << VOLUMERAMPPRECISION; } SoundBuffer += isStereo ? 2 : 1; - MPT_CONSTANT_IF(hasRear) RearBuffer += 2; + if constexpr(hasRear) RearBuffer += 2; } } @@ -2656,8 +2699,8 @@ void CSoundFile::ProcessGlobalVolume(long lCount) // If step is too big (might cause click), extend ramp length. // Warning: This increases the volume ramp length by EXTREME amounts (factors of 100 are easily reachable) // compared to the user-defined setting, so this really should not be used! - int32 maxStep = std::max(50, (10000 / (m_PlayState.m_nGlobalVolumeRampAmount + 1))); - while(mpt::abs(step) > maxStep) + int32 maxStep = std::max(int32(50), static_cast((10000 / (m_PlayState.m_nGlobalVolumeRampAmount + 1)))); + while(std::abs(step) > maxStep) { m_PlayState.m_nSamplesToGlobalVolRampDest += m_PlayState.m_nGlobalVolumeRampAmount; step = delta / static_cast(m_PlayState.m_nSamplesToGlobalVolRampDest); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/Tables.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/Tables.cpp index fc0f3fd54..331a8f843 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/Tables.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/Tables.cpp @@ -24,13 +24,13 @@ OPENMPT_NAMESPACE_BEGIN ///////////////////////////////////////////////////////////////////////////// // Note Name Tables -const MPT_UCHAR_TYPE NoteNamesSharp[12][4] = +const mpt::uchar NoteNamesSharp[12][4] = { UL_("C-"), UL_("C#"), UL_("D-"), UL_("D#"), UL_("E-"), UL_("F-"), UL_("F#"), UL_("G-"), UL_("G#"), UL_("A-"), UL_("A#"), UL_("B-") }; -const MPT_UCHAR_TYPE NoteNamesFlat[12][4] = +const mpt::uchar NoteNamesFlat[12][4] = { UL_("C-"), UL_("Db"), UL_("D-"), UL_("Eb"), UL_("E-"), UL_("F-"), UL_("Gb"), UL_("G-"), UL_("Ab"), UL_("A-"), UL_("Bb"), UL_("B-") @@ -43,7 +43,7 @@ const MPT_UCHAR_TYPE NoteNamesFlat[12][4] = struct ModFormatInfo { MODTYPE format; // MOD_TYPE_XXXX - const MPT_UCHAR_TYPE *name; // "ProTracker" + const mpt::uchar *name; // "ProTracker" const char *extension; // "mod" }; @@ -93,10 +93,11 @@ static constexpr ModFormatInfo modFormatInfo[] = { MOD_TYPE_STM, UL_("ScreamTracker 2"), "stm" }, { MOD_TYPE_STP, UL_("Soundtracker Pro II"), "stp" }, { MOD_TYPE_ULT, UL_("UltraTracker"), "ult" }, - { MOD_TYPE_MOD, UL_("Grave Composer"), "wow" }, + { MOD_TYPE_MOD, UL_("Mod's Grave"), "wow" }, // converted formats (no MODTYPE) { MOD_TYPE_NONE, UL_("General Digital Music"), "gdm" }, { MOD_TYPE_NONE, UL_("Un4seen MO3"), "mo3" }, + { MOD_TYPE_NONE, UL_("OggMod FastTracker 2"), "oxm" }, #ifndef NO_ARCHIVE_SUPPORT // Compressed modules { MOD_TYPE_MOD, UL_("Compressed ProTracker"), "mdz" }, @@ -112,7 +113,7 @@ static constexpr ModFormatInfo modFormatInfo[] = struct ModContainerInfo { MODCONTAINERTYPE format; // MOD_CONTAINERTYPE_XXXX - const MPT_UCHAR_TYPE *name; // "Unreal Music" + const mpt::uchar *name; // "Unreal Music" const char *extension; // "umx" }; @@ -175,21 +176,19 @@ std::vector CSoundFile::GetSupportedExtensions(bool otherFormats) } -static bool IsEqualExtension(const char *a, const char *b) +static bool IsEqualExtension(std::string_view a, std::string_view b) { - std::size_t lena = std::strlen(a); - std::size_t lenb = std::strlen(b); - if(lena != lenb) + if(a.length() != b.length()) { return false; } - return mpt::CompareNoCaseAscii(a, b, lena) == 0; + return mpt::CompareNoCaseAscii(a, b) == 0; } -bool CSoundFile::IsExtensionSupported(const char *ext) +bool CSoundFile::IsExtensionSupported(std::string_view ext) { - if(ext == nullptr || ext[0] == 0) + if(ext.length() == 0) { return false; } @@ -217,7 +216,7 @@ mpt::ustring CSoundFile::ModContainerTypeToString(MODCONTAINERTYPE containertype { if(containerInfo.format == containertype) { - return mpt::ToUnicode(mpt::CharsetUTF8, containerInfo.extension); + return mpt::ToUnicode(mpt::Charset::UTF8, containerInfo.extension); } } return mpt::ustring(); @@ -682,27 +681,29 @@ const int16 CResampler::FastSincTable[256*4] = // Compute Bessel function Izero(y) using a series approximation -static double izero(double y) +double Izero(double y) { - double s=1, ds=1, d=0; + double s = 1, ds = 1, d = 0; do { - d = d + 2; ds = ds * (y*y)/(d*d); + d = d + 2; + ds = ds * (y * y) / (d * d); s = s + ds; - } while (ds > 1E-7 * s); + } while(ds > 1E-7 * s); return s; } -static void getsinc(SINC_TYPE *psinc, double beta, double lowpass_factor) + +static void getsinc(SINC_TYPE *psinc, double beta, double cutoff) { - if(lowpass_factor >= 0.999) + if(cutoff >= 0.999) { // Avoid mixer overflows. // 1.0 itself does not make much sense. - lowpass_factor = 0.999; + cutoff = 0.999; } - const double izero_beta = izero(beta); - const double kPi = 4.0*atan(1.0)*lowpass_factor; + const double izeroBeta = Izero(beta); + const double kPi = 4.0 * std::atan(1.0) * cutoff; for (int isrc=0; isrc<8*SINC_PHASES; isrc++) { double fsinc; @@ -713,10 +714,11 @@ static void getsinc(SINC_TYPE *psinc, double beta, double lowpass_factor) fsinc = 1.0; } else { - double x = (double)(ix - (4*SINC_PHASES)) * (double)(1.0/SINC_PHASES); - fsinc = sin(x*kPi) * izero(beta*sqrt(1-x*x*(1.0/16.0))) / (izero_beta*x*kPi); // Kaiser window + const double x = (double)(ix - (4*SINC_PHASES)) * (double)(1.0/SINC_PHASES); + const double xPi = x * kPi; + fsinc = std::sin(xPi) * Izero(beta * std::sqrt(1 - x * x * (1.0 / 16.0))) / (izeroBeta * xPi); // Kaiser window } - double coeff = fsinc * lowpass_factor; + double coeff = fsinc * cutoff; #ifdef MPT_INTMIXER int n = (int)std::floor(coeff * (1< z) +{ + if(b == 0) + w[i] = z; + else + { + GenerateTwiddleFactors(i, b >> 1, z); + GenerateTwiddleFactors(i | b, b >> 1, z * w[b]); + } +} + + +TinyFFT::TinyFFT(const uint32 fftSize) + : w(std::size_t(1) << (fftSize - 1)) + , k(fftSize) +{ + const uint32 m = 1 << k; + constexpr double PI2_ = 6.28318530717958647692; + const double arg = -PI2_ / m; + for(uint32 i = 1, j = m / 4; j; i <<= 1, j >>= 1) + { + w[i] = std::exp(I * (arg * j)); + } + GenerateTwiddleFactors(0, m / 4, 1); +} + + +uint32 TinyFFT::Size() const noexcept +{ + return 1 << k; +} + + +// Computes in-place FFT of size 2^k of A, result is in bit-reversed order. +void TinyFFT::FFT(std::vector> &A) const +{ + MPT_ASSERT(A.size() == (std::size_t(1) << k)); + const uint32 m = 1 << k; + uint32 u = 1; + uint32 v = m / 4; + if(k & 1) + { + for(uint32 j = 0; j < m / 2; j++) + { + auto Ajv = A[j + (m / 2)]; + A[j + (m / 2)] = A[j] - Ajv; + A[j] += Ajv; + } + u <<= 1; + v >>= 1; + } + for(uint32 i = k & ~1; i > 0; i -= 2) + { + for(uint32 jh = 0; jh < u; jh++) + { + auto wj = w[jh << 1]; + auto wj2 = w[jh]; + auto wj3 = wj2 * wj; + for(uint32 j = jh << i, je = j + v; j < je; j++) + { + auto tmp0 = A[j]; + auto tmp1 = wj * A[j + v]; + auto tmp2 = wj2 * A[j + 2 * v]; + auto tmp3 = wj3 * A[j + 3 * v]; + + auto ttmp0 = tmp0 + tmp2; + auto ttmp2 = tmp0 - tmp2; + auto ttmp1 = tmp1 + tmp3; + auto ttmp3 = -I * (tmp1 - tmp3); + + A[j] = ttmp0 + ttmp1; + A[j + v] = ttmp0 - ttmp1; + A[j + 2 * v] = ttmp2 + ttmp3; + A[j + 3 * v] = ttmp2 - ttmp3; + } + } + u <<= 2; + v >>= 2; + } +} + + +// Computes in-place IFFT of size 2^k of A, input is expected to be in bit-reversed order. +void TinyFFT::IFFT(std::vector> &A) const +{ + MPT_ASSERT(A.size() == (std::size_t(1) << k)); + const uint32 m = 1 << k; + uint32 u = m / 4; + uint32 v = 1; + for(uint32 i = 2; i <= k; i += 2) + { + for(uint32 jh = 0; jh < u; jh++) + { + auto wj = std::conj(w[jh << 1]); + auto wj2 = std::conj(w[jh]); + auto wj3 = wj2 * wj; + for(uint32 j = jh << i, je = j + v; j < je; j++) + { + auto tmp0 = A[j]; + auto tmp1 = A[j + v]; + auto tmp2 = A[j + 2 * v]; + auto tmp3 = A[j + 3 * v]; + + auto ttmp0 = tmp0 + tmp1; + auto ttmp1 = tmp0 - tmp1; + auto ttmp2 = tmp2 + tmp3; + auto ttmp3 = I * (tmp2 - tmp3); + + A[j] = ttmp0 + ttmp2; + A[j + v] = wj * (ttmp1 + ttmp3); + A[j + 2 * v] = wj2 * (ttmp0 - ttmp2); + A[j + 3 * v] = wj3 * (ttmp1 - ttmp3); + } + } + u >>= 2; + v <<= 2; + } + if(k & 1) + { + for(uint32 j = 0; j < m / 2; j++) + { + auto Ajv = A[j + (m / 2)]; + A[j + (m / 2)] = A[j] - Ajv; + A[j] += Ajv; + } + } +} + + +void TinyFFT::Normalize(std::vector> &data) +{ + const double s = static_cast(data.size()); + for(auto &v : data) + v /= s; +} + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/TinyFFT.h b/Frameworks/OpenMPT/OpenMPT/soundlib/TinyFFT.h new file mode 100644 index 000000000..21229d0f9 --- /dev/null +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/TinyFFT.h @@ -0,0 +1,42 @@ +/* + * TinyFFT.h + * --------- + * Purpose: A simple FFT implementation for power-of-two FFTs + * Notes : This is a C++ adaption of Ryuhei Mori's BSD 2-clause licensed TinyFFT + * available from https://github.com/ryuhei-mori/tinyfft + * Authors: Ryuhei Mori + * OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include "BuildSettings.h" +#include + +OPENMPT_NAMESPACE_BEGIN + +class TinyFFT +{ + static constexpr std::complex I{0.0, 1.0}; + std::vector> w; // Pre-computed twiddle factors + const uint32 k; // log2 of FFT size + + void GenerateTwiddleFactors(uint32 i, uint32 b, std::complex z); + +public: + TinyFFT(const uint32 fftSize); + + uint32 Size() const noexcept; + + // Computes in-place FFT of size 2^k of A, result is in bit-reversed order. + void FFT(std::vector> &A) const; + + // Computes in-place IFFT of size 2^k of A, input is expected to be in bit-reversed order. + void IFFT(std::vector> &A) const; + + static void Normalize(std::vector> &data); +}; + +OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp index c614d583e..2b95d6182 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/UpgradeModule.cpp @@ -43,7 +43,7 @@ struct UpgradePatternData { // Out-of-range global volume commands should be ignored in S3M. Fixed in OpenMPT 1.19 (r831). // So for tracks made with older versions of OpenMPT, we limit invalid global volume commands. - if(version < MAKE_VERSION_NUMERIC(1, 19, 00, 00) && m.command == CMD_GLOBALVOLUME) + if(version < MPT_V("1.19.00.00") && m.command == CMD_GLOBALVOLUME) { LimitMax(m.param, ModCommand::PARAM(64)); } @@ -51,8 +51,8 @@ struct UpgradePatternData else if(modType & (MOD_TYPE_IT | MOD_TYPE_MPT)) { - if(version < MAKE_VERSION_NUMERIC(1, 17, 03, 02) || - (!compatPlay && version < MAKE_VERSION_NUMERIC(1, 20, 00, 00))) + if(version < MPT_V("1.17.03.02") || + (!compatPlay && version < MPT_V("1.20.00.00"))) { if(m.command == CMD_GLOBALVOLUME) { @@ -80,14 +80,14 @@ struct UpgradePatternData // In the IT format, slide commands with both nibbles set should be ignored. // For note volume slides, OpenMPT 1.18 fixes this in compatible mode, OpenMPT 1.20 fixes this in normal mode as well. const bool noteVolSlide = - (version < MAKE_VERSION_NUMERIC(1, 18, 00, 00) || - (!compatPlay && version < MAKE_VERSION_NUMERIC(1, 20, 00, 00))) + (version < MPT_V("1.18.00.00") || + (!compatPlay && version < MPT_V("1.20.00.00"))) && (m.command == CMD_VOLUMESLIDE || m.command == CMD_VIBRATOVOL || m.command == CMD_TONEPORTAVOL || m.command == CMD_PANNINGSLIDE); // OpenMPT 1.20 also fixes this for global volume and channel volume slides. const bool chanVolSlide = - (version < MAKE_VERSION_NUMERIC(1, 20, 00, 00)) + (version < MPT_V("1.20.00.00")) && (m.command == CMD_GLOBALVOLSLIDE || m.command == CMD_CHANNELVOLSLIDE); @@ -95,12 +95,15 @@ struct UpgradePatternData { if((m.param & 0x0F) != 0x00 && (m.param & 0x0F) != 0x0F && (m.param & 0xF0) != 0x00 && (m.param & 0xF0) != 0xF0) { - m.param &= 0x0F; + if(m.command == CMD_GLOBALVOLSLIDE) + m.param &= 0xF0; + else + m.param &= 0x0F; } } - if(version < MAKE_VERSION_NUMERIC(1, 22, 01, 04) - && version != MAKE_VERSION_NUMERIC(1, 22, 00, 00)) // Ignore compatibility export + if(version < MPT_V("1.22.01.04") + && version != MPT_V("1.22.00.00")) // Ignore compatibility export { // OpenMPT 1.22.01.04 fixes illegal (out of range) instrument numbers; they should do nothing. In previous versions, they stopped the playing sample. if(sndFile.GetNumInstruments() && m.instr > sndFile.GetNumInstruments() && !compatPlay) @@ -115,16 +118,16 @@ struct UpgradePatternData { // Something made be believe that out-of-range global volume commands are ignored in XM // just like they are ignored in IT, but apparently they are not. Aaaaaargh! - if(((version >= MAKE_VERSION_NUMERIC(1, 17, 03, 02) && compatPlay) || (version >= MAKE_VERSION_NUMERIC(1, 20, 00, 00))) - && version < MAKE_VERSION_NUMERIC(1, 24, 02, 02) + if(((version >= MPT_V("1.17.03.02") && compatPlay) || (version >= MPT_V("1.20.00.00"))) + && version < MPT_V("1.24.02.02") && m.command == CMD_GLOBALVOLUME && m.param > 64) { m.command = CMD_NONE; } - if(version < MAKE_VERSION_NUMERIC(1, 19, 00, 00) - || (!compatPlay && version < MAKE_VERSION_NUMERIC(1, 20, 00, 00))) + if(version < MPT_V("1.19.00.00") + || (!compatPlay && version < MPT_V("1.20.00.00"))) { if(m.command == CMD_OFFSET && m.volcmd == VOLCMD_TONEPORTAMENTO) { @@ -134,7 +137,7 @@ struct UpgradePatternData } } - if(version < MAKE_VERSION_NUMERIC(1, 20, 01, 10) + if(version < MPT_V("1.20.01.10") && m.volcmd == VOLCMD_TONEPORTAMENTO && m.command == CMD_TONEPORTAMENTO && (m.vol != 0 || compatPlay) && m.param != 0) { @@ -145,7 +148,7 @@ struct UpgradePatternData m.param = mpt::saturate_cast(param); } - if(version < MAKE_VERSION_NUMERIC(1, 22, 07, 09) + if(version < MPT_V("1.22.07.09") && m.command == CMD_SPEED && m.param == 0) { // OpenMPT can emulate FT2's F00 behaviour now. @@ -153,7 +156,7 @@ struct UpgradePatternData } } - if(version < MAKE_VERSION_NUMERIC(1, 20, 00, 00)) + if(version < MPT_V("1.20.00.00")) { // Pattern Delay fixes @@ -161,7 +164,7 @@ struct UpgradePatternData // We also fix X6x commands in hacked XM files, since they are treated identically to the S6x command in IT/S3M files. // We don't treat them in files made with OpenMPT 1.18+ that have compatible play enabled, though, since they are ignored there anyway. const bool fixX6x = (m.command == CMD_XFINEPORTAUPDOWN && (m.param & 0xF0) == 0x60 - && (!(compatPlay && modType == MOD_TYPE_XM) || version < MAKE_VERSION_NUMERIC(1, 18, 00, 00))); + && (!(compatPlay && modType == MOD_TYPE_XM) || version < MPT_V("1.18.00.00"))); if(fixS6x || fixX6x) { @@ -192,8 +195,8 @@ struct UpgradePatternData } if(m.volcmd == VOLCMD_VIBRATODEPTH - && version < MAKE_VERSION_NUMERIC(1, 27, 00, 37) - && version != MAKE_VERSION_NUMERIC(1, 27, 00, 00)) + && version < MPT_V("1.27.00.37") + && version != MPT_V("1.27.00.00")) { // Fix handling of double vibrato commands - previously only one of them was applied at a time if(m.command == CMD_VIBRATOVOL && m.vol > 0) @@ -228,7 +231,7 @@ struct UpgradePatternData void CSoundFile::UpgradeModule() { - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 02, 46) && m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 17, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("1.17.02.46") && m_dwLastSavedWithVersion != MPT_V("1.17.00.00")) { // Compatible playback mode didn't exist in earlier versions, so definitely disable it. m_playBehaviour.reset(MSF_COMPATIBLE_PLAY); @@ -237,22 +240,22 @@ void CSoundFile::UpgradeModule() const bool compatModeIT = m_playBehaviour[MSF_COMPATIBLE_PLAY] && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)); const bool compatModeXM = m_playBehaviour[MSF_COMPATIBLE_PLAY] && GetType() == MOD_TYPE_XM; - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("1.20.00.00")) { for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if(Instruments[i] != nullptr) { ModInstrument *ins = Instruments[i]; // Previously, volume swing values ranged from 0 to 64. They should reach from 0 to 100 instead. - ins->nVolSwing = static_cast(std::min(ins->nVolSwing * 100 / 64, 100)); + ins->nVolSwing = static_cast(std::min(static_cast(ins->nVolSwing * 100 / 64), uint32(100))); - if(!compatModeIT || m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00)) + if(!compatModeIT || m_dwLastSavedWithVersion < MPT_V("1.18.00.00")) { // Previously, Pitch/Pan Separation was only half depth. // This was corrected in compatible mode in OpenMPT 1.18, and in OpenMPT 1.20 it is corrected in normal mode as well. ins->nPPS = (ins->nPPS + (ins->nPPS >= 0 ? 1 : -1)) / 2; } - if(!compatModeIT || m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 03, 02)) + if(!compatModeIT || m_dwLastSavedWithVersion < MPT_V("1.17.03.02")) { // IT compatibility 24. Short envelope loops // Previously, the pitch / filter envelope loop handling was broken, the loop was shortened by a tick (like in XM). @@ -260,7 +263,7 @@ void CSoundFile::UpgradeModule() ins->GetEnvelope(ENV_PITCH).Convert(MOD_TYPE_XM, GetType()); } - if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 00, 00) && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 02, 50)) + if(m_dwLastSavedWithVersion >= MPT_V("1.17.00.00") && m_dwLastSavedWithVersion < MPT_V("1.17.02.50")) { // If there are any plugins that can receive volume commands, enable volume bug emulation. if(ins->nMixPlug && ins->HasValidMIDIChannel()) @@ -269,7 +272,7 @@ void CSoundFile::UpgradeModule() } } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 02, 50) && (ins->nVolSwing | ins->nPanSwing | ins->nCutSwing | ins->nResSwing)) + if(m_dwLastSavedWithVersion < MPT_V("1.17.02.50") && (ins->nVolSwing | ins->nPanSwing | ins->nCutSwing | ins->nResSwing)) { // If there are any instruments with random variation, enable the old random variation behaviour. m_playBehaviour.set(kMPTOldSwingBehaviour); @@ -277,7 +280,7 @@ void CSoundFile::UpgradeModule() } } - if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 03, 02) || !compatModeIT)) + if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (m_dwLastSavedWithVersion < MPT_V("1.17.03.02") || !compatModeIT)) { // In the IT format, a sweep value of 0 shouldn't apply vibrato at all. Previously, a value of 0 was treated as "no sweep". // In OpenMPT 1.17.03.02, this was corrected in compatible mode, in OpenMPT 1.20 it is corrected in normal mode as well, @@ -295,8 +298,8 @@ void CSoundFile::UpgradeModule() m_MidiCfg.UpgradeMacros(); } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 02, 10) - && m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 20, 00, 00) + if(m_dwLastSavedWithVersion < MPT_V("1.20.02.10") + && m_dwLastSavedWithVersion != MPT_V("1.20.00.00") && (GetType() & (MOD_TYPE_XM | MOD_TYPE_IT | MOD_TYPE_MPT))) { bool instrPlugs = false; @@ -315,8 +318,8 @@ void CSoundFile::UpgradeModule() } } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 03, 12) - && m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 22, 00, 00) + if(m_dwLastSavedWithVersion < MPT_V("1.22.03.12") + && m_dwLastSavedWithVersion != MPT_V("1.22.00.00") && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (m_playBehaviour[MSF_COMPATIBLE_PLAY] || m_playBehaviour[kMPTOldSwingBehaviour])) { @@ -332,17 +335,17 @@ void CSoundFile::UpgradeModule() } #ifndef NO_PLUGINS - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 22, 07, 01)) + if(m_dwLastSavedWithVersion < MPT_V("1.22.07.01")) { // Convert ANSI plugin path names to UTF-8 (irrelevant in probably 99% of all cases anyway, I think I've never seen a VST plugin with a non-ASCII file name) for(auto &plugin : m_MixPlugins) { #if defined(MODPLUG_TRACKER) - const std::string name = mpt::ToCharset(mpt::CharsetUTF8, mpt::CharsetLocale, plugin.Info.szLibraryName); + const std::string name = mpt::ToCharset(mpt::Charset::UTF8, mpt::Charset::Locale, plugin.Info.szLibraryName); #else - const std::string name = mpt::ToCharset(mpt::CharsetUTF8, mpt::CharsetWindows1252, plugin.Info.szLibraryName); + const std::string name = mpt::ToCharset(mpt::Charset::UTF8, mpt::Charset::Windows1252, plugin.Info.szLibraryName); #endif - mpt::String::Copy(plugin.Info.szLibraryName, name); + plugin.Info.szLibraryName = name; } } #endif // NO_PLUGINS @@ -351,15 +354,15 @@ void CSoundFile::UpgradeModule() // Starting from OpenMPT 1.23.01.04, FT2-style panning has its own mix mode instead. if(GetType() == MOD_TYPE_XM) { - if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 22, 07, 19) - && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 23, 01, 04) + if(m_dwLastSavedWithVersion >= MPT_V("1.22.07.19") + && m_dwLastSavedWithVersion < MPT_V("1.23.01.04") && GetMixLevels() == mixLevelsCompatible) { SetMixLevels(mixLevelsCompatibleFT2); } } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 25, 00, 07) && m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 25, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("1.25.00.07") && m_dwLastSavedWithVersion != MPT_V("1.25.00.00")) { // Instrument plugins can now receive random volume variation. // For old instruments, disable volume swing in case there was no sample associated. @@ -384,7 +387,7 @@ void CSoundFile::UpgradeModule() } } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("1.26.00.00")) { for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if(Instruments[i] != nullptr) { @@ -394,14 +397,14 @@ void CSoundFile::UpgradeModule() // OpenMPT 1.18 fixed the depth of random pan in compatible mode. // OpenMPT 1.26 fixes it in normal mode too. - if(!compatModeIT || m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00)) + if(!compatModeIT || m_dwLastSavedWithVersion < MPT_V("1.18.00.00")) { ins->nPanSwing = (ins->nPanSwing + 3) / 4u; } } } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 28, 00, 12)) + if(m_dwLastSavedWithVersion < MPT_V("1.28.00.12")) { for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if(Instruments[i] != nullptr) { @@ -413,7 +416,7 @@ void CSoundFile::UpgradeModule() } } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 28, 03, 04)) + if(m_dwLastSavedWithVersion < MPT_V("1.28.03.04")) { for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if (Instruments[i] != nullptr) { @@ -437,95 +440,95 @@ void CSoundFile::UpgradeModule() Version version; }; - if(compatModeIT && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00)) + if(compatModeIT && m_dwLastSavedWithVersion < MPT_V("1.26.00.00")) { // Pre-1.26: Detailed compatibility flags did not exist. static constexpr PlayBehaviourVersion behaviours[] = { - { kTempoClamp, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kPerChannelGlobalVolSlide, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kPanOverride, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITInstrWithoutNote, MAKE_VERSION_NUMERIC(1, 17, 02, 46) }, - { kITVolColFinePortamento, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, - { kITArpeggio, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, - { kITOutOfRangeDelay, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, - { kITPortaMemoryShare, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, - { kITPatternLoopTargetReset, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, - { kITFT2PatternLoop, MAKE_VERSION_NUMERIC(1, 17, 02, 49) }, - { kITPingPongNoReset, MAKE_VERSION_NUMERIC(1, 17, 02, 51) }, - { kITEnvelopeReset, MAKE_VERSION_NUMERIC(1, 17, 02, 51) }, - { kITClearOldNoteAfterCut, MAKE_VERSION_NUMERIC(1, 17, 02, 52) }, - { kITVibratoTremoloPanbrello, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITTremor, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITRetrigger, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITMultiSampleBehaviour, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITPortaTargetReached, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITPatternLoopBreak, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITOffset, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITSwingBehaviour, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, - { kITNNAReset, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, - { kITSCxStopsSample, MAKE_VERSION_NUMERIC(1, 18, 00, 01) }, - { kITEnvelopePositionHandling, MAKE_VERSION_NUMERIC(1, 18, 01, 00) }, - { kITPortamentoInstrument, MAKE_VERSION_NUMERIC(1, 19, 00, 01) }, - { kITPingPongMode, MAKE_VERSION_NUMERIC(1, 19, 00, 21) }, - { kITRealNoteMapping, MAKE_VERSION_NUMERIC(1, 19, 00, 30) }, - { kITHighOffsetNoRetrig, MAKE_VERSION_NUMERIC(1, 20, 00, 14) }, - { kITFilterBehaviour, MAKE_VERSION_NUMERIC(1, 20, 00, 35) }, - { kITNoSurroundPan, MAKE_VERSION_NUMERIC(1, 20, 00, 53) }, - { kITShortSampleRetrig, MAKE_VERSION_NUMERIC(1, 20, 00, 54) }, - { kITPortaNoNote, MAKE_VERSION_NUMERIC(1, 20, 00, 56) }, - { kRowDelayWithNoteDelay, MAKE_VERSION_NUMERIC(1, 20, 00, 76) }, - { kITDontResetNoteOffOnPorta, MAKE_VERSION_NUMERIC(1, 20, 02, 06) }, - { kITVolColMemory, MAKE_VERSION_NUMERIC(1, 21, 01, 16) }, - { kITPortamentoSwapResetsPos, MAKE_VERSION_NUMERIC(1, 21, 01, 25) }, - { kITEmptyNoteMapSlot, MAKE_VERSION_NUMERIC(1, 21, 01, 25) }, - { kITFirstTickHandling, MAKE_VERSION_NUMERIC(1, 22, 07, 09) }, - { kITSampleAndHoldPanbrello, MAKE_VERSION_NUMERIC(1, 22, 07, 19) }, - { kITClearPortaTarget, MAKE_VERSION_NUMERIC(1, 23, 04, 03) }, - { kITPanbrelloHold, MAKE_VERSION_NUMERIC(1, 24, 01, 06) }, - { kITPanningReset, MAKE_VERSION_NUMERIC(1, 24, 01, 06) }, - { kITPatternLoopWithJumps, MAKE_VERSION_NUMERIC(1, 25, 00, 19) }, + { kTempoClamp, MPT_V("1.17.03.02") }, + { kPerChannelGlobalVolSlide, MPT_V("1.17.03.02") }, + { kPanOverride, MPT_V("1.17.03.02") }, + { kITInstrWithoutNote, MPT_V("1.17.02.46") }, + { kITVolColFinePortamento, MPT_V("1.17.02.49") }, + { kITArpeggio, MPT_V("1.17.02.49") }, + { kITOutOfRangeDelay, MPT_V("1.17.02.49") }, + { kITPortaMemoryShare, MPT_V("1.17.02.49") }, + { kITPatternLoopTargetReset, MPT_V("1.17.02.49") }, + { kITFT2PatternLoop, MPT_V("1.17.02.49") }, + { kITPingPongNoReset, MPT_V("1.17.02.51") }, + { kITEnvelopeReset, MPT_V("1.17.02.51") }, + { kITClearOldNoteAfterCut, MPT_V("1.17.02.52") }, + { kITVibratoTremoloPanbrello, MPT_V("1.17.03.02") }, + { kITTremor, MPT_V("1.17.03.02") }, + { kITRetrigger, MPT_V("1.17.03.02") }, + { kITMultiSampleBehaviour, MPT_V("1.17.03.02") }, + { kITPortaTargetReached, MPT_V("1.17.03.02") }, + { kITPatternLoopBreak, MPT_V("1.17.03.02") }, + { kITOffset, MPT_V("1.17.03.02") }, + { kITSwingBehaviour, MPT_V("1.18.00.00") }, + { kITNNAReset, MPT_V("1.18.00.00") }, + { kITSCxStopsSample, MPT_V("1.18.00.01") }, + { kITEnvelopePositionHandling, MPT_V("1.18.01.00") }, + { kITPortamentoInstrument, MPT_V("1.19.00.01") }, + { kITPingPongMode, MPT_V("1.19.00.21") }, + { kITRealNoteMapping, MPT_V("1.19.00.30") }, + { kITHighOffsetNoRetrig, MPT_V("1.20.00.14") }, + { kITFilterBehaviour, MPT_V("1.20.00.35") }, + { kITNoSurroundPan, MPT_V("1.20.00.53") }, + { kITShortSampleRetrig, MPT_V("1.20.00.54") }, + { kITPortaNoNote, MPT_V("1.20.00.56") }, + { kRowDelayWithNoteDelay, MPT_V("1.20.00.76") }, + { kITFT2DontResetNoteOffOnPorta, MPT_V("1.20.02.06") }, + { kITVolColMemory, MPT_V("1.21.01.16") }, + { kITPortamentoSwapResetsPos, MPT_V("1.21.01.25") }, + { kITEmptyNoteMapSlot, MPT_V("1.21.01.25") }, + { kITFirstTickHandling, MPT_V("1.22.07.09") }, + { kITSampleAndHoldPanbrello, MPT_V("1.22.07.19") }, + { kITClearPortaTarget, MPT_V("1.23.04.03") }, + { kITPanbrelloHold, MPT_V("1.24.01.06") }, + { kITPanningReset, MPT_V("1.24.01.06") }, + { kITPatternLoopWithJumpsOld, MPT_V("1.25.00.19") }, }; for(const auto &b : behaviours) { m_playBehaviour.set(b.behaviour, (m_dwLastSavedWithVersion >= b.version || m_dwLastSavedWithVersion == b.version.Masked(0xFFFF0000u))); } - } else if(compatModeXM && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00)) + } else if(compatModeXM && m_dwLastSavedWithVersion < MPT_V("1.26.00.00")) { // Pre-1.26: Detailed compatibility flags did not exist. static constexpr PlayBehaviourVersion behaviours[] = { - { kTempoClamp, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kPerChannelGlobalVolSlide, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kPanOverride, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kITFT2PatternLoop, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2Arpeggio, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2Retrigger, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2VolColVibrato, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2PortaNoNote, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2KeyOff, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2PanSlide, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2OffsetOutOfRange, MAKE_VERSION_NUMERIC(1, 17, 03, 02) }, - { kFT2RestrictXCommand, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, - { kFT2RetrigWithNoteDelay, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, - { kFT2SetPanEnvPos, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, - { kFT2PortaIgnoreInstr, MAKE_VERSION_NUMERIC(1, 18, 00, 01) }, - { kFT2VolColMemory, MAKE_VERSION_NUMERIC(1, 18, 01, 00) }, - { kFT2LoopE60Restart, MAKE_VERSION_NUMERIC(1, 18, 02, 01) }, - { kFT2ProcessSilentChannels, MAKE_VERSION_NUMERIC(1, 18, 02, 01) }, - { kFT2ReloadSampleSettings, MAKE_VERSION_NUMERIC(1, 20, 00, 36) }, - { kFT2PortaDelay, MAKE_VERSION_NUMERIC(1, 20, 00, 40) }, - { kFT2Transpose, MAKE_VERSION_NUMERIC(1, 20, 00, 62) }, - { kFT2PatternLoopWithJumps, MAKE_VERSION_NUMERIC(1, 20, 00, 69) }, - { kFT2PortaTargetNoReset, MAKE_VERSION_NUMERIC(1, 20, 00, 69) }, - { kFT2EnvelopeEscape, MAKE_VERSION_NUMERIC(1, 20, 00, 77) }, - { kFT2Tremor, MAKE_VERSION_NUMERIC(1, 20, 01, 11) }, - { kFT2OutOfRangeDelay, MAKE_VERSION_NUMERIC(1, 20, 02, 02) }, - { kFT2Periods, MAKE_VERSION_NUMERIC(1, 22, 03, 01) }, - { kFT2PanWithDelayedNoteOff, MAKE_VERSION_NUMERIC(1, 22, 03, 02) }, - { kFT2VolColDelay, MAKE_VERSION_NUMERIC(1, 22, 07, 19) }, - { kFT2FinetunePrecision, MAKE_VERSION_NUMERIC(1, 22, 07, 19) }, + { kTempoClamp, MPT_V("1.17.03.02") }, + { kPerChannelGlobalVolSlide, MPT_V("1.17.03.02") }, + { kPanOverride, MPT_V("1.17.03.02") }, + { kITFT2PatternLoop, MPT_V("1.17.03.02") }, + { kFT2Arpeggio, MPT_V("1.17.03.02") }, + { kFT2Retrigger, MPT_V("1.17.03.02") }, + { kFT2VolColVibrato, MPT_V("1.17.03.02") }, + { kFT2PortaNoNote, MPT_V("1.17.03.02") }, + { kFT2KeyOff, MPT_V("1.17.03.02") }, + { kFT2PanSlide, MPT_V("1.17.03.02") }, + { kFT2ST3OffsetOutOfRange, MPT_V("1.17.03.02") }, + { kFT2RestrictXCommand, MPT_V("1.18.00.00") }, + { kFT2RetrigWithNoteDelay, MPT_V("1.18.00.00") }, + { kFT2SetPanEnvPos, MPT_V("1.18.00.00") }, + { kFT2PortaIgnoreInstr, MPT_V("1.18.00.01") }, + { kFT2VolColMemory, MPT_V("1.18.01.00") }, + { kFT2LoopE60Restart, MPT_V("1.18.02.01") }, + { kFT2ProcessSilentChannels, MPT_V("1.18.02.01") }, + { kFT2ReloadSampleSettings, MPT_V("1.20.00.36") }, + { kFT2PortaDelay, MPT_V("1.20.00.40") }, + { kFT2Transpose, MPT_V("1.20.00.62") }, + { kFT2PatternLoopWithJumps, MPT_V("1.20.00.69") }, + { kFT2PortaTargetNoReset, MPT_V("1.20.00.69") }, + { kFT2EnvelopeEscape, MPT_V("1.20.00.77") }, + { kFT2Tremor, MPT_V("1.20.01.11") }, + { kFT2OutOfRangeDelay, MPT_V("1.20.02.02") }, + { kFT2Periods, MPT_V("1.22.03.01") }, + { kFT2PanWithDelayedNoteOff, MPT_V("1.22.03.02") }, + { kFT2VolColDelay, MPT_V("1.22.07.19") }, + { kFT2FinetunePrecision, MPT_V("1.22.07.19") }, }; for(const auto &b : behaviours) @@ -539,9 +542,12 @@ void CSoundFile::UpgradeModule() // The following behaviours were added in/after OpenMPT 1.26, so are not affected by the upgrade mechanism above. static constexpr PlayBehaviourVersion behaviours[] = { - { kITInstrWithNoteOff, MAKE_VERSION_NUMERIC(1, 26, 00, 01) }, - { kITMultiSampleInstrumentNumber, MAKE_VERSION_NUMERIC(1, 27, 00, 27) }, - { kITInstrWithNoteOffOldEffects, MAKE_VERSION_NUMERIC(1, 28, 02, 06) }, + { kITInstrWithNoteOff, MPT_V("1.26.00.01") }, + { kITMultiSampleInstrumentNumber, MPT_V("1.27.00.27") }, + { kITInstrWithNoteOffOldEffects, MPT_V("1.28.02.06") }, + { kITDoNotOverrideChannelPan, MPT_V("1.29.00.22") }, + { kITPatternLoopWithJumps, MPT_V("1.29.00.32") }, + { kITDCTBehaviour, MPT_V("1.29.00.57") }, }; for(const auto &b : behaviours) @@ -557,12 +563,13 @@ void CSoundFile::UpgradeModule() // The following behaviours were added after OpenMPT 1.26, so are not affected by the upgrade mechanism above. static constexpr PlayBehaviourVersion behaviours[] = { - { kFT2NoteOffFlags, MAKE_VERSION_NUMERIC(1, 27, 00, 27) }, - { kRowDelayWithNoteDelay, MAKE_VERSION_NUMERIC(1, 27, 00, 37) }, - { kFT2TremoloRampWaveform, MAKE_VERSION_NUMERIC(1, 27, 00, 37) }, - { kFT2PortaUpDownMemory, MAKE_VERSION_NUMERIC(1, 27, 00, 37) }, - { kFT2PanSustainRelease, MAKE_VERSION_NUMERIC(1, 28, 00, 09) }, - { kFT2NoteDelayWithoutInstr, MAKE_VERSION_NUMERIC(1, 28, 00, 44) }, + { kFT2NoteOffFlags, MPT_V("1.27.00.27") }, + { kRowDelayWithNoteDelay, MPT_V("1.27.00.37") }, + { kFT2TremoloRampWaveform, MPT_V("1.27.00.37") }, + { kFT2PortaUpDownMemory, MPT_V("1.27.00.37") }, + { kFT2PanSustainRelease, MPT_V("1.28.00.09") }, + { kFT2NoteDelayWithoutInstr, MPT_V("1.28.00.44") }, + { kITFT2DontResetNoteOffOnPorta, MPT_V("1.29.00.34" )}, }; for(const auto &b : behaviours) @@ -575,14 +582,16 @@ void CSoundFile::UpgradeModule() // We do not store any of these flags in S3M files. static constexpr PlayBehaviourVersion behaviours[] = { - { kST3NoMutedChannels, MAKE_VERSION_NUMERIC(1, 18, 00, 00) }, - { kST3EffectMemory, MAKE_VERSION_NUMERIC(1, 20, 00, 00) }, - { kRowDelayWithNoteDelay, MAKE_VERSION_NUMERIC(1, 20, 00, 00) }, - { kST3PortaSampleChange, MAKE_VERSION_NUMERIC(1, 22, 00, 00) }, - { kST3VibratoMemory, MAKE_VERSION_NUMERIC(1, 26, 00, 00) }, - { kITPanbrelloHold, MAKE_VERSION_NUMERIC(1, 26, 00, 00) }, - { KST3PortaAfterArpeggio, MAKE_VERSION_NUMERIC(1, 27, 00, 00) }, - { kST3OffsetWithoutInstrument, MAKE_VERSION_NUMERIC(1, 28, 00, 00) }, + { kST3NoMutedChannels, MPT_V("1.18.00.00") }, + { kST3EffectMemory, MPT_V("1.20.00.00") }, + { kRowDelayWithNoteDelay, MPT_V("1.20.00.00") }, + { kST3PortaSampleChange, MPT_V("1.22.00.00") }, + { kST3VibratoMemory, MPT_V("1.26.00.00") }, + { kITPanbrelloHold, MPT_V("1.26.00.00") }, + { KST3PortaAfterArpeggio, MPT_V("1.27.00.00") }, + { kST3OffsetWithoutInstrument, MPT_V("1.28.00.00") }, + { kST3RetrigAfterNoteCut, MPT_V("1.29.00.00") }, + { kFT2ST3OffsetOutOfRange, MPT_V("1.29.00.00") }, }; for(const auto &b : behaviours) @@ -592,13 +601,13 @@ void CSoundFile::UpgradeModule() } } - if(GetType() == MOD_TYPE_XM && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 19, 00, 00)) + if(GetType() == MOD_TYPE_XM && m_dwLastSavedWithVersion < MPT_V("1.19.00.00")) { // This bug was introduced sometime between 1.18.03.00 and 1.19.01.00 m_playBehaviour.set(kFT2NoteDelayWithoutInstr); } - if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 27, 00, 27) && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 27, 00, 49)) + if(m_dwLastSavedWithVersion >= MPT_V("1.27.00.27") && m_dwLastSavedWithVersion < MPT_V("1.27.00.49")) { // OpenMPT 1.27 inserted some IT/FT2 flags before the S3M flags that are never saved to files anyway, to keep the flag IDs a bit more compact. // However, it was overlooked that these flags would still be read by OpenMPT 1.26 and thus S3M-specific behaviour would be enabled in IT/XM files. @@ -611,28 +620,28 @@ void CSoundFile::UpgradeModule() } } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 17, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("1.17.00.00")) { // MPT 1.16 has a maximum tempo of 255. m_playBehaviour.set(kTempoClamp); - } else if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 17, 00, 00) && m_dwLastSavedWithVersion <= MAKE_VERSION_NUMERIC(1, 20, 01, 03) && m_dwLastSavedWithVersion != MAKE_VERSION_NUMERIC(1, 20, 00, 00)) + } else if(m_dwLastSavedWithVersion >= MPT_V("1.17.00.00") && m_dwLastSavedWithVersion <= MPT_V("1.20.01.03") && m_dwLastSavedWithVersion != MPT_V("1.20.00.00")) { // OpenMPT introduced some "fixes" that execute regular portamentos also at speed 1. m_playBehaviour.set(kSlidesAtSpeed1); } - if(m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 24, 00, 00)) + if(m_dwLastSavedWithVersion < MPT_V("1.24.00.00")) { // No frequency slides in Hz before OpenMPT 1.24 m_playBehaviour.reset(kHertzInLinearMode); - } else if(m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 24, 00, 00) && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 26, 00, 00) && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) + } else if(m_dwLastSavedWithVersion >= MPT_V("1.24.00.00") && m_dwLastSavedWithVersion < MPT_V("1.26.00.00") && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) { // Frequency slides were always in Hz rather than periods in this version range. m_playBehaviour.set(kHertzInLinearMode); } if(m_playBehaviour[kITEnvelopePositionHandling] - && m_dwLastSavedWithVersion >= MAKE_VERSION_NUMERIC(1, 23, 01, 02) && m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 28, 00, 43)) + && m_dwLastSavedWithVersion >= MPT_V("1.23.01.02") && m_dwLastSavedWithVersion < MPT_V("1.28.00.43")) { // Bug that effectively clamped the release node to the sustain end for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++) if(Instruments[i] != nullptr) @@ -646,6 +655,18 @@ void CSoundFile::UpgradeModule() } } } + + if(GetType() == MOD_TYPE_MPT && GetNumInstruments() && m_dwLastSavedWithVersion >= MPT_V("1.28.00.20") && m_dwLastSavedWithVersion <= MPT_V("1.29.55.00")) + { + for(SAMPLEINDEX i = 1; i <= GetNumSamples(); i++) + { + if(Samples[i].uFlags[CHN_ADLIB]) + { + m_playBehaviour.set(kOPLNoResetAtEnvelopeEnd); + break; + } + } + } } OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.cpp index 9640159af..842925fb3 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.cpp @@ -163,30 +163,30 @@ uint16 WAVReader::GetFileCodePage(ChunkReader::ChunkList &chunks) std::string versionString; iSFT.ReadString(versionString, iSFT.BytesLeft()); versionString = mpt::String::Trim(versionString); - Version version = Version::Parse(mpt::ToUnicode(mpt::CharsetISO8859_1, versionString)); - if(version && version < MAKE_VERSION_NUMERIC(1,28,00,02)) + Version version = Version::Parse(mpt::ToUnicode(mpt::Charset::ISO8859_1, versionString)); + if(version && version < MPT_V("1.28.00.02")) { - return 1252; // mpt::CharsetWindows1252; // OpenMPT up to and including 1.28.00.01 wrote metadata in windows-1252 encoding + return 1252; // mpt::Charset::Windows1252; // OpenMPT up to and including 1.28.00.01 wrote metadata in windows-1252 encoding } else { - return 28591; // mpt::CharsetISO8859_1; // as per spec + return 28591; // mpt::Charset::ISO8859_1; // as per spec } } else { - return 28591; // mpt::CharsetISO8859_1; // as per spec + return 28591; // mpt::Charset::ISO8859_1; // as per spec } } if(!csetChunk.CanRead(2)) { // chunk not parsable - return 28591; // mpt::CharsetISO8859_1; + return 28591; // mpt::Charset::ISO8859_1; } uint16 codepage = csetChunk.ReadUint16LE(); return codepage; } -void WAVReader::ApplySampleSettings(ModSample &sample, mpt::Charset sampleCharset, char (&sampleName)[MAX_SAMPLENAME]) +void WAVReader::ApplySampleSettings(ModSample &sample, mpt::Charset sampleCharset, mpt::charbuf &sampleName) { // Read sample name FileReader textChunk = infoChunk.GetChunk(RIFFChunk::idINAM); @@ -194,12 +194,12 @@ void WAVReader::ApplySampleSettings(ModSample &sample, mpt::Charset sampleCharse { std::string sampleNameEncoded; textChunk.ReadString(sampleNameEncoded, textChunk.GetLength()); - mpt::String::Copy(sampleName, mpt::ToCharset(sampleCharset, mpt::ToUnicode(codePage, mpt::CharsetWindows1252, sampleNameEncoded))); + sampleName = mpt::ToCharset(sampleCharset, mpt::ToUnicode(codePage, mpt::Charset::Windows1252, sampleNameEncoded)); } if(isDLS) { // DLS sample -> sample filename - mpt::String::Copy(sample.filename, sampleName); + sample.filename = sampleName; } // Read software name @@ -255,6 +255,7 @@ void WAVReader::ApplySampleSettings(ModSample &sample, mpt::Charset sampleCharse cueChunk.ReadStruct(cuePoint); sample.cues[i] = cuePoint.position; } + std::fill(std::begin(sample.cues) + numPoints, std::end(sample.cues), MAX_SAMPLE_LENGTH); } // Read MPT extra info @@ -264,9 +265,9 @@ void WAVReader::ApplySampleSettings(ModSample &sample, mpt::Charset sampleCharse { if(mptInfo.flags & WAVExtraChunk::setPanning) sample.uFlags.set(CHN_PANNING); - sample.nPan = std::min(mptInfo.defaultPan, 256); - sample.nVolume = std::min(mptInfo.defaultVolume, 256); - sample.nGlobalVol = std::min(mptInfo.globalVolume, 64); + sample.nPan = std::min(static_cast(mptInfo.defaultPan), uint16(256)); + sample.nVolume = std::min(static_cast(mptInfo.defaultVolume), uint16(256)); + sample.nGlobalVol = std::min(static_cast(mptInfo.globalVolume), uint16(64)); sample.nVibType = static_cast(mptInfo.vibratoType.get()); sample.nVibSweep = mptInfo.vibratoSweep; sample.nVibDepth = mptInfo.vibratoDepth; @@ -365,6 +366,7 @@ size_t WAVWriter::Finalize() FinalizeChunk(); RIFFHeader fileHeader; + Clear(fileHeader); fileHeader.magic = RIFFHeader::idRIFF; fileHeader.length = static_cast(totalSize - 8); fileHeader.type = RIFFHeader::idWAVE; @@ -433,7 +435,7 @@ void WAVWriter::Write(const void *data, size_t numBytes) { if(s != nullptr) { - s->write(static_cast(data), numBytes); + s->write(mpt::void_cast(data), numBytes); } else if(!memory.empty()) { if(position <= memory.size() && numBytes <= memory.size() - position) @@ -455,6 +457,7 @@ void WAVWriter::WriteFormat(uint32 sampleRate, uint16 bitDepth, uint16 numChanne { StartChunk(RIFFChunk::idfmt_); WAVFormatChunk wavFormat; + Clear(wavFormat); bool extensible = (numChannels > 2); @@ -470,6 +473,7 @@ void WAVWriter::WriteFormat(uint32 sampleRate, uint16 bitDepth, uint16 numChanne if(extensible) { WAVFormatChunkExtension extFormat; + Clear(extFormat); extFormat.size = sizeof(WAVFormatChunkExtension) - sizeof(uint16); extFormat.validBitsPerSample = bitDepth; switch(numChannels) @@ -525,13 +529,14 @@ void WAVWriter::WriteMetatags(const FileTags &tags) // Write a single tag into a open idLIST chunk void WAVWriter::WriteTag(RIFFChunk::ChunkIdentifiers id, const mpt::ustring &utext) { - std::string text = mpt::ToCharset(mpt::CharsetUTF8, utext); + std::string text = mpt::ToCharset(mpt::Charset::UTF8, utext); text = text.substr(0, uint32_max - 1u); if(!text.empty()) { const uint32 length = mpt::saturate_cast(text.length() + 1); RIFFChunk chunk; + Clear(chunk); chunk.id = static_cast(id); chunk.length = length; Write(chunk); @@ -567,6 +572,7 @@ void WAVWriter::WriteLoopInformation(const ModSample &sample) // Set up loops WAVSampleLoop loops[2]; + Clear(loops); if(sample.uFlags[CHN_SUSTAINLOOP]) { loops[info.numLoops++].ConvertToWAV(sample.nSustainStart, sample.nSustainEnd, sample.uFlags[CHN_PINGPONGSUSTAIN]); @@ -593,15 +599,24 @@ void WAVWriter::WriteLoopInformation(const ModSample &sample) // Write a sample's cue points to the file. void WAVWriter::WriteCueInformation(const ModSample &sample) { - StartChunk(RIFFChunk::idcue_); + uint32 numMarkers = 0; + for(const auto cue : sample.cues) { - Write(mpt::as_le(static_cast(CountOf(sample.cues)))); + if(cue < sample.nLength) + numMarkers++; } - for(uint32 i = 0; i < CountOf(sample.cues); i++) + + StartChunk(RIFFChunk::idcue_); + Write(mpt::as_le(numMarkers)); + uint32 i = 0; + for(const auto cue : sample.cues) { - WAVCuePoint cuePoint; - cuePoint.ConvertToWAV(i, sample.cues[i]); - Write(cuePoint); + if(cue < sample.nLength) + { + WAVCuePoint cuePoint; + cuePoint.ConvertToWAV(i++, cue); + Write(cuePoint); + } } } @@ -624,11 +639,11 @@ void WAVWriter::WriteExtraInformation(const ModSample &sample, MODTYPE modType, // also specify encoding. char name[MAX_SAMPLENAME]; - mpt::String::Write(name, sampleName, MAX_SAMPLENAME); + mpt::String::WriteBuf(mpt::String::nullTerminated, name) = sampleName; WriteArray(name); char filename[MAX_SAMPLEFILENAME]; - mpt::String::Write(filename, sample.filename); + mpt::String::WriteBuf(mpt::String::nullTerminated, filename) = sample.filename; WriteArray(filename); } } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.h b/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.h index 8d3e6e4e0..97d44c04f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/WAVTools.h @@ -322,7 +322,7 @@ public: SmpLength GetSampleLength() const { return mpt::saturate_cast(sampleLength); } // Apply sample settings from file (loop points, MPT extra settings, ...) to a sample. - void ApplySampleSettings(ModSample &sample, mpt::Charset charset, char (&sampleCharset)[MAX_SAMPLENAME]); + void ApplySampleSettings(ModSample &sample, mpt::Charset sampleCharset, mpt::charbuf &sampleName); }; @@ -373,12 +373,12 @@ public: template void Write(const T &data) { - MPT_STATIC_ASSERT((mpt::is_binary_safe::value)); + static_assert((mpt::is_binary_safe::value)); Write(&data, sizeof(T)); } // Write a buffer to the file. - void WriteBuffer(const char *data, size_t size) + void WriteBuffer(const std::byte *data, size_t size) { Write(data, size); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.cpp index a9dbfd965..eb799bd04 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/XMTools.cpp @@ -22,7 +22,7 @@ OPENMPT_NAMESPACE_BEGIN // Convert OpenMPT's internal envelope representation to XM envelope data. void XMInstrument::ConvertEnvelopeToXM(const InstrumentEnvelope &mptEnv, uint8le &numPoints, uint8le &flags, uint8le &sustain, uint8le &loopStart, uint8le &loopEnd, EnvType env) { - numPoints = static_cast(std::min(12u, mptEnv.size())); + numPoints = static_cast(std::min(std::size_t(12), static_cast(mptEnv.size()))); // Envelope Data for(uint8 i = 0; i < numPoints; i++) @@ -125,7 +125,7 @@ std::vector XMInstrument::GetSampleList(const ModInstrument &mptIns // Convert XM envelope data to an OpenMPT's internal envelope representation. void XMInstrument::ConvertEnvelopeToMPT(InstrumentEnvelope &mptEnv, uint8 numPoints, uint8 flags, uint8 sustain, uint8 loopStart, uint8 loopEnd, EnvType env) const { - mptEnv.resize(std::min(numPoints, 12)); + mptEnv.resize(std::min(numPoints, uint8(12))); // Envelope Data for(uint32 i = 0; i < mptEnv.size(); i++) @@ -142,16 +142,15 @@ void XMInstrument::ConvertEnvelopeToMPT(InstrumentEnvelope &mptEnv, uint8 numPoi break; } - if(i > 0 && mptEnv[i].tick < mptEnv[i - 1].tick) + if(i > 0 && mptEnv[i].tick < mptEnv[i - 1].tick && !(mptEnv[i].tick & 0xFF00)) { // libmikmod code says: "Some broken XM editing program will only save the low byte of the position // value. Try to compensate by adding the missing high byte." - // Note: It appears that MPT 1.07's XI instrument saver omitted the high byte of envelope nodes. + // Note: MPT 1.07's XI instrument saver omitted the high byte of envelope nodes. // This might be the source for some broken envelopes in IT and XM files. - - mptEnv[i].tick &= 0xFF; - mptEnv[i].tick += mptEnv[i - 1].tick & 0xFF00; - if(mptEnv[i].tick < mptEnv[i - 1].tick) mptEnv[i].tick += 0x100; + mptEnv[i].tick |= mptEnv[i - 1].tick & 0xFF00; + if(mptEnv[i].tick < mptEnv[i - 1].tick) + mptEnv[i].tick += 0x100; } } @@ -194,7 +193,7 @@ void XMInstrument::ConvertToMPT(ModInstrument &mptIns) const { mptIns.nMidiChannel = midiChannel + MidiFirstChannel; Limit(mptIns.nMidiChannel, uint8(MidiFirstChannel), uint8(MidiLastChannel)); - mptIns.nMidiProgram = static_cast(std::min(midiProgram, 127) + 1); + mptIns.nMidiProgram = static_cast(std::min(static_cast(midiProgram), uint16(127)) + 1); } mptIns.midiPWD = static_cast(pitchWheelRange); } @@ -248,7 +247,7 @@ void XMInstrumentHeader::Finalise() void XMInstrumentHeader::ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport) { numSamples = instrument.ConvertToXM(mptIns, compatibilityExport); - mpt::String::Write(name, mptIns.name); + mpt::String::WriteBuf(mpt::String::spacePadded, name) = mptIns.name; type = mptIns.nMidiProgram; // If FT2 writes crap here, we can do so, too! (we probably shouldn't, though. This is just for backwards compatibility with old MPT versions.) } @@ -271,7 +270,7 @@ void XMInstrumentHeader::ConvertToMPT(ModInstrument &mptIns) const } } - mpt::String::Read(mptIns.name, name); + mptIns.name = mpt::String::ReadBuf(mpt::String::spacePadded, name); // Old MPT backwards compatibility if(!instrument.midiEnabled) @@ -287,11 +286,11 @@ void XIInstrumentHeader::ConvertToXM(const ModInstrument &mptIns, bool compatibi numSamples = instrument.ConvertToXM(mptIns, compatibilityExport); memcpy(signature, "Extended Instrument: ", 21); - mpt::String::Write(name, mptIns.name); + mpt::String::WriteBuf(mpt::String::spacePadded, name) = mptIns.name; eof = 0x1A; - const std::string openMptTrackerName = mpt::ToCharset(mpt::CharsetCP437, Version::Current().GetOpenMPTVersionString()); - mpt::String::Write(trackerName, openMptTrackerName); + const std::string openMptTrackerName = mpt::ToCharset(mpt::Charset::CP437, Version::Current().GetOpenMPTVersionString()); + mpt::String::WriteBuf(mpt::String::spacePadded, trackerName) = openMptTrackerName; version = 0x102; } @@ -311,7 +310,7 @@ void XIInstrumentHeader::ConvertToMPT(ModInstrument &mptIns) const } } - mpt::String::Read(mptIns.name, name); + mptIns.name = mpt::String::ReadBuf(mpt::String::spacePadded, name); } @@ -332,8 +331,8 @@ void XMSample::ConvertToXM(const ModSample &mptSmp, MODTYPE fromType, bool compa } else { int f2t = ModSample::FrequencyToTranspose(mptSmp.nC5Speed); - relnote = (int8)(f2t >> 7); - finetune = (int8)(f2t & 0x7F); + relnote = static_cast(f2t / 128); + finetune = static_cast(f2t & 0x7F); } flags = 0; @@ -401,7 +400,7 @@ void XMSample::ConvertToMPT(ModSample &mptSmp) const mptSmp.nLoopEnd /= 2; } - if((flags & (XMSample::sampleLoop | XMSample::sampleBidiLoop)) && mptSmp.nLoopStart < mptSmp.nLength && mptSmp.nLoopEnd > mptSmp.nLoopStart) + if((flags & (XMSample::sampleLoop | XMSample::sampleBidiLoop)) && mptSmp.nLoopEnd > mptSmp.nLoopStart) { mptSmp.uFlags.set(CHN_LOOP); if((flags & XMSample::sampleBidiLoop)) @@ -410,7 +409,7 @@ void XMSample::ConvertToMPT(ModSample &mptSmp) const } } - strcpy(mptSmp.filename, ""); + mptSmp.filename = ""; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/load_j2b.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/load_j2b.cpp index 0e923b59b..5c90e6f09 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/load_j2b.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/load_j2b.cpp @@ -22,14 +22,16 @@ #endif -//#define J2B_LOG +#ifdef MPT_ALL_LOGGING +#define J2B_LOG +#endif OPENMPT_NAMESPACE_BEGIN // First off, a nice vibrato translation LUT. -static const VibratoType j2bAutoVibratoTrans[] = +static constexpr VibratoType j2bAutoVibratoTrans[] = { VIB_SINE, VIB_SQUARE, VIB_RAMP_UP, VIB_RAMP_DOWN, VIB_RANDOM, }; @@ -203,10 +205,10 @@ struct AMFFInstrumentHeader // Convert instrument data to OpenMPT's internal format. void ConvertToMPT(ModInstrument &mptIns, SAMPLEINDEX baseSample) { - mpt::String::Read(mptIns.name, name); + mptIns.name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, name); - STATIC_ASSERT(CountOf(sampleMap) <= CountOf(mptIns.Keyboard)); - for(size_t i = 0; i < CountOf(sampleMap); i++) + static_assert(mpt::array_size::size <= mpt::array_size::size); + for(size_t i = 0; i < std::size(sampleMap); i++) { mptIns.Keyboard[i] = sampleMap[i] + baseSample + 1; } @@ -259,7 +261,7 @@ struct AMFFSampleHeader mptSmp.nLoopEnd = loopEnd; mptSmp.nC5Speed = sampleRate; - if(instrHeader.vibratoType < mpt::size(j2bAutoVibratoTrans)) + if(instrHeader.vibratoType < std::size(j2bAutoVibratoTrans)) mptSmp.nVibType = j2bAutoVibratoTrans[instrHeader.vibratoType]; mptSmp.nVibSweep = static_cast(instrHeader.vibratoSweep); mptSmp.nVibRate = static_cast(instrHeader.vibratoRate / 16); @@ -300,16 +302,16 @@ struct AMEnvelope struct EnvPoint { uint16le tick; - uint16le value; + int16le value; }; uint16le flags; - uint8le numPoints; // actually, it's num. points - 1, and 0xFF if there is no envelope + uint8le numPoints; // actually, it's num. points - 1, and 0xFF if there is no envelope uint8le sustainPoint; uint8le loopStart; uint8le loopEnd; EnvPoint values[10]; - uint16le fadeout; // why is this here? it's only needed for the volume envelope... + uint16le fadeout; // why is this here? it's only needed for the volume envelope... // Convert envelope data to OpenMPT's internal format. void ConvertToMPT(InstrumentEnvelope &mptEnv, EnvelopeType envType) const @@ -324,6 +326,23 @@ struct AMEnvelope mptEnv.nLoopStart = loopStart; mptEnv.nLoopEnd = loopEnd; + int32 scale = 0, offset = 0; + switch(envType) + { + case ENV_VOLUME: // 0....32767 + default: + scale = 32767 / ENVELOPE_MAX; + break; + case ENV_PITCH: // -4096....4096 + scale = 8192 / ENVELOPE_MAX; + offset = 4096; + break; + case ENV_PANNING: // -32768...32767 + scale = 65536 / ENVELOPE_MAX; + offset = 32768; + break; + } + for(uint32 i = 0; i < mptEnv.size(); i++) { mptEnv[i].tick = values[i].tick >> 4; @@ -332,21 +351,9 @@ struct AMEnvelope else if(mptEnv[i].tick < mptEnv[i - 1].tick) mptEnv[i].tick = mptEnv[i - 1].tick + 1; - const uint16 val = values[i].value; - switch(envType) - { - case ENV_VOLUME: // 0....32767 - default: - mptEnv[i].value = (uint8)((val + 1) >> 9); - break; - case ENV_PITCH: // -4096....4096 - mptEnv[i].value = (uint8)((((int16)val) + 0x1001) >> 7); - break; - case ENV_PANNING: // -32768...32767 - mptEnv[i].value = (uint8)((((int16)val) + 0x8001) >> 10); - break; - } - Limit(mptEnv[i].value, uint8(ENVELOPE_MIN), uint8(ENVELOPE_MAX)); + int32 val = values[i].value + offset; + val = (val + scale / 2) / scale; + mptEnv[i].value = static_cast(std::clamp(val, int32(ENVELOPE_MIN), int32(ENVELOPE_MAX))); } mptEnv.dwFlags.set(ENV_ENABLED, (flags & AMFFEnvelope::envEnabled) != 0); @@ -380,10 +387,10 @@ struct AMInstrumentHeader // Convert instrument data to OpenMPT's internal format. void ConvertToMPT(ModInstrument &mptIns, SAMPLEINDEX baseSample) { - mpt::String::Read(mptIns.name, name); + mptIns.name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, name); - STATIC_ASSERT(CountOf(sampleMap) <= CountOf(mptIns.Keyboard)); - for(uint8 i = 0; i < CountOf(sampleMap); i++) + static_assert(mpt::array_size::size <= mpt::array_size::size); + for(uint8 i = 0; i < std::size(sampleMap); i++) { mptIns.Keyboard[i] = sampleMap[i] + baseSample + 1; } @@ -422,8 +429,8 @@ struct AMSampleHeader void ConvertToMPT(AMInstrumentHeader &instrHeader, ModSample &mptSmp) const { mptSmp.Initialize(); - mptSmp.nPan = std::min(pan, 32767) * 256 / 32767; - mptSmp.nVolume = std::min(volume, 32767) * 256 / 32767; + mptSmp.nPan = std::min(pan.get(), uint16(32767)) * 256 / 32767; + mptSmp.nVolume = std::min(volume.get(), uint16(32767)) * 256 / 32767; mptSmp.nGlobalVol = 64; mptSmp.nLength = length; mptSmp.nLoopStart = loopStart; @@ -469,7 +476,7 @@ MPT_BINARY_STRUCT(AMSampleHeader, 60) static bool ConvertAMPattern(FileReader chunk, PATTERNINDEX pat, bool isAM, CSoundFile &sndFile) { // Effect translation LUT - static const EffectCommand amEffTrans[] = + static constexpr EffectCommand amEffTrans[] = { CMD_ARPEGGIO, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO, CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_TREMOLO, @@ -516,7 +523,7 @@ static bool ConvertAMPattern(FileReader chunk, PATTERNINDEX pat, bool isAM, CSou continue; } - ModCommand &m = *sndFile.Patterns[pat].GetpModCommand(row, std::min((flags & channelMask), channels - 1)); + ModCommand &m = *sndFile.Patterns[pat].GetpModCommand(row, std::min(static_cast(flags & channelMask), static_cast(channels - 1))); if(flags & dataFlag) { @@ -532,7 +539,7 @@ static bool ConvertAMPattern(FileReader chunk, PATTERNINDEX pat, bool isAM, CSou } else { #ifdef J2B_LOG - Log(mpt::format("J2B: Unknown command: 0x%1, param 0x%2")(mpt::fmt::HEX0<2>(command), mpt::fmt::HEX0<2>(m.param))); + MPT_LOG(LogDebug, "J2B", mpt::uformat(U_("J2B: Unknown command: 0x%1, param 0x%2"))(mpt::ufmt::HEX0<2>(command), mpt::ufmt::HEX0<2>(m.param))); #endif m.command = CMD_NONE; } @@ -560,7 +567,7 @@ static bool ConvertAMPattern(FileReader chunk, PATTERNINDEX pat, bool isAM, CSou if (m.param & 0xF0) m.param &= 0xF0; break; case CMD_PANNING8: - if(m.param <= 0x80) m.param = MIN(m.param << 1, 0xFF); + if(m.param <= 0x80) m.param = mpt::saturate_cast(m.param * 2); else if(m.param == 0xA4) {m.command = CMD_S3MCMDEX; m.param = 0x91;} break; case CMD_PATTERNBREAK: @@ -589,8 +596,9 @@ static bool ConvertAMPattern(FileReader chunk, PATTERNINDEX pat, bool isAM, CSou if (flags & noteFlag) // note + ins { - m.instr = chunk.ReadUint8(); - m.note = chunk.ReadUint8(); + const auto [instr, note] = chunk.ReadArray(); + m.instr = instr; + m.note = note; if(m.note == 0x80) m.note = NOTE_KEYOFF; else if(m.note > 0x80) m.note = NOTE_FADE; // I guess the support for IT "note fade" notes was not intended in mod2j2b, but hey, it works! :-D } @@ -731,16 +739,16 @@ bool CSoundFile::ReadAM(FileReader &file, ModLoadingFlags loadFlags) m_SongFlags = SONG_ITOLDEFFECTS | SONG_ITCOMPATGXX; m_SongFlags.set(SONG_LINEARSLIDES, !(mainChunk.flags & AMFFMainChunk::amigaSlides)); - m_nChannels = MIN(mainChunk.channels, MAX_BASECHANNELS); + m_nChannels = std::min(static_cast(mainChunk.channels), static_cast(MAX_BASECHANNELS)); m_nDefaultSpeed = mainChunk.speed; m_nDefaultTempo.Set(mainChunk.tempo); m_nDefaultGlobalVolume = mainChunk.globalvolume * 2; m_modFormat.formatName = isAM ? UL_("Galaxy Sound System (new version)") : UL_("Galaxy Sound System (old version)"); m_modFormat.type = U_("j2b"); - m_modFormat.charset = mpt::CharsetCP437; + m_modFormat.charset = mpt::Charset::CP437; - mpt::String::Read(m_songName, mainChunk.songname); + m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, mainChunk.songname); // It seems like there's no way to differentiate between // Muted and Surround channels (they're all 0xA0) - might @@ -822,7 +830,7 @@ bool CSoundFile::ReadAM(FileReader &file, ModLoadingFlags loadFlags) { AMFFSampleHeader sampleHeader; - if(m_nSamples + 1 >= MAX_SAMPLES || !chunk.ReadStruct(sampleHeader)) + if(!CanAddMoreSamples() || !chunk.ReadStruct(sampleHeader)) { continue; } @@ -834,7 +842,7 @@ bool CSoundFile::ReadAM(FileReader &file, ModLoadingFlags loadFlags) continue; } - mpt::String::Read(m_szNames[smp], sampleHeader.name); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); sampleHeader.ConvertToMPT(instrHeader, Samples[smp]); if(loadFlags & loadSampleData) sampleHeader.GetSampleFormat().ReadSample(Samples[smp], chunk); @@ -884,7 +892,7 @@ bool CSoundFile::ReadAM(FileReader &file, ModLoadingFlags loadFlags) for(auto sampleChunk : sampleChunks) { - if(sampleChunk.ReadUint32LE() != AMFFRiffChunk::idAS__ || m_nSamples + 1 >= MAX_SAMPLES) + if(sampleChunk.ReadUint32LE() != AMFFRiffChunk::idAS__ || !CanAddMoreSamples()) { continue; } @@ -912,7 +920,7 @@ bool CSoundFile::ReadAM(FileReader &file, ModLoadingFlags loadFlags) break; } - mpt::String::Read(m_szNames[smp], sampleHeader.name); + m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name); sampleHeader.ConvertToMPT(instrHeader, Samples[smp]); @@ -1016,23 +1024,41 @@ bool CSoundFile::ReadJ2B(FileReader &file, ModLoadingFlags loadFlags) return true; } - FileReader::PinnedRawDataView filePackedView = file.GetPinnedRawDataView(fileHeader.packedLength); - -#ifndef MPT_BUILD_FUZZER - if(fileHeader.crc32 != crc32(0, mpt::byte_cast(filePackedView.data()), static_cast(filePackedView.size()))) - { - return false; - } -#endif - // Header is valid, now unpack the RIFF AM file using inflate - uLongf destSize = fileHeader.unpackedLength; - std::vector amFileData(destSize); - int retVal = uncompress(amFileData.data(), &destSize, mpt::byte_cast(filePackedView.data()), static_cast(filePackedView.size())); + z_stream strm{}; + if(inflateInit(&strm) != Z_OK) + return false; + + uint32 remainRead = fileHeader.packedLength, remainWrite = fileHeader.unpackedLength, totalWritten = 0; + uint32 crc = 0; + std::vector amFileData(remainWrite); + int retVal = Z_OK; + while(remainRead && remainWrite && retVal != Z_STREAM_END) + { + Bytef buffer[mpt::IO::BUFFERSIZE_TINY]; + uint32 readSize = std::min(static_cast(sizeof(buffer)), remainRead); + file.ReadRaw(buffer, readSize); + crc = crc32(crc, buffer, readSize); + + strm.avail_in = readSize; + strm.next_in = buffer; + do + { + strm.avail_out = remainWrite; + strm.next_out = amFileData.data() + totalWritten; + retVal = inflate(&strm, Z_NO_FLUSH); + uint32 written = remainWrite - strm.avail_out; + totalWritten += written; + remainWrite -= written; + } while(remainWrite && strm.avail_out == 0); + + remainRead -= readSize; + } + inflateEnd(&strm); bool result = false; #ifndef MPT_BUILD_FUZZER - if(destSize == fileHeader.unpackedLength && retVal == Z_OK) + if(fileHeader.crc32 == crc && !remainWrite && retVal == Z_STREAM_END) #endif { // Success, now load the RIFF AM(FF) module. diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.cpp index 1890a89ca..5fe972a65 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/mod_specifications.cpp @@ -24,7 +24,7 @@ namespace ModSpecs #define SongFlag(x) (FlagSet::store_type(0) | x) -MPT_CONSTEXPR11_VAR CModSpecifications mptm_ = +constexpr CModSpecifications mptm_ = { /* TODO: Proper, less arbitrarily chosen values here. @@ -41,7 +41,7 @@ MPT_CONSTEXPR11_VAR CModSpecifications mptm_ = 1, // Channel min 127, // Channel max 32, // Min tempo - 512, // Max tempo + 1000, // Max tempo 1, // Min Speed 255, // Max Speed 1, // Min pattern rows @@ -72,14 +72,14 @@ MPT_CONSTEXPR11_VAR CModSpecifications mptm_ = true, // Has artist name true, // Has default resampling true, // Fixed point tempo - " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z\\:#???????", // Supported Effects + " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z\\:#????????", // Supported Effects " vpcdabuh??gfe?o", // Supported Volume Column commands }; -MPT_CONSTEXPR11_VAR CModSpecifications mod_ = +constexpr CModSpecifications mod_ = { MOD_TYPE_MOD, // Internal MODTYPE value "mod", // File extension @@ -122,12 +122,12 @@ MPT_CONSTEXPR11_VAR CModSpecifications mod_ = false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo - " 0123456789ABCD?FF?E??????????????????????", // Supported Effects + " 0123456789ABCD?FF?E???????????????????????", // Supported Effects " ???????????????", // Supported Volume Column commands }; -MPT_CONSTEXPR11_VAR CModSpecifications xm_ = +constexpr CModSpecifications xm_ = { MOD_TYPE_XM, // Internal MODTYPE value "xm", // File extension @@ -139,7 +139,7 @@ MPT_CONSTEXPR11_VAR CModSpecifications xm_ = 1, // Channel min 32, // Channel max 32, // Min tempo - 512, // Max tempo + 1000, // Max tempo 1, // Min Speed 31, // Max Speed 1, // Min pattern rows @@ -170,12 +170,12 @@ MPT_CONSTEXPR11_VAR CModSpecifications xm_ = false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo - " 0123456789ABCDRFFTE???GHK??XPL???????????", // Supported Effects + " 0123456789ABCDRFFTE???GHK??XPL???????????W", // Supported Effects " vpcdabuhlrg????", // Supported Volume Column commands }; // XM with MPT extensions -MPT_CONSTEXPR11_VAR CModSpecifications xmEx_ = +constexpr CModSpecifications xmEx_ = { MOD_TYPE_XM, // Internal MODTYPE value "xm", // File extension @@ -187,7 +187,7 @@ MPT_CONSTEXPR11_VAR CModSpecifications xmEx_ = 1, // Channel min 127, // Channel max 32, // Min tempo - 512, // Max tempo + 1000, // Max tempo 1, // Min Speed 31, // Max Speed 1, // Min pattern rows @@ -218,11 +218,11 @@ MPT_CONSTEXPR11_VAR CModSpecifications xmEx_ = true, // Has artist name false, // Doesn't have default resampling false, // Integer tempo - " 0123456789ABCDRFFTE???GHK?YXPLZ\\?#???????", // Supported Effects + " 0123456789ABCDRFFTE???GHK?YXPLZ\\?#???????W", // Supported Effects " vpcdabuhlrg????", // Supported Volume Column commands }; -MPT_CONSTEXPR11_VAR CModSpecifications s3m_ = +constexpr CModSpecifications s3m_ = { MOD_TYPE_S3M, // Internal MODTYPE value "s3m", // File extension @@ -265,12 +265,12 @@ MPT_CONSTEXPR11_VAR CModSpecifications s3m_ = false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo - " JFEGHLKRXODB?CQATI?SMNVW?U???????????????", // Supported Effects + " JFEGHLKRXODB?CQATI?SMNVW?U??????????????? ", // Supported Effects " vp?????????????", // Supported Volume Column commands }; // S3M with MPT extensions -MPT_CONSTEXPR11_VAR CModSpecifications s3mEx_ = +constexpr CModSpecifications s3mEx_ = { MOD_TYPE_S3M, // Internal MODTYPE value "s3m", // File extension @@ -313,11 +313,11 @@ MPT_CONSTEXPR11_VAR CModSpecifications s3mEx_ = false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo - " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z??????????", // Supported Effects + " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z?????????? ", // Supported Effects " vp?????????????", // Supported Volume Column commands }; -MPT_CONSTEXPR11_VAR CModSpecifications it_ = +constexpr CModSpecifications it_ = { MOD_TYPE_IT, // Internal MODTYPE value "it", // File extension @@ -360,11 +360,11 @@ MPT_CONSTEXPR11_VAR CModSpecifications it_ = false, // Doesn't have artist name false, // Doesn't have default resampling false, // Integer tempo - " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z??????????", // Supported Effects + " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z?????????? ", // Supported Effects " vpcdab?h??gfe??", // Supported Volume Column commands }; -MPT_CONSTEXPR11_VAR CModSpecifications itEx_ = +constexpr CModSpecifications itEx_ = { MOD_TYPE_IT, // Internal MODTYPE value "it", // File extension @@ -407,7 +407,7 @@ MPT_CONSTEXPR11_VAR CModSpecifications itEx_ = true, // Has artist name false, // Doesn't have default resampling false, // Integer tempo - " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z\\?#???????", // Supported Effects + " JFEGHLKRXODB?CQATI?SMNVW?UY?P?Z\\?#??????? ", // Supported Effects " vpcdab?h??gfe??", // Supported Volume Column commands }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp index 99a4fc253..80caed2dd 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.cpp @@ -1,5 +1,5 @@ /* - * ModCommand.cpp + * modcommand.cpp * -------------- * Purpose: Various functions for writing effects to patterns, converting ModCommands, etc. * Notes : (currently none) @@ -29,10 +29,10 @@ const EffectType effectTypes[] = EFFECT_TYPE_PITCH, EFFECT_TYPE_PANNING, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_NORMAL, - EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, + EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, }; -STATIC_ASSERT(CountOf(effectTypes) == MAX_EFFECTS); +static_assert(std::size(effectTypes) == MAX_EFFECTS); const EffectType volumeEffectTypes[] = @@ -43,30 +43,24 @@ const EffectType volumeEffectTypes[] = EFFECT_TYPE_PITCH, EFFECT_TYPE_PITCH, EFFECT_TYPE_NORMAL, EFFECT_TYPE_NORMAL, }; -STATIC_ASSERT(CountOf(volumeEffectTypes) == MAX_VOLCMDS); +static_assert(std::size(volumeEffectTypes) == MAX_VOLCMDS); EffectType ModCommand::GetEffectType(COMMAND cmd) { - if(cmd < CountOf(effectTypes)) - { + if(cmd < std::size(effectTypes)) return effectTypes[cmd]; - } else - { + else return EFFECT_TYPE_NORMAL; - } } EffectType ModCommand::GetVolumeEffectType(VOLCMD volcmd) { - if(volcmd < CountOf(volumeEffectTypes)) - { + if(volcmd < std::size(volumeEffectTypes)) return volumeEffectTypes[volcmd]; - } else - { + else return EFFECT_TYPE_NORMAL; - } } @@ -206,7 +200,7 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd } } - param = (PARAM)(std::min(maxColumnValue, GetValueEffectCol()) * 0x7F / maxColumnValue); + param = static_cast(std::min(static_cast(maxColumnValue), GetValueEffectCol()) * 0x7F / maxColumnValue); command = newCmd; // might be removed later volcmd = VOLCMD_NONE; note = NOTE_NONE; @@ -303,10 +297,10 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd // swap L/R, convert to fine slide if(param & 0xF0) { - param = 0xF0 | std::min(0x0E, (param >> 4)); + param = 0xF0 | std::min(PARAM(0x0E), static_cast(param >> 4)); } else { - param = 0x0F | (std::min(0x0E, param & 0x0F) << 4); + param = 0x0F | (std::min(PARAM(0x0E), static_cast(param & 0x0F)) << 4); } break; @@ -357,7 +351,7 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd vol = 0; } - MPT_FALLTHROUGH; + [[fallthrough]]; case CMD_VOLUMESLIDE: if((param & 0xF0) && ((param & 0x0F) == 0x0F)) { @@ -481,7 +475,7 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd break; case CMD_GLOBALVOLUME: - param = (std::min(0x80, param) + 1) / 2u; + param = (std::min(PARAM(0x80), param) + 1) / 2u; break; default: @@ -500,7 +494,7 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd param = (param & 0xF0) | (((param & 0x0F) + 1) / 2u); break; case CMD_GLOBALVOLUME: - param = (std::min(0x80, param) + 1) / 2u; + param = (std::min(PARAM(0x80), param) + 1) / 2u; break; } } // End if(oldTypeIsIT_MPT && newTypeIsXM) @@ -513,10 +507,10 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd { case CMD_VIBRATO: // With linear slides, strength is roughly halved. - param = (param & 0xF0) | std::min((param & 0x0F) * 2u, 15); + param = (param & 0xF0) | std::min(static_cast((param & 0x0F) * 2u), PARAM(15)); break; case CMD_GLOBALVOLUME: - param = std::min(0x40, param) * 2u; + param = std::min(PARAM(0x40), param) * 2u; break; } } // End if(oldTypeIsIT_MPT && newTypeIsXM) @@ -528,11 +522,11 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd switch(command) { case CMD_SPEED: - param = std::min(param, 0x1F); + param = std::min(param, PARAM(0x1F)); break; break; case CMD_TEMPO: - param = std::max(param, 0x20); + param = std::max(param, PARAM(0x20)); break; } } @@ -816,7 +810,7 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd case VOLCMD_VIBRATODEPTH: // OpenMPT-specific commands case VOLCMD_OFFSET: - vol = std::min(vol, 9); + vol = std::min(vol, VOL(9)); break; } } // End if(newTypeIsIT_MPT) @@ -853,7 +847,7 @@ void ModCommand::Convert(MODTYPE fromType, MODTYPE toType, const CSoundFile &snd } -bool ModCommand::IsGlobalCommand() const +bool ModCommand::IsGlobalCommand(COMMAND command, PARAM param) { switch(command) { @@ -900,9 +894,10 @@ bool ModCommand::IsGlobalCommand() const size_t ModCommand::GetEffectWeight(COMMAND cmd) { // Effect weights, sorted from lowest to highest weight. - static const COMMAND weights[] = + static constexpr COMMAND weights[] = { CMD_NONE, + CMD_DUMMY, CMD_XPARAM, CMD_SETENVPOSITION, CMD_KEYOFF, @@ -945,9 +940,9 @@ size_t ModCommand::GetEffectWeight(COMMAND cmd) CMD_POSITIONJUMP, CMD_PATTERNBREAK, }; - STATIC_ASSERT(CountOf(weights) == MAX_EFFECTS); + static_assert(std::size(weights) == MAX_EFFECTS); - for(size_t i = 0; i < CountOf(weights); i++) + for(size_t i = 0; i < std::size(weights); i++) { if(weights[i] == cmd) { @@ -972,7 +967,7 @@ bool ModCommand::ConvertVolEffect(uint8 &effect, uint8 ¶m, bool force) return true; case CMD_VOLUME: effect = VOLCMD_VOLUME; - param = std::min(param, 64); + param = std::min(param, PARAM(64)); break; case CMD_PORTAMENTOUP: // if not force, reject when dividing causes loss of data in LSB, or if the final value is too @@ -1010,7 +1005,7 @@ bool ModCommand::ConvertVolEffect(uint8 &effect, uint8 ¶m, bool force) return false; case CMD_VIBRATO: if(force) - param = std::min(param & 0x0F, 9); + param = std::min(static_cast(param & 0x0F), PARAM(9)); else if((param & 0x0F) > 9 || (param & 0xF0) != 0) return false; param &= 0x0F; @@ -1125,13 +1120,13 @@ bool ModCommand::CombineEffects(uint8 &eff1, uint8 ¶m1, uint8 &eff2, uint8 & } -bool ModCommand::TwoRegularCommandsToMPT(uint8 &effect1, uint8 ¶m1, uint8 &effect2, uint8 ¶m2) +std::pair ModCommand::TwoRegularCommandsToMPT(uint8 &effect1, uint8 ¶m1, uint8 &effect2, uint8 ¶m2) { for(uint8 n = 0; n < 4; n++) { if(ModCommand::ConvertVolEffect(effect1, param1, (n > 1))) { - return true; + return {CMD_NONE, ModCommand::PARAM(0)}; } std::swap(effect1, effect2); std::swap(param1, param2); @@ -1143,9 +1138,10 @@ bool ModCommand::TwoRegularCommandsToMPT(uint8 &effect1, uint8 ¶m1, uint8 &e std::swap(effect1, effect2); std::swap(param1, param2); } + std::pair lostCommand = {static_cast(effect1), param1}; effect1 = VOLCMD_NONE; param1 = 0; - return false; + return lostCommand; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h index da5f14a6f..096c40977 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/modcommand.h @@ -1,5 +1,5 @@ /* - * ModCommand.h + * modcommand.h * ------------ * Purpose: ModCommand declarations and helpers. One ModCommand corresponds to one pattern cell. * Notes : (currently none) @@ -20,88 +20,92 @@ OPENMPT_NAMESPACE_BEGIN class CSoundFile; // Note definitions -#define NOTE_NONE (ModCommand::NOTE(0)) // Empty note cell -#define NOTE_MIN (ModCommand::NOTE(1)) // Minimum note value -#define NOTE_MAX (ModCommand::NOTE(120)) // Maximum note value -#define NOTE_MIDDLEC (ModCommand::NOTE(5 * 12 + NOTE_MIN)) -#define NOTE_KEYOFF (ModCommand::NOTE(0xFF)) // === (Note Off, releases envelope / fades samples, stops plugin note) -#define NOTE_NOTECUT (ModCommand::NOTE(0xFE)) // ^^^ (Cuts sample / stops all plugin notes) -#define NOTE_FADE (ModCommand::NOTE(0xFD)) // ~~~ (Fades samples, stops plugin note) -#define NOTE_PC (ModCommand::NOTE(0xFC)) // Param Control 'note'. Changes param value on first tick. -#define NOTE_PCS (ModCommand::NOTE(0xFB)) // Param Control (Smooth) 'note'. Interpolates param value during the whole row. -#define NOTE_MAX_SPECIAL NOTE_KEYOFF -#define NOTE_MIN_SPECIAL NOTE_PCS +enum : uint8 // ModCommand::NOTE +{ + NOTE_NONE = 0, // Empty note cell + NOTE_MIN = 1, // Minimum note value + NOTE_MAX = 120, // Maximum note value + NOTE_MIDDLEC = (5 * 12 + NOTE_MIN), + NOTE_KEYOFF = 0xFF, // === (Note Off, releases envelope / fades samples, stops plugin note) + NOTE_NOTECUT = 0xFE, // ^^^ (Cuts sample / stops all plugin notes) + NOTE_FADE = 0xFD, // ~~~ (Fades samples, stops plugin note) + NOTE_PC = 0xFC, // Param Control 'note'. Changes param value on first tick. + NOTE_PCS = 0xFB, // Param Control (Smooth) 'note'. Interpolates param value during the whole row. + NOTE_MIN_SPECIAL = NOTE_PCS, + NOTE_MAX_SPECIAL = NOTE_KEYOFF, +}; // Volume Column commands enum VolumeCommand : uint8 { - VOLCMD_NONE = 0, - VOLCMD_VOLUME = 1, - VOLCMD_PANNING = 2, - VOLCMD_VOLSLIDEUP = 3, - VOLCMD_VOLSLIDEDOWN = 4, - VOLCMD_FINEVOLUP = 5, - VOLCMD_FINEVOLDOWN = 6, - VOLCMD_VIBRATOSPEED = 7, - VOLCMD_VIBRATODEPTH = 8, - VOLCMD_PANSLIDELEFT = 9, - VOLCMD_PANSLIDERIGHT = 10, - VOLCMD_TONEPORTAMENTO = 11, - VOLCMD_PORTAUP = 12, - VOLCMD_PORTADOWN = 13, - VOLCMD_DELAYCUT = 14, //currently unused - VOLCMD_OFFSET = 15, - MAX_VOLCMDS = 16 + VOLCMD_NONE = 0, + VOLCMD_VOLUME = 1, + VOLCMD_PANNING = 2, + VOLCMD_VOLSLIDEUP = 3, + VOLCMD_VOLSLIDEDOWN = 4, + VOLCMD_FINEVOLUP = 5, + VOLCMD_FINEVOLDOWN = 6, + VOLCMD_VIBRATOSPEED = 7, + VOLCMD_VIBRATODEPTH = 8, + VOLCMD_PANSLIDELEFT = 9, + VOLCMD_PANSLIDERIGHT = 10, + VOLCMD_TONEPORTAMENTO = 11, + VOLCMD_PORTAUP = 12, + VOLCMD_PORTADOWN = 13, + VOLCMD_DELAYCUT = 14, //currently unused + VOLCMD_OFFSET = 15, + MAX_VOLCMDS }; // Effect column commands enum EffectCommand : uint8 { - CMD_NONE = 0, - CMD_ARPEGGIO = 1, - CMD_PORTAMENTOUP = 2, - CMD_PORTAMENTODOWN = 3, - CMD_TONEPORTAMENTO = 4, - CMD_VIBRATO = 5, - CMD_TONEPORTAVOL = 6, - CMD_VIBRATOVOL = 7, - CMD_TREMOLO = 8, - CMD_PANNING8 = 9, - CMD_OFFSET = 10, - CMD_VOLUMESLIDE = 11, - CMD_POSITIONJUMP = 12, - CMD_VOLUME = 13, - CMD_PATTERNBREAK = 14, - CMD_RETRIG = 15, - CMD_SPEED = 16, - CMD_TEMPO = 17, - CMD_TREMOR = 18, - CMD_MODCMDEX = 19, - CMD_S3MCMDEX = 20, - CMD_CHANNELVOLUME = 21, - CMD_CHANNELVOLSLIDE = 22, - CMD_GLOBALVOLUME = 23, - CMD_GLOBALVOLSLIDE = 24, - CMD_KEYOFF = 25, - CMD_FINEVIBRATO = 26, - CMD_PANBRELLO = 27, - CMD_XFINEPORTAUPDOWN = 28, - CMD_PANNINGSLIDE = 29, - CMD_SETENVPOSITION = 30, - CMD_MIDI = 31, - CMD_SMOOTHMIDI = 32, - CMD_DELAYCUT = 33, - CMD_XPARAM = 34, - CMD_NOTESLIDEUP = 35, // IMF Gxy / PTM Jxy (Slide y notes up every x ticks) - CMD_NOTESLIDEDOWN = 36, // IMF Hxy / PTM Kxy (Slide y notes down every x ticks) - CMD_NOTESLIDEUPRETRIG = 37, // PTM Lxy (Slide y notes up every x ticks + retrigger note) - CMD_NOTESLIDEDOWNRETRIG = 38, // PTM Mxy (Slide y notes down every x ticks + retrigger note) - CMD_REVERSEOFFSET = 39, // PTM Nxx Revert sample + offset - CMD_DBMECHO = 40, // DBM enable/disable echo - CMD_OFFSETPERCENTAGE = 41, // PLM Percentage Offset - MAX_EFFECTS = 42 + CMD_NONE = 0, + CMD_ARPEGGIO = 1, + CMD_PORTAMENTOUP = 2, + CMD_PORTAMENTODOWN = 3, + CMD_TONEPORTAMENTO = 4, + CMD_VIBRATO = 5, + CMD_TONEPORTAVOL = 6, + CMD_VIBRATOVOL = 7, + CMD_TREMOLO = 8, + CMD_PANNING8 = 9, + CMD_OFFSET = 10, + CMD_VOLUMESLIDE = 11, + CMD_POSITIONJUMP = 12, + CMD_VOLUME = 13, + CMD_PATTERNBREAK = 14, + CMD_RETRIG = 15, + CMD_SPEED = 16, + CMD_TEMPO = 17, + CMD_TREMOR = 18, + CMD_MODCMDEX = 19, + CMD_S3MCMDEX = 20, + CMD_CHANNELVOLUME = 21, + CMD_CHANNELVOLSLIDE = 22, + CMD_GLOBALVOLUME = 23, + CMD_GLOBALVOLSLIDE = 24, + CMD_KEYOFF = 25, + CMD_FINEVIBRATO = 26, + CMD_PANBRELLO = 27, + CMD_XFINEPORTAUPDOWN = 28, + CMD_PANNINGSLIDE = 29, + CMD_SETENVPOSITION = 30, + CMD_MIDI = 31, + CMD_SMOOTHMIDI = 32, + CMD_DELAYCUT = 33, + CMD_XPARAM = 34, + CMD_NOTESLIDEUP = 35, // IMF Gxy / PTM Jxy (Slide y notes up every x ticks) + CMD_NOTESLIDEDOWN = 36, // IMF Hxy / PTM Kxy (Slide y notes down every x ticks) + CMD_NOTESLIDEUPRETRIG = 37, // PTM Lxy (Slide y notes up every x ticks + retrigger note) + CMD_NOTESLIDEDOWNRETRIG = 38, // PTM Mxy (Slide y notes down every x ticks + retrigger note) + CMD_REVERSEOFFSET = 39, // PTM Nxx Revert sample + offset + CMD_DBMECHO = 40, // DBM enable/disable echo + CMD_OFFSETPERCENTAGE = 41, // PLM Percentage Offset + CMD_DUMMY = 42, + MAX_EFFECTS }; @@ -128,7 +132,7 @@ public: // Defines the maximum value for column data when interpreted as 2-byte value // (for example volcmd and vol). The valid value range is [0, maxColumnValue]. - enum : int { maxColumnValue = 999 }; + static constexpr int maxColumnValue = 999; // Returns empty modcommand. static ModCommand Empty() { return ModCommand(); } @@ -182,7 +186,8 @@ public: // Returns true if any of the commands in this cell trigger a tone portamento. bool IsPortamento() const { return command == CMD_TONEPORTAMENTO || command == CMD_TONEPORTAVOL || volcmd == VOLCMD_TONEPORTAMENTO; } // Returns true if the cell contains an effect command that may affect the global state of the module. - bool IsGlobalCommand() const; + bool IsGlobalCommand() const { return IsGlobalCommand(command, param); } + static bool IsGlobalCommand(COMMAND command, PARAM param); // Returns true if the note is inside the Amiga frequency range bool IsAmigaNote() const { return IsAmigaNote(note); } @@ -205,8 +210,8 @@ public: static size_t GetEffectWeight(COMMAND cmd); // Try to convert a an effect into a volume column effect. Returns true on success. static bool ConvertVolEffect(uint8 &effect, uint8 ¶m, bool force); - // Takes two "normal" effect commands and converts them to volume column + effect column commands. Returns true on success, false (if one effect was lost) otherwise. - static bool TwoRegularCommandsToMPT(uint8 &effect1, uint8 ¶m1, uint8 &effect2, uint8 ¶m2); + // Takes two "normal" effect commands and converts them to volume column + effect column commands. Returns the dropped command + param (CMD_NONE if nothing had to be dropped). + static std::pair TwoRegularCommandsToMPT(uint8 &effect1, uint8 ¶m1, uint8 &effect2, uint8 ¶m2); // Try to combine two commands into one. Returns true on success and the combined command is placed in eff1 / param1. static bool CombineEffects(uint8 &eff1, uint8 ¶m1, uint8 &eff2, uint8 ¶m2); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.cpp index 2d61e1a6d..27760f6fe 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.cpp @@ -1,8 +1,8 @@ /* * modsmp_ctrl.cpp * --------------- - * Purpose: Basic sample editing code (resizing, adding silence, normalizing, ...). - * Notes : Most of this stuff is not required in libopenmpt and should be moved to tracker-specific files. The rest could be merged into struct ModSample. + * Purpose: Basic sample editing code. + * Notes : This is a legacy namespace. Some of this stuff is not required in libopenmpt (but stuff in soundlib/ still depends on it). The rest could be merged into struct ModSample. * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ @@ -40,286 +40,6 @@ void ReplaceSample(ModSample &smp, void *pNewSample, const SmpLength newLength, } -SmpLength InsertSilence(ModSample &smp, const SmpLength silenceLength, const SmpLength startFrom, CSoundFile &sndFile) -{ - if(silenceLength == 0 || silenceLength > MAX_SAMPLE_LENGTH || smp.nLength > MAX_SAMPLE_LENGTH - silenceLength || startFrom > smp.nLength) - return smp.nLength; - - const bool wasEmpty = !smp.HasSampleData(); - const SmpLength newLength = smp.nLength + silenceLength; - - char *pNewSmp = nullptr; - - pNewSmp = static_cast(ModSample::AllocateSample(newLength, smp.GetBytesPerSample())); - if(pNewSmp == nullptr) - return smp.nLength; //Sample allocation failed. - - if(!wasEmpty) - { - // Copy over old sample - const SmpLength silenceOffset = startFrom * smp.GetBytesPerSample(); - const SmpLength silenceBytes = silenceLength * smp.GetBytesPerSample(); - if(startFrom > 0) - { - memcpy(pNewSmp, smp.samplev(), silenceOffset); - } - if(startFrom < smp.nLength) - { - memcpy(pNewSmp + silenceOffset + silenceBytes, smp.sampleb() + silenceOffset, smp.GetSampleSizeInBytes() - silenceOffset); - } - - // Update loop points if necessary. - if(smp.nLoopStart >= startFrom) smp.nLoopStart += silenceLength; - if(smp.nLoopEnd >= startFrom) smp.nLoopEnd += silenceLength; - if(smp.nSustainStart >= startFrom) smp.nSustainStart += silenceLength; - if(smp.nSustainEnd >= startFrom) smp.nSustainEnd += silenceLength; - for(auto &cue : smp.cues) - { - if(cue >= startFrom) cue += silenceLength; - } - } else - { - // Set loop points automatically - smp.nLoopStart = 0; - smp.nLoopEnd = newLength; - smp.uFlags.set(CHN_LOOP); - } - - ReplaceSample(smp, pNewSmp, newLength, sndFile); - PrecomputeLoops(smp, sndFile, true); - - return smp.nLength; -} - - -namespace -{ - // Update loop points and cues after deleting a sample selection - static void AdjustLoopPoints(SmpLength selStart, SmpLength selEnd, SmpLength &loopStart, SmpLength &loopEnd, SmpLength length) - { - Util::DeleteRange(selStart, selEnd - 1, loopStart, loopEnd); - - LimitMax(loopEnd, length); - if(loopStart + 2 >= loopEnd) - { - loopStart = loopEnd = 0; - } - } -} - -SmpLength RemoveRange(ModSample &smp, SmpLength selStart, SmpLength selEnd, CSoundFile &sndFile) -{ - LimitMax(selEnd, smp.nLength); - if(selEnd <= selStart) - { - return smp.nLength; - } - const uint8 bps = smp.GetBytesPerSample(); - memmove(smp.sampleb() + selStart * bps, smp.sampleb() + selEnd * bps, (smp.nLength - selEnd) * bps); - smp.nLength -= (selEnd - selStart); - - // Did loops or cue points cover the deleted selection? - AdjustLoopPoints(selStart, selEnd, smp.nLoopStart, smp.nLoopEnd, smp.nLength); - AdjustLoopPoints(selStart, selEnd, smp.nSustainStart, smp.nSustainEnd, smp.nLength); - - if(smp.nLoopEnd == 0) smp.uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP); - if(smp.nSustainEnd == 0) smp.uFlags.reset(CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN); - - for(auto &cue : smp.cues) - { - Util::DeleteItem(selStart, selEnd - 1, cue); - } - - smp.PrecomputeLoops(sndFile); - return smp.nLength; -} - - -SmpLength ResizeSample(ModSample &smp, const SmpLength newLength, CSoundFile &sndFile) -{ - // Invalid sample size - if(newLength > MAX_SAMPLE_LENGTH || newLength == smp.nLength) - return smp.nLength; - - // New sample will be bigger so we'll just use "InsertSilence" as it's already there. - if(newLength > smp.nLength) - return InsertSilence(smp, newLength - smp.nLength, smp.nLength, sndFile); - - // Else: Shrink sample - - const SmpLength newSmpBytes = newLength * smp.GetBytesPerSample(); - - void *pNewSmp = ModSample::AllocateSample(newLength, smp.GetBytesPerSample()); - if(pNewSmp == nullptr) - return smp.nLength; //Sample allocation failed. - - // Copy over old data and replace sample by the new one - memcpy(pNewSmp, smp.sampleb(), newSmpBytes); - ReplaceSample(smp, pNewSmp, newLength, sndFile); - - // Adjust loops - if(smp.nLoopStart > newLength) - { - smp.nLoopStart = smp.nLoopEnd = 0; - smp.uFlags.reset(CHN_LOOP); - } - if(smp.nLoopEnd > newLength) smp.nLoopEnd = newLength; - if(smp.nSustainStart > newLength) - { - smp.nSustainStart = smp.nSustainEnd = 0; - smp.uFlags.reset(CHN_SUSTAINLOOP); - } - if(smp.nSustainEnd > newLength) smp.nSustainEnd = newLength; - - PrecomputeLoops(smp, sndFile); - - return smp.nLength; -} - -namespace // Unnamed namespace for local implementation functions. -{ - - -template -class PrecomputeLoop -{ -protected: - T *target; - const T *sampleData; - SmpLength loopEnd; - int numChannels; - bool pingpong; - bool ITPingPongMode; - -public: - PrecomputeLoop(T *target, const T *sampleData, SmpLength loopEnd, int numChannels, bool pingpong, bool ITPingPongMode) - : target(target), sampleData(sampleData), loopEnd(loopEnd), numChannels(numChannels), pingpong(pingpong), ITPingPongMode(ITPingPongMode) - { - if(loopEnd > 0) - { - CopyLoop(true); - CopyLoop(false); - } - } - - void CopyLoop(bool direction) const - { - // Direction: true = start reading and writing forward, false = start reading and writing backward (write direction never changes) - const int numSamples = 2 * InterpolationMaxLookahead + (direction ? 1 : 0); // Loop point is included in forward loop expansion - T *dest = target + numChannels * (2 * InterpolationMaxLookahead - 1); // Write buffer offset - SmpLength readPosition = loopEnd - 1; - const int writeIncrement = direction ? 1 : -1; - int readIncrement = writeIncrement; - - for(int i = 0; i < numSamples; i++) - { - // Copy sample over to lookahead buffer - for(int c = 0; c < numChannels; c++) - { - dest[c] = sampleData[readPosition * numChannels + c]; - } - dest += writeIncrement * numChannels; - - if(readPosition == loopEnd - 1 && readIncrement > 0) - { - // Reached end of loop while going forward - if(pingpong) - { - readIncrement = -1; - if(ITPingPongMode && readPosition > 0) - { - readPosition--; - } - } else - { - readPosition = 0; - } - } else if(readPosition == 0 && readIncrement < 0) - { - // Reached start of loop while going backward - if(pingpong) - { - readIncrement = 1; - } else - { - readPosition = loopEnd - 1; - } - } else - { - readPosition += readIncrement; - } - } - } -}; - - -template -void PrecomputeLoopsImpl(ModSample &smp, const CSoundFile &sndFile) -{ - const int numChannels = smp.GetNumChannels(); - const int copySamples = numChannels * InterpolationMaxLookahead; - - T *sampleData = reinterpret_cast(smp.samplev()); - T *afterSampleStart = sampleData + smp.nLength * numChannels; - T *loopLookAheadStart = afterSampleStart + copySamples; - T *sustainLookAheadStart = loopLookAheadStart + 4 * copySamples; - - // Hold sample on the same level as the last sampling point at the end to prevent extra pops with interpolation. - // Do the same at the sample start, too. - for(int i = 0; i < (int)InterpolationMaxLookahead; i++) - { - for(int c = 0; c < numChannels; c++) - { - afterSampleStart[i * numChannels + c] = afterSampleStart[-numChannels + c]; - sampleData[-(i + 1) * numChannels + c] = sampleData[c]; - } - } - - if(smp.uFlags[CHN_LOOP]) - { - PrecomputeLoop(loopLookAheadStart, - sampleData + smp.nLoopStart * numChannels, - smp.nLoopEnd - smp.nLoopStart, - numChannels, - smp.uFlags[CHN_PINGPONGLOOP], - sndFile.m_playBehaviour[kITPingPongMode]); - } - if(smp.uFlags[CHN_SUSTAINLOOP]) - { - PrecomputeLoop(sustainLookAheadStart, - sampleData + smp.nSustainStart * numChannels, - smp.nSustainEnd - smp.nSustainStart, - numChannels, - smp.uFlags[CHN_PINGPONGSUSTAIN], - sndFile.m_playBehaviour[kITPingPongMode]); - } -} - -} // unnamed namespace. - - -bool PrecomputeLoops(ModSample &smp, CSoundFile &sndFile, bool updateChannels) -{ - if(!smp.HasSampleData()) - return false; - - smp.SanitizeLoops(); - - // Update channels with possibly changed loop values - if(updateChannels) - { - UpdateLoopPoints(smp, sndFile); - } - - if(smp.GetElementarySampleSize() == 2) - PrecomputeLoopsImpl(smp, sndFile); - else if(smp.GetElementarySampleSize() == 1) - PrecomputeLoopsImpl(smp, sndFile); - - return true; -} - - // Propagate loop point changes to player bool UpdateLoopPoints(const ModSample &smp, CSoundFile &sndFile) { @@ -372,247 +92,6 @@ bool UpdateLoopPoints(const ModSample &smp, CSoundFile &sndFile) } -void ResetSamples(CSoundFile &sndFile, ResetFlag resetflag, SAMPLEINDEX minSample, SAMPLEINDEX maxSample) -{ - if(minSample == SAMPLEINDEX_INVALID) - { - minSample = 1; - } - if(maxSample == SAMPLEINDEX_INVALID) - { - maxSample = sndFile.GetNumSamples(); - } - Limit(minSample, SAMPLEINDEX(1), SAMPLEINDEX(MAX_SAMPLES - 1)); - Limit(maxSample, SAMPLEINDEX(1), SAMPLEINDEX(MAX_SAMPLES - 1)); - - if(minSample > maxSample) - { - std::swap(minSample, maxSample); - } - - for(SAMPLEINDEX i = minSample; i <= maxSample; i++) - { - ModSample &sample = sndFile.GetSample(i); - switch(resetflag) - { - case SmpResetInit: - strcpy(sndFile.m_szNames[i], ""); - strcpy(sample.filename, ""); - sample.nC5Speed = 8363; - // note: break is left out intentionally. keep this order or c&p the stuff from below if you change anything! - MPT_FALLTHROUGH; - case SmpResetCompo: - sample.nPan = 128; - sample.nGlobalVol = 64; - sample.nVolume = 256; - sample.nVibDepth = 0; - sample.nVibRate = 0; - sample.nVibSweep = 0; - sample.nVibType = VIB_SINE; - sample.uFlags.reset(CHN_PANNING | SMP_NODEFAULTVOLUME); - break; - case SmpResetVibrato: - sample.nVibDepth = 0; - sample.nVibRate = 0; - sample.nVibSweep = 0; - sample.nVibType = VIB_SINE; - break; - default: - break; - } - } -} - - -namespace -{ - struct OffsetData - { - double max = 0.0, min = 0.0, offset = 0.0; - }; - - // Returns maximum sample amplitude for given sample type (int8/int16). - template - double GetMaxAmplitude() {return 1.0 + (std::numeric_limits::max)();} - - // Calculates DC offset and returns struct with DC offset, max and min values. - // DC offset value is average of [-1.0, 1.0[-normalized offset values. - template - OffsetData CalculateOffset(const T *pStart, const SmpLength length) - { - OffsetData offsetVals; - - if(length < 1) - return offsetVals; - - const double maxAmplitude = GetMaxAmplitude(); - double max = -1, min = 1, sum = 0; - - const T *p = pStart; - for(SmpLength i = 0; i < length; i++, p++) - { - const double val = double(*p) / maxAmplitude; - sum += val; - if(val > max) max = val; - if(val < min) min = val; - } - - offsetVals.max = max; - offsetVals.min = min; - offsetVals.offset = (-sum / (double)(length)); - return offsetVals; - } - - template - void RemoveOffsetAndNormalize(T *pStart, const SmpLength length, const double offset, const double amplify) - { - T *p = pStart; - for(SmpLength i = 0; i < length; i++, p++) - { - double var = (*p) * amplify + offset; - *p = mpt::saturate_round(var); - } - } -} - - -// Remove DC offset -double RemoveDCOffset(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile) -{ - if(!smp.HasSampleData()) - return 0; - - if(end > smp.nLength) end = smp.nLength; - if(start > end) start = end; - if(start == end) - { - start = 0; - end = smp.nLength; - } - - start *= smp.GetNumChannels(); - end *= smp.GetNumChannels(); - - const double maxAmplitude = (smp.GetElementarySampleSize() == 2) ? GetMaxAmplitude() : GetMaxAmplitude(); - - // step 1: Calculate offset. - OffsetData oData; - if(smp.GetElementarySampleSize() == 2) - oData = CalculateOffset(smp.sample16() + start, end - start); - else if(smp.GetElementarySampleSize() == 1) - oData = CalculateOffset(smp.sample8() + start, end - start); - else - return 0; - - double offset = oData.offset; - - if((int)(offset * maxAmplitude) == 0) - return 0; - - // those will be changed... - oData.max += offset; - oData.min += offset; - - // ... and that might cause distortion, so we will normalize this. - const double amplify = 1 / std::max(oData.max, -oData.min); - - // step 2: centralize + normalize sample - offset *= maxAmplitude * amplify; - if(smp.GetElementarySampleSize() == 2) - RemoveOffsetAndNormalize(smp.sample16() + start, end - start, offset, amplify); - else if(smp.GetElementarySampleSize() == 1) - RemoveOffsetAndNormalize(smp.sample8() + start, end - start, offset, amplify); - - // step 3: adjust global vol (if available) - if((sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && (start == 0) && (end == smp.nLength * smp.GetNumChannels())) - { - CriticalSection cs; - - smp.nGlobalVol = std::min(mpt::saturate_round(smp.nGlobalVol / amplify), uint16(64)); - for(auto &chn : sndFile.m_PlayState.Chn) - { - if(chn.pModSample == &smp) - { - chn.UpdateInstrumentVolume(&smp, chn.pModInstrument); - } - } - } - - PrecomputeLoops(smp, sndFile, false); - - return oData.offset; -} - - -template -static void ReverseSampleImpl(T *pStart, const SmpLength nLength) -{ - for(SmpLength i = 0; i < nLength / 2; i++) - { - std::swap(pStart[i], pStart[nLength - 1 - i]); - } -} - -// Reverse sample data -bool ReverseSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile) -{ - if(!smp.HasSampleData()) return false; - if(end == 0 || start > smp.nLength || end > smp.nLength) - { - start = 0; - end = smp.nLength; - } - - if(end - start < 2) return false; - - STATIC_ASSERT(MaxSamplingPointSize <= 4); - if(smp.GetBytesPerSample() == 4) // 16 bit stereo - ReverseSampleImpl(static_cast(smp.samplev()) + start, end - start); - else if(smp.GetBytesPerSample() == 2) // 16 bit mono / 8 bit stereo - ReverseSampleImpl(static_cast(smp.samplev()) + start, end - start); - else if(smp.GetBytesPerSample() == 1) // 8 bit mono - ReverseSampleImpl(static_cast(smp.samplev()) + start, end - start); - else - return false; - - PrecomputeLoops(smp, sndFile, false); - return true; -} - - -template -static void UnsignSampleImpl(T *pStart, const SmpLength length) -{ - const T offset = (std::numeric_limits::min)(); - for(SmpLength i = 0; i < length; i++) - { - pStart[i] += offset; - } -} - -// Virtually unsign sample data -bool UnsignSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile) -{ - if(!smp.HasSampleData()) return false; - if(end == 0 || start > smp.nLength || end > smp.nLength) - { - start = 0; - end = smp.nLength; - } - start *= smp.GetNumChannels(); - end *= smp.GetNumChannels(); - if(smp.GetElementarySampleSize() == 2) - UnsignSampleImpl(smp.sample16() + start, end - start); - else if(smp.GetElementarySampleSize() == 1) - UnsignSampleImpl(smp.sample8() + start, end - start); - else - return false; - - PrecomputeLoops(smp, sndFile, false); - return true; -} - - template static void InvertSampleImpl(T *pStart, const SmpLength length) { @@ -640,7 +119,7 @@ bool InvertSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sn else return false; - PrecomputeLoops(smp, sndFile, false); + smp.PrecomputeLoops(sndFile, false); return true; } @@ -691,83 +170,7 @@ bool XFadeSample(ModSample &smp, SmpLength fadeLength, int fadeLaw, bool afterlo } else return false; - PrecomputeLoops(smp, sndFile, true); - return true; -} - - -template -static void SilenceSampleImpl(T *p, SmpLength length, SmpLength inc, bool fromStart, bool toEnd) -{ - const int dest = toEnd ? 0 : p[(length - 1) * inc]; - const int base = fromStart ? 0 :p[0]; - const int delta = dest - base; - const int64 len_m1 = length - 1; - for(SmpLength i = 0; i < length; i++) - { - int n = base + static_cast((static_cast(delta) * static_cast(i)) / len_m1); - *p = static_cast(n); - p += inc; - } -} - -// X-Fade sample data to create smooth loop transitions -bool SilenceSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile) -{ - LimitMax(end, smp.nLength); - if(!smp.HasSampleData() || start >= end) return false; - - const SmpLength length = end - start; - const bool fromStart = start == 0; - const bool toEnd = end == smp.nLength; - const uint8 numChn = smp.GetNumChannels(); - - for(uint8 chn = 0; chn < numChn; chn++) - { - if(smp.GetElementarySampleSize() == 2) - SilenceSampleImpl(smp.sample16() + start * numChn + chn, length, numChn, fromStart, toEnd); - else if(smp.GetElementarySampleSize() == 1) - SilenceSampleImpl(smp.sample8() + start * numChn + chn, length, numChn, fromStart, toEnd); - else - return false; - } - - PrecomputeLoops(smp, sndFile, false); - return true; -} - - -template -static void StereoSepSampleImpl(T *p, SmpLength length, int32 separation) -{ - const int32 fac1 = static_cast(32768 + separation / 2), fac2 = static_cast(32768 - separation / 2); - while(length--) - { - const int32 l = p[0], r = p[1]; - p[0] = mpt::saturate_cast((Util::mul32to64(l, fac1) + Util::mul32to64(r, fac2)) >> 16); - p[1] = mpt::saturate_cast((Util::mul32to64(l, fac2) + Util::mul32to64(r, fac1)) >> 16); - p += 2; - } -} - -// X-Fade sample data to create smooth loop transitions -bool StereoSepSample(ModSample &smp, SmpLength start, SmpLength end, double separation, CSoundFile &sndFile) -{ - LimitMax(end, smp.nLength); - if(!smp.HasSampleData() || start >= end || smp.GetNumChannels() != 2) return false; - - const SmpLength length = end - start; - const uint8 numChn = smp.GetNumChannels(); - const int32 sep32 = mpt::saturate_round(separation * (65536.0 / 100.0)); - - if(smp.GetElementarySampleSize() == 2) - StereoSepSampleImpl(smp.sample16() + start * numChn, length, sep32); - else if(smp.GetElementarySampleSize() == 1) - StereoSepSampleImpl(smp.sample8() + start * numChn, length, sep32); - else - return false; - - PrecomputeLoops(smp, sndFile, false); + smp.PrecomputeLoops(sndFile, true); return true; } @@ -832,7 +235,7 @@ bool ConvertToMono(ModSample &smp, CSoundFile &sndFile, StereoToMonoMode convers } } - PrecomputeLoops(smp, sndFile, false); + smp.PrecomputeLoops(sndFile, false); return true; } @@ -872,43 +275,6 @@ bool ConvertToStereo(ModSample &smp, CSoundFile &sndFile) smp.uFlags.set(CHN_STEREO); ReplaceSample(smp, newSample, smp.nLength, sndFile); - PrecomputeLoops(smp, sndFile, false); - return true; -} - - -// Convert 16-bit sample to 8-bit -bool ConvertTo8Bit(ModSample &smp, CSoundFile &sndFile) -{ - if(!smp.HasSampleData() || smp.GetElementarySampleSize() != 2) - return false; - - CopySample, SC::DecodeIdentity > >(reinterpret_cast(smp.samplev()), smp.nLength * smp.GetNumChannels(), 1, smp.sample16(), smp.GetSampleSizeInBytes(), 1); - smp.uFlags.reset(CHN_16BIT); - for(auto &chn : sndFile.m_PlayState.Chn) - { - if(chn.pModSample == &smp) - chn.dwFlags.reset(CHN_16BIT); - } - - smp.PrecomputeLoops(sndFile, false); - return true; -} - - -// Convert 8-bit sample to 16-bit -bool ConvertTo16Bit(ModSample &smp, CSoundFile &sndFile) -{ - if(!smp.HasSampleData() || smp.GetElementarySampleSize() != 1) - return false; - - int16 *newSample = static_cast(ModSample::AllocateSample(smp.nLength, 2 * smp.GetNumChannels())); - if(newSample == nullptr) - return false; - - CopySample, SC::DecodeIdentity > >(newSample, smp.nLength * smp.GetNumChannels(), 1, smp.sample8(), smp.GetSampleSizeInBytes(), 1); - smp.uFlags.set(CHN_16BIT); - ctrlSmp::ReplaceSample(smp, newSample, smp.nLength, sndFile); smp.PrecomputeLoops(sndFile, false); return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.h b/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.h index 28df8030d..366aa20e3 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/modsmp_ctrl.h @@ -1,7 +1,7 @@ /* * modsmp_ctrl.h * ------------- - * Purpose: Basic sample editing code (resizing, adding silence, normalizing, ...). + * Purpose: Basic sample editing code * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. @@ -12,78 +12,29 @@ #include "BuildSettings.h" -OPENMPT_NAMESPACE_BEGIN -class CSoundFile; -struct ModSample; -struct ModChannel; -OPENMPT_NAMESPACE_END - #include "Snd_defs.h" OPENMPT_NAMESPACE_BEGIN +class CSoundFile; +struct ModSample; +struct ModChannel; + namespace ctrlSmp { -enum ResetFlag -{ - SmpResetCompo = 1, - SmpResetInit, - SmpResetVibrato, -}; - -// Insert silence to given location. -// Note: Is currently implemented only for inserting silence to the beginning and to the end of the sample. -// Return: Length of the new sample. -SmpLength InsertSilence(ModSample &smp, const SmpLength silenceLength, const SmpLength startFrom, CSoundFile &sndFile); - -// Remove part of a sample [selStart, selEnd[. -// Note: Removed memory is not freed. -// Return: Length of the new sample. -SmpLength RemoveRange(ModSample &smp, SmpLength selStart, SmpLength selEnd, CSoundFile &sndFile); - -// Change sample size. -// Note: If resized sample is bigger, silence will be added to the sample's tail. -// Return: Length of the new sample. -SmpLength ResizeSample(ModSample &smp, const SmpLength newLength, CSoundFile &sndFile); - // Replaces sample in 'smp' with given sample and frees the old sample. void ReplaceSample(ModSample &smp, void *pNewSample, const SmpLength newLength, CSoundFile &sndFile); -// Update loop wrap-around buffers -bool PrecomputeLoops(ModSample &smp, CSoundFile &sndFile, bool updateChannels = true); - // Propagate loop point changes to player bool UpdateLoopPoints(const ModSample &smp, CSoundFile &sndFile); -// Resets samples. -void ResetSamples(CSoundFile &sndFile, ResetFlag resetflag, SAMPLEINDEX minSample = SAMPLEINDEX_INVALID, SAMPLEINDEX maxSample = SAMPLEINDEX_INVALID); - -// Remove DC offset and normalize. -// Return: If DC offset was removed, returns original offset value, zero otherwise. -double RemoveDCOffset(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile); - -// Amplify / fade sample data -bool AmplifySample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile, double amplifyStart, double amplifyEnd); - -// Reverse sample data -bool ReverseSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile); - -// Virtually unsign sample data -bool UnsignSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile); - // Invert sample data (flip by 180 degrees) bool InvertSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile); // Crossfade sample data to create smooth loops bool XFadeSample(ModSample &smp, SmpLength fadeLength, int fadeLaw, bool afterloopFade, bool useSustainLoop, CSoundFile &sndFile); -// Silence parts of the sample data -bool SilenceSample(ModSample &smp, SmpLength start, SmpLength end, CSoundFile &sndFile); - -// Modify stereo separation of the sample data. separation is in range [-200, 200] -bool StereoSepSample(ModSample &smp, SmpLength start, SmpLength end, double separation, CSoundFile &sndFile); - enum StereoToMonoMode { mixChannels, @@ -98,12 +49,6 @@ bool ConvertToMono(ModSample &smp, CSoundFile &sndFile, StereoToMonoMode convers // Convert a mono sample to stereo bool ConvertToStereo(ModSample &smp, CSoundFile &sndFile); -// Convert 16-bit sample to 8-bit -bool ConvertTo8Bit(ModSample &smp, CSoundFile &sndFile); - -// Convert 8-bit sample to 16-bit -bool ConvertTo16Bit(ModSample &smp, CSoundFile &sndFile); - } // Namespace ctrlSmp namespace ctrlChn diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h b/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h index ab10f7f47..7ac894508 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/opal.h @@ -1,6 +1,7 @@ // This is the Opal OPL3 emulator from Reality Adlib Tracker v2.0a (http://www.3eality.com/productions/reality-adlib-tracker). // It was released by Shayde/Reality into the public domain. // Minor modifications to silence some warnings and fix a bug in the envelope generator have been applied. +// Additional fixes by JP Cimalando. /* @@ -144,6 +145,7 @@ class Opal { uint16_t GetOctave() const { return Octave; } uint16_t GetKeyScaleNumber() const { return KeyScaleNumber; } uint16_t GetModulationType() const { return ModulationType; } + Channel * GetChannelPair() const { return ChannelPair; } void ComputeKeyScaleNumber(); @@ -307,6 +309,7 @@ void Opal::Init(int sample_rate) { Clock = 0; TremoloClock = 0; + TremoloLevel = 0; VibratoTick = 0; VibratoClock = 0; NoteSel = false; @@ -375,7 +378,7 @@ void Opal::SetSampleRate(int sample_rate) { //================================================================================================== void Opal::Port(uint16_t reg_num, uint8_t val) { - static const int8_t op_lookup[] = { + static constexpr int8_t op_lookup[] = { // 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, // 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F @@ -450,20 +453,28 @@ void Opal::Port(uint16_t reg_num, uint8_t val) { Channel &chan = Chan[chan_num]; + // Registers Ax and Bx affect both channels + Channel *chans[2] = {&chan, chan.GetChannelPair()}; + int numchans = chans[1] ? 2 : 1; + // Do specific registers switch (reg_num & 0xF0) { // Frequency low case 0xA0: { - chan.SetFrequencyLow(val); + for (int i = 0; i < numchans; i++) { + chans[i]->SetFrequencyLow(val); + } break; } // Key-on / Octave / Frequency High case 0xB0: { - chan.SetKeyOn((val & 0x20) != 0); - chan.SetOctave(val >> 2 & 7); - chan.SetFrequencyHigh(val & 3); + for (int i = 0; i < numchans; i++) { + chans[i]->SetKeyOn((val & 0x20) != 0); + chans[i]->SetOctave(val >> 2 & 7); + chans[i]->SetFrequencyHigh(val & 3); + } break; } @@ -900,11 +911,11 @@ int16_t Opal::Operator::Output(uint16_t /*keyscalenum*/, uint32_t phase_step, in // Attack stage case EnvAtt: { - if (AttackRate == 0) - break; - if (AttackMask && (Master->Clock & AttackMask)) - break; uint16_t add = ((AttackAdd >> AttackTab[Master->Clock >> AttackShift & 7]) * ~EnvelopeLevel) >> 3; + if (AttackRate == 0) + add = 0; + if (AttackMask && (Master->Clock & AttackMask)) + add = 0; EnvelopeLevel += add; if (EnvelopeLevel <= 0) { EnvelopeLevel = 0; @@ -915,12 +926,12 @@ int16_t Opal::Operator::Output(uint16_t /*keyscalenum*/, uint32_t phase_step, in // Decay stage case EnvDec: { + uint16_t add = DecayAdd >> DecayTab[Master->Clock >> DecayShift & 7]; + if (DecayRate == 0) + add = 0; if (DecayMask && (Master->Clock & DecayMask)) - break; - if (DecayRate != 0) { - uint16_t add = DecayAdd >> DecayTab[Master->Clock >> DecayShift & 7]; - EnvelopeLevel += add; - } + add = 0; + EnvelopeLevel += add; if (EnvelopeLevel >= SustainLevel) { EnvelopeLevel = SustainLevel; EnvelopeStage = EnvSus; @@ -935,16 +946,16 @@ int16_t Opal::Operator::Output(uint16_t /*keyscalenum*/, uint32_t phase_step, in break; // Note: fall-through! - MPT_FALLTHROUGH; + [[fallthrough]]; } // Release stage case EnvRel: { - if (ReleaseRate == 0) - break; - if (ReleaseMask && (Master->Clock & ReleaseMask)) - break; uint16_t add = ReleaseAdd >> ReleaseTab[Master->Clock >> ReleaseShift & 7]; + if (ReleaseRate == 0) + add = 0; + if (ReleaseMask && (Master->Clock & ReleaseMask)) + add = 0; EnvelopeLevel += add; if (EnvelopeLevel >= 0x1FF) { EnvelopeLevel = 0x1FF; @@ -1185,7 +1196,7 @@ void Opal::Operator::SetFrequencyMultiplier(uint16_t scale) { //================================================================================================== void Opal::Operator::SetKeyScale(uint16_t scale) { - static const uint8_t kslShift[4] = { 15, 1, 2, 0 }; + static constexpr uint8_t kslShift[4] = { 8, 1, 2, 0 }; KeyScaleShift = kslShift[scale]; ComputeKeyScaleLevel(); } @@ -1314,7 +1325,7 @@ void Opal::Operator::ComputeRates() { //================================================================================================== void Opal::Operator::ComputeKeyScaleLevel() { - static const uint8_t levtab[] = { + static constexpr uint8_t levtab[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 16, 20, 24, 28, 32, 0, 0, 0, 0, 0, 12, 20, 28, 32, 40, 44, 48, 52, 56, 60, 64, diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.cpp index 4d419ec8a..5df1fcb78 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/pattern.cpp @@ -240,7 +240,7 @@ bool CPattern::Shrink() } } } - m_ModCommands.resize(m_ModCommands.size() / 2); + m_ModCommands.resize(m_Rows * nChns); return true; } @@ -262,7 +262,7 @@ bool CPattern::SetName(const char *newName, size_t maxChars) { return false; } - m_PatternName.assign(newName, mpt::strnlen(newName, maxChars)); + m_PatternName = mpt::truncate(newName, maxChars); return true; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/patternContainer.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/patternContainer.cpp index ff69ca0f4..fe1ff17ad 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/patternContainer.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/patternContainer.cpp @@ -112,15 +112,7 @@ bool CPatternContainer::IsPatternEmpty(const PATTERNINDEX nPat) const void CPatternContainer::ResizeArray(const PATTERNINDEX newSize) { - if(Size() <= newSize) - { - m_Patterns.resize(newSize, CPattern(*this)); - } else - { - for(PATTERNINDEX i = Size(); i > newSize; i--) - Remove(i - 1); - m_Patterns.resize(newSize, CPattern(*this)); - } + m_Patterns.resize(newSize, CPattern(*this)); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.cpp index 06d8d62e0..c14b6e923 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.cpp @@ -62,9 +62,9 @@ void DigiBoosterEcho::Process(float *pOutL, float *pOutR, uint32 numFrames) ar += lDelay * m_PCrossPBack; // Prevent denormals - if(mpt::abs(al) < 1e-24f) + if(std::abs(al) < 1e-24f) al = 0.0f; - if(mpt::abs(ar) < 1e-24f) + if(std::abs(ar) < 1e-24f) ar = 0.0f; m_delayLine[m_writePos * 2] = al; @@ -199,7 +199,7 @@ CString DigiBoosterEcho::GetParamDisplay(PlugParamIndex param) IMixPlugin::ChunkData DigiBoosterEcho::GetChunk(bool) { - auto data = reinterpret_cast(&m_chunk); + auto data = reinterpret_cast(&m_chunk); return ChunkData(data, sizeof(m_chunk)); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.h index 61e9e9cad..60fb1a9ab 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/DigiBoosterEcho.h @@ -14,7 +14,7 @@ OPENMPT_NAMESPACE_BEGIN -class DigiBoosterEcho : public IMixPlugin +class DigiBoosterEcho final : public IMixPlugin { public: enum Parameters @@ -34,7 +34,7 @@ public: static PluginChunk Create(uint8 delay, uint8 feedback, uint8 mix, uint8 cross) { - STATIC_ASSERT(sizeof(PluginChunk) == 8); + static_assert(sizeof(PluginChunk) == 8); PluginChunk result; memcpy(result.id, "Echo", 4); result.param[kEchoDelay] = delay; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.cpp index cb36c4fad..6e789fe4f 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.cpp @@ -87,7 +87,7 @@ void LFOPlugin::Process(float *pOutL, float *pOutR, uint32 numFrames) value = std::sin(m_phase * 2.0 * M_PI); break; case kTriangle: - value = 1.0 - 4.0 * mpt::abs(m_phase - 0.5); + value = 1.0 - 4.0 * std::abs(m_phase - 0.5); break; case kSaw: value = 2.0 * m_phase - 1.0; @@ -415,7 +415,7 @@ CString LFOPlugin::GetParamDisplay(PlugParamIndex param) return m_bypassed ? _T("Yes") : _T("No"); } else if(param == kWaveform) { - static const TCHAR *waveforms[] = { _T("Sine"), _T("Triangle"), _T("Saw"), _T("Square"), _T("Noise"), _T("Smoothed Noise") }; + static constexpr const TCHAR * const waveforms[] = { _T("Sine"), _T("Triangle"), _T("Saw"), _T("Square"), _T("Noise"), _T("Smoothed Noise") }; if(m_waveForm < MPT_ARRAY_COUNT(waveforms)) return waveforms[m_waveForm]; } else if(param == kLoopMode) diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.h index 73010709f..030753554 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/LFOPlugin.h @@ -19,7 +19,7 @@ OPENMPT_NAMESPACE_BEGIN -class LFOPlugin : public IMixPlugin +class LFOPlugin final : public IMixPlugin { friend class LFOPluginEditor; @@ -49,7 +49,7 @@ protected: kNumWaveforms }; - std::vector m_chunkData; + std::vector m_chunkData; // LFO parameters float m_amplitude, m_offset, m_frequency; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/OpCodes.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/OpCodes.h index c4db88829..401feefb5 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/OpCodes.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/OpCodes.h @@ -15,7 +15,7 @@ OPENMPT_NAMESPACE_BEGIN #ifndef NO_VST -static const char *VstOpCodes[] = +static constexpr const char *VstOpCodes[] = { "effOpen", "effClose", diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp index 8b82a0ab2..981fb213a 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.cpp @@ -102,10 +102,10 @@ CString IMixPlugin::GetFormattedParamName(PlugParamIndex param) CString name; if(paramName.IsEmpty()) { - name.Format(_T("%02u: Parameter %02u"), param, param); + name = mpt::cformat(_T("%1: Parameter %2"))(mpt::cfmt::dec0<2>(param), mpt::cfmt::dec0<2>(param)); } else { - name.Format(_T("%02u: %s"), param, paramName.GetString()); + name = mpt::cformat(_T("%1: %2"))(mpt::cfmt::dec0<2>(param), paramName); } return name; } @@ -476,7 +476,7 @@ void IMixPlugin::SaveAllParameters() m_pMixStruct->defaultProgram = -1; // Default implementation: Save all parameter values - PlugParamIndex numParams = std::min(GetNumParameters(), (std::numeric_limits::max() - sizeof(uint32)) / sizeof(IEEE754binary32LE)); + PlugParamIndex numParams = std::min(GetNumParameters(), static_cast((std::numeric_limits::max() - sizeof(uint32)) / sizeof(IEEE754binary32LE))); uint32 nLen = numParams * sizeof(IEEE754binary32LE); if (!nLen) return; nLen += sizeof(uint32); @@ -486,10 +486,12 @@ void IMixPlugin::SaveAllParameters() m_pMixStruct->pluginData.resize(nLen); auto memFile = std::make_pair(mpt::as_span(m_pMixStruct->pluginData), mpt::IO::Offset(0)); mpt::IO::WriteIntLE(memFile, 0); // Plugin data type + BeginGetProgram(); for(PlugParamIndex i = 0; i < numParams; i++) { mpt::IO::Write(memFile, IEEE754binary32LE(GetParameter(i))); } + EndGetProgram(); } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) { m_pMixStruct->pluginData.clear(); @@ -509,7 +511,7 @@ void IMixPlugin::RestoreAllParameters(int32 /*program*/) const uint32 numParams = GetNumParameters(); if((m_pMixStruct->pluginData.size() - sizeof(uint32)) >= (numParams * sizeof(IEEE754binary32LE))) { - BeginSetProgram(-1); + BeginSetProgram(); for(uint32 i = 0; i < numParams; i++) { SetParameter(i, memFile.ReadFloatLE()); @@ -588,28 +590,26 @@ void IMixPlugin::AutomateParameter(PlugParamIndex param) modDoc->RecordParamChange(GetSlot(), param); } - modDoc->PostMessageToAllViews(WM_MOD_PLUGPARAMAUTOMATE, m_nSlot, param); - // TODO: This should rather be posted to the GUI thread! - CAbstractVstEditor *pVstEditor = GetEditor(); + modDoc->SendNotifyMessageToAllViews(WM_MOD_PLUGPARAMAUTOMATE, m_nSlot, param); - if(pVstEditor && pVstEditor->m_hWnd) + if(auto *vstEditor = GetEditor(); vstEditor && vstEditor->m_hWnd) { // Mark track modified if GUI is open and format supports plugins SetModified(); - if (CMainFrame::GetInputHandler()->ShiftPressed() && TrackerSettings::Instance().midiMappingInPluginEditor) + // Do not use InputHandler in case we are coming from a bridged plugin editor + if((GetAsyncKeyState(VK_SHIFT) & 0x8000) && TrackerSettings::Instance().midiMappingInPluginEditor) { // Shift pressed -> Open MIDI mapping dialog - CMainFrame::GetInputHandler()->SetModifierMask(ModNone); // Make sure that the dialog will open only once. CMainFrame::GetMainFrame()->PostMessage(WM_MOD_MIDIMAPPING, m_nSlot, param); } // Learn macro - int macroToLearn = pVstEditor->GetLearnMacro(); + int macroToLearn = vstEditor->GetLearnMacro(); if (macroToLearn > -1) { modDoc->LearnMacro(macroToLearn, param); - pVstEditor->SetLearnMacro(-1); + vstEditor->SetLearnMacro(-1); } } } @@ -628,7 +628,7 @@ void IMixPlugin::SetModified() bool IMixPlugin::SaveProgram() { mpt::PathString defaultDir = TrackerSettings::Instance().PathPluginPresets.GetWorkingDir(); - bool useDefaultDir = !defaultDir.empty(); + const bool useDefaultDir = !defaultDir.empty(); if(!useDefaultDir && m_Factory.dllPath.IsFile()) { defaultDir = m_Factory.dllPath.GetPath(); @@ -650,19 +650,21 @@ bool IMixPlugin::SaveProgram() TrackerSettings::Instance().PathPluginPresets.SetWorkingDir(dlg.GetWorkingDirectory()); } - bool bank = (dlg.GetExtension() == P_("fxb")); + const bool isBank = (dlg.GetExtension() == P_("fxb")); - mpt::SafeOutputFile sf(dlg.GetFirstFile(), std::ios::binary, mpt::FlushModeFromBool(TrackerSettings::Instance().MiscFlushFileBuffersOnSave)); - mpt::ofstream& f = sf; - if(f.good() && VSTPresets::SaveFile(f, *this, bank)) + try { - return true; - } else + mpt::SafeOutputFile sf(dlg.GetFirstFile(), std::ios::binary, mpt::FlushModeFromBool(TrackerSettings::Instance().MiscFlushFileBuffersOnSave)); + mpt::ofstream &f = sf; + f.exceptions(f.exceptions() | std::ios::badbit | std::ios::failbit); + if(f.good() && VSTPresets::SaveFile(f, *this, isBank)) + return true; + } catch(const std::exception &) { - Reporting::Error("Error saving preset.", m_pEditor); - return false; + } - + Reporting::Error("Error saving preset.", m_pEditor); + return false; } @@ -694,7 +696,7 @@ bool IMixPlugin::LoadProgram(mpt::PathString fileName) } const char *errorStr = nullptr; - InputFile f(fileName); + InputFile f(fileName, SettingCacheCompleteFileBeforeLoading()); if(f.IsValid()) { FileReader file = GetFileReader(f); @@ -922,8 +924,17 @@ void IMidiPlugin::MidiCommand(const ModInstrument &instr, uint16 note, uint16 vo // Problem: if a note dies out naturally and we never send a note off, this counter // will block at max until note off. Is this a problem? // Safe to assume we won't need more than 16 note offs max on a given note? +#if MPT_COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable:6385) // false-positive: Reading invalid data from 'channel.noteOnMap': the readable size is '32768' bytes, but 'note' bytes may be read. +#endif // MPT_COMPILER_MSVC if(channel.noteOnMap[note][trackChannel] < uint8_max) +#if MPT_COMPILER_MSVC +#pragma warning(pop) +#endif // MPT_COMPILER_MSVC + { channel.noteOnMap[note][trackChannel]++; + } MidiSend(MIDIEvents::NoteOn(midiCh, static_cast(note), volume)); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h index bb5531264..d8e6ac188 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PlugInterface.h @@ -209,16 +209,18 @@ public: void SetModified(); #endif - virtual void BeginSetProgram(int32 /*program*/ = -1) { } - virtual void EndSetProgram() { } - virtual int GetNumInputChannels() const = 0; virtual int GetNumOutputChannels() const = 0; - typedef mpt::const_byte_span ChunkData; + using ChunkData = mpt::const_byte_span; virtual bool ProgramsAreChunks() const { return false; } virtual ChunkData GetChunk(bool /*isBank*/) { return ChunkData(); } virtual void SetChunk(const ChunkData &/*chunk*/, bool /*isBank*/) { } + + virtual void BeginSetProgram(int32 /*program*/ = -1) {} + virtual void EndSetProgram() {} + virtual void BeginGetProgram(int32 /*program*/ = -1) {} + virtual void EndGetProgram() {} }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.cpp index 786bb7590..ad515561e 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.cpp @@ -44,11 +44,11 @@ #include "../../pluginBridge/BridgeWrapper.h" #endif // NO_VST -#ifndef NO_DMO +#if defined(MPT_WITH_DMO) #include #include #include -#endif // NO_DMO +#endif // MPT_WITH_DMO #ifdef MODPLUG_TRACKER #include "../../mptrack/Mptrack.h" @@ -60,31 +60,159 @@ OPENMPT_NAMESPACE_BEGIN -//#define VST_LOG -//#define DMO_LOG +#ifdef MPT_ALL_LOGGING +#define VST_LOG +#define DMO_LOG +#endif #ifdef MODPLUG_TRACKER -static const MPT_UCHAR_TYPE *const cacheSection = UL_("PluginCache"); +static constexpr const mpt::uchar *cacheSection = UL_("PluginCache"); #endif // MODPLUG_TRACKER -uint8 VSTPluginLib::GetDllBits(bool fromCache) const +#ifndef NO_VST + + +uint8 VSTPluginLib::GetNativePluginArch() +{ + uint8 result = 0; + switch(mpt::Windows::GetProcessArchitecture()) + { + case mpt::Windows::Architecture::x86: + result = PluginArch_x86; + break; + case mpt::Windows::Architecture::amd64: + result = PluginArch_amd64; + break; + case mpt::Windows::Architecture::arm: + result = PluginArch_arm; + break; + case mpt::Windows::Architecture::arm64: + result = PluginArch_arm64; + break; + default: + result = 0; + break; + } + return result; +} + + +mpt::ustring VSTPluginLib::GetPluginArchName(uint8 arch) +{ + mpt::ustring result; + switch(arch) + { + case PluginArch_x86: + result = U_("x86"); + break; + case PluginArch_amd64: + result = U_("amd64"); + break; + case PluginArch_arm: + result = U_("arm"); + break; + case PluginArch_arm64: + result = U_("arm64"); + break; + default: + result = U_(""); + break; + } + return result; +} + + +mpt::ustring VSTPluginLib::GetPluginArchNameUser(uint8 arch) +{ + mpt::ustring result; + #if defined(MPT_WITH_WINDOWS10) + switch(arch) + { + case PluginArch_x86: + result = U_("x86 (32bit)"); + break; + case PluginArch_amd64: + result = U_("amd64 (64bit)"); + break; + case PluginArch_arm: + result = U_("arm (32bit)"); + break; + case PluginArch_arm64: + result = U_("arm64 (64bit)"); + break; + default: + result = U_(""); + break; + } + #else // !MPT_WITH_WINDOWS10 + switch(arch) + { + case PluginArch_x86: + result = U_("32-Bit"); + break; + case PluginArch_amd64: + result = U_("64-Bit"); + break; + case PluginArch_arm: + result = U_("32-Bit"); + break; + case PluginArch_arm64: + result = U_("64-Bit"); + break; + default: + result = U_(""); + break; + } + #endif // MPT_WITH_WINDOWS10 + return result; +} + + +uint8 VSTPluginLib::GetDllArch(bool fromCache) const { // Built-in plugins are always native. if(dllPath.empty()) - return mpt::arch_bits; + return GetNativePluginArch(); #ifndef NO_VST - if(!dllBits || !fromCache) + if(!dllArch || !fromCache) { - dllBits = static_cast(BridgeWrapper::GetPluginBinaryType(dllPath)); + dllArch = static_cast(BridgeWrapper::GetPluginBinaryType(dllPath)); } #else MPT_UNREFERENCED_PARAMETER(fromCache); #endif // NO_VST - return dllBits; + return dllArch; } +mpt::ustring VSTPluginLib::GetDllArchName(bool fromCache) const +{ + return GetPluginArchName(GetDllArch(fromCache)); +} + + +mpt::ustring VSTPluginLib::GetDllArchNameUser(bool fromCache) const +{ + return GetPluginArchNameUser(GetDllArch(fromCache)); +} + + +bool VSTPluginLib::IsNative(bool fromCache) const +{ + return GetDllArch(fromCache) == GetNativePluginArch(); +} + + +bool VSTPluginLib::IsNativeFromCache() const +{ + return dllArch == GetNativePluginArch() || dllArch == 0; +} + + +#endif // !NO_VST + + // PluginCache format: // FullDllPath = (hex-encoded) // .Flags = Plugin Flags (see VSTPluginLib::DecodeCacheFlags). @@ -96,13 +224,13 @@ void VSTPluginLib::WriteToCache() const SettingsContainer &cacheFile = theApp.GetPluginCache(); const std::string crcName = dllPath.ToUTF8(); - const uint32 crc = mpt::crc32(crcName); - const mpt::ustring IDs = mpt::ufmt::HEX0<8>(pluginId1) + mpt::ufmt::HEX0<8>(pluginId2) + mpt::ufmt::HEX0<8>(crc); + const mpt::crc32 crc(crcName); + const mpt::ustring IDs = mpt::ufmt::HEX0<8>(pluginId1) + mpt::ufmt::HEX0<8>(pluginId2) + mpt::ufmt::HEX0<8>(crc.result()); mpt::PathString writePath = dllPath; if(theApp.IsPortableMode()) { - writePath = theApp.AbsolutePathToRelative(writePath); + writePath = theApp.PathAbsoluteToInstallRelative(writePath); } cacheFile.Write(cacheSection, writePath.ToUnicode(), IDs); @@ -124,7 +252,7 @@ bool CreateMixPluginProc(SNDMIXPLUGIN &mixPlugin, CSoundFile &sndFile) #else if(!sndFile.m_PluginManager) { - sndFile.m_PluginManager = mpt::make_unique(); + sndFile.m_PluginManager = std::make_unique(); } return sndFile.m_PluginManager->CreateMixPlugin(mixPlugin, sndFile); #endif // MODPLUG_TRACKER @@ -133,7 +261,7 @@ bool CreateMixPluginProc(SNDMIXPLUGIN &mixPlugin, CSoundFile &sndFile) CVstPluginManager::CVstPluginManager() { -#ifndef NO_DMO +#if defined(MPT_WITH_DMO) HRESULT COMinit = CoInitializeEx(NULL, COINIT_MULTITHREADED); if(COMinit == S_OK || COMinit == S_FALSE) { @@ -170,7 +298,7 @@ CVstPluginManager::CVstPluginManager() #endif // MODPLUG_TRACKER }; - pluginList.reserve(mpt::size(BuiltInPlugins)); + pluginList.reserve(std::size(BuiltInPlugins)); for(const auto &plugin : BuiltInPlugins) { VSTPluginLib *plug = new (std::nothrow) VSTPluginLib(plugin.createProc, true, mpt::PathString::FromUTF8(plugin.filename), mpt::PathString::FromUTF8(plugin.name)); @@ -205,7 +333,7 @@ CVstPluginManager::~CVstPluginManager() } delete plug; } -#ifndef NO_DMO +#if defined(MPT_WITH_DMO) if(MustUnInitilizeCOM) { CoUninitialize(); @@ -223,14 +351,8 @@ bool CVstPluginManager::IsValidPlugin(const VSTPluginLib *pLib) const void CVstPluginManager::EnumerateDirectXDMOs() { -#ifndef NO_DMO -#if MPT_MSVC_BEFORE(2017,0) - // VS2015 crashes if knownDMOs is constexpr. - const -#else - constexpr -#endif - mpt::UUID knownDMOs[] = +#if defined(MPT_WITH_DMO) + constexpr mpt::UUID knownDMOs[] = { "745057C7-F353-4F2D-A7EE-58434477730E"_uuid, // AEC (Acoustic echo cancellation, not usable) "EFE6629C-81F7-4281-BD91-C9D604A95AF6"_uuid, // Chorus @@ -287,7 +409,7 @@ void CVstPluginManager::EnumerateDirectXDMOs() delete plug; } #ifdef DMO_LOG - Log(mpt::format(U_("Found \"%1\" clsid=%2\n"))(plug->libraryName, plug->dllPath)); + MPT_LOG(LogDebug, "DMO", mpt::format(U_("Found \"%1\" clsid=%2\n"))(plug->libraryName, plug->dllPath)); #endif } } @@ -299,7 +421,7 @@ void CVstPluginManager::EnumerateDirectXDMOs() index++; } if (hkEnum) RegCloseKey(hkEnum); -#endif // NO_DMO +#endif // MPT_WITH_DMO } @@ -322,7 +444,7 @@ static void GetPluginInformation(Vst::AEffect *effect, VSTPluginLib &library) #ifdef MODPLUG_TRACKER std::vector s(256, 0); CVstPlugin::DispatchSEH(effect, Vst::effGetVendorString, 0, 0, s.data(), 0, exception); - library.vendor = mpt::ToCString(mpt::CharsetLocale, s.data()); + library.vendor = mpt::ToCString(mpt::Charset::Locale, s.data()); #endif // MODPLUG_TRACKER } #endif // NO_VST @@ -354,7 +476,7 @@ VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, const if(IDs.length() < 16) { // If that didn't work out, find relative path - mpt::PathString relPath = theApp.AbsolutePathToRelative(dllPath); + mpt::PathString relPath = theApp.PathAbsoluteToInstallRelative(dllPath); IDs = cacheFile.Read(cacheSection, relPath.ToUnicode(), U_("")); } @@ -387,13 +509,13 @@ VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, const plug->vendor = cacheFile.Read(cacheSection, IDs + U_(".Vendor"), CString()); #ifdef VST_LOG - Log("Plugin \"%s\" found in PluginCache\n", plug->libraryName.ToLocale().c_str()); + MPT_LOG(LogDebug, "VST", mpt::format(U_("Plugin \"%1\" found in PluginCache"))(plug->libraryName)); #endif // VST_LOG return plug; } else { #ifdef VST_LOG - Log("Plugin \"%s\" mismatch in PluginCache: \"%s\" [%s]=\"%s\"\n", s, dllPath, (LPCTSTR)IDs, (LPCTSTR)strFullPath); + MPT_LOG(LogDebug, "VST", mpt::format(U_("Plugin mismatch in PluginCache: \"%1\" [%2]"))(dllPath, IDs)); #endif // VST_LOG } } @@ -425,13 +547,13 @@ VSTPluginLib *CVstPluginManager::AddPlugin(const mpt::PathString &dllPath, const GetPluginInformation(pEffect, *plug); #ifdef VST_LOG - int nver = CVstPlugin::DispatchSEH(pEffect, effGetVstVersion, 0,0, nullptr, 0, exception); + intptr_t nver = CVstPlugin::DispatchSEH(pEffect, Vst::effGetVstVersion, 0,0, nullptr, 0, exception); if (!nver) nver = pEffect->version; - Log("%-20s: v%d.0, %d in, %d out, %2d programs, %2d params, flags=0x%04X realQ=%d offQ=%d\n", - plug->libraryName.ToLocale().c_str(), nver, + MPT_LOG(LogDebug, "VST", mpt::format(U_("%1: v%2.0, %3 in, %4 out, %5 programs, %6 params, flags=0x%7 realQ=%8 offQ=%9"))( + plug->libraryName, nver, pEffect->numInputs, pEffect->numOutputs, - pEffect->numPrograms, pEffect->numParams, - pEffect->flags, pEffect->realQualities, pEffect->offQualities); + mpt::ufmt::dec0<2>(pEffect->numPrograms), mpt::ufmt::dec0<2>(pEffect->numParams), + mpt::ufmt::HEX0<4>(static_cast(pEffect->flags)), pEffect->realQualities, pEffect->offQualities)); #endif // VST_LOG CVstPlugin::DispatchSEH(pEffect, Vst::effClose, 0, 0, 0, 0, exception); @@ -497,7 +619,15 @@ bool CVstPluginManager::CreateMixPlugin(SNDMIXPLUGIN &mixPlugin, CSoundFile &snd #endif // MODPLUG_TRACKER // Find plugin in library - int8 match = 0; // "Match quality" of found plugin. Higher value = better match. + enum PlugMatchQuality + { + kNoMatch, + kMatchName, + kMatchId, + kMatchNameAndId, + }; + + PlugMatchQuality match = kNoMatch; // "Match quality" of found plugin. Higher value = better match. #if MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT const mpt::PathString libraryName = mpt::PathString::FromUTF8(mixPlugin.GetLibraryName()); #else @@ -516,20 +646,22 @@ bool CVstPluginManager::CreateMixPlugin(SNDMIXPLUGIN &mixPlugin, CSoundFile &snd if(matchID && matchName) { pFound = plug; +#ifndef NO_VST if(plug->IsNative(false)) { break; } +#endif //!NO_VST // If the plugin isn't native, first check if a native version can be found. - match = 3; - } else if(matchID && match < 2) + match = kMatchNameAndId; + } else if(matchID && match < kMatchId) { pFound = plug; - match = 2; - } else if(matchName && match < 1) + match = kMatchId; + } else if(matchName && match < kMatchName) { pFound = plug; - match = 1; + match = kMatchName; } } @@ -546,7 +678,7 @@ bool CVstPluginManager::CreateMixPlugin(SNDMIXPLUGIN &mixPlugin, CSoundFile &snd mpt::PathString fullPath = TrackerSettings::Instance().PathPlugins.GetDefaultDir(); if(fullPath.empty()) { - fullPath = theApp.GetAppDirPath() + P_("Plugins\\"); + fullPath = theApp.GetInstallPath() + P_("Plugins\\"); } fullPath += mpt::PathString::FromUTF8(mixPlugin.GetLibraryName()) + P_(".dll"); @@ -555,13 +687,13 @@ bool CVstPluginManager::CreateMixPlugin(SNDMIXPLUGIN &mixPlugin, CSoundFile &snd { // Try plugin cache (search for library name) SettingsContainer &cacheFile = theApp.GetPluginCache(); - mpt::ustring IDs = cacheFile.Read(cacheSection, mpt::ToUnicode(mpt::CharsetUTF8, mixPlugin.GetLibraryName()), U_("")); + mpt::ustring IDs = cacheFile.Read(cacheSection, mpt::ToUnicode(mpt::Charset::UTF8, mixPlugin.GetLibraryName()), U_("")); if(IDs.length() >= 16) { fullPath = cacheFile.Read(cacheSection, IDs, P_("")); if(!fullPath.empty()) { - fullPath = theApp.RelativePathToAbsolute(fullPath); + fullPath = theApp.PathInstallRelativeToAbsolute(fullPath); if(fullPath.IsFile()) { pFound = AddPlugin(fullPath); @@ -606,7 +738,7 @@ bool CVstPluginManager::CreateMixPlugin(SNDMIXPLUGIN &mixPlugin, CSoundFile &snd { // "plug not found" notification code MOVED to CSoundFile::Create #ifdef VST_LOG - Log("Unknown plugin\n"); + MPT_LOG(LogDebug, "VST", U_("Unknown plugin")); #endif } #endif // NO_VST @@ -645,7 +777,7 @@ void CVstPluginManager::ReportPlugException(const mpt::ustring &msg) { Reporting::Notification(msg); #ifdef VST_LOG - Log(mpt::ToUnicode(msg)); + MPT_LOG(LogDebug, "VST", mpt::ToUnicode(msg)); #endif } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.h index 45c3e5c71..df2bcd8b3 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginManager.h @@ -23,6 +23,7 @@ OPENMPT_NAMESPACE_BEGIN class CSoundFile; class IMixPlugin; struct SNDMIXPLUGIN; +enum PluginArch : int; struct VSTPluginLib { @@ -66,7 +67,7 @@ public: bool isInstrument : 1; bool useBridge : 1, shareBridgeInstance : 1; protected: - mutable uint8 dllBits = 0; + mutable uint8 dllArch = 0; public: VSTPluginLib(CreateProc factoryProc, bool isBuiltIn, const mpt::PathString &dllPath, const mpt::PathString &libraryName @@ -86,23 +87,34 @@ public: { } +#ifndef NO_VST + + // Get native phost process arch encoded as plugin arch + static uint8 GetNativePluginArch(); + static mpt::ustring GetPluginArchName(uint8 arch); + static mpt::ustring GetPluginArchNameUser(uint8 arch); + // Check whether a plugin can be hosted inside OpenMPT or requires bridging - uint8 GetDllBits(bool fromCache = true) const; - bool IsNative(bool fromCache = true) const { return GetDllBits(fromCache) == mpt::arch_bits; } + uint8 GetDllArch(bool fromCache = true) const; + mpt::ustring GetDllArchName(bool fromCache = true) const; + mpt::ustring GetDllArchNameUser(bool fromCache = true) const; + bool IsNative(bool fromCache = true) const; // Check if a plugin is native, and if it is currently unknown, assume that it is native. Use this function only for performance reasons // (e.g. if tons of unscanned plugins would slow down generation of the plugin selection dialog) - bool IsNativeFromCache() const { return dllBits == mpt::arch_bits || dllBits == 0; } + bool IsNativeFromCache() const; + +#endif // !NO_VST void WriteToCache() const; uint32 EncodeCacheFlags() const { - // Format: 00000000.00000000.DDDDDDSB.CCCCCCCI + // Format: 00000000.00000000.AAAAAASB.CCCCCCCI return (isInstrument ? 1 : 0) | (category << 1) | (useBridge ? 0x100 : 0) | (shareBridgeInstance ? 0x200 : 0) - | ((dllBits / 8) << 10); + | ((dllArch / 8) << 10); } void DecodeCacheFlags(uint32 flags) @@ -119,7 +131,7 @@ public: } useBridge = (flags & 0x100) != 0; shareBridgeInstance = (flags & 0x200) != 0; - dllBits = ((flags >> 10) & 0x3F) * 8; + dllArch = ((flags >> 10) & 0x3F) * 8; } }; @@ -128,7 +140,7 @@ class CVstPluginManager { #ifndef NO_PLUGINS protected: -#ifndef NO_DMO +#if defined(MPT_WITH_DMO) bool MustUnInitilizeCOM = false; #endif std::vector pluginList; @@ -137,14 +149,15 @@ public: CVstPluginManager(); ~CVstPluginManager(); - typedef std::vector::iterator iterator; - typedef std::vector::const_iterator const_iterator; + using iterator = std::vector::iterator; + using const_iterator = std::vector::const_iterator; iterator begin() { return pluginList.begin(); } const_iterator begin() const { return pluginList.begin(); } iterator end() { return pluginList.end(); } const_iterator end() const { return pluginList.end(); } void reserve(size_t num) { pluginList.reserve(num); } + size_t size() const { return pluginList.size(); } bool IsValidPlugin(const VSTPluginLib *pLib) const; VSTPluginLib *AddPlugin(const mpt::PathString &dllPath, const mpt::ustring &tags = mpt::ustring(), bool fromCache = true, bool *fileFound = nullptr); @@ -161,6 +174,7 @@ public: const VSTPluginLib **begin() const { return nullptr; } const VSTPluginLib **end() const { return nullptr; } void reserve(size_t) { } + size_t size() const { return 0; } void OnIdle() {} #endif // NO_PLUGINS diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginMixBuffer.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginMixBuffer.h index 4e18524c9..da5989984 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginMixBuffer.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginMixBuffer.h @@ -12,6 +12,9 @@ #include "BuildSettings.h" +#include +#include + #include "../../common/mptAlloc.h" @@ -24,27 +27,26 @@ OPENMPT_NAMESPACE_BEGIN template class PluginMixBuffer { -protected: - std::vector inputs; // Pointers to input buffers - std::vector outputs; // Pointers to output buffers +private: + #if defined(MPT_ENABLE_ALIGNED_ALLOC) - mpt::aligned_buffer alignedBuffer; // Aligned buffer pointed into -#else - std::vector alignedBuffer; + static constexpr std::align_val_t alignment = std::align_val_t{16}; + static_assert(sizeof(mpt::aligned_array) == sizeof(std::array)); + static_assert(alignof(mpt::aligned_array) == static_cast(alignment)); #endif - // Return pointer to an aligned buffer - const buffer_t *GetBuffer(size_t index) const - { - MPT_ASSERT(index < inputs.size() + outputs.size()); - return &alignedBuffer[bufferSize * index]; - } - buffer_t *GetBuffer(size_t index) - { - MPT_ASSERT(index < inputs.size() + outputs.size()); - return &alignedBuffer[bufferSize * index]; - } +protected: + +#if defined(MPT_ENABLE_ALIGNED_ALLOC) + std::vector> inputs; + std::vector> outputs; +#else + std::vector> inputs; + std::vector> outputs; +#endif + std::vector inputsarray; + std::vector outputsarray; public: @@ -61,14 +63,8 @@ public: { inputs.resize(numInputs); outputs.resize(numOutputs); - - // Create inputs + outputs buffers - #if defined(MPT_ENABLE_ALIGNED_ALLOC) - alignedBuffer.destructive_resize(bufferSize * (numInputs + numOutputs)); - #else - alignedBuffer.resize(bufferSize * (numInputs + numOutputs)); - #endif - + inputsarray.resize(numInputs); + outputsarray.resize(numOutputs); } MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) { MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e); @@ -76,22 +72,21 @@ public: inputs.shrink_to_fit(); outputs.clear(); outputs.shrink_to_fit(); - #if defined(MPT_ENABLE_ALIGNED_ALLOC) - alignedBuffer.destructive_resize(0); - #else - alignedBuffer.resize(0); - #endif + inputsarray.clear(); + inputsarray.shrink_to_fit(); + outputsarray.clear(); + outputsarray.shrink_to_fit(); return false; } for(uint32 i = 0; i < numInputs; i++) { - inputs[i] = GetInputBuffer(i); + inputsarray[i] = inputs[i].data(); } for(uint32 i = 0; i < numOutputs; i++) { - outputs[i] = GetOutputBuffer(i); + outputsarray[i] = outputs[i].data(); } return true; @@ -103,7 +98,7 @@ public: MPT_ASSERT(numSamples <= bufferSize); for(size_t i = 0; i < inputs.size(); i++) { - std::memset(inputs[i], 0, numSamples * sizeof(buffer_t)); + std::fill(inputs[i].data(), inputs[i].data() + numSamples, buffer_t{0}); } } @@ -113,7 +108,7 @@ public: MPT_ASSERT(numSamples <= bufferSize); for(size_t i = 0; i < outputs.size(); i++) { - std::memset(outputs[i], 0, numSamples * sizeof(buffer_t)); + std::fill(outputs[i].data(), outputs[i].data() + numSamples, buffer_t{0}); } } @@ -123,16 +118,17 @@ public: } // Return pointer to a given input or output buffer - const buffer_t *GetInputBuffer(uint32 index) const { return GetBuffer(index); } - const buffer_t *GetOutputBuffer(uint32 index) const { return GetBuffer(inputs.size() + index); } - buffer_t *GetInputBuffer(uint32 index) { return GetBuffer(index); } - buffer_t *GetOutputBuffer(uint32 index) { return GetBuffer(inputs.size() + index); } + const buffer_t *GetInputBuffer(uint32 index) const { return inputs[index].data(); } + const buffer_t *GetOutputBuffer(uint32 index) const { return outputs[index].data(); } + buffer_t *GetInputBuffer(uint32 index) { return inputs[index].data(); } + buffer_t *GetOutputBuffer(uint32 index) { return outputs[index].data(); } // Return pointer array to all input or output buffers - buffer_t **GetInputBufferArray() { return inputs.empty() ? nullptr : inputs.data(); } - buffer_t **GetOutputBufferArray() { return outputs.empty() ? nullptr : outputs.data(); } + buffer_t **GetInputBufferArray() { return inputs.empty() ? nullptr : inputsarray.data(); } + buffer_t **GetOutputBufferArray() { return outputs.empty() ? nullptr : outputsarray.data(); } + + bool Ok() const { return (inputs.size() + outputs.size()) > 0; } - bool Ok() const { return alignedBuffer.size() > 0; } }; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginStructs.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginStructs.h index 03223fe6f..947d761eb 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginStructs.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/PluginStructs.h @@ -52,8 +52,8 @@ struct SNDMIXPLUGININFO uint8le reserved; uint32le dwOutputRouting; // 0 = send to master 0x80 + x = send to plugin x uint32le dwReserved[4]; // Reserved for routing info - char szName[32]; // User-chosen plugin display name - this is locale ANSI! - char szLibraryName[64]; // original DLL name - this is UTF-8! + mpt::charbuf<32, mpt::String::nullTerminated> szName; // User-chosen plugin display name - this is locale ANSI! + mpt::charbuf<64, mpt::String::nullTerminated> szLibraryName; // original DLL name - this is UTF-8! // Should only be called from SNDMIXPLUGIN::SetBypass() and IMixPlugin::Bypass() void SetBypass(bool bypass = true) { if(bypass) routingFlags |= irBypass; else routingFlags &= uint8(~irBypass); } @@ -65,7 +65,7 @@ MPT_BINARY_STRUCT(SNDMIXPLUGININFO, 128) // this is directly written to files, s struct SNDMIXPLUGIN { IMixPlugin *pMixPlugin; - std::vector pluginData; + std::vector pluginData; SNDMIXPLUGININFO Info; float fDryRatio; int32 defaultProgram; @@ -81,9 +81,9 @@ struct SNDMIXPLUGIN } const char *GetName() const - { return Info.szName; } + { return Info.szName.buf; } const char *GetLibraryName() const - { return Info.szLibraryName; } + { return Info.szLibraryName.buf; } // Check if a plugin is loaded into this slot (also returns true if the plugin in this slot has not been found) bool IsValidPlugin() const { return (Info.dwPluginId1 | Info.dwPluginId2) != 0; } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.cpp index f128551a7..a254a6660 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Chorus.cpp @@ -86,8 +86,8 @@ void Chorus::Process(float *pOutL, float *pOutR, uint32 numFrames) m_waveShapeMin -= 2; if(m_waveShapeMax > 1) m_waveShapeMax -= 2; - waveMin = mpt::abs(m_waveShapeMin) * 2 - 1; - waveMax = mpt::abs(m_waveShapeMax) * 2 - 1; + waveMin = std::abs(m_waveShapeMin) * 2 - 1; + waveMax = std::abs(m_waveShapeMax) * 2 - 1; } else { m_waveShapeMin = m_waveShapeMax * m_waveShapeVal + m_waveShapeMin; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.cpp index 757e74f69..9549b2929 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.cpp @@ -64,11 +64,11 @@ void Compressor::Process(float *pOutL, float *pOutR, uint32 numFrames) m_buffer[m_bufPos * 2] = leftIn; m_buffer[m_bufPos * 2 + 1] = rightIn; - leftIn = mpt::abs(leftIn); - rightIn = mpt::abs(rightIn); + leftIn = std::abs(leftIn); + rightIn = std::abs(rightIn); float mono = (leftIn + rightIn) * (0.5f * 32768.0f * 32768.0f); - float monoLog = mpt::abs(logGain(mono, 31, 5)) * (1.0f / float(1u << 31)); + float monoLog = std::abs(logGain(mono, 31, 5)) * (1.0f / float(1u << 31)); float newPeak = monoLog + (m_peak - monoLog) * ((m_peak <= monoLog) ? m_attack : m_release); m_peak = newPeak; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.h index 56fb3cead..2fae55a20 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Compressor.h @@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN namespace DMO { -class Compressor : public IMixPlugin +class Compressor final : public IMixPlugin { protected: enum Parameters @@ -106,4 +106,4 @@ protected: OPENMPT_NAMESPACE_END -#endif // !NO_PLUGINS && NO_DMO +#endif // !NO_PLUGINS diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.cpp index 83e50641e..8b4806242 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.cpp @@ -12,7 +12,7 @@ #include "stdafx.h" -#ifndef NO_DMO +#if defined(MPT_WITH_DMO) #include "../../Sndfile.h" #include "../../../common/mptUUID.h" #include "DMOPlugin.h" @@ -20,15 +20,20 @@ #include #include #include -#endif // !NO_DMO +#endif // MPT_WITH_DMO OPENMPT_NAMESPACE_BEGIN -#ifndef NO_DMO +#if defined(MPT_WITH_DMO) +#ifdef MPT_ALL_LOGGING #define DMO_LOG +#else +#define DMO_LOG +#endif + IMixPlugin* DMOPlugin::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) { @@ -51,11 +56,11 @@ IMixPlugin* DMOPlugin::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIX return p; } #ifdef DMO_LOG - Log(factory.libraryName.ToUnicode() + U_(": Unable to use this DMO")); + MPT_LOG(LogDebug, "DMO", factory.libraryName.ToUnicode() + U_(": Unable to use this DMO")); #endif } #ifdef DMO_LOG - else Log(factory.libraryName.ToUnicode() + U_(": Failed to get IMediaObject & IMediaObjectInPlace interfaces")); + else MPT_LOG(LogDebug, "DMO", factory.libraryName.ToUnicode() + U_(": Failed to get IMediaObject & IMediaObjectInPlace interfaces")); #endif if (pMO) pMO->Release(); if (pMOIP) pMOIP->Release(); @@ -121,8 +126,8 @@ uint32 DMOPlugin::GetLatency() const } -static const float _f2si = 32768.0f; -static const float _si2f = 1.0f / 32768.0f; +static constexpr float _f2si = 32768.0f; +static constexpr float _si2f = 1.0f / 32768.0f; static void InterleaveStereo(const float * MPT_RESTRICT inputL, const float * MPT_RESTRICT inputR, float * MPT_RESTRICT output, uint32 numFrames) @@ -131,7 +136,7 @@ static void InterleaveStereo(const float * MPT_RESTRICT inputL, const float * MP if(GetProcSupport() & PROCSUPPORT_SSE) { // We may read beyond the wanted length... this works because we know that we will always work on our buffers of size MIXBUFFERSIZE - STATIC_ASSERT((MIXBUFFERSIZE & 7) == 0); + static_assert((MIXBUFFERSIZE & 7) == 0); __m128 factor = _mm_set_ps1(_f2si); numFrames = (numFrames + 3) / 4; do @@ -165,7 +170,7 @@ static void DeinterleaveStereo(const float * MPT_RESTRICT input, float * MPT_RES if(GetProcSupport() & PROCSUPPORT_SSE) { // We may read beyond the wanted length... this works because we know that we will always work on our buffers of size MIXBUFFERSIZE - STATIC_ASSERT((MIXBUFFERSIZE & 7) == 0); + static_assert((MIXBUFFERSIZE & 7) == 0); __m128 factor = _mm_set_ps1(_si2f); numFrames = (numFrames + 3) / 4; do @@ -202,7 +207,7 @@ static void InterleaveFloatToInt16(const float * MPT_RESTRICT inputL, const floa if((GetProcSupport() & (PROCSUPPORT_MMX | PROCSUPPORT_SSE)) == (PROCSUPPORT_MMX | PROCSUPPORT_SSE)) { // We may read beyond the wanted length... this works because we know that we will always work on our buffers of size MIXBUFFERSIZE - STATIC_ASSERT((MIXBUFFERSIZE & 7) == 0); + static_assert((MIXBUFFERSIZE & 7) == 0); __m64 *out = reinterpret_cast<__m64 *>(output); __m128 factor = _mm_set_ps1(_f2si); numFrames = (numFrames + 3) / 4; @@ -254,7 +259,7 @@ static void DeinterleaveInt16ToFloat(const int16 * MPT_RESTRICT input, float * M if((GetProcSupport() & (PROCSUPPORT_MMX | PROCSUPPORT_SSE)) == (PROCSUPPORT_MMX | PROCSUPPORT_SSE)) { // We may read beyond the wanted length... this works because we know that we will always work on our buffers of size MIXBUFFERSIZE - STATIC_ASSERT((MIXBUFFERSIZE & 7) == 0); + static_assert((MIXBUFFERSIZE & 7) == 0); const __m128i *in = reinterpret_cast(input); __m128 factor = _mm_set_ps1(_si2f); numFrames = (numFrames + 3) / 4; @@ -433,7 +438,7 @@ void DMOPlugin::Resume() || FAILED(m_pMediaObject->SetOutputType(0, &mt, 0))) { #ifdef DMO_LOG - Log(U_("DMO: Failed to set I/O media type")); + MPT_LOG(LogDebug, "DMO", U_("DMO: Failed to set I/O media type")); #endif } } @@ -552,11 +557,11 @@ CString DMOPlugin::GetParamDisplay(PlugParamIndex param) #endif // MODPLUG_TRACKER -#else // NO_DMO +#else // !MPT_WITH_DMO MPT_MSVC_WORKAROUND_LNK4221(DMOPlugin) -#endif // !NO_DMO +#endif // MPT_WITH_DMO OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.h index 0fbb86341..9d0c737f8 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/DMOPlugin.h @@ -8,7 +8,7 @@ */ -#ifndef NO_DMO +#if defined(MPT_WITH_DMO) #include "../PlugInterface.h" #include @@ -21,7 +21,7 @@ typedef interface IMediaParams IMediaParams; OPENMPT_NAMESPACE_BEGIN -class DMOPlugin : public IMixPlugin +class DMOPlugin final : public IMixPlugin { protected: IMediaObject *m_pMediaObject; @@ -96,5 +96,5 @@ public: OPENMPT_NAMESPACE_END -#endif // NO_DMO +#endif // MPT_WITH_DMO diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.cpp index eb15b0f03..fb9da9b89 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.cpp @@ -222,7 +222,7 @@ void Distortion::RecalculateDistortionParams() shift = 5; m_shift = shift; - static const float LogNorm[32] = + static constexpr float LogNorm[32] = { 1.00f, 1.00f, 1.50f, 1.00f, 1.75f, 1.40f, 1.17f, 1.00f, 1.88f, 1.76f, 1.50f, 1.36f, 1.25f, 1.15f, 1.07f, 1.00f, diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.h index 9ce1ff158..39a700917 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Distortion.h @@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN namespace DMO { -class Distortion : public IMixPlugin +class Distortion final : public IMixPlugin { protected: enum Parameters diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.cpp index f51756432..14d6c0a47 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.cpp @@ -71,7 +71,7 @@ void Echo::Process(float *pOutL, float *pOutR, uint32 numFrames) chnOutput += chnDelay * m_param[kEchoFeedback]; // Prevent denormals - if(mpt::abs(chnOutput) < 1e-24f) + if(std::abs(chnOutput) < 1e-24f) chnOutput = 0.0f; m_delayLine[m_writePos * 2 + channel] = chnOutput; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.h index 5d4e6322b..b56d6c95b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Echo.h @@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN namespace DMO { -class Echo : public IMixPlugin +class Echo final : public IMixPlugin { protected: enum Parameters diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.h index 549921c8f..8b964ed14 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Flanger.h @@ -21,7 +21,7 @@ OPENMPT_NAMESPACE_BEGIN namespace DMO { -class Flanger : public Chorus +class Flanger final : public Chorus { protected: enum Parameters diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Gargle.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Gargle.h index a05960bfa..6fa6b20f7 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Gargle.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/Gargle.h @@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN namespace DMO { -class Gargle : public IMixPlugin +class Gargle final : public IMixPlugin { protected: enum Parameters diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.cpp index e339fe38c..658767830 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.cpp @@ -13,6 +13,9 @@ #ifndef NO_PLUGINS #include "../../Sndfile.h" #include "I3DL2Reverb.h" +#ifdef MODPLUG_TRACKER +#include "../../../sounddsp/Reverb.h" +#endif // MODPLUG_TRACKER #endif // !NO_PLUGINS OPENMPT_NAMESPACE_BEGIN @@ -76,7 +79,6 @@ IMixPlugin* I3DL2Reverb::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDM I3DL2Reverb::I3DL2Reverb(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct) : IMixPlugin(factory, sndFile, mixStruct) - , m_recalcParams(true) { m_param[kI3DL2ReverbRoom] = 0.9f; m_param[kI3DL2ReverbRoomHF] = 0.99f; @@ -92,6 +94,8 @@ I3DL2Reverb::I3DL2Reverb(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGI m_param[kI3DL2ReverbHFReference] = (5000.0f - 20.0f) / 19980.0f; m_param[kI3DL2ReverbQuality] = 2.0f / 3.0f; + SetCurrentProgram(m_program); + m_mixBuffer.Initialize(2, 2); InsertIntoFactoryList(); } @@ -266,8 +270,8 @@ void I3DL2Reverb::Process(float *pOutL, float *pOutR, uint32 numFrames) float outL = earlyRefOutL + lateRevOutL; float outR = earlyRefOutR + lateRevOutR; - for(std::size_t d = 0; d < mpt::size(m_delayLines); d++) - m_delayLines[d].Advance(); + for(auto &line : m_delayLines) + line.Advance(); if(!(m_quality & kFullSampleRate)) { @@ -292,6 +296,42 @@ void I3DL2Reverb::Process(float *pOutL, float *pOutR, uint32 numFrames) } +int32 I3DL2Reverb::GetNumPrograms() const +{ +#ifdef MODPLUG_TRACKER + return NUM_REVERBTYPES; +#else + return 0; +#endif +} + +void I3DL2Reverb::SetCurrentProgram(int32 program) +{ +#ifdef MODPLUG_TRACKER + if(program < NUM_REVERBTYPES) + { + m_program = program; + const auto &preset = *GetReverbPreset(m_program); + m_param[kI3DL2ReverbRoom] = (preset.lRoom + 10000) / 10000.0f; + m_param[kI3DL2ReverbRoomHF] = (preset.lRoomHF + 10000) / 10000.0f; + m_param[kI3DL2ReverbRoomRolloffFactor] = 0.0f; + m_param[kI3DL2ReverbDecayTime] = (preset.flDecayTime - 0.1f) / 19.9f; + m_param[kI3DL2ReverbDecayHFRatio] = (preset.flDecayHFRatio - 0.1f) / 1.9f; + m_param[kI3DL2ReverbReflections] = (preset.lReflections + 10000) / 11000.0f; + m_param[kI3DL2ReverbReflectionsDelay] = preset.flReflectionsDelay / 0.3f; + m_param[kI3DL2ReverbReverb] = (preset.lReverb + 10000) / 12000.0f; + m_param[kI3DL2ReverbReverbDelay] = preset.flReverbDelay / 0.1f; + m_param[kI3DL2ReverbDiffusion] = preset.flDiffusion / 100.0f; + m_param[kI3DL2ReverbDensity] = preset.flDensity / 100.0f; + m_param[kI3DL2ReverbHFReference] = (5000.0f - 20.0f) / 19980.0f; + RecalculateI3DL2ReverbParams(); + } +#else + MPT_UNUSED_VARIABLE(program); +#endif +} + + PlugParamValue I3DL2Reverb::GetParameter(PlugParamIndex index) { if(index < kI3DL2ReverbNumParameters) @@ -410,7 +450,7 @@ CString I3DL2Reverb::GetParamLabel(PlugParamIndex param) CString I3DL2Reverb::GetParamDisplay(PlugParamIndex param) { - static const TCHAR *modes[] = { _T("LQ"), _T("LQ+"), _T("HQ"), _T("HQ+") }; + static constexpr const TCHAR * const modes[] = { _T("LQ"), _T("LQ+"), _T("HQ"), _T("HQ+") }; float value = m_param[param]; switch(param) { @@ -433,6 +473,18 @@ CString I3DL2Reverb::GetParamDisplay(PlugParamIndex param) return s; } + +CString I3DL2Reverb::GetCurrentProgramName() +{ + return GetProgramName(m_program); +} + + +CString I3DL2Reverb::GetProgramName(int32 program) +{ + return mpt::ToCString(GetReverbPresetName(program)); +} + #endif // MODPLUG_TRACKER @@ -468,7 +520,7 @@ void I3DL2Reverb::RecalculateI3DL2ReverbParams() void I3DL2Reverb::SetDelayTaps() { // Early reflections - static const float delays[] = + static constexpr float delays[] = { 1.0000f, 1.0000f, 0.0000f, 0.1078f, 0.1768f, 0.2727f, 0.3953f, 0.5386f, 0.6899f, 0.8306f, 0.9400f, 0.9800f, @@ -490,7 +542,7 @@ void I3DL2Reverb::SetDelayTaps() for(int i = 0, power = 0; i < 6; i++) { power += i; - float factor = std::pow(0.93f, power); + float factor = std::pow(0.93f, static_cast(power)); m_delayTaps[i + 0] = static_cast(delayL * factor); m_delayTaps[i + 6] = static_cast(delayR * factor); } @@ -499,7 +551,7 @@ void I3DL2Reverb::SetDelayTaps() m_delayTaps[13] = static_cast(3.25f / 1000.0f * sampleRate); m_delayTaps[14] = static_cast(3.53f / 1000.0f * sampleRate); - for(std::size_t d = 0; d < mpt::size(m_delayTaps); d++) + for(std::size_t d = 0; d < std::size(m_delayTaps); d++) m_delayLines[d].SetDelayTap(m_delayTaps[d]); } @@ -569,7 +621,7 @@ float I3DL2Reverb::CalcDecayCoeffs(int32 index) float c22 = -2.0f * c21 - 2.0f; float c23 = std::sqrt(c22 * c22 - c21 * c21 * 4.0f); c2 = (c23 - c22) / (c21 + c21); - if(mpt::abs(c2) > 1.0f) + if(std::abs(c2) > 1.0f) c2 = (-c22 - c23) / (c21 + c21); } m_delayCoeffs[index][0] = c1; diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.h index 29e9dfdd4..e172224f8 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/I3DL2Reverb.h @@ -21,7 +21,7 @@ OPENMPT_NAMESPACE_BEGIN namespace DMO { -class I3DL2Reverb : public IMixPlugin +class I3DL2Reverb final : public IMixPlugin { protected: enum Parameters @@ -64,6 +64,7 @@ protected: }; float m_param[kI3DL2ReverbNumParameters]; + int32 m_program = 0; // Calculated parameters uint32 m_quality; @@ -85,9 +86,9 @@ protected: // Remaining frame for downsampled reverb float m_prevL; float m_prevR; - bool m_remain; + bool m_remain = false; - bool m_ok, m_recalcParams; + bool m_ok = false, m_recalcParams = true; public: static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct); @@ -103,9 +104,10 @@ public: float RenderSilence(uint32) override { return 0.0f; } - int32 GetNumPrograms() const override { return 0; } - int32 GetCurrentProgram() override { return 0; } - void SetCurrentProgram(int32) override { } + int32 GetNumPrograms() const override; + int32 GetCurrentProgram() override { return m_program; } + // cppcheck-suppress virtualCallInConstructor + void SetCurrentProgram(int32) override; PlugParamIndex GetNumParameters() const override { return kI3DL2ReverbNumParameters; } PlugParamValue GetParameter(PlugParamIndex index) override; @@ -125,9 +127,9 @@ public: CString GetParamLabel(PlugParamIndex) override; CString GetParamDisplay(PlugParamIndex param) override; - CString GetCurrentProgramName() override { return CString(); } + CString GetCurrentProgramName() override; void SetCurrentProgramName(const CString &) override { } - CString GetProgramName(int32) override { return CString(); } + CString GetProgramName(int32 program) override; bool HasEditor() const override { return false; } #endif diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/ParamEq.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/ParamEq.h index 16c59dafc..4326a09b6 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/ParamEq.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/ParamEq.h @@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN namespace DMO { -class ParamEq : public IMixPlugin +class ParamEq final : public IMixPlugin { protected: enum Parameters diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/WavesReverb.h b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/WavesReverb.h index a732d1c21..f6ef95518 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/WavesReverb.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/plugins/dmo/WavesReverb.h @@ -17,7 +17,7 @@ OPENMPT_NAMESPACE_BEGIN namespace DMO { -class WavesReverb : public IMixPlugin +class WavesReverb final : public IMixPlugin { protected: enum Parameters diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp index 84593272e..3f60d8ae1 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.cpp @@ -26,12 +26,12 @@ namespace Tuning { namespace CTuningS11n { - void ReadStr(std::istream& iStrm, std::string& str, const size_t); - void ReadNoteMap(std::istream& iStrm, std::map& m, const size_t); + void ReadStr(std::istream &iStrm, mpt::ustring &ustr, const std::size_t dummy, mpt::Charset charset); + void ReadNoteMap(std::istream &iStrm, std::map &m, const std::size_t dummy, mpt::Charset charset); void ReadRatioTable(std::istream& iStrm, std::vector& v, const size_t); - void WriteNoteMap(std::ostream& oStrm, const std::map& m); - void WriteStr(std::ostream& oStrm, const std::string& str); + void WriteNoteMap(std::ostream &oStrm, const std::map &m); + void WriteStr(std::ostream &oStrm, const mpt::ustring &ustr); struct RatioWriter { @@ -46,6 +46,12 @@ namespace CTuningS11n using namespace CTuningS11n; +/* +Version history: + 4->5: Lots of changes, finestep interpretation revamp, fileformat revamp. + 3->4: Changed sizetypes in serialisation from size_t(uint32) to + smaller types (uint8, USTEPTYPE) (March 2007) +*/ /* Version changes: 3->4: Finetune related internal structure and serialization revamp. @@ -54,85 +60,128 @@ Version changes: */ -MPT_STATIC_ASSERT(CTuningRTI::s_RatioTableFineSizeMaxDefault < static_cast(FINESTEPCOUNT_MAX)); +static_assert(CTuning::s_RatioTableFineSizeMaxDefault < static_cast(FINESTEPCOUNT_MAX)); -CTuningRTI::CTuningRTI() - : m_TuningType(TT_GENERAL) +CTuning::CTuning() + : m_TuningType(Type::GENERAL) , m_FineStepCount(0) { - { - m_RatioTable.clear(); - m_StepMin = s_StepMinDefault; - m_RatioTable.resize(s_RatioTableSizeDefault, 1); - m_GroupSize = 0; - m_GroupRatio = 0; - m_RatioTableFine.clear(); - } + m_RatioTable.clear(); + m_NoteMin = s_NoteMinDefault; + m_RatioTable.resize(s_RatioTableSizeDefault, 1); + m_GroupSize = 0; + m_GroupRatio = 0; + m_RatioTableFine.clear(); } -bool CTuningRTI::ProCreateGroupGeometric(const std::vector& v, const RATIOTYPE& r, const VRPAIR& vr, const NOTEINDEXTYPE& ratiostartpos) +bool CTuning::CreateGroupGeometric(const NOTEINDEXTYPE &s, const RATIOTYPE &r, const NOTEINDEXTYPE &startindex) { - if(v.size() == 0 - || r <= 0 - || vr.second < vr.first - || ratiostartpos < vr.first) + if(s < 1 || !IsValidRatio(r) || startindex < GetNoteRange().first) { - return true; + return false; } + std::vector v; + v.reserve(s); + for(NOTEINDEXTYPE i = startindex; i < startindex + s; i++) + { + v.push_back(GetRatio(i)); + } + return CreateGroupGeometric(v, r, GetNoteRange(), startindex); +} - m_StepMin = vr.first; + +bool CTuning::CreateGroupGeometric(const std::vector &v, const RATIOTYPE &r, const NoteRange &range, const NOTEINDEXTYPE &ratiostartpos) +{ + if(range.first > range.last || v.size() == 0) + { + return false; + } + if(ratiostartpos < range.first || range.last < ratiostartpos || static_cast(range.last - ratiostartpos) < static_cast(v.size() - 1)) + { + return false; + } + if(GetFineStepCount() > FINESTEPCOUNT_MAX) + { + return false; + } + for(size_t i = 0; i < v.size(); i++) + { + if(v[i] < 0) + { + return false; + } + } + if(r <= 0) + { + return false; + } + m_TuningType = Type::GROUPGEOMETRIC; + m_NoteMin = range.first; m_GroupSize = mpt::saturate_cast(v.size()); m_GroupRatio = std::fabs(r); - - m_RatioTable.resize(vr.second-vr.first+1); - std::copy(v.begin(), v.end(), m_RatioTable.begin() + (ratiostartpos - vr.first)); - - for(int32 i = ratiostartpos-1; i>=m_StepMin && ratiostartpos > NOTEINDEXTYPE_MIN; i--) + m_RatioTable.resize(range.last - range.first + 1); + std::copy(v.begin(), v.end(), m_RatioTable.begin() + (ratiostartpos - range.first)); + for(int32 i = ratiostartpos - 1; i >= m_NoteMin && ratiostartpos > NOTEINDEXTYPE_MIN; i--) { - m_RatioTable[i-m_StepMin] = m_RatioTable[i - m_StepMin + m_GroupSize] / m_GroupRatio; + m_RatioTable[i - m_NoteMin] = m_RatioTable[i - m_NoteMin + m_GroupSize] / m_GroupRatio; } - for(int32 i = ratiostartpos+m_GroupSize; i<=vr.second && ratiostartpos <= (NOTEINDEXTYPE_MAX - m_GroupSize); i++) + for(int32 i = ratiostartpos + m_GroupSize; i <= range.last && ratiostartpos <= (NOTEINDEXTYPE_MAX - m_GroupSize); i++) { - m_RatioTable[i-m_StepMin] = m_GroupRatio * m_RatioTable[i - m_StepMin - m_GroupSize]; + m_RatioTable[i - m_NoteMin] = m_GroupRatio * m_RatioTable[i - m_NoteMin - m_GroupSize]; } - - return false; + UpdateFineStepTable(); + return true; } -bool CTuningRTI::ProCreateGeometric(const UNOTEINDEXTYPE& s, const RATIOTYPE& r, const VRPAIR& vr) +bool CTuning::CreateGeometric(const UNOTEINDEXTYPE &p, const RATIOTYPE &r) { - if(vr.second - vr.first + 1 > NOTEINDEXTYPE_MAX) return true; - //Note: Setting finestep is handled by base class when CreateGeometric is called. + return CreateGeometric(p, r, GetNoteRange()); +} + + +bool CTuning::CreateGeometric(const UNOTEINDEXTYPE &s, const RATIOTYPE &r, const NoteRange &range) +{ + if(range.first > range.last) { - m_RatioTable.clear(); - m_StepMin = s_StepMinDefault; - m_RatioTable.resize(s_RatioTableSizeDefault, static_cast(1.0)); - m_GroupSize = 0; - m_GroupRatio = 0; - m_RatioTableFine.clear(); + return false; } - m_StepMin = vr.first; - + if(s < 1 || !IsValidRatio(r)) + { + return false; + } + if(range.last - range.first + 1 > NOTEINDEXTYPE_MAX) + { + return false; + } + m_TuningType = Type::GEOMETRIC; + m_RatioTable.clear(); + m_NoteMin = s_NoteMinDefault; + m_RatioTable.resize(s_RatioTableSizeDefault, static_cast(1.0)); + m_GroupSize = 0; + m_GroupRatio = 0; + m_RatioTableFine.clear(); + m_NoteMin = range.first; m_GroupSize = mpt::saturate_cast(s); m_GroupRatio = std::fabs(r); - const RATIOTYPE stepRatio = std::pow(m_GroupRatio, static_cast(1.0)/ static_cast(m_GroupSize)); - - m_RatioTable.resize(vr.second - vr.first + 1); - for(int32 i = vr.first; i<=vr.second; i++) + const RATIOTYPE stepRatio = std::pow(m_GroupRatio, static_cast(1.0) / static_cast(m_GroupSize)); + m_RatioTable.resize(range.last - range.first + 1); + for(int32 i = range.first; i <= range.last; i++) { - m_RatioTable[i-m_StepMin] = std::pow(stepRatio, static_cast(i)); + m_RatioTable[i - m_NoteMin] = std::pow(stepRatio, static_cast(i)); } - return false; + UpdateFineStepTable(); + return true; } -std::string CTuningRTI::GetNoteName(const NOTEINDEXTYPE& x, bool addOctave) const + +mpt::ustring CTuning::GetNoteName(const NOTEINDEXTYPE &x, bool addOctave) const { if(!IsValidNote(x)) { - return std::string(); + return mpt::ustring(); } if(GetGroupSize() < 1) { @@ -140,20 +189,20 @@ std::string CTuningRTI::GetNoteName(const NOTEINDEXTYPE& x, bool addOctave) cons if(i != m_NoteNameMap.end()) return i->second; else - return mpt::fmt::val(x); + return mpt::ufmt::val(x); } else { const NOTEINDEXTYPE pos = static_cast(mpt::wrapping_modulo(x, m_GroupSize)); const NOTEINDEXTYPE middlePeriodNumber = 5; - std::string rValue; + mpt::ustring rValue; const auto nmi = m_NoteNameMap.find(pos); if(nmi != m_NoteNameMap.end()) { rValue = nmi->second; if(addOctave) { - rValue += mpt::fmt::val(middlePeriodNumber + mpt::wrapping_divide(x, m_GroupSize)); + rValue += mpt::ufmt::val(middlePeriodNumber + mpt::wrapping_divide(x, m_GroupSize)); } } else @@ -163,19 +212,19 @@ std::string CTuningRTI::GetNoteName(const NOTEINDEXTYPE& x, bool addOctave) cons //C:5, D:3, R:7 if(m_GroupSize <= 26) { - rValue = std::string(1, static_cast(pos + 'A')); - rValue += ":"; + rValue = mpt::ToUnicode(mpt::Charset::UTF8, std::string(1, static_cast(pos + 'A'))); + rValue += UL_(":"); } else { - rValue = mpt::fmt::HEX0<1>(pos % 16) + mpt::fmt::HEX0<1>((pos / 16) % 16); + rValue = mpt::ufmt::HEX0<1>(pos % 16) + mpt::ufmt::HEX0<1>((pos / 16) % 16); if(pos > 0xff) { - rValue = mpt::ToLowerCaseAscii(rValue); + rValue = mpt::ToUnicode(mpt::Charset::UTF8, mpt::ToLowerCaseAscii(mpt::ToCharset(mpt::Charset::UTF8, rValue))); } } if(addOctave) { - rValue += mpt::fmt::val(middlePeriodNumber + mpt::wrapping_divide(x, m_GroupSize)); + rValue += mpt::ufmt::val(middlePeriodNumber + mpt::wrapping_divide(x, m_GroupSize)); } } return rValue; @@ -183,86 +232,76 @@ std::string CTuningRTI::GetNoteName(const NOTEINDEXTYPE& x, bool addOctave) cons } -const RATIOTYPE CTuningRTI::s_DefaultFallbackRatio = 1.0f; - - -//Without finetune -RATIOTYPE CTuningRTI::GetRatio(const NOTEINDEXTYPE& stepsFromCentre) const +void CTuning::SetNoteName(const NOTEINDEXTYPE &n, const mpt::ustring &str) { - if(stepsFromCentre < m_StepMin) return s_DefaultFallbackRatio; - if(stepsFromCentre >= m_StepMin + static_cast(m_RatioTable.size())) return s_DefaultFallbackRatio; - return m_RatioTable[stepsFromCentre - m_StepMin]; -} - - -//With finetune -RATIOTYPE CTuningRTI::GetRatio(const NOTEINDEXTYPE& baseNote, const STEPINDEXTYPE& baseStepDiff) const -{ - const STEPINDEXTYPE fsCount = static_cast(GetFineStepCount()); - if(fsCount < 0 || fsCount > FINESTEPCOUNT_MAX) + if(!str.empty()) { - return s_DefaultFallbackRatio; - } - if(fsCount == 0 || baseStepDiff == 0) + m_NoteNameMap[n] = str; + } else { - return GetRatio(static_cast(baseNote + baseStepDiff)); - } - - //If baseStepDiff is more than the number of finesteps between notes, - //note is increased. So first figuring out what step and fineStep values to - //actually use. Interpreting finestep -1 on note x so that it is the same as - //finestep GetFineStepCount() on note x-1. - //Note: If finestepcount is n, n+1 steps are needed to get to - //next note. - NOTEINDEXTYPE note; - STEPINDEXTYPE fineStep; - note = static_cast(baseNote + mpt::wrapping_divide(baseStepDiff, (fsCount+1))); - fineStep = mpt::wrapping_modulo(baseStepDiff, (fsCount+1)); - - if(note < m_StepMin) return s_DefaultFallbackRatio; - if(note >= m_StepMin + static_cast(m_RatioTable.size())) return s_DefaultFallbackRatio; - - if(fineStep) return m_RatioTable[note - m_StepMin] * GetRatioFine(note, fineStep); - else return m_RatioTable[note - m_StepMin]; -} - - -RATIOTYPE CTuningRTI::GetRatioFine(const NOTEINDEXTYPE& note, USTEPINDEXTYPE sd) const -{ - if(GetFineStepCount() <= 0 || GetFineStepCount() > static_cast(FINESTEPCOUNT_MAX)) - { - return s_DefaultFallbackRatio; - } - - //Neither of these should happen. - if(sd <= 0) sd = 1; - if(sd > GetFineStepCount()) sd = GetFineStepCount(); - - if(GetType() != TT_GENERAL && m_RatioTableFine.size() > 0) //Taking fineratio from table - { - if(GetType() == TT_GEOMETRIC) + const auto iter = m_NoteNameMap.find(n); + if(iter != m_NoteNameMap.end()) { - return m_RatioTableFine[sd-1]; + m_NoteNameMap.erase(iter); } - if(GetType() == TT_GROUPGEOMETRIC) - return m_RatioTableFine[GetRefNote(note) * GetFineStepCount() + sd - 1]; - - MPT_ASSERT_NOTREACHED(); - return m_RatioTableFine[0]; //Shouldn't happen. } - else //Calculating ratio 'on the fly'. - { - //'Geometric finestepping'. - return std::pow(GetRatio(note+1) / GetRatio(note), static_cast(sd)/(GetFineStepCount()+1)); - - } - } -bool CTuningRTI::SetRatio(const NOTEINDEXTYPE& s, const RATIOTYPE& r) +// Without finetune +RATIOTYPE CTuning::GetRatio(const NOTEINDEXTYPE note) const { - if(GetType() != TT_GROUPGEOMETRIC && GetType() != TT_GENERAL) + if(!IsValidNote(note)) + { + return s_DefaultFallbackRatio; + } + return m_RatioTable[note - m_NoteMin]; +} + + +// With finetune +RATIOTYPE CTuning::GetRatio(const NOTEINDEXTYPE baseNote, const STEPINDEXTYPE baseFineSteps) const +{ + const STEPINDEXTYPE fineStepCount = static_cast(GetFineStepCount()); + if(fineStepCount == 0 || baseFineSteps == 0) + { + return GetRatio(static_cast(baseNote + baseFineSteps)); + } + + // If baseFineSteps is more than the number of finesteps between notes, note is increased. + // So first figuring out what note and fineStep values to actually use. + // Interpreting finestep==-1 on note x so that it is the same as finestep==fineStepCount on note x-1. + // Note: If fineStepCount is n, n+1 steps are needed to get to next note. + const NOTEINDEXTYPE note = static_cast(baseNote + mpt::wrapping_divide(baseFineSteps, (fineStepCount + 1))); + const STEPINDEXTYPE fineStep = mpt::wrapping_modulo(baseFineSteps, (fineStepCount + 1)); + if(!IsValidNote(note)) + { + return s_DefaultFallbackRatio; + } + if(fineStep == 0) + { + return m_RatioTable[note - m_NoteMin]; + } + + RATIOTYPE fineRatio = static_cast(1.0); + if(GetType() == Type::GEOMETRIC && m_RatioTableFine.size() > 0) + { + fineRatio = m_RatioTableFine[fineStep - 1]; + } else if(GetType() == Type::GROUPGEOMETRIC && m_RatioTableFine.size() > 0) + { + fineRatio = m_RatioTableFine[GetRefNote(note) * fineStepCount + fineStep - 1]; + } else + { + // Geometric finestepping + fineRatio = std::pow(GetRatio(note + 1) / GetRatio(note), static_cast(fineStep) / (fineStepCount + 1)); + } + return m_RatioTable[note - m_NoteMin] * fineRatio; +} + + +bool CTuning::SetRatio(const NOTEINDEXTYPE& s, const RATIOTYPE& r) +{ + if(GetType() != Type::GROUPGEOMETRIC && GetType() != Type::GENERAL) { return false; } @@ -270,23 +309,23 @@ bool CTuningRTI::SetRatio(const NOTEINDEXTYPE& s, const RATIOTYPE& r) if(m_RatioTable.empty()) { m_RatioTable.assign(s_RatioTableSizeDefault, 1); - m_StepMin = s_StepMinDefault; + m_NoteMin = s_NoteMinDefault; } - if(!IsNoteInTable(s)) + if(!IsValidNote(s)) { return false; } - m_RatioTable[s - m_StepMin] = std::fabs(r); - if(GetType() == TT_GROUPGEOMETRIC) + m_RatioTable[s - m_NoteMin] = std::fabs(r); + if(GetType() == Type::GROUPGEOMETRIC) { // update other groups - for(NOTEINDEXTYPE n = m_StepMin; n < m_StepMin + static_cast(m_RatioTable.size()); ++n) + for(NOTEINDEXTYPE n = m_NoteMin; n < m_NoteMin + static_cast(m_RatioTable.size()); ++n) { if(n == s) { // nothing - } else if(mpt::abs(n - s) % m_GroupSize == 0) + } else if(std::abs(n - s) % m_GroupSize == 0) { - m_RatioTable[n - m_StepMin] = std::pow(m_GroupRatio, static_cast(n - s) / static_cast(m_GroupSize)) * m_RatioTable[s - m_StepMin]; + m_RatioTable[n - m_NoteMin] = std::pow(m_GroupRatio, static_cast(n - s) / static_cast(m_GroupSize)) * m_RatioTable[s - m_NoteMin]; } } UpdateFineStepTable(); @@ -295,21 +334,21 @@ bool CTuningRTI::SetRatio(const NOTEINDEXTYPE& s, const RATIOTYPE& r) } -void CTuningRTI::SetFineStepCount(const USTEPINDEXTYPE& fs) +void CTuning::SetFineStepCount(const USTEPINDEXTYPE& fs) { - m_FineStepCount = mpt::clamp(mpt::saturate_cast(fs), STEPINDEXTYPE(0), FINESTEPCOUNT_MAX); + m_FineStepCount = std::clamp(mpt::saturate_cast(fs), STEPINDEXTYPE(0), FINESTEPCOUNT_MAX); UpdateFineStepTable(); } -void CTuningRTI::UpdateFineStepTable() +void CTuning::UpdateFineStepTable() { if(m_FineStepCount <= 0) { m_RatioTableFine.clear(); return; } - if(GetType() == TT_GEOMETRIC) + if(GetType() == Type::GEOMETRIC) { if(m_FineStepCount > s_RatioTableFineSizeMaxDefault) { @@ -317,13 +356,13 @@ void CTuningRTI::UpdateFineStepTable() return; } m_RatioTableFine.resize(m_FineStepCount); - const RATIOTYPE q = GetRatio(GetValidityRange().first + 1) / GetRatio(GetValidityRange().first); + const RATIOTYPE q = GetRatio(GetNoteRange().first + 1) / GetRatio(GetNoteRange().first); const RATIOTYPE rFineStep = std::pow(q, static_cast(1)/(m_FineStepCount+1)); for(USTEPINDEXTYPE i = 1; i<=m_FineStepCount; i++) m_RatioTableFine[i-1] = std::pow(rFineStep, static_cast(i)); return; } - if(GetType() == TT_GROUPGEOMETRIC) + if(GetType() == Type::GROUPGEOMETRIC) { const UNOTEINDEXTYPE p = GetGroupSize(); if(p > s_RatioTableFineSizeMaxDefault / m_FineStepCount) @@ -337,7 +376,7 @@ void CTuningRTI::UpdateFineStepTable() { //Creating 'geometric' finestepping between notes. m_RatioTableFine.resize(p * m_FineStepCount); - const NOTEINDEXTYPE startnote = GetRefNote(GetValidityRange().first); + const NOTEINDEXTYPE startnote = GetRefNote(GetNoteRange().first); for(UNOTEINDEXTYPE i = 0; i(mpt::wrapping_modulo(note, GetGroupSize())); + if(!IsValidRatio(r)) + { + return false; + } + for(auto & ratio : m_RatioTable) + { + ratio *= r; + } + return true; } -SerializationResult CTuningRTI::InitDeserialize(std::istream& iStrm) +bool CTuning::ChangeGroupsize(const NOTEINDEXTYPE& s) +{ + if(s < 1) + return false; + + if(m_TuningType == Type::GROUPGEOMETRIC) + return CreateGroupGeometric(s, GetGroupRatio(), 0); + + if(m_TuningType == Type::GEOMETRIC) + return CreateGeometric(s, GetGroupRatio()); + + return false; +} + + +bool CTuning::ChangeGroupRatio(const RATIOTYPE& r) +{ + if(!IsValidRatio(r)) + return false; + + if(m_TuningType == Type::GROUPGEOMETRIC) + return CreateGroupGeometric(GetGroupSize(), r, 0); + + if(m_TuningType == Type::GEOMETRIC) + return CreateGeometric(GetGroupSize(), r); + + return false; +} + + +SerializationResult CTuning::InitDeserialize(std::istream &iStrm, mpt::Charset defaultCharset) { // Note: OpenMPT since at least r323 writes version number (4<<24)+4 while it // reads version number (5<<24)+4 or earlier. @@ -382,29 +458,34 @@ SerializationResult CTuningRTI::InitDeserialize(std::istream& iStrm) srlztn::SsbRead ssb(iStrm); ssb.BeginRead("CTB244RTI", (5 << 24) + 4); // version - ssb.ReadItem(m_TuningName, "0", ReadStr); + int8 use_utf8 = 0; + ssb.ReadItem(use_utf8, "UTF8"); + const mpt::Charset charset = use_utf8 ? mpt::Charset::UTF8 : defaultCharset; + ssb.ReadItem(m_TuningName, "0", [charset](std::istream &iStrm, mpt::ustring &ustr, const std::size_t dummy){ return ReadStr(iStrm, ustr, dummy, charset); }); uint16 dummyEditMask = 0xffff; ssb.ReadItem(dummyEditMask, "1"); - ssb.ReadItem(m_TuningType, "2"); - ssb.ReadItem(m_NoteNameMap, "3", ReadNoteMap); + std::underlying_type::type type = 0; + ssb.ReadItem(type, "2"); + m_TuningType = static_cast(type); + ssb.ReadItem(m_NoteNameMap, "3", [charset](std::istream &iStrm, std::map &m, const std::size_t dummy){ return ReadNoteMap(iStrm, m, dummy, charset); }); ssb.ReadItem(m_FineStepCount, "4"); // RTI entries. ssb.ReadItem(m_RatioTable, "RTI0", ReadRatioTable); - ssb.ReadItem(m_StepMin, "RTI1"); + ssb.ReadItem(m_NoteMin, "RTI1"); ssb.ReadItem(m_GroupSize, "RTI2"); ssb.ReadItem(m_GroupRatio, "RTI3"); UNOTEINDEXTYPE ratiotableSize = 0; ssb.ReadItem(ratiotableSize, "RTI4"); - // If reader status is ok and m_StepMin is somewhat reasonable, process data. - if(!((ssb.GetStatus() & srlztn::SNT_FAILURE) == 0 && m_StepMin >= -300 && m_StepMin <= 300)) + // If reader status is ok and m_NoteMin is somewhat reasonable, process data. + if(!((ssb.GetStatus() & srlztn::SNT_FAILURE) == 0 && m_NoteMin >= -300 && m_NoteMin <= 300)) { return SerializationResult::Failure; } // reject unknown types - if(m_TuningType != TT_GENERAL && m_TuningType != TT_GROUPGEOMETRIC && m_TuningType != TT_GEOMETRIC) + if(m_TuningType != Type::GENERAL && m_TuningType != Type::GROUPGEOMETRIC && m_TuningType != Type::GEOMETRIC) { return SerializationResult::Failure; } @@ -412,26 +493,26 @@ SerializationResult CTuningRTI::InitDeserialize(std::istream& iStrm) { return SerializationResult::Failure; } - m_FineStepCount = mpt::clamp(mpt::saturate_cast(m_FineStepCount), STEPINDEXTYPE(0), FINESTEPCOUNT_MAX); + m_FineStepCount = std::clamp(mpt::saturate_cast(m_FineStepCount), STEPINDEXTYPE(0), FINESTEPCOUNT_MAX); if(m_RatioTable.size() > static_cast(NOTEINDEXTYPE_MAX)) { return SerializationResult::Failure; } - if((GetType() == TT_GROUPGEOMETRIC) || (GetType() == TT_GEOMETRIC)) + if((GetType() == Type::GROUPGEOMETRIC) || (GetType() == Type::GEOMETRIC)) { if(ratiotableSize < 1 || ratiotableSize > NOTEINDEXTYPE_MAX) { return SerializationResult::Failure; } - if(GetType() == TT_GEOMETRIC) + if(GetType() == Type::GEOMETRIC) { - if(CreateGeometric(GetGroupSize(), GetGroupRatio(), VRPAIR(m_StepMin, static_cast(m_StepMin + ratiotableSize - 1))) != false) + if(!CreateGeometric(GetGroupSize(), GetGroupRatio(), NoteRange{m_NoteMin, static_cast(m_NoteMin + ratiotableSize - 1)})) { return SerializationResult::Failure; } } else { - if(CreateGroupGeometric(m_RatioTable, GetGroupRatio(), VRPAIR(m_StepMin, static_cast(m_StepMin+ratiotableSize-1)), m_StepMin) != false) + if(!CreateGroupGeometric(m_RatioTable, GetGroupRatio(), NoteRange{m_NoteMin, static_cast(m_NoteMin + ratiotableSize - 1)}, m_NoteMin)) { return SerializationResult::Failure; } @@ -447,13 +528,14 @@ SerializationResult CTuningRTI::InitDeserialize(std::istream& iStrm) template static bool VectorFromBinaryStream(std::istream& inStrm, std::vector& v, const SIZETYPE maxSize = (std::numeric_limits::max)()) { - if(!inStrm.good()) return true; + if(!inStrm.good()) + return false; SIZETYPE size = 0; mpt::IO::ReadIntLE(inStrm, size); if(size > maxSize) - return true; + return false; v.resize(size); for(std::size_t i = 0; i& v, c mpt::IO::Read(inStrm, tmp); v[i] = tmp; } - if(inStrm.good()) - return false; - else - return true; + + return inStrm.good(); } -SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) +SerializationResult CTuning::InitDeserializeOLD(std::istream &inStrm, mpt::Charset defaultCharset) { if(!inStrm.good()) return SerializationResult::Failure; @@ -511,16 +591,20 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) //Tuning name if(version2 <= 3) { - if(!mpt::IO::ReadSizedStringLE(inStrm, m_TuningName, 0xffff)) + std::string tmpName; + if(!mpt::IO::ReadSizedStringLE(inStrm, tmpName, 0xffff)) { return SerializationResult::Failure; } + m_TuningName = mpt::ToUnicode(defaultCharset, tmpName); } else { - if(!mpt::IO::ReadSizedStringLE(inStrm, m_TuningName)) + std::string tmpName; + if(!mpt::IO::ReadSizedStringLE(inStrm, tmpName)) { return SerializationResult::Failure; } + m_TuningName = mpt::ToUnicode(defaultCharset, tmpName); } //Const mask @@ -530,7 +614,7 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) //Tuning type int16 tt = 0; mpt::IO::ReadIntLE(inStrm, tt); - m_TuningType = tt; + m_TuningType = static_cast(tt); //Notemap uint16 size = 0; @@ -565,7 +649,7 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) return SerializationResult::Failure; } } - m_NoteNameMap[n] = str; + m_NoteNameMap[n] = mpt::ToUnicode(defaultCharset, str); } //End marker @@ -578,7 +662,7 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) } // reject unknown types - if(m_TuningType != TT_GENERAL && m_TuningType != TT_GROUPGEOMETRIC && m_TuningType != TT_GEOMETRIC) + if(m_TuningType != Type::GENERAL && m_TuningType != Type::GROUPGEOMETRIC && m_TuningType != Type::GEOMETRIC) { return SerializationResult::Failure; } @@ -586,13 +670,13 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) //Ratiotable if(version <= 2) { - if(VectorFromBinaryStream(inStrm, m_RatioTable, 0xffff)) + if(!VectorFromBinaryStream(inStrm, m_RatioTable, 0xffff)) { return SerializationResult::Failure; } } else { - if(VectorFromBinaryStream(inStrm, m_RatioTable)) + if(!VectorFromBinaryStream(inStrm, m_RatioTable)) { return SerializationResult::Failure; } @@ -601,24 +685,24 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) //Fineratios if(version <= 2) { - if(VectorFromBinaryStream(inStrm, m_RatioTableFine, 0xffff)) + if(!VectorFromBinaryStream(inStrm, m_RatioTableFine, 0xffff)) { return SerializationResult::Failure; } } else { - if(VectorFromBinaryStream(inStrm, m_RatioTableFine)) + if(!VectorFromBinaryStream(inStrm, m_RatioTableFine)) { return SerializationResult::Failure; } } m_FineStepCount = mpt::saturate_cast(m_RatioTableFine.size()); - //m_StepMin - int16 stepmin = 0; - mpt::IO::ReadIntLE(inStrm, stepmin); - m_StepMin = stepmin; - if(m_StepMin < -200 || m_StepMin > 200) + // m_NoteMin + int16 notemin = 0; + mpt::IO::ReadIntLE(inStrm, notemin); + m_NoteMin = notemin; + if(m_NoteMin < -200 || m_NoteMin > 200) { return SerializationResult::Failure; } @@ -654,11 +738,11 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) { return SerializationResult::Failure; } - if((m_GroupSize <= 0 || m_GroupRatio <= 0) && m_TuningType != TT_GENERAL) + if((m_GroupSize <= 0 || m_GroupRatio <= 0) && m_TuningType != Type::GENERAL) { return SerializationResult::Failure; } - if(m_TuningType == TT_GROUPGEOMETRIC || m_TuningType == TT_GEOMETRIC) + if(m_TuningType == Type::GROUPGEOMETRIC || m_TuningType == Type::GEOMETRIC) { if(m_RatioTable.size() < static_cast(m_GroupSize)) { @@ -671,21 +755,21 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) { m_FineStepCount -= 1; } - m_FineStepCount = mpt::clamp(mpt::saturate_cast(m_FineStepCount), STEPINDEXTYPE(0), FINESTEPCOUNT_MAX); + m_FineStepCount = std::clamp(mpt::saturate_cast(m_FineStepCount), STEPINDEXTYPE(0), FINESTEPCOUNT_MAX); UpdateFineStepTable(); - if(m_TuningType == TT_GEOMETRIC) + if(m_TuningType == Type::GEOMETRIC) { // Convert old geometric to new groupgeometric because old geometric tunings // can have ratio(0) != 1.0, which would get lost when saving nowadays. - if(mpt::saturate_cast(m_RatioTable.size()) >= m_GroupSize - m_StepMin) + if(mpt::saturate_cast(m_RatioTable.size()) >= m_GroupSize - m_NoteMin) { std::vector ratios; for(NOTEINDEXTYPE n = 0; n < m_GroupSize; ++n) { - ratios.push_back(m_RatioTable[n - m_StepMin]); + ratios.push_back(m_RatioTable[n - m_NoteMin]); } - CreateGroupGeometric(ratios, m_GroupRatio, GetValidityRange(), 0); + CreateGroupGeometric(ratios, m_GroupRatio, GetNoteRange(), 0); } } @@ -693,41 +777,42 @@ SerializationResult CTuningRTI::InitDeserializeOLD(std::istream& inStrm) } -Tuning::SerializationResult CTuningRTI::Serialize(std::ostream& outStrm) const +Tuning::SerializationResult CTuning::Serialize(std::ostream& outStrm) const { // Note: OpenMPT since at least r323 writes version number (4<<24)+4 while it // reads version number (5<<24)+4. // We keep this behaviour. srlztn::SsbWrite ssb(outStrm); ssb.BeginWrite("CTB244RTI", (4 << 24) + 4); // version + ssb.WriteItem(int8(1), "UTF8"); if (m_TuningName.length() > 0) ssb.WriteItem(m_TuningName, "0", WriteStr); uint16 dummyEditMask = 0xffff; ssb.WriteItem(dummyEditMask, "1"); - ssb.WriteItem(m_TuningType, "2"); + ssb.WriteItem(static_cast::type>(m_TuningType), "2"); if (m_NoteNameMap.size() > 0) ssb.WriteItem(m_NoteNameMap, "3", WriteNoteMap); if (GetFineStepCount() > 0) ssb.WriteItem(m_FineStepCount, "4"); - const TUNINGTYPE tt = GetType(); + const Tuning::Type tt = GetType(); if (GetGroupRatio() > 0) ssb.WriteItem(m_GroupRatio, "RTI3"); - if (tt == TT_GROUPGEOMETRIC) + if (tt == Type::GROUPGEOMETRIC) ssb.WriteItem(m_RatioTable, "RTI0", RatioWriter(GetGroupSize())); - if (tt == TT_GENERAL) + if (tt == Type::GENERAL) ssb.WriteItem(m_RatioTable, "RTI0", RatioWriter()); - if (tt == TT_GEOMETRIC) + if (tt == Type::GEOMETRIC) ssb.WriteItem(m_GroupSize, "RTI2"); - if(tt == TT_GEOMETRIC || tt == TT_GROUPGEOMETRIC) + if(tt == Type::GEOMETRIC || tt == Type::GROUPGEOMETRIC) { //For Groupgeometric this data is the number of ratios in ratiotable. UNOTEINDEXTYPE ratiotableSize = static_cast(m_RatioTable.size()); ssb.WriteItem(ratiotableSize, "RTI4"); } - //m_StepMin - ssb.WriteItem(m_StepMin, "RTI1"); + // m_NoteMin + ssb.WriteItem(m_NoteMin, "RTI1"); ssb.FinishWrite(); @@ -737,15 +822,15 @@ Tuning::SerializationResult CTuningRTI::Serialize(std::ostream& outStrm) const #ifdef MODPLUG_TRACKER -bool CTuningRTI::WriteSCL(std::ostream &f, const mpt::PathString &filename) const +bool CTuning::WriteSCL(std::ostream &f, const mpt::PathString &filename) const { - mpt::IO::WriteTextCRLF(f, mpt::format("! %1")(mpt::ToCharset(mpt::CharsetISO8859_1, (filename.GetFileName() + filename.GetFileExt()).ToUnicode()))); + mpt::IO::WriteTextCRLF(f, mpt::format("! %1")(mpt::ToCharset(mpt::Charset::ISO8859_1, (filename.GetFileName() + filename.GetFileExt()).ToUnicode()))); mpt::IO::WriteTextCRLF(f, "!"); - std::string name = mpt::ToCharset(mpt::CharsetISO8859_1, mpt::CharsetLocale, GetName()); + std::string name = mpt::ToCharset(mpt::Charset::ISO8859_1, GetName()); for(auto & c : name) { if(static_cast(c) < 32) c = ' '; } // remove control characters if(name.length() >= 1 && name[0] == '!') name[0] = '?'; // do not confuse description with comment mpt::IO::WriteTextCRLF(f, name); - if(GetType() == TT_GEOMETRIC) + if(GetType() == Type::GEOMETRIC) { mpt::IO::WriteTextCRLF(f, mpt::format(" %1")(m_GroupSize)); mpt::IO::WriteTextCRLF(f, "!"); @@ -755,10 +840,10 @@ bool CTuningRTI::WriteSCL(std::ostream &f, const mpt::PathString &filename) cons double cents = std::log2(ratio) * 1200.0; mpt::IO::WriteTextCRLF(f, mpt::format(" %1 ! %2")( mpt::fmt::fix(cents), - mpt::ToCharset(mpt::CharsetISO8859_1, mpt::CharsetLocale, GetNoteName((n + 1) % m_GroupSize, false)) + mpt::ToCharset(mpt::Charset::ISO8859_1, GetNoteName((n + 1) % m_GroupSize, false)) )); } - } else if(GetType() == TT_GROUPGEOMETRIC) + } else if(GetType() == Type::GROUPGEOMETRIC) { mpt::IO::WriteTextCRLF(f, mpt::format(" %1")(m_GroupSize)); mpt::IO::WriteTextCRLF(f, "!"); @@ -770,10 +855,10 @@ bool CTuningRTI::WriteSCL(std::ostream &f, const mpt::PathString &filename) cons double cents = std::log2(ratio) * 1200.0; mpt::IO::WriteTextCRLF(f, mpt::format(" %1 ! %2")( mpt::fmt::fix(cents), - mpt::ToCharset(mpt::CharsetISO8859_1, mpt::CharsetLocale, GetNoteName((n + 1) % m_GroupSize, false)) + mpt::ToCharset(mpt::Charset::ISO8859_1, GetNoteName((n + 1) % m_GroupSize, false)) )); } - } else if(GetType() == TT_GENERAL) + } else if(GetType() == Type::GENERAL) { mpt::IO::WriteTextCRLF(f, mpt::format(" %1")(m_RatioTable.size() + 1)); mpt::IO::WriteTextCRLF(f, "!"); @@ -788,7 +873,7 @@ bool CTuningRTI::WriteSCL(std::ostream &f, const mpt::PathString &filename) cons double cents = std::log2(ratio) * 1200.0; mpt::IO::WriteTextCRLF(f, mpt::format(" %1 ! %2")( mpt::fmt::fix(cents), - mpt::ToCharset(mpt::CharsetISO8859_1, mpt::CharsetLocale, GetNoteName(n + m_StepMin, false)) + mpt::ToCharset(mpt::Charset::ISO8859_1, GetNoteName(n + m_NoteMin, false)) )); } mpt::IO::WriteTextCRLF(f, mpt::format(" %1 ! %2")( @@ -810,15 +895,16 @@ namespace CTuningS11n void RatioWriter::operator()(std::ostream& oStrm, const std::vector& v) { - const size_t nWriteCount = MIN(v.size(), m_nWriteCount); + const std::size_t nWriteCount = std::min(v.size(), static_cast(m_nWriteCount)); mpt::IO::WriteAdaptiveInt64LE(oStrm, nWriteCount); for(size_t i = 0; i < nWriteCount; i++) mpt::IO::Write(oStrm, IEEE754binary32LE(v[i])); } -void ReadNoteMap(std::istream& iStrm, std::map& m, const size_t) +void ReadNoteMap(std::istream &iStrm, std::map &m, const std::size_t dummy, mpt::Charset charset) { + MPT_UNREFERENCED_PARAMETER(dummy); uint64 val; mpt::IO::ReadAdaptiveInt64LE(iStrm, val); LimitMax(val, 256u); // Read 256 at max. @@ -828,7 +914,7 @@ void ReadNoteMap(std::istream& iStrm, std::map& m, co mpt::IO::ReadIntLE(iStrm, key); std::string str; mpt::IO::ReadSizedStringLE(iStrm, str); - m[key] = str; + m[key] = mpt::ToUnicode(charset, str); } } @@ -837,7 +923,7 @@ void ReadRatioTable(std::istream& iStrm, std::vector& v, const size_t { uint64 val; mpt::IO::ReadAdaptiveInt64LE(iStrm, val); - v.resize( static_cast(MIN(val, 256u))); // Read 256 vals at max. + v.resize(std::min(mpt::saturate_cast(val), std::size_t(256))); // Read 256 vals at max. for(size_t i = 0; i < v.size(); i++) { IEEE754binary32LE tmp(0.0f); @@ -847,8 +933,10 @@ void ReadRatioTable(std::istream& iStrm, std::vector& v, const size_t } -void ReadStr(std::istream& iStrm, std::string& str, const size_t) +void ReadStr(std::istream &iStrm, mpt::ustring &ustr, const std::size_t dummy, mpt::Charset charset) { + MPT_UNREFERENCED_PARAMETER(dummy); + std::string str; uint64 val; mpt::IO::ReadAdaptiveInt64LE(iStrm, val); size_t nSize = (val > 255) ? 255 : static_cast(val); // Read 255 characters at max. @@ -860,22 +948,24 @@ void ReadStr(std::istream& iStrm, std::string& str, const size_t) { // trim \0 at the end str.resize(str.find_first_of('\0')); } + ustr = mpt::ToUnicode(charset, str); } -void WriteNoteMap(std::ostream& oStrm, const std::map& m) +void WriteNoteMap(std::ostream &oStrm, const std::map &m) { mpt::IO::WriteAdaptiveInt64LE(oStrm, m.size()); for(auto &mi : m) { mpt::IO::WriteIntLE(oStrm, mi.first); - mpt::IO::WriteSizedStringLE(oStrm, mi.second); + mpt::IO::WriteSizedStringLE(oStrm, mpt::ToCharset(mpt::Charset::UTF8, mi.second)); } } -void WriteStr(std::ostream& oStrm, const std::string& str) +void WriteStr(std::ostream &oStrm, const mpt::ustring &ustr) { + std::string str = mpt::ToCharset(mpt::Charset::UTF8, ustr); mpt::IO::WriteAdaptiveInt64LE(oStrm, str.size()); oStrm.write(str.c_str(), str.size()); } diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.h b/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.h index 828f2e39f..1692d3e6b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuning.h @@ -23,51 +23,52 @@ OPENMPT_NAMESPACE_BEGIN namespace Tuning { -class CTuningRTI +class CTuning { public: - static const char s_FileExtension[5]; + static constexpr char s_FileExtension[5] = ".tun"; - enum - { - TT_GENERAL = 0, - TT_GROUPGEOMETRIC = 1, - TT_GEOMETRIC = 3, - }; - - static const RATIOTYPE s_DefaultFallbackRatio; - enum : NOTEINDEXTYPE { s_StepMinDefault = -64 }; - enum : UNOTEINDEXTYPE { s_RatioTableSizeDefault = 128 }; - enum : USTEPINDEXTYPE { s_RatioTableFineSizeMaxDefault = 1000 }; + static constexpr RATIOTYPE s_DefaultFallbackRatio = 1.0f; + static constexpr NOTEINDEXTYPE s_NoteMinDefault = -64; + static constexpr UNOTEINDEXTYPE s_RatioTableSizeDefault = 128; + static constexpr USTEPINDEXTYPE s_RatioTableFineSizeMaxDefault = 1000; public: - //To return ratio of certain note. - RATIOTYPE GetRatio(const NOTEINDEXTYPE& stepsFromCentre) const; + // To return ratio of certain note. + RATIOTYPE GetRatio(const NOTEINDEXTYPE note) const; - //To return ratio from a 'step'(noteindex + stepindex) - RATIOTYPE GetRatio(const NOTEINDEXTYPE& stepsFromCentre, const STEPINDEXTYPE& fineSteps) const; - - UNOTEINDEXTYPE GetRatioTableSize() const {return static_cast(m_RatioTable.size());} - - NOTEINDEXTYPE GetRatioTableBeginNote() const {return m_StepMin;} + // To return ratio from a 'step'(noteindex + stepindex) + RATIOTYPE GetRatio(const NOTEINDEXTYPE baseNote, const STEPINDEXTYPE baseFineSteps) const; //Tuning might not be valid for arbitrarily large range, //so this can be used to ask where it is valid. Tells the lowest and highest //note that are valid. - VRPAIR GetValidityRange() const {return VRPAIR(m_StepMin, static_cast(m_StepMin + static_cast(m_RatioTable.size()) - 1));} + MPT_FORCEINLINE NoteRange GetNoteRange() const + { + return NoteRange{m_NoteMin, static_cast(m_NoteMin + static_cast(m_RatioTable.size()) - 1)}; + } - //Return true if note is within validity range - false otherwise. - bool IsValidNote(const NOTEINDEXTYPE n) const {return (n >= GetValidityRange().first && n <= GetValidityRange().second);} + // Return true if note is within note range + MPT_FORCEINLINE bool IsValidNote(const NOTEINDEXTYPE n) const + { + return (GetNoteRange().first <= n && n <= GetNoteRange().last); + } - UNOTEINDEXTYPE GetGroupSize() const {return m_GroupSize;} + MPT_FORCEINLINE UNOTEINDEXTYPE GetGroupSize() const + { + return m_GroupSize; + } RATIOTYPE GetGroupRatio() const {return m_GroupRatio;} - //To return (fine)stepcount between two consecutive mainsteps. - USTEPINDEXTYPE GetFineStepCount() const {return m_FineStepCount;} + // To return (fine)stepcount between two consecutive mainsteps. + MPT_FORCEINLINE USTEPINDEXTYPE GetFineStepCount() const + { + return m_FineStepCount; + } //To return 'directed distance' between given notes. STEPINDEXTYPE GetStepDistance(const NOTEINDEXTYPE& from, const NOTEINDEXTYPE& to) const @@ -82,83 +83,81 @@ public: //stepdistances become the same as note distances. void SetFineStepCount(const USTEPINDEXTYPE& fs); - //Multiply all ratios by given number. - bool Multiply(const RATIOTYPE&); + // Multiply all ratios by given number. + bool Multiply(const RATIOTYPE r); bool SetRatio(const NOTEINDEXTYPE& s, const RATIOTYPE& r); - TUNINGTYPE GetType() const {return m_TuningType;} - - std::string GetNoteName(const NOTEINDEXTYPE& x, bool addOctave = true) const; - - void SetNoteName(const NOTEINDEXTYPE&, const std::string&); - - static CTuningRTI* CreateDeserialize(std::istream & f) + MPT_FORCEINLINE Tuning::Type GetType() const { - CTuningRTI *pT = new CTuningRTI(); - if(pT->InitDeserialize(f) != SerializationResult::Success) + return m_TuningType; + } + + mpt::ustring GetNoteName(const NOTEINDEXTYPE &x, bool addOctave = true) const; + + void SetNoteName(const NOTEINDEXTYPE &, const mpt::ustring &); + + static std::unique_ptr CreateDeserialize(std::istream &f, mpt::Charset defaultCharset) + { + std::unique_ptr pT = std::unique_ptr(new CTuning()); + if(pT->InitDeserialize(f, defaultCharset) != SerializationResult::Success) { - delete pT; return nullptr; } return pT; } //Try to read old version (v.3) and return pointer to new instance if succesfull, else nullptr. - static CTuningRTI* CreateDeserializeOLD(std::istream & f) + static std::unique_ptr CreateDeserializeOLD(std::istream &f, mpt::Charset defaultCharset) { - CTuningRTI *pT = new CTuningRTI(); - if(pT->InitDeserializeOLD(f) != SerializationResult::Success) + std::unique_ptr pT = std::unique_ptr(new CTuning()); + if(pT->InitDeserializeOLD(f, defaultCharset) != SerializationResult::Success) { - delete pT; return nullptr; } return pT; } - static CTuningRTI* CreateGeneral(const std::string &name) + static std::unique_ptr CreateGeneral(const mpt::ustring &name) { - CTuningRTI *pT = new CTuningRTI(); + std::unique_ptr pT = std::unique_ptr(new CTuning()); pT->SetName(name); return pT; } - static CTuningRTI* CreateGroupGeometric(const std::string &name, UNOTEINDEXTYPE groupsize, RATIOTYPE groupratio, USTEPINDEXTYPE finestepcount) + static std::unique_ptr CreateGroupGeometric(const mpt::ustring &name, UNOTEINDEXTYPE groupsize, RATIOTYPE groupratio, USTEPINDEXTYPE finestepcount) { - CTuningRTI *pT = new CTuningRTI(); + std::unique_ptr pT = std::unique_ptr(new CTuning()); pT->SetName(name); - if(pT->CreateGroupGeometric(groupsize, groupratio, 0) != false) + if(!pT->CreateGroupGeometric(groupsize, groupratio, 0)) { - delete pT; return nullptr; } pT->SetFineStepCount(finestepcount); return pT; } - static CTuningRTI* CreateGroupGeometric(const std::string &name, const std::vector &ratios, RATIOTYPE groupratio, USTEPINDEXTYPE finestepcount) + static std::unique_ptr CreateGroupGeometric(const mpt::ustring &name, const std::vector &ratios, RATIOTYPE groupratio, USTEPINDEXTYPE finestepcount) { - CTuningRTI *pT = new CTuningRTI(); + std::unique_ptr pT = std::unique_ptr(new CTuning()); pT->SetName(name); - VRPAIR range = std::make_pair(s_StepMinDefault, static_cast(static_cast(s_StepMinDefault) + static_cast(s_RatioTableSizeDefault) - 1)); - range.second = std::max(range.second, mpt::saturate_cast(ratios.size() - 1)); - range.first = 0 - range.second - 1; - if(pT->CreateGroupGeometric(ratios, groupratio, range, 0) != false) + NoteRange range = NoteRange{s_NoteMinDefault, static_cast(s_NoteMinDefault + s_RatioTableSizeDefault - 1)}; + range.last = std::max(range.last, mpt::saturate_cast(ratios.size() - 1)); + range.first = 0 - range.last - 1; + if(!pT->CreateGroupGeometric(ratios, groupratio, range, 0)) { - delete pT; return nullptr; } pT->SetFineStepCount(finestepcount); return pT; } - static CTuningRTI* CreateGeometric(const std::string &name, UNOTEINDEXTYPE groupsize, RATIOTYPE groupratio, USTEPINDEXTYPE finestepcount) + static std::unique_ptr CreateGeometric(const mpt::ustring &name, UNOTEINDEXTYPE groupsize, RATIOTYPE groupratio, USTEPINDEXTYPE finestepcount) { - CTuningRTI *pT = new CTuningRTI(); + std::unique_ptr pT = std::unique_ptr(new CTuning()); pT->SetName(name); - if(pT->CreateGeometric(groupsize, groupratio) != false) + if(!pT->CreateGeometric(groupsize, groupratio)) { - delete pT; return nullptr; } pT->SetFineStepCount(finestepcount); @@ -174,54 +173,55 @@ public: bool ChangeGroupsize(const NOTEINDEXTYPE&); bool ChangeGroupRatio(const RATIOTYPE&); - void SetName(const std::string& s) { m_TuningName = s; } - std::string GetName() const {return m_TuningName;} - -private: - - CTuningRTI(); - - SerializationResult InitDeserialize(std::istream& inStrm); - - //Try to read old version (v.3) and return pointer to new instance if succesfull, else nullptr. - SerializationResult InitDeserializeOLD(std::istream&); - - //Create GroupGeometric tuning of *this using virtual ProCreateGroupGeometric. - bool CreateGroupGeometric(const std::vector&, const RATIOTYPE&, const VRPAIR vr, const NOTEINDEXTYPE ratiostartpos); - - //Create GroupGeometric of *this using ratios from 'itself' and ratios starting from - //position given as third argument. - bool CreateGroupGeometric(const NOTEINDEXTYPE&, const RATIOTYPE&, const NOTEINDEXTYPE&); - - //Create geometric tuning of *this using ratio(0) = 1. - bool CreateGeometric(const UNOTEINDEXTYPE& p, const RATIOTYPE& r) {return CreateGeometric(p,r,GetValidityRange());} - bool CreateGeometric(const UNOTEINDEXTYPE&, const RATIOTYPE&, const VRPAIR vr); - - //The two methods below return false if action was done, true otherwise. - bool ProCreateGroupGeometric(const std::vector&, const RATIOTYPE&, const VRPAIR&, const NOTEINDEXTYPE& ratiostartpos); - bool ProCreateGeometric(const UNOTEINDEXTYPE&, const RATIOTYPE&, const VRPAIR&); - - void UpdateFineStepTable(); - - //Note: Stepdiff should be in range [1, finestepcount] - RATIOTYPE GetRatioFine(const NOTEINDEXTYPE& note, USTEPINDEXTYPE stepDiff) const; - - //GroupPeriodic-specific. - //Get the corresponding note in [0, period-1]. - //For example GetRefNote(-1) is to return note :'groupsize-1'. - NOTEINDEXTYPE GetRefNote(NOTEINDEXTYPE note) const; - - bool IsNoteInTable(const NOTEINDEXTYPE& s) const + void SetName(const mpt::ustring &s) { - if(s < m_StepMin || s >= m_StepMin + static_cast(m_RatioTable.size())) - return false; - else - return true; + m_TuningName = s; + } + + mpt::ustring GetName() const + { + return m_TuningName; } private: - TUNINGTYPE m_TuningType; + CTuning(); + + SerializationResult InitDeserialize(std::istream &inStrm, mpt::Charset defaultCharset); + + //Try to read old version (v.3) and return pointer to new instance if succesfull, else nullptr. + SerializationResult InitDeserializeOLD(std::istream &inStrm, mpt::Charset defaultCharset); + + //Create GroupGeometric tuning of *this using virtual ProCreateGroupGeometric. + bool CreateGroupGeometric(const std::vector &v, const RATIOTYPE &r, const NoteRange &range, const NOTEINDEXTYPE &ratiostartpos); + + //Create GroupGeometric of *this using ratios from 'itself' and ratios starting from + //position given as third argument. + bool CreateGroupGeometric(const NOTEINDEXTYPE &s, const RATIOTYPE &r, const NOTEINDEXTYPE &startindex); + + //Create geometric tuning of *this using ratio(0) = 1. + bool CreateGeometric(const UNOTEINDEXTYPE &p, const RATIOTYPE &r); + bool CreateGeometric(const UNOTEINDEXTYPE &s, const RATIOTYPE &r, const NoteRange &range); + + void UpdateFineStepTable(); + + // GroupPeriodic-specific. + // Get the corresponding note in [0, period-1]. + // For example GetRefNote(-1) is to return note :'groupsize-1'. + MPT_FORCEINLINE NOTEINDEXTYPE GetRefNote(NOTEINDEXTYPE note) const + { + MPT_ASSERT(GetType() == Type::GROUPGEOMETRIC || GetType() == Type::GEOMETRIC); + return static_cast(mpt::wrapping_modulo(note, GetGroupSize())); + } + + static bool IsValidRatio(RATIOTYPE ratio) + { + return (ratio > static_cast(0.0)); + } + +private: + + Tuning::Type m_TuningType; //Noteratios std::vector m_RatioTable; @@ -229,24 +229,21 @@ private: //'Fineratios' std::vector m_RatioTableFine; - //The lowest index of note in the table - NOTEINDEXTYPE m_StepMin; // this should REALLY be called 'm_NoteMin' renaming was missed in r192 + // The lowest index of note in the table + NOTEINDEXTYPE m_NoteMin; //For groupgeometric tunings, tells the 'group size' and 'group ratio' //m_GroupSize should always be >= 0. NOTEINDEXTYPE m_GroupSize; RATIOTYPE m_GroupRatio; - USTEPINDEXTYPE m_FineStepCount; + USTEPINDEXTYPE m_FineStepCount; // invariant: 0 <= m_FineStepCount <= FINESTEPCOUNT_MAX - std::string m_TuningName; + mpt::ustring m_TuningName; - std::map m_NoteNameMap; + std::map m_NoteNameMap; -}; // class CTuningRTI - - -typedef CTuningRTI CTuning; +}; // class CTuning } // namespace Tuning diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningCollection.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningCollection.cpp index c8027059b..6fa7eb4bb 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningCollection.cpp +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningCollection.cpp @@ -34,21 +34,19 @@ Version history: */ -const char CTuningCollection::s_FileExtension[4] = ".tc"; - - namespace CTuningS11n { - void ReadStr(std::istream& iStrm, std::string& str, const size_t); - void WriteStr(std::ostream& oStrm, const std::string& str); + void ReadStr(std::istream &iStrm, mpt::ustring &ustr, const std::size_t dummy, mpt::Charset charset); + void WriteStr(std::ostream &oStrm, const mpt::ustring &ustr); } // namespace CTuningS11n using namespace CTuningS11n; -static void ReadTuning(std::istream& iStrm, CTuningCollection& Tc, const size_t) +static void ReadTuning(std::istream &iStrm, CTuningCollection &Tc, const std::size_t dummy, mpt::Charset defaultCharset) { - Tc.AddTuning(iStrm); + MPT_UNREFERENCED_PARAMETER(dummy); + Tc.AddTuning(iStrm, defaultCharset); } static void WriteTuning(std::ostream& oStrm, const CTuning& t) @@ -57,7 +55,7 @@ static void WriteTuning(std::ostream& oStrm, const CTuning& t) } -CTuning* CTuningCollection::GetTuning(const std::string& name) +CTuning* CTuningCollection::GetTuning(const mpt::ustring &name) { for(std::size_t i = 0; i(inStrm, name, 256)) return Tuning::SerializationResult::Failure; + uname = mpt::ToUnicode(defaultCharset, name); } else { + std::string name; if(!mpt::IO::ReadSizedStringLE(inStrm, name)) return Tuning::SerializationResult::Failure; + uname = mpt::ToUnicode(defaultCharset, name); } //4. Editmask @@ -181,8 +187,10 @@ Tuning::SerializationResult CTuningCollection::DeserializeOLD(std::istream& inSt return Tuning::SerializationResult::Failure; for(size_t i = 0; i pT) { if(m_Tunings.size() >= s_nMaxTuningCount) - return true; - - if(pT == NULL) - return true; - - m_Tunings.push_back(std::unique_ptr(pT)); - - return false; + { + return nullptr; + } + if(!pT) + { + return nullptr; + } + CTuning *result = pT.get(); + m_Tunings.push_back(std::move(pT)); + return result; } -bool CTuningCollection::AddTuning(std::istream& inStrm) +CTuning* CTuningCollection::AddTuning(std::istream &inStrm, mpt::Charset defaultCharset) { if(m_Tunings.size() >= s_nMaxTuningCount) - return true; - - if(!inStrm.good()) return true; - - CTuning* pT = CTuning::CreateDeserializeOLD(inStrm); - if(pT == 0) pT = CTuning::CreateDeserialize(inStrm); - - if(pT == 0) - return true; - else { - m_Tunings.push_back(std::unique_ptr(pT)); - return false; + return nullptr; } + if(!inStrm.good()) + { + return nullptr; + } + std::unique_ptr pT = CTuning::CreateDeserializeOLD(inStrm, defaultCharset); + if(!pT) + { + pT = CTuning::CreateDeserialize(inStrm, defaultCharset); + } + if(!pT) + { + return nullptr; + } + CTuning *result = pT.get(); + m_Tunings.push_back(std::move(pT)); + return result; } @@ -271,7 +286,7 @@ bool UnpackTuningCollection(const CTuningCollection &tc, const mpt::PathString & const CTuning & tuning = tc.GetTuning(i); mpt::PathString fn; fn += prefix; - mpt::ustring tuningName = mpt::ToUnicode(mpt::CharsetLocale, tuning.GetName()); + mpt::ustring tuningName = tuning.GetName(); if(tuningName.empty()) { tuningName = U_("untitled"); diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.cpp b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.cpp deleted file mode 100644 index 58428acf1..000000000 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - * tuningbase.cpp - * -------------- - * Purpose: Alternative sample tuning. - * 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 "tuningbase.h" -#include "tuning.h" -#include "../common/mptIO.h" -#include "../common/serialization_utils.h" - -#include - -#include -#include - - -OPENMPT_NAMESPACE_BEGIN - - -namespace Tuning { - - -/* -Version history: - 4->5: Lots of changes, finestep interpretation revamp, fileformat revamp. - 3->4: Changed sizetypes in serialisation from size_t(uint32) to - smaller types (uint8, USTEPTYPE) (March 2007) -*/ - - - -const char CTuningRTI::s_FileExtension[5] = ".tun"; - - - -void CTuningRTI::SetNoteName(const NOTEINDEXTYPE& n, const std::string& str) -{ - if(!str.empty()) - { - m_NoteNameMap[n] = str; - } else - { - const auto iter = m_NoteNameMap.find(n); - if(iter != m_NoteNameMap.end()) - { - m_NoteNameMap.erase(iter); - } - } -} - - - -bool CTuningRTI::Multiply(const RATIOTYPE& r) -{ - if(r <= 0) - return true; - - //Note: Multiplying ratios by constant doesn't - //change, e.g. 'geometricness' status. - for(auto & ratio : m_RatioTable) - { - ratio *= r; - } - return false; -} - - -bool CTuningRTI::CreateGroupGeometric(const NOTEINDEXTYPE& s, const RATIOTYPE& r, const NOTEINDEXTYPE& startindex) -{ - if(s < 1 || r <= 0 || startindex < GetValidityRange().first) - return true; - - std::vector v; - v.reserve(s); - for(NOTEINDEXTYPE i = startindex; i& v, const RATIOTYPE& r, const VRPAIR vr, const NOTEINDEXTYPE ratiostartpos) -{ - { - if(vr.first > vr.second || v.size() == 0) return true; - if(ratiostartpos < vr.first || vr.second < ratiostartpos || static_cast(vr.second - ratiostartpos) < static_cast(v.size() - 1)) return true; - if(GetFineStepCount() > FINESTEPCOUNT_MAX) return true; - for(size_t i = 0; i vr.second) return true; - if(s < 1 || r <= 0) return true; - if(ProCreateGeometric(s,r,vr)) - return true; - else - { - m_TuningType = TT_GEOMETRIC; - UpdateFineStepTable(); - return false; - } - } -} - - - - -bool CTuningRTI::ChangeGroupsize(const NOTEINDEXTYPE& s) -{ - if(s < 1) - return true; - - if(m_TuningType == TT_GROUPGEOMETRIC) - return CreateGroupGeometric(s, GetGroupRatio(), 0); - - if(m_TuningType == TT_GEOMETRIC) - return CreateGeometric(s, GetGroupRatio()); - - return true; -} - - - -bool CTuningRTI::ChangeGroupRatio(const RATIOTYPE& r) -{ - if(r <= 0) - return true; - - if(m_TuningType == TT_GROUPGEOMETRIC) - return CreateGroupGeometric(GetGroupSize(), r, 0); - - if(m_TuningType == TT_GEOMETRIC) - return CreateGeometric(GetGroupSize(), r); - - return true; -} - - -} // namespace Tuning - - -OPENMPT_NAMESPACE_END diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.h b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.h index 14654566c..c245fcdad 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningbase.h @@ -29,40 +29,46 @@ enum class SerializationResult : int { }; - //NOTEINDEXTYPE: Some signed integer-type. - //UNOTEINDEXTYPE: Unsigned NOTEINDEXTYPE - //RATIOTYPE: Some 'real figure' type able to present ratios. - //STEPINDEXTYPE: Counter of steps between notes. If there is no 'finetune'(finestepcount == 0), - //then 'step difference' between notes is the - //same as differences in NOTEINDEXTYPE. In a way similar to ticks and rows in pattern - - //ticks <-> STEPINDEX, rows <-> NOTEINDEX +using NOTEINDEXTYPE = int16; // Some signed integer-type. +using UNOTEINDEXTYPE = uint16; // Unsigned NOTEINDEXTYPE. - typedef int16 NOTEINDEXTYPE; - typedef uint16 UNOTEINDEXTYPE; - typedef float32 RATIOTYPE; //If changing RATIOTYPE, serialization methods may need modifications. - typedef int32 STEPINDEXTYPE; - typedef uint32 USTEPINDEXTYPE; +using RATIOTYPE = float32; // Some 'real figure' type able to present ratios. If changing RATIOTYPE, serialization methods may need modifications. - typedef std::pair VRPAIR; +// Counter of steps between notes. If there is no 'finetune'(finestepcount == 0), +// then 'step difference' between notes is the +// same as differences in NOTEINDEXTYPE. In a way similar to ticks and rows in pattern - +// ticks <-> STEPINDEX, rows <-> NOTEINDEX +using STEPINDEXTYPE = int32; +using USTEPINDEXTYPE = uint32; - typedef uint16 TUNINGTYPE; +struct NoteRange +{ + NOTEINDEXTYPE first; + NOTEINDEXTYPE last; +}; // Derived from old IsStepCountRangeSufficient(), this is actually a more // sensible value than what was calculated in earlier versions. -static MPT_CONSTEXPR11_VAR STEPINDEXTYPE FINESTEPCOUNT_MAX = 0xffff; +static constexpr STEPINDEXTYPE FINESTEPCOUNT_MAX = 0xffff; + +static constexpr auto NOTEINDEXTYPE_MIN = std::numeric_limits::min(); +static constexpr auto NOTEINDEXTYPE_MAX = std::numeric_limits::max(); +static constexpr auto UNOTEINDEXTYPE_MAX = std::numeric_limits::max(); +static constexpr auto STEPINDEXTYPE_MIN = std::numeric_limits::min(); +static constexpr auto STEPINDEXTYPE_MAX = std::numeric_limits::max(); +static constexpr auto USTEPINDEXTYPE_MAX = std::numeric_limits::max(); -#define NOTEINDEXTYPE_MIN (std::numeric_limits::min)() -#define NOTEINDEXTYPE_MAX (std::numeric_limits::max)() -#define UNOTEINDEXTYPE_MAX (std::numeric_limits::max)() -#define STEPINDEXTYPE_MIN (std::numeric_limits::min)() -#define STEPINDEXTYPE_MAX (std::numeric_limits::max)() -#define USTEPINDEXTYPE_MAX (std::numeric_limits::max)() +enum class Type : uint16 +{ + GENERAL = 0, + GROUPGEOMETRIC = 1, + GEOMETRIC = 3, +}; -class CTuningRTI; -typedef CTuningRTI CTuning; +class CTuning; } // namespace Tuning diff --git a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningcollection.h b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningcollection.h index 7e388f4fa..91b81980b 100644 --- a/Frameworks/OpenMPT/OpenMPT/soundlib/tuningcollection.h +++ b/Frameworks/OpenMPT/OpenMPT/soundlib/tuningcollection.h @@ -28,7 +28,7 @@ class CTuningCollection public: - static const char s_FileExtension[4]; + static constexpr char s_FileExtension[4] = ".tc"; // OpenMPT <= 1.26 had to following limits: // * 255 built-in tunings (only 2 were ever actually provided) @@ -44,23 +44,29 @@ public: public: - //Note: Given pointer is deleted by CTuningCollection - //at some point. - bool AddTuning(CTuning *pT); - bool AddTuning(std::istream& inStrm); + // returns observer ptr if successfull + CTuning* AddTuning(std::unique_ptr pT); + CTuning* AddTuning(std::istream &inStrm, mpt::Charset defaultCharset); bool Remove(const std::size_t i); bool Remove(const CTuning *pT); CTuning& GetTuning(size_t i) {return *m_Tunings.at(i).get();} const CTuning& GetTuning(size_t i) const {return *m_Tunings.at(i).get();} - CTuning* GetTuning(const std::string& name); - const CTuning* GetTuning(const std::string& name) const; + CTuning* GetTuning(const mpt::ustring &name); + const CTuning* GetTuning(const mpt::ustring &name) const; size_t GetNumTunings() const {return m_Tunings.size();} - Tuning::SerializationResult Serialize(std::ostream&, const std::string &name) const; - Tuning::SerializationResult Deserialize(std::istream&, std::string &name); + Tuning::SerializationResult Serialize(std::ostream &oStrm, const mpt::ustring &name) const; + Tuning::SerializationResult Deserialize(std::istream &iStrm, mpt::ustring &name, mpt::Charset defaultCharset); + + auto begin() { return m_Tunings.begin(); } + auto begin() const { return m_Tunings.begin(); } + auto cbegin() { return m_Tunings.cbegin(); } + auto end() { return m_Tunings.end(); } + auto end() const { return m_Tunings.end(); } + auto cend() { return m_Tunings.cend(); } private: @@ -68,7 +74,7 @@ private: private: - Tuning::SerializationResult DeserializeOLD(std::istream&, std::string &name); + Tuning::SerializationResult DeserializeOLD(std::istream &inStrm, mpt::ustring &uname, mpt::Charset defaultCharset); }; diff --git a/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.cpp b/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.cpp index 9a3caac11..98b7555c5 100644 --- a/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.cpp +++ b/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.cpp @@ -197,12 +197,12 @@ MPT_NOINLINE void AssertHandler(const mpt::source_location &loc, const char *exp if(msg) { mpt::log::Logger().SendLogMessage(loc, LogError, "ASSERT", - U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetASCII, msg) + U_(" (") + mpt::ToUnicode(mpt::CharsetASCII, expr) + U_(")") + U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::Charset::ASCII, msg) + U_(" (") + mpt::ToUnicode(mpt::Charset::ASCII, expr) + U_(")") ); } else { mpt::log::Logger().SendLogMessage(loc, LogError, "ASSERT", - U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::CharsetASCII, expr) + U_("ASSERTION FAILED: ") + mpt::ToUnicode(mpt::Charset::ASCII, expr) ); } #if defined(MPT_BUILD_FATAL_ASSERTS) diff --git a/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.h b/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.h index 8202fceea..1c49ca33b 100644 --- a/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.h +++ b/Frameworks/OpenMPT/OpenMPT/test/TestToolsLib.h @@ -190,7 +190,7 @@ private: template inline bool IsEqualEpsilon(const Tx &x, const Ty &y, const Teps &eps) { - return mpt::abs(x - y) <= eps; + return std::abs(x - y) <= eps; } public: diff --git a/Frameworks/OpenMPT/OpenMPT/test/TestToolsTracker.h b/Frameworks/OpenMPT/OpenMPT/test/TestToolsTracker.h index 978efa508..493dacefb 100644 --- a/Frameworks/OpenMPT/OpenMPT/test/TestToolsTracker.h +++ b/Frameworks/OpenMPT/OpenMPT/test/TestToolsTracker.h @@ -50,7 +50,7 @@ namespace Test { #define VERIFY_EQUAL_EPS(x,y,eps) \ MPT_DO { \ - if(mpt::abs((x) - (y)) > (eps)) { \ + if(std::abs((x) - (y)) > (eps)) { \ MyDebugBreak(); \ } \ } MPT_WHILE_0 \ diff --git a/Frameworks/OpenMPT/OpenMPT/test/test.cpp b/Frameworks/OpenMPT/OpenMPT/test/test.cpp index 39b2c2da3..f7014d89b 100644 --- a/Frameworks/OpenMPT/OpenMPT/test/test.cpp +++ b/Frameworks/OpenMPT/OpenMPT/test/test.cpp @@ -32,7 +32,7 @@ #include "../soundlib/ITCompression.h" #include "../soundlib/tuningcollection.h" #include "../soundlib/tuning.h" -#include "../soundlib/Dither.h" +#include "../soundbase/Dither.h" #ifdef MODPLUG_TRACKER #include "../mptrack/Mptrack.h" #include "../mptrack/Moddoc.h" @@ -48,7 +48,7 @@ #ifndef NO_PLUGINS #include "../soundlib/plugins/PlugInterface.h" #endif -#include "../common/mptBufferIO.h" +#include #include #ifdef LIBOPENMPT_BUILD #include @@ -75,7 +75,7 @@ #define MPT_TEST_HAS_FILESYSTEM 0 #endif -#if MPT_COMPILER_MSVC && defined(_MFC_VER) && defined(_DEBUG) +#if MPT_COMPILER_MSVC && defined(MPT_WITH_MFC) && defined(_DEBUG) #define new DEBUG_NEW #endif @@ -140,12 +140,12 @@ void DoTests() std::cout << "libopenmpt test suite" << std::endl; - std::cout << "Version.: " << mpt::ToCharset(mpt::CharsetASCII, Build::GetVersionString(Build::StringVersion | Build::StringRevision | Build::StringBitness | Build::StringSourceInfo | Build::StringBuildFlags | Build::StringBuildFeatures)) << std::endl; - std::cout << "Compiler: " << mpt::ToCharset(mpt::CharsetASCII, Build::GetBuildCompilerString()) << std::endl; + std::cout << "Version.: " << mpt::ToCharset(mpt::Charset::ASCII, Build::GetVersionString(Build::StringVersion | Build::StringRevision | Build::StringBitness | Build::StringSourceInfo | Build::StringBuildFlags | Build::StringBuildFeatures)) << std::endl; + std::cout << "Compiler: " << mpt::ToCharset(mpt::Charset::ASCII, Build::GetBuildCompilerString()) << std::endl; #if MPT_OS_WINDOWS - std::cout << "Required Windows Kernel Level: " << mpt::ToCharset(mpt::CharsetASCII, mpt::Windows::Version::VersionToString(mpt::Windows::Version::GetMinimumKernelLevel())) << std::endl; - std::cout << "Required Windows API Level...: " << mpt::ToCharset(mpt::CharsetASCII, mpt::Windows::Version::VersionToString(mpt::Windows::Version::GetMinimumAPILevel())) << std::endl; - std::cout << "Windows.: " << mpt::ToCharset(mpt::CharsetASCII, mpt::Windows::Version::Current().GetName()) << std::endl; + std::cout << "Required Windows Kernel Level: " << mpt::ToCharset(mpt::Charset::ASCII, mpt::Windows::Version::VersionToString(mpt::Windows::Version::GetMinimumKernelLevel())) << std::endl; + std::cout << "Required Windows API Level...: " << mpt::ToCharset(mpt::Charset::ASCII, mpt::Windows::Version::VersionToString(mpt::Windows::Version::GetMinimumAPILevel())) << std::endl; + std::cout << "Windows.: " << mpt::ToCharset(mpt::Charset::ASCII, mpt::Windows::Version::Current().GetName()) << std::endl; #endif std::cout << std::flush; @@ -255,24 +255,28 @@ static MPT_NOINLINE void TestVersion() VERIFY_EQUAL( Version::Parse(U_("1.fe.02.28")), Version(0x01fe0228) ); VERIFY_EQUAL( Version::Parse(U_("01.fe.02.28")), Version(0x01fe0228) ); VERIFY_EQUAL( Version::Parse(U_("1.22")), Version(0x01220000) ); - VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,19,02,00).WithoutTestNumber(), MAKE_VERSION_NUMERIC(1,19,02,00)); - VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,18,03,20).WithoutTestNumber(), MAKE_VERSION_NUMERIC(1,18,03,00)); - VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,18,01,13).IsTestVersion(), true); - VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,19,01,00).IsTestVersion(), false); - VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,17,02,54).IsTestVersion(), false); - VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,18,00,00).IsTestVersion(), false); - VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,18,02,00).IsTestVersion(), false); - VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,18,02,01).IsTestVersion(), true); + VERIFY_EQUAL( MPT_V("1.17.02.28"), Version(18285096) ); + VERIFY_EQUAL( MPT_V("1.fe.02.28"), Version(0x01fe0228) ); + VERIFY_EQUAL( MPT_V("01.fe.02.28"), Version(0x01fe0228) ); + VERIFY_EQUAL( MPT_V("1.22"), Version(0x01220000) ); + VERIFY_EQUAL( MPT_V("1.19.02.00").WithoutTestNumber(), MPT_V("1.19.02.00")); + VERIFY_EQUAL( MPT_V("1.18.03.20").WithoutTestNumber(), MPT_V("1.18.03.00")); + VERIFY_EQUAL( MPT_V("1.18.01.13").IsTestVersion(), true); + VERIFY_EQUAL( MPT_V("1.19.01.00").IsTestVersion(), false); + VERIFY_EQUAL( MPT_V("1.17.02.54").IsTestVersion(), false); + VERIFY_EQUAL( MPT_V("1.18.00.00").IsTestVersion(), false); + VERIFY_EQUAL( MPT_V("1.18.02.00").IsTestVersion(), false); + VERIFY_EQUAL( MPT_V("1.18.02.01").IsTestVersion(), true); // Ensure that versions ending in .00.00 (which are ambiguous to truncated version numbers in certain file formats (e.g. S3M and IT) do not get qualified as test builds. - VERIFY_EQUAL( MAKE_VERSION_NUMERIC(1,23,00,00).IsTestVersion(), false); + VERIFY_EQUAL( MPT_V("1.23.00.00").IsTestVersion(), false); - STATIC_ASSERT( MAKE_VERSION_NUMERIC(1,17,2,28).GetRawVersion() == 18285096 ); - STATIC_ASSERT( MAKE_VERSION_NUMERIC(1,17,02,48).GetRawVersion() == 18285128 ); - STATIC_ASSERT( MAKE_VERSION_NUMERIC(01,17,02,52).GetRawVersion() == 18285138 ); + static_assert( MPT_V("1.17.2.28").GetRawVersion() == 18285096 ); + static_assert( MPT_V("1.17.02.48").GetRawVersion() == 18285128 ); + static_assert( MPT_V("01.17.02.52").GetRawVersion() == 18285138 ); // Ensure that bit-shifting works (used in some mod loaders for example) - STATIC_ASSERT( MAKE_VERSION_NUMERIC(01,17,00,00).GetRawVersion() == 0x0117 << 16 ); - STATIC_ASSERT( MAKE_VERSION_NUMERIC(01,17,03,00).GetRawVersion() >> 8 == 0x011703 ); + static_assert( MPT_V("01.17.00.00").GetRawVersion() == 0x0117 << 16 ); + static_assert( MPT_V("01.17.03.00").GetRawVersion() >> 8 == 0x011703 ); } #ifdef MODPLUG_TRACKER @@ -284,7 +288,7 @@ static MPT_NOINLINE void TestVersion() DWORD dwVerInfoSize; // Get version information from the application - ::GetModuleFileNameW(NULL, szFullPath, mpt::saturate_cast(mpt::size(szFullPath))); + ::GetModuleFileNameW(NULL, szFullPath, mpt::saturate_cast(std::size(szFullPath))); dwVerInfoSize = ::GetFileVersionInfoSizeW(szFullPath, &dwVerHnd); if (!dwVerInfoSize) throw std::runtime_error("!dwVerInfoSize is true"); @@ -356,10 +360,10 @@ static MPT_NOINLINE void TestVersion() static MPT_NOINLINE void TestTypes() { - MPT_STATIC_ASSERT(sizeof(std::uintptr_t) == sizeof(void*)); + static_assert(sizeof(std::uintptr_t) == sizeof(void*)); #if defined(__SIZEOF_POINTER__) - MPT_STATIC_ASSERT(__SIZEOF_POINTER__ == mpt::pointer_size); - MPT_STATIC_ASSERT(__SIZEOF_POINTER__ == sizeof(void*)); + static_assert(__SIZEOF_POINTER__ == mpt::pointer_size); + static_assert(__SIZEOF_POINTER__ == sizeof(void*)); #endif VERIFY_EQUAL(int8_min, (std::numeric_limits::min)()); @@ -379,54 +383,54 @@ static MPT_NOINLINE void TestTypes() VERIFY_EQUAL(uint64_max, (std::numeric_limits::max)()); - STATIC_ASSERT(int8_max == (std::numeric_limits::max)()); - STATIC_ASSERT(uint8_max == (std::numeric_limits::max)()); + static_assert(int8_max == (std::numeric_limits::max)()); + static_assert(uint8_max == (std::numeric_limits::max)()); - STATIC_ASSERT(int16_max == (std::numeric_limits::max)()); - STATIC_ASSERT(uint16_max == (std::numeric_limits::max)()); + static_assert(int16_max == (std::numeric_limits::max)()); + static_assert(uint16_max == (std::numeric_limits::max)()); - STATIC_ASSERT(int32_max == (std::numeric_limits::max)()); - STATIC_ASSERT(uint32_max == (std::numeric_limits::max)()); + static_assert(int32_max == (std::numeric_limits::max)()); + static_assert(uint32_max == (std::numeric_limits::max)()); - STATIC_ASSERT(int64_max == (std::numeric_limits::max)()); - STATIC_ASSERT(uint64_max == (std::numeric_limits::max)()); + static_assert(int64_max == (std::numeric_limits::max)()); + static_assert(uint64_max == (std::numeric_limits::max)()); - STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); - STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + static_assert((mpt::limits::max)() == (std::numeric_limits::max)()); + static_assert((mpt::limits::max)() == (std::numeric_limits::max)()); - STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); - STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + static_assert((mpt::limits::max)() == (std::numeric_limits::max)()); + static_assert((mpt::limits::max)() == (std::numeric_limits::max)()); - STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); - STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + static_assert((mpt::limits::max)() == (std::numeric_limits::max)()); + static_assert((mpt::limits::max)() == (std::numeric_limits::max)()); - STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); - STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + static_assert((mpt::limits::max)() == (std::numeric_limits::max)()); + static_assert((mpt::limits::max)() == (std::numeric_limits::max)()); - STATIC_ASSERT((mpt::limits::min)() == (std::numeric_limits::min)()); - STATIC_ASSERT((mpt::limits::min)() == (std::numeric_limits::min)()); + static_assert((mpt::limits::min)() == (std::numeric_limits::min)()); + static_assert((mpt::limits::min)() == (std::numeric_limits::min)()); - STATIC_ASSERT((mpt::limits::min)() == (std::numeric_limits::min)()); - STATIC_ASSERT((mpt::limits::min)() == (std::numeric_limits::min)()); + static_assert((mpt::limits::min)() == (std::numeric_limits::min)()); + static_assert((mpt::limits::min)() == (std::numeric_limits::min)()); - STATIC_ASSERT((mpt::limits::min)() == (std::numeric_limits::min)()); - STATIC_ASSERT((mpt::limits::min)() == (std::numeric_limits::min)()); + static_assert((mpt::limits::min)() == (std::numeric_limits::min)()); + static_assert((mpt::limits::min)() == (std::numeric_limits::min)()); - STATIC_ASSERT((mpt::limits::min)() == (std::numeric_limits::min)()); - STATIC_ASSERT((mpt::limits::min)() == (std::numeric_limits::min)()); + static_assert((mpt::limits::min)() == (std::numeric_limits::min)()); + static_assert((mpt::limits::min)() == (std::numeric_limits::min)()); - STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); - STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + static_assert((mpt::limits::max)() == (std::numeric_limits::max)()); + static_assert((mpt::limits::max)() == (std::numeric_limits::max)()); - STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); - STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + static_assert((mpt::limits::max)() == (std::numeric_limits::max)()); + static_assert((mpt::limits::max)() == (std::numeric_limits::max)()); - STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); - STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + static_assert((mpt::limits::max)() == (std::numeric_limits::max)()); + static_assert((mpt::limits::max)() == (std::numeric_limits::max)()); - STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); - STATIC_ASSERT((mpt::limits::max)() == (std::numeric_limits::max)()); + static_assert((mpt::limits::max)() == (std::numeric_limits::max)()); + static_assert((mpt::limits::max)() == (std::numeric_limits::max)()); } @@ -623,12 +627,12 @@ static MPT_NOINLINE void TestStringFormatting() VERIFY_EQUAL(mpt::fmt::center(3, "a"), " a "); VERIFY_EQUAL(mpt::fmt::center(4, "a"), " a "); - #if defined(_MFC_VER) + #if defined(MPT_WITH_MFC) VERIFY_EQUAL(mpt::cfmt::left(3, CString(_T("a"))), CString(_T("a "))); VERIFY_EQUAL(mpt::cfmt::right(3, CString(_T("a"))), CString(_T(" a"))); VERIFY_EQUAL(mpt::cfmt::center(3, CString(_T("a"))), CString(_T(" a "))); VERIFY_EQUAL(mpt::cfmt::center(4, CString(_T("a"))), CString(_T(" a "))); - #endif + #endif // MPT_WITH_MFC VERIFY_EQUAL(ConvertStrTo("586"), 586u); VERIFY_EQUAL(ConvertStrTo("2147483647"), (uint32)int32_max); @@ -725,12 +729,12 @@ static MPT_NOINLINE void TestStringFormatting() VERIFY_EQUAL(mpt::format("%%%1")("a"), "%a"); VERIFY_EQUAL(mpt::format("%b")("a"), "%b"); -#if defined(_MFC_VER) +#if defined(MPT_WITH_MFC) VERIFY_EQUAL(mpt::ufmt::val(CString(_T("foobar"))), U_("foobar")); VERIFY_EQUAL(mpt::ufmt::val(CString(_T("foobar"))), U_("foobar")); VERIFY_EQUAL(mpt::format(CString(_T("%1%2%3")))(1,2,3), _T("123")); VERIFY_EQUAL(mpt::format(CString(_T("%1%2%3")))(1,mpt::cfmt::dec0<3>(2),3), _T("10023")); -#endif +#endif // MPT_WITH_MFC } @@ -776,14 +780,12 @@ Gregorian TestDate2(int s, int m, int h, int D, int M, int Y) { return Gregorian{Y,M,D,h,m,s}; } -#if MPT_ENDIAN_IS_CONSTEXPR -static constexpr int32le TestEndianConstexpr(uint32 x) +static MPT_CONSTEXPR20_FUN int32le TestEndianConstexpr(uint32 x) { - int32le foo; + int32le foo{}; foo = x; return foo; } -#endif static MPT_NOINLINE void TestMisc1() { @@ -791,24 +793,31 @@ static MPT_NOINLINE void TestMisc1() #if MPT_CXX_BEFORE(20) VERIFY_EQUAL(mpt::get_endian(), mpt::detail::endian_probe()); #endif - #if MPT_PLATFORM_ENDIAN_KNOWN && defined(MPT_PLATFORM_LITTLE_ENDIAN) + MPT_MAYBE_CONSTANT_IF(mpt::endian_is_little()) + { VERIFY_EQUAL(mpt::get_endian(), mpt::endian::little); - VERIFY_EQUAL(mpt::endian::native, mpt::endian::little); + MPT_MAYBE_CONSTANT_IF((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) + { + VERIFY_EQUAL(mpt::endian::native, mpt::endian::little); + } #if MPT_CXX_BEFORE(20) VERIFY_EQUAL(mpt::detail::endian_probe(), mpt::endian::little); #endif - #elif MPT_PLATFORM_ENDIAN_KNOWN && defined(MPT_PLATFORM_BIG_ENDIAN) + } + MPT_MAYBE_CONSTANT_IF(mpt::endian_is_big()) + { VERIFY_EQUAL(mpt::get_endian(), mpt::endian::big); - VERIFY_EQUAL(mpt::endian::native, mpt::endian::big); + MPT_MAYBE_CONSTANT_IF((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) + { + VERIFY_EQUAL(mpt::endian::native, mpt::endian::big); + } #if MPT_CXX_BEFORE(20) VERIFY_EQUAL(mpt::detail::endian_probe(), mpt::endian::big); #endif - #endif + } -#if MPT_ENDIAN_IS_CONSTEXPR - constexpr int32le foo = TestEndianConstexpr(23); - (void)foo; -#endif + MPT_CONSTEXPR20_VAR int32le foo = TestEndianConstexpr(23); + static_cast(foo); VERIFY_EQUAL(mpt::detail::SwapBytes(uint8(0x12)), 0x12); VERIFY_EQUAL(mpt::detail::SwapBytes(uint16(0x1234)), 0x3412); @@ -918,75 +927,75 @@ static MPT_NOINLINE void TestMisc1() VERIFY_EQUAL( mpt::saturate_round(110.1), 110 ); VERIFY_EQUAL( mpt::saturate_round(-110.1), -110 ); - VERIFY_EQUAL(mpt::weight(int32(-1)), 32); - VERIFY_EQUAL(mpt::weight(0), 0); - VERIFY_EQUAL(mpt::weight(1), 1); - VERIFY_EQUAL(mpt::weight(2), 1); - VERIFY_EQUAL(mpt::weight(3), 2); + VERIFY_EQUAL(mpt::popcount(static_cast(int32(-1))), 32); + VERIFY_EQUAL(mpt::popcount(0u), 0); + VERIFY_EQUAL(mpt::popcount(1u), 1); + VERIFY_EQUAL(mpt::popcount(2u), 1); + VERIFY_EQUAL(mpt::popcount(3u), 2); - VERIFY_EQUAL(mpt::ispow2(0u), false); - VERIFY_EQUAL(mpt::ispow2(1u), true); - VERIFY_EQUAL(mpt::ispow2(2u), true); - VERIFY_EQUAL(mpt::ispow2(3u), false); - VERIFY_EQUAL(mpt::ispow2(4u), true); - VERIFY_EQUAL(mpt::ispow2(5u), false); - VERIFY_EQUAL(mpt::ispow2(6u), false); - VERIFY_EQUAL(mpt::ispow2(7u), false); - VERIFY_EQUAL(mpt::ispow2(8u), true); - VERIFY_EQUAL(mpt::ispow2(9u), false); - VERIFY_EQUAL(mpt::ispow2(uint32(0x7fffffffu)), false); - VERIFY_EQUAL(mpt::ispow2(uint32(0x80000000u)), true); - VERIFY_EQUAL(mpt::ispow2(uint32(0x80000001u)), false); - VERIFY_EQUAL(mpt::ispow2(uint32(0xfffffffeu)), false); - VERIFY_EQUAL(mpt::ispow2(uint32(0xffffffffu)), false); + VERIFY_EQUAL(mpt::has_single_bit(0u), false); + VERIFY_EQUAL(mpt::has_single_bit(1u), true); + VERIFY_EQUAL(mpt::has_single_bit(2u), true); + VERIFY_EQUAL(mpt::has_single_bit(3u), false); + VERIFY_EQUAL(mpt::has_single_bit(4u), true); + VERIFY_EQUAL(mpt::has_single_bit(5u), false); + VERIFY_EQUAL(mpt::has_single_bit(6u), false); + VERIFY_EQUAL(mpt::has_single_bit(7u), false); + VERIFY_EQUAL(mpt::has_single_bit(8u), true); + VERIFY_EQUAL(mpt::has_single_bit(9u), false); + VERIFY_EQUAL(mpt::has_single_bit(uint32(0x7fffffffu)), false); + VERIFY_EQUAL(mpt::has_single_bit(uint32(0x80000000u)), true); + VERIFY_EQUAL(mpt::has_single_bit(uint32(0x80000001u)), false); + VERIFY_EQUAL(mpt::has_single_bit(uint32(0xfffffffeu)), false); + VERIFY_EQUAL(mpt::has_single_bit(uint32(0xffffffffu)), false); - VERIFY_EQUAL(mpt::ceil2(0u), 1u); - VERIFY_EQUAL(mpt::ceil2(1u), 1u); - VERIFY_EQUAL(mpt::ceil2(2u), 2u); - VERIFY_EQUAL(mpt::ceil2(3u), 4u); - VERIFY_EQUAL(mpt::ceil2(4u), 4u); - VERIFY_EQUAL(mpt::ceil2(5u), 8u); - VERIFY_EQUAL(mpt::ceil2(6u), 8u); - VERIFY_EQUAL(mpt::ceil2(7u), 8u); - VERIFY_EQUAL(mpt::ceil2(8u), 8u); - VERIFY_EQUAL(mpt::ceil2(9u), 16u); - VERIFY_EQUAL(mpt::ceil2(uint32(0x7fffffffu)), 0x80000000u); - VERIFY_EQUAL(mpt::ceil2(uint32(0x80000000u)), 0x80000000u); - //VERIFY_EQUAL(mpt::ceil2(uint32(0x80000001u)), 0u); - //VERIFY_EQUAL(mpt::ceil2(uint32(0xfffffffeu)), 0u); - //VERIFY_EQUAL(mpt::ceil2(uint32(0xffffffffu)), 0u); + VERIFY_EQUAL(mpt::bit_ceil(0u), 1u); + VERIFY_EQUAL(mpt::bit_ceil(1u), 1u); + VERIFY_EQUAL(mpt::bit_ceil(2u), 2u); + VERIFY_EQUAL(mpt::bit_ceil(3u), 4u); + VERIFY_EQUAL(mpt::bit_ceil(4u), 4u); + VERIFY_EQUAL(mpt::bit_ceil(5u), 8u); + VERIFY_EQUAL(mpt::bit_ceil(6u), 8u); + VERIFY_EQUAL(mpt::bit_ceil(7u), 8u); + VERIFY_EQUAL(mpt::bit_ceil(8u), 8u); + VERIFY_EQUAL(mpt::bit_ceil(9u), 16u); + VERIFY_EQUAL(mpt::bit_ceil(uint32(0x7fffffffu)), 0x80000000u); + VERIFY_EQUAL(mpt::bit_ceil(uint32(0x80000000u)), 0x80000000u); + //VERIFY_EQUAL(mpt::bit_ceil(uint32(0x80000001u)), 0u); + //VERIFY_EQUAL(mpt::bit_ceil(uint32(0xfffffffeu)), 0u); + //VERIFY_EQUAL(mpt::bit_ceil(uint32(0xffffffffu)), 0u); - VERIFY_EQUAL(mpt::floor2(0u), 0u); - VERIFY_EQUAL(mpt::floor2(1u), 1u); - VERIFY_EQUAL(mpt::floor2(2u), 2u); - VERIFY_EQUAL(mpt::floor2(3u), 2u); - VERIFY_EQUAL(mpt::floor2(4u), 4u); - VERIFY_EQUAL(mpt::floor2(5u), 4u); - VERIFY_EQUAL(mpt::floor2(6u), 4u); - VERIFY_EQUAL(mpt::floor2(7u), 4u); - VERIFY_EQUAL(mpt::floor2(8u), 8u); - VERIFY_EQUAL(mpt::floor2(9u), 8u); - VERIFY_EQUAL(mpt::floor2(uint32(0x7fffffffu)), 0x40000000u); - VERIFY_EQUAL(mpt::floor2(uint32(0x80000000u)), 0x80000000u); - VERIFY_EQUAL(mpt::floor2(uint32(0x80000001u)), 0x80000000u); - VERIFY_EQUAL(mpt::floor2(uint32(0xfffffffeu)), 0x80000000u); - VERIFY_EQUAL(mpt::floor2(uint32(0xffffffffu)), 0x80000000u); + VERIFY_EQUAL(mpt::bit_floor(0u), 0u); + VERIFY_EQUAL(mpt::bit_floor(1u), 1u); + VERIFY_EQUAL(mpt::bit_floor(2u), 2u); + VERIFY_EQUAL(mpt::bit_floor(3u), 2u); + VERIFY_EQUAL(mpt::bit_floor(4u), 4u); + VERIFY_EQUAL(mpt::bit_floor(5u), 4u); + VERIFY_EQUAL(mpt::bit_floor(6u), 4u); + VERIFY_EQUAL(mpt::bit_floor(7u), 4u); + VERIFY_EQUAL(mpt::bit_floor(8u), 8u); + VERIFY_EQUAL(mpt::bit_floor(9u), 8u); + VERIFY_EQUAL(mpt::bit_floor(uint32(0x7fffffffu)), 0x40000000u); + VERIFY_EQUAL(mpt::bit_floor(uint32(0x80000000u)), 0x80000000u); + VERIFY_EQUAL(mpt::bit_floor(uint32(0x80000001u)), 0x80000000u); + VERIFY_EQUAL(mpt::bit_floor(uint32(0xfffffffeu)), 0x80000000u); + VERIFY_EQUAL(mpt::bit_floor(uint32(0xffffffffu)), 0x80000000u); - VERIFY_EQUAL(mpt::log2p1(0u), 0u); - VERIFY_EQUAL(mpt::log2p1(1u), 1u); - VERIFY_EQUAL(mpt::log2p1(2u), 2u); - VERIFY_EQUAL(mpt::log2p1(3u), 2u); - VERIFY_EQUAL(mpt::log2p1(4u), 3u); - VERIFY_EQUAL(mpt::log2p1(5u), 3u); - VERIFY_EQUAL(mpt::log2p1(6u), 3u); - VERIFY_EQUAL(mpt::log2p1(7u), 3u); - VERIFY_EQUAL(mpt::log2p1(8u), 4u); - VERIFY_EQUAL(mpt::log2p1(9u), 4u); - VERIFY_EQUAL(mpt::log2p1(uint32(0x7fffffffu)), 31u); - VERIFY_EQUAL(mpt::log2p1(uint32(0x80000000u)), 32u); - VERIFY_EQUAL(mpt::log2p1(uint32(0x80000001u)), 32u); - VERIFY_EQUAL(mpt::log2p1(uint32(0xfffffffeu)), 32u); - VERIFY_EQUAL(mpt::log2p1(uint32(0xffffffffu)), 32u); + VERIFY_EQUAL(mpt::bit_width(0u), 0u); + VERIFY_EQUAL(mpt::bit_width(1u), 1u); + VERIFY_EQUAL(mpt::bit_width(2u), 2u); + VERIFY_EQUAL(mpt::bit_width(3u), 2u); + VERIFY_EQUAL(mpt::bit_width(4u), 3u); + VERIFY_EQUAL(mpt::bit_width(5u), 3u); + VERIFY_EQUAL(mpt::bit_width(6u), 3u); + VERIFY_EQUAL(mpt::bit_width(7u), 3u); + VERIFY_EQUAL(mpt::bit_width(8u), 4u); + VERIFY_EQUAL(mpt::bit_width(9u), 4u); + VERIFY_EQUAL(mpt::bit_width(uint32(0x7fffffffu)), 31u); + VERIFY_EQUAL(mpt::bit_width(uint32(0x80000000u)), 32u); + VERIFY_EQUAL(mpt::bit_width(uint32(0x80000001u)), 32u); + VERIFY_EQUAL(mpt::bit_width(uint32(0xfffffffeu)), 32u); + VERIFY_EQUAL(mpt::bit_width(uint32(0xffffffffu)), 32u); // trivials VERIFY_EQUAL( mpt::saturate_cast(-1), -1 ); @@ -1255,7 +1264,7 @@ static MPT_NOINLINE void TestMisc1() #endif - VERIFY_EQUAL(mpt::rshift_signed(-0x8000000000000000ll, 1), mpt::rshift_signed_standard(-0x8000000000000000ll, 1)); + VERIFY_EQUAL(mpt::rshift_signed(0ull-0x8000000000000000ull, 1), mpt::rshift_signed_standard(0ull-0x8000000000000000ull, 1)); VERIFY_EQUAL(mpt::rshift_signed(-0x7fffffffffffffffll, 1), mpt::rshift_signed_standard(-0x7fffffffffffffffll, 1)); VERIFY_EQUAL(mpt::rshift_signed(-0x7ffffffffffffffell, 1), mpt::rshift_signed_standard(-0x7ffffffffffffffell, 1)); VERIFY_EQUAL(mpt::rshift_signed( -1ll, 1), mpt::rshift_signed_standard( -1ll, 1)); @@ -1264,7 +1273,7 @@ static MPT_NOINLINE void TestMisc1() VERIFY_EQUAL(mpt::rshift_signed( 0x7ffffffffffffffell, 1), mpt::rshift_signed_standard( 0x7ffffffffffffffell, 1)); VERIFY_EQUAL(mpt::rshift_signed( 0x7fffffffffffffffll, 1), mpt::rshift_signed_standard( 0x7fffffffffffffffll, 1)); - VERIFY_EQUAL(mpt::rshift_signed(-0x8000000000000000ll, 63), mpt::rshift_signed_standard(-0x8000000000000000ll, 63)); + VERIFY_EQUAL(mpt::rshift_signed(0ull-0x8000000000000000ull, 63), mpt::rshift_signed_standard(0ull-0x8000000000000000ull, 63)); VERIFY_EQUAL(mpt::rshift_signed(-0x7fffffffffffffffll, 63), mpt::rshift_signed_standard(-0x7fffffffffffffffll, 63)); VERIFY_EQUAL(mpt::rshift_signed(-0x7ffffffffffffffell, 63), mpt::rshift_signed_standard(-0x7ffffffffffffffell, 63)); VERIFY_EQUAL(mpt::rshift_signed( -1ll, 63), mpt::rshift_signed_standard( -1ll, 63)); @@ -1273,7 +1282,7 @@ static MPT_NOINLINE void TestMisc1() VERIFY_EQUAL(mpt::rshift_signed( 0x7ffffffffffffffell, 63), mpt::rshift_signed_standard( 0x7ffffffffffffffell, 63)); VERIFY_EQUAL(mpt::rshift_signed( 0x7fffffffffffffffll, 63), mpt::rshift_signed_standard( 0x7fffffffffffffffll, 63)); - VERIFY_EQUAL(mpt::lshift_signed(-0x8000000000000000ll, 1), mpt::lshift_signed_standard(-0x8000000000000000ll, 1)); + VERIFY_EQUAL(mpt::lshift_signed(0ull-0x8000000000000000ull, 1), mpt::lshift_signed_standard(0ull-0x8000000000000000ull, 1)); VERIFY_EQUAL(mpt::lshift_signed(-0x7fffffffffffffffll, 1), mpt::lshift_signed_standard(-0x7fffffffffffffffll, 1)); VERIFY_EQUAL(mpt::lshift_signed(-0x7ffffffffffffffell, 1), mpt::lshift_signed_standard(-0x7ffffffffffffffell, 1)); VERIFY_EQUAL(mpt::lshift_signed( -1ll, 1), mpt::lshift_signed_standard( -1ll, 1)); @@ -1282,7 +1291,7 @@ static MPT_NOINLINE void TestMisc1() VERIFY_EQUAL(mpt::lshift_signed( 0x7ffffffffffffffell, 1), mpt::lshift_signed_standard( 0x7ffffffffffffffell, 1)); VERIFY_EQUAL(mpt::lshift_signed( 0x7fffffffffffffffll, 1), mpt::lshift_signed_standard( 0x7fffffffffffffffll, 1)); - VERIFY_EQUAL(mpt::lshift_signed(-0x8000000000000000ll, 63), mpt::lshift_signed_standard(-0x8000000000000000ll, 63)); + VERIFY_EQUAL(mpt::lshift_signed(0ull-0x8000000000000000ull, 63), mpt::lshift_signed_standard(0ull-0x8000000000000000ull, 63)); VERIFY_EQUAL(mpt::lshift_signed(-0x7fffffffffffffffll, 63), mpt::lshift_signed_standard(-0x7fffffffffffffffll, 63)); VERIFY_EQUAL(mpt::lshift_signed(-0x7ffffffffffffffell, 63), mpt::lshift_signed_standard(-0x7ffffffffffffffell, 63)); VERIFY_EQUAL(mpt::lshift_signed( -1ll, 63), mpt::lshift_signed_standard( -1ll, 63)); @@ -1293,7 +1302,7 @@ static MPT_NOINLINE void TestMisc1() #if MPT_COMPILER_SHIFT_SIGNED - VERIFY_EQUAL(mpt::rshift_signed(-0x8000000000000000ll, 1), mpt::rshift_signed_undefined(-0x8000000000000000ll, 1)); + VERIFY_EQUAL(mpt::rshift_signed(0ull-0x8000000000000000ull, 1), mpt::rshift_signed_undefined(0ull-0x8000000000000000ull, 1)); VERIFY_EQUAL(mpt::rshift_signed(-0x7fffffffffffffffll, 1), mpt::rshift_signed_undefined(-0x7fffffffffffffffll, 1)); VERIFY_EQUAL(mpt::rshift_signed(-0x7ffffffffffffffell, 1), mpt::rshift_signed_undefined(-0x7ffffffffffffffell, 1)); VERIFY_EQUAL(mpt::rshift_signed( -1ll, 1), mpt::rshift_signed_undefined( -1ll, 1)); @@ -1302,7 +1311,7 @@ static MPT_NOINLINE void TestMisc1() VERIFY_EQUAL(mpt::rshift_signed( 0x7ffffffffffffffell, 1), mpt::rshift_signed_undefined( 0x7ffffffffffffffell, 1)); VERIFY_EQUAL(mpt::rshift_signed( 0x7fffffffffffffffll, 1), mpt::rshift_signed_undefined( 0x7fffffffffffffffll, 1)); - VERIFY_EQUAL(mpt::rshift_signed(-0x8000000000000000ll, 63), mpt::rshift_signed_undefined(-0x8000000000000000ll, 63)); + VERIFY_EQUAL(mpt::rshift_signed(0ull-0x8000000000000000ull, 63), mpt::rshift_signed_undefined(0ull-0x8000000000000000ull, 63)); VERIFY_EQUAL(mpt::rshift_signed(-0x7fffffffffffffffll, 63), mpt::rshift_signed_undefined(-0x7fffffffffffffffll, 63)); VERIFY_EQUAL(mpt::rshift_signed(-0x7ffffffffffffffell, 63), mpt::rshift_signed_undefined(-0x7ffffffffffffffell, 63)); VERIFY_EQUAL(mpt::rshift_signed( -1ll, 63), mpt::rshift_signed_undefined( -1ll, 63)); @@ -1311,7 +1320,7 @@ static MPT_NOINLINE void TestMisc1() VERIFY_EQUAL(mpt::rshift_signed( 0x7ffffffffffffffell, 63), mpt::rshift_signed_undefined( 0x7ffffffffffffffell, 63)); VERIFY_EQUAL(mpt::rshift_signed( 0x7fffffffffffffffll, 63), mpt::rshift_signed_undefined( 0x7fffffffffffffffll, 63)); - VERIFY_EQUAL(mpt::lshift_signed(-0x8000000000000000ll, 1), mpt::lshift_signed_undefined(-0x8000000000000000ll, 1)); + VERIFY_EQUAL(mpt::lshift_signed(0ull-0x8000000000000000ull, 1), mpt::lshift_signed_undefined(0ull-0x8000000000000000ull, 1)); VERIFY_EQUAL(mpt::lshift_signed(-0x7fffffffffffffffll, 1), mpt::lshift_signed_undefined(-0x7fffffffffffffffll, 1)); VERIFY_EQUAL(mpt::lshift_signed(-0x7ffffffffffffffell, 1), mpt::lshift_signed_undefined(-0x7ffffffffffffffell, 1)); VERIFY_EQUAL(mpt::lshift_signed( -1ll, 1), mpt::lshift_signed_undefined( -1ll, 1)); @@ -1320,7 +1329,7 @@ static MPT_NOINLINE void TestMisc1() VERIFY_EQUAL(mpt::lshift_signed( 0x7ffffffffffffffell, 1), mpt::lshift_signed_undefined( 0x7ffffffffffffffell, 1)); VERIFY_EQUAL(mpt::lshift_signed( 0x7fffffffffffffffll, 1), mpt::lshift_signed_undefined( 0x7fffffffffffffffll, 1)); - VERIFY_EQUAL(mpt::lshift_signed(-0x8000000000000000ll, 63), mpt::lshift_signed_undefined(-0x8000000000000000ll, 63)); + VERIFY_EQUAL(mpt::lshift_signed(0ull-0x8000000000000000ull, 63), mpt::lshift_signed_undefined(0ull-0x8000000000000000ull, 63)); VERIFY_EQUAL(mpt::lshift_signed(-0x7fffffffffffffffll, 63), mpt::lshift_signed_undefined(-0x7fffffffffffffffll, 63)); VERIFY_EQUAL(mpt::lshift_signed(-0x7ffffffffffffffell, 63), mpt::lshift_signed_undefined(-0x7ffffffffffffffell, 63)); VERIFY_EQUAL(mpt::lshift_signed( -1ll, 63), mpt::lshift_signed_undefined( -1ll, 63)); @@ -1494,14 +1503,18 @@ static MPT_NOINLINE void TestMisc2() // UUID { VERIFY_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull).ToUString(), U_("2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32")); - #if defined(MODPLUG_TRACKER) || !defined(NO_DMO) + #if defined(MODPLUG_TRACKER) || defined(MPT_WITH_DMO) constexpr mpt::UUID uuid_tmp = "2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32"_uuid; VERIFY_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), uuid_tmp); VERIFY_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), mpt::UUID(Util::StringToGUID(_T("{2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32}")))); VERIFY_EQUAL(mpt::UUID(0x2ed6593au, 0xdfe6, 0x4cf8, 0xb2e575ad7f600c32ull), mpt::UUID(Util::StringToCLSID(_T("{2ed6593a-dfe6-4cf8-b2e5-75ad7f600c32}")))); + VERIFY_EQUAL(mpt::UUID(0x00112233u, 0x4455, 0x6677, 0x8899AABBCCDDEEFFull), mpt::UUID(Util::StringToGUID(_T("{00112233-4455-6677-8899-AABBCCDDEEFF}")))); + VERIFY_EQUAL(mpt::UUID(0x00112233u, 0x4455, 0x6677, 0xC899AABBCCDDEEFFull), mpt::UUID(Util::StringToGUID(_T("{00112233-4455-6677-C899-AABBCCDDEEFF}")))); + VERIFY_EQUAL(Util::GUIDToString(mpt::UUID(0x00112233u, 0x4455, 0x6677, 0x8899AABBCCDDEEFFull)), _T("{00112233-4455-6677-8899-AABBCCDDEEFF}")); + VERIFY_EQUAL(Util::GUIDToString(mpt::UUID(0x00112233u, 0x4455, 0x6677, 0xC899AABBCCDDEEFFull)), _T("{00112233-4455-6677-C899-AABBCCDDEEFF}")); #endif -#if defined(MODPLUG_TRACKER) || !defined(NO_DMO) +#if defined(MODPLUG_TRACKER) || defined(MPT_WITH_DMO) VERIFY_EQUAL(Util::IsValid(Util::CreateGUID()), true); { mpt::UUID uuid = mpt::UUID::Generate(); @@ -1523,12 +1536,12 @@ static MPT_NOINLINE void TestMisc2() VERIFY_EQUAL(mpt::UUID::Generate() != mpt::UUID::Generate(), true); mpt::UUID a = mpt::UUID::Generate(); VERIFY_EQUAL(a, mpt::UUID::FromString(a.ToUString())); - mpt::byte uuiddata[16]{}; + std::byte uuiddata[16]{}; for(std::size_t i = 0; i < 16; ++i) { - uuiddata[i] = mpt::byte_cast(static_cast(i)); + uuiddata[i] = mpt::byte_cast(static_cast(i)); } - STATIC_ASSERT(sizeof(mpt::UUID) == 16); + static_assert(sizeof(mpt::UUID) == 16); UUIDbin uuid2; std::memcpy(&uuid2, uuiddata, 16); VERIFY_EQUAL(mpt::UUID(uuid2).ToUString(), U_("00010203-0405-0607-0809-0a0b0c0d0e0f")); @@ -1539,17 +1552,17 @@ static MPT_NOINLINE void TestMisc2() // check that empty stringstream behaves correctly with our MSVC workarounds when using iostream interface directly - { mpt::ostringstream ss; VERIFY_EQUAL(ss.tellp(), std::streampos(0)); } - { mpt::ostringstream ss; ss.seekp(0); VERIFY_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } - { mpt::ostringstream ss; ss.seekp(0, std::ios_base::beg); VERIFY_EQUAL(!ss.fail(), true); } - { mpt::ostringstream ss; ss.seekp(0, std::ios_base::cur); VERIFY_EQUAL(!ss.fail(), true); } - { mpt::istringstream ss; VERIFY_EQUAL(ss.tellg(), std::streampos(0)); } - { mpt::istringstream ss; ss.seekg(0); VERIFY_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } - { mpt::istringstream ss; ss.seekg(0, std::ios_base::beg); VERIFY_EQUAL(!ss.fail(), true); } - { mpt::istringstream ss; ss.seekg(0, std::ios_base::cur); VERIFY_EQUAL(!ss.fail(), true); } + { std::ostringstream ss; VERIFY_EQUAL(ss.tellp(), std::streampos(0)); } + { std::ostringstream ss; ss.seekp(0); VERIFY_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } + { std::ostringstream ss; ss.seekp(0, std::ios_base::beg); VERIFY_EQUAL(!ss.fail(), true); } + { std::ostringstream ss; ss.seekp(0, std::ios_base::cur); VERIFY_EQUAL(!ss.fail(), true); } + { std::istringstream ss; VERIFY_EQUAL(ss.tellg(), std::streampos(0)); } + { std::istringstream ss; ss.seekg(0); VERIFY_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } + { std::istringstream ss; ss.seekg(0, std::ios_base::beg); VERIFY_EQUAL(!ss.fail(), true); } + { std::istringstream ss; ss.seekg(0, std::ios_base::cur); VERIFY_EQUAL(!ss.fail(), true); } { - mpt::ostringstream s; + std::ostringstream s; char b = 23; VERIFY_EQUAL(!s.fail(), true); VERIFY_EQUAL(s.tellp(), std::streampos(0)); @@ -1574,7 +1587,7 @@ static MPT_NOINLINE void TestMisc2() } { - mpt::istringstream s; + std::istringstream s; VERIFY_EQUAL(!s.fail(), true); VERIFY_EQUAL(s.tellg(), std::streampos(0)); VERIFY_EQUAL(!s.fail(), true); @@ -1589,7 +1602,7 @@ static MPT_NOINLINE void TestMisc2() } { - mpt::istringstream s("a"); + std::istringstream s("a"); char a = 0; VERIFY_EQUAL(!s.fail(), true); VERIFY_EQUAL(s.tellg(), std::streampos(0)); @@ -1616,14 +1629,14 @@ static MPT_NOINLINE void TestMisc2() // check that empty native and fixed stringstream both behaves correctly with out IO functions - { mpt::ostringstream ss; VERIFY_EQUAL(mpt::IO::TellWrite(ss), 0); } - { mpt::ostringstream ss; VERIFY_EQUAL(mpt::IO::SeekBegin(ss), true); } - { mpt::ostringstream ss; VERIFY_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } - { mpt::ostringstream ss; VERIFY_EQUAL(mpt::IO::SeekRelative(ss, 0), true); } - { mpt::istringstream ss; VERIFY_EQUAL(mpt::IO::TellRead(ss), 0); } - { mpt::istringstream ss; VERIFY_EQUAL(mpt::IO::SeekBegin(ss), true); } - { mpt::istringstream ss; VERIFY_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } - { mpt::istringstream ss; VERIFY_EQUAL(mpt::IO::SeekRelative(ss, 0), true); } + { std::ostringstream ss; VERIFY_EQUAL(mpt::IO::TellWrite(ss), 0); } + { std::ostringstream ss; VERIFY_EQUAL(mpt::IO::SeekBegin(ss), true); } + { std::ostringstream ss; VERIFY_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } + { std::ostringstream ss; VERIFY_EQUAL(mpt::IO::SeekRelative(ss, 0), true); } + { std::istringstream ss; VERIFY_EQUAL(mpt::IO::TellRead(ss), 0); } + { std::istringstream ss; VERIFY_EQUAL(mpt::IO::SeekBegin(ss), true); } + { std::istringstream ss; VERIFY_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true); } + { std::istringstream ss; VERIFY_EQUAL(mpt::IO::SeekRelative(ss, 0), true); } { std::ostringstream ss; VERIFY_EQUAL(mpt::IO::TellWrite(ss), 0); } { std::ostringstream ss; VERIFY_EQUAL(mpt::IO::SeekBegin(ss), true); } @@ -1635,7 +1648,7 @@ static MPT_NOINLINE void TestMisc2() { std::istringstream ss; VERIFY_EQUAL(mpt::IO::SeekRelative(ss, 0), true); } { - mpt::ostringstream s; + std::ostringstream s; char b = 23; VERIFY_EQUAL(mpt::IO::IsValid(s), true); VERIFY_EQUAL(mpt::IO::TellWrite(s), 0); @@ -1660,7 +1673,7 @@ static MPT_NOINLINE void TestMisc2() } { - mpt::istringstream s; + std::istringstream s; VERIFY_EQUAL(mpt::IO::IsValid(s), true); VERIFY_EQUAL(mpt::IO::TellRead(s), 0); VERIFY_EQUAL(mpt::IO::IsValid(s), true); @@ -1675,7 +1688,7 @@ static MPT_NOINLINE void TestMisc2() } { - mpt::istringstream s("a"); + std::istringstream s("a"); char a = 0; VERIFY_EQUAL(mpt::IO::IsValid(s), true); VERIFY_EQUAL(mpt::IO::TellRead(s), 0); @@ -1767,7 +1780,7 @@ static MPT_NOINLINE void TestMisc2() { auto TestAdaptive16 = [](uint16 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) { - mpt::stringstream f; + std::stringstream f; VERIFY_EQUAL(mpt::IO::WriteAdaptiveInt16LE(f, value, fixedSize), true); VERIFY_EQUAL(mpt::IO::TellWrite(f), expected_size); if(bytes) @@ -1787,7 +1800,7 @@ static MPT_NOINLINE void TestMisc2() }; auto TestAdaptive32 = [](uint32 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) { - mpt::stringstream f; + std::stringstream f; VERIFY_EQUAL(mpt::IO::WriteAdaptiveInt32LE(f, value, fixedSize), true); VERIFY_EQUAL(mpt::IO::TellWrite(f), expected_size); if(bytes) @@ -1807,7 +1820,7 @@ static MPT_NOINLINE void TestMisc2() }; auto TestAdaptive64 = [](uint64 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) { - mpt::stringstream f; + std::stringstream f; VERIFY_EQUAL(mpt::IO::WriteAdaptiveInt64LE(f, value, fixedSize), true); VERIFY_EQUAL(mpt::IO::TellWrite(f), expected_size); if(bytes) @@ -1955,7 +1968,7 @@ static MPT_NOINLINE void TestMisc2() #ifdef MPT_ENABLE_FILEIO { - std::vector data; + std::vector data; data.push_back(mpt::as_byte(0)); data.push_back(mpt::as_byte(255)); data.push_back(mpt::as_byte(1)); @@ -1964,7 +1977,7 @@ static MPT_NOINLINE void TestMisc2() RemoveFile(fn); mpt::LazyFileRef f(fn); f = data; - std::vector data2; + std::vector data2; data2 = f; VERIFY_EQUAL(data.size(), data2.size()); for(std::size_t i = 0; i < data.size() && i < data2.size(); ++i) @@ -1987,7 +2000,7 @@ static MPT_NOINLINE void TestMisc2() VERIFY_EQUAL(mpt::crc32_ogg(std::string("123456789")), 0x89a1897fu); // Check floating-point accuracy in TransposeToFrequency - int32 transposeToFrequency[] = + static constexpr int32 transposeToFrequency[] = { 5, 5, 5, 5, 31, 32, 33, 34, @@ -2001,8 +2014,23 @@ static MPT_NOINLINE void TestMisc2() int freqIndex = 0; for(int32 transpose = -128; transpose < 128; transpose += 32) + { for(int32 finetune = -128; finetune < 128; finetune += 64, freqIndex++) - VERIFY_EQUAL_EPS(transposeToFrequency[freqIndex], static_cast(ModSample::TransposeToFrequency(transpose, finetune)), 1); + { + const auto freq = ModSample::TransposeToFrequency(transpose, finetune); + VERIFY_EQUAL_EPS(transposeToFrequency[freqIndex], static_cast(freq), 1); + if(transpose >= -96) + { + // Verify transpose+finetune <-> frequency roundtrip + // (not for transpose = -128 because it would require fractional precision that we don't have here) + ModSample smp; + smp.nC5Speed = freq; + smp.FrequencyToTranspose(); + smp.TransposeToFrequency(); + VERIFY_EQUAL(freq, smp.nC5Speed); + } + } + } { ModSample smp; @@ -2336,98 +2364,98 @@ static MPT_NOINLINE void TestCharsets() // MPT_UTF8 version // Charset conversions (basic sanity checks) - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetUTF8, U_("a")), "a"); - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetISO8859_1, U_("a")), "a"); - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetASCII, U_("a")), "a"); - VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetUTF8, "a"), U_("a")); - VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetISO8859_1, "a"), U_("a")); - VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetASCII, "a"), U_("a")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::UTF8, U_("a")), "a"); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::ISO8859_1, U_("a")), "a"); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::ASCII, U_("a")), "a"); + VERIFY_EQUAL(mpt::ToUnicode(mpt::Charset::UTF8, "a"), U_("a")); + VERIFY_EQUAL(mpt::ToUnicode(mpt::Charset::ISO8859_1, "a"), U_("a")); + VERIFY_EQUAL(mpt::ToUnicode(mpt::Charset::ASCII, "a"), U_("a")); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetLocale, U_("a")), "a"); - VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetLocale, "a"), U_("a")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::Locale, U_("a")), "a"); + VERIFY_EQUAL(mpt::ToUnicode(mpt::Charset::Locale, "a"), U_("a")); #endif - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetUTF8, MPT_UTF8("a")), "a"); - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetISO8859_1, MPT_UTF8("a")), "a"); - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetASCII, MPT_UTF8("a")), "a"); - VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetUTF8, "a"), MPT_UTF8("a")); - VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetISO8859_1, "a"), MPT_UTF8("a")); - VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetASCII, "a"), MPT_UTF8("a")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::UTF8, MPT_UTF8("a")), "a"); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::ISO8859_1, MPT_UTF8("a")), "a"); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::ASCII, MPT_UTF8("a")), "a"); + VERIFY_EQUAL(mpt::ToUnicode(mpt::Charset::UTF8, "a"), MPT_UTF8("a")); + VERIFY_EQUAL(mpt::ToUnicode(mpt::Charset::ISO8859_1, "a"), MPT_UTF8("a")); + VERIFY_EQUAL(mpt::ToUnicode(mpt::Charset::ASCII, "a"), MPT_UTF8("a")); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetLocale, MPT_UTF8("a")), "a"); - VERIFY_EQUAL(mpt::ToUnicode(mpt::CharsetLocale, "a"), MPT_UTF8("a")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::Locale, MPT_UTF8("a")), "a"); + VERIFY_EQUAL(mpt::ToUnicode(mpt::Charset::Locale, "a"), MPT_UTF8("a")); #endif // Check that some character replacement is done (and not just empty strings or truncated strings are returned) // We test german umlaut-a (U+00E4) (\xC3\xA4) and CJK U+5BB6 (\xE5\xAE\xB6) - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetASCII,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetISO8859_1,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetCP437,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetUTF8,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetASCII,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetISO8859_1,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetCP437,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetUTF8,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::ASCII,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::ISO8859_1,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::CP437,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::UTF8,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::ASCII,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::ISO8859_1,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::CP437,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::UTF8,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetLocale,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetLocale,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::Locale,MPT_UTF8("abc\xC3\xA4xyz")),"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::Locale,MPT_UTF8("abc\xC3\xA4xyz")),"abc"),true); #endif - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetASCII,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetISO8859_1,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetCP437,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetUTF8,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetASCII,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetISO8859_1,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetCP437,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetUTF8,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::ASCII,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::ISO8859_1,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::CP437,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::UTF8,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::ASCII,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::ISO8859_1,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::CP437,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::UTF8,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetLocale,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetLocale,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::Locale,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::Locale,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc"),true); #endif - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetASCII,"abc\xC3\xA4xyz"),U_("xyz")),true); - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetISO8859_1,"abc\xC3\xA4xyz"),U_("xyz")),true); - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetCP437,"abc\xC3\xA4xyz"),U_("xyz")),true); - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetUTF8,"abc\xC3\xA4xyz"),U_("xyz")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetASCII,"abc\xC3\xA4xyz"),U_("abc")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetISO8859_1,"abc\xC3\xA4xyz"),U_("abc")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetCP437,"abc\xC3\xA4xyz"),U_("abc")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetUTF8,"abc\xC3\xA4xyz"),U_("abc")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::ASCII,"abc\xC3\xA4xyz"),U_("xyz")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::ISO8859_1,"abc\xC3\xA4xyz"),U_("xyz")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::CP437,"abc\xC3\xA4xyz"),U_("xyz")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::UTF8,"abc\xC3\xA4xyz"),U_("xyz")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::ASCII,"abc\xC3\xA4xyz"),U_("abc")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::ISO8859_1,"abc\xC3\xA4xyz"),U_("abc")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::CP437,"abc\xC3\xA4xyz"),U_("abc")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::UTF8,"abc\xC3\xA4xyz"),U_("abc")),true); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetLocale,"abc\xC3\xA4xyz"),U_("xyz")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetLocale,"abc\xC3\xA4xyz"),U_("abc")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::Locale,"abc\xC3\xA4xyz"),U_("xyz")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::Locale,"abc\xC3\xA4xyz"),U_("abc")),true); #endif - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetASCII,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetISO8859_1,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetCP437,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetUTF8,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetASCII,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetISO8859_1,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetCP437,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetUTF8,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::ASCII,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::ISO8859_1,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::CP437,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::UTF8,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::ASCII,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::ISO8859_1,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::CP437,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::UTF8,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::CharsetLocale,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); - VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::CharsetLocale,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); + VERIFY_EQUAL(EndsWith(mpt::ToUnicode(mpt::Charset::Locale,"abc\xE5\xAE\xB6xyz"),U_("xyz")),true); + VERIFY_EQUAL(BeginsWith(mpt::ToUnicode(mpt::Charset::Locale,"abc\xE5\xAE\xB6xyz"),U_("abc")),true); #endif // Check that characters are correctly converted // We test german umlaut-a (U+00E4) and CJK U+5BB6 // cp437 - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetCP437,MPT_UTF8("abc\xC3\xA4xyz")),"abc\x84xyz"); - VERIFY_EQUAL(MPT_UTF8("abc\xC3\xA4xyz"),mpt::ToUnicode(mpt::CharsetCP437,"abc\x84xyz")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::CP437,MPT_UTF8("abc\xC3\xA4xyz")),"abc\x84xyz"); + VERIFY_EQUAL(MPT_UTF8("abc\xC3\xA4xyz"),mpt::ToUnicode(mpt::Charset::CP437,"abc\x84xyz")); // iso8859 - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetISO8859_1,MPT_UTF8("abc\xC3\xA4xyz")),"abc\xE4xyz"); - VERIFY_EQUAL(MPT_UTF8("abc\xC3\xA4xyz"),mpt::ToUnicode(mpt::CharsetISO8859_1,"abc\xE4xyz")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::ISO8859_1,MPT_UTF8("abc\xC3\xA4xyz")),"abc\xE4xyz"); + VERIFY_EQUAL(MPT_UTF8("abc\xC3\xA4xyz"),mpt::ToUnicode(mpt::Charset::ISO8859_1,"abc\xE4xyz")); // utf8 - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetUTF8,MPT_UTF8("abc\xC3\xA4xyz")),"abc\xC3\xA4xyz"); - VERIFY_EQUAL(MPT_UTF8("abc\xC3\xA4xyz"),mpt::ToUnicode(mpt::CharsetUTF8,"abc\xC3\xA4xyz")); - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetUTF8,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc\xE5\xAE\xB6xyz"); - VERIFY_EQUAL(MPT_UTF8("abc\xE5\xAE\xB6xyz"),mpt::ToUnicode(mpt::CharsetUTF8,"abc\xE5\xAE\xB6xyz")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::UTF8,MPT_UTF8("abc\xC3\xA4xyz")),"abc\xC3\xA4xyz"); + VERIFY_EQUAL(MPT_UTF8("abc\xC3\xA4xyz"),mpt::ToUnicode(mpt::Charset::UTF8,"abc\xC3\xA4xyz")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::UTF8,MPT_UTF8("abc\xE5\xAE\xB6xyz")),"abc\xE5\xAE\xB6xyz"); + VERIFY_EQUAL(MPT_UTF8("abc\xE5\xAE\xB6xyz"),mpt::ToUnicode(mpt::Charset::UTF8,"abc\xE5\xAE\xB6xyz")); #if MPT_WSTRING_CONVERT @@ -2435,15 +2463,15 @@ static MPT_NOINLINE void TestCharsets() // wide L"" version // Charset conversions (basic sanity checks) - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetUTF8, L"a"), "a"); - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetISO8859_1, L"a"), "a"); - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetASCII, L"a"), "a"); - VERIFY_EQUAL(mpt::ToWide(mpt::CharsetUTF8, "a"), L"a"); - VERIFY_EQUAL(mpt::ToWide(mpt::CharsetISO8859_1, "a"), L"a"); - VERIFY_EQUAL(mpt::ToWide(mpt::CharsetASCII, "a"), L"a"); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::UTF8, L"a"), "a"); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::ISO8859_1, L"a"), "a"); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::ASCII, L"a"), "a"); + VERIFY_EQUAL(mpt::ToWide(mpt::Charset::UTF8, "a"), L"a"); + VERIFY_EQUAL(mpt::ToWide(mpt::Charset::ISO8859_1, "a"), L"a"); + VERIFY_EQUAL(mpt::ToWide(mpt::Charset::ASCII, "a"), L"a"); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetLocale, L"a"), "a"); - VERIFY_EQUAL(mpt::ToWide(mpt::CharsetLocale, "a"), L"a"); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::Locale, L"a"), "a"); + VERIFY_EQUAL(mpt::ToWide(mpt::Charset::Locale, "a"), L"a"); #endif // Check that some character replacement is done (and not just empty strings or truncated strings are returned) @@ -2454,74 +2482,74 @@ static MPT_NOINLINE void TestCharsets() #pragma warning(disable:4428) // universal-character-name encountered in source #endif - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetASCII,L"abc\u00E4xyz"),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetISO8859_1,L"abc\u00E4xyz"),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetCP437,L"abc\u00E4xyz"),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetUTF8,L"abc\u00E4xyz"),"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetASCII,L"abc\u00E4xyz"),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetISO8859_1,L"abc\u00E4xyz"),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetCP437,L"abc\u00E4xyz"),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetUTF8,L"abc\u00E4xyz"),"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::ASCII,L"abc\u00E4xyz"),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::ISO8859_1,L"abc\u00E4xyz"),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::CP437,L"abc\u00E4xyz"),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::UTF8,L"abc\u00E4xyz"),"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::ASCII,L"abc\u00E4xyz"),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::ISO8859_1,L"abc\u00E4xyz"),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::CP437,L"abc\u00E4xyz"),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::UTF8,L"abc\u00E4xyz"),"abc"),true); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetLocale,L"abc\u00E4xyz"),"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetLocale,L"abc\u00E4xyz"),"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::Locale,L"abc\u00E4xyz"),"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::Locale,L"abc\u00E4xyz"),"abc"),true); #endif - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetASCII,L"abc\u5BB6xyz"),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetISO8859_1,L"abc\u5BB6xyz"),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetCP437,L"abc\u5BB6xyz"),"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetUTF8,L"abc\u5BB6xyz"),"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetASCII,L"abc\u5BB6xyz"),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetISO8859_1,L"abc\u5BB6xyz"),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetCP437,L"abc\u5BB6xyz"),"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetUTF8,L"abc\u5BB6xyz"),"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::ASCII,L"abc\u5BB6xyz"),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::ISO8859_1,L"abc\u5BB6xyz"),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::CP437,L"abc\u5BB6xyz"),"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::UTF8,L"abc\u5BB6xyz"),"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::ASCII,L"abc\u5BB6xyz"),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::ISO8859_1,L"abc\u5BB6xyz"),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::CP437,L"abc\u5BB6xyz"),"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::UTF8,L"abc\u5BB6xyz"),"abc"),true); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::CharsetLocale,L"abc\u5BB6xyz"),"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::CharsetLocale,L"abc\u5BB6xyz"),"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToCharset(mpt::Charset::Locale,L"abc\u5BB6xyz"),"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToCharset(mpt::Charset::Locale,L"abc\u5BB6xyz"),"abc"),true); #endif - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetASCII,"abc\xC3\xA4xyz"),L"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetISO8859_1,"abc\xC3\xA4xyz"),L"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetCP437,"abc\xC3\xA4xyz"),L"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetUTF8,"abc\xC3\xA4xyz"),L"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetASCII,"abc\xC3\xA4xyz"),L"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetISO8859_1,"abc\xC3\xA4xyz"),L"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetCP437,"abc\xC3\xA4xyz"),L"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetUTF8,"abc\xC3\xA4xyz"),L"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::ASCII,"abc\xC3\xA4xyz"),L"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::ISO8859_1,"abc\xC3\xA4xyz"),L"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::CP437,"abc\xC3\xA4xyz"),L"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::UTF8,"abc\xC3\xA4xyz"),L"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::ASCII,"abc\xC3\xA4xyz"),L"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::ISO8859_1,"abc\xC3\xA4xyz"),L"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::CP437,"abc\xC3\xA4xyz"),L"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::UTF8,"abc\xC3\xA4xyz"),L"abc"),true); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetLocale,"abc\xC3\xA4xyz"),L"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetLocale,"abc\xC3\xA4xyz"),L"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::Locale,"abc\xC3\xA4xyz"),L"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::Locale,"abc\xC3\xA4xyz"),L"abc"),true); #endif - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetASCII,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetISO8859_1,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetCP437,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetUTF8,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetASCII,"abc\xE5\xAE\xB6xyz"),L"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetISO8859_1,"abc\xE5\xAE\xB6xyz"),L"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetCP437,"abc\xE5\xAE\xB6xyz"),L"abc"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetUTF8,"abc\xE5\xAE\xB6xyz"),L"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::ASCII,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::ISO8859_1,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::CP437,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::UTF8,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::ASCII,"abc\xE5\xAE\xB6xyz"),L"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::ISO8859_1,"abc\xE5\xAE\xB6xyz"),L"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::CP437,"abc\xE5\xAE\xB6xyz"),L"abc"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::UTF8,"abc\xE5\xAE\xB6xyz"),L"abc"),true); #if defined(MPT_ENABLE_CHARSET_LOCALE) - VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::CharsetLocale,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); - VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::CharsetLocale,"abc\xE5\xAE\xB6xyz"),L"abc"),true); + VERIFY_EQUAL(EndsWith(mpt::ToWide(mpt::Charset::Locale,"abc\xE5\xAE\xB6xyz"),L"xyz"),true); + VERIFY_EQUAL(BeginsWith(mpt::ToWide(mpt::Charset::Locale,"abc\xE5\xAE\xB6xyz"),L"abc"),true); #endif // Check that characters are correctly converted // We test german umlaut-a (U+00E4) and CJK U+5BB6 // cp437 - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetCP437,L"abc\u00E4xyz"),"abc\x84xyz"); - VERIFY_EQUAL(L"abc\u00E4xyz",mpt::ToWide(mpt::CharsetCP437,"abc\x84xyz")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::CP437,L"abc\u00E4xyz"),"abc\x84xyz"); + VERIFY_EQUAL(L"abc\u00E4xyz",mpt::ToWide(mpt::Charset::CP437,"abc\x84xyz")); // iso8859 - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetISO8859_1,L"abc\u00E4xyz"),"abc\xE4xyz"); - VERIFY_EQUAL(L"abc\u00E4xyz",mpt::ToWide(mpt::CharsetISO8859_1,"abc\xE4xyz")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::ISO8859_1,L"abc\u00E4xyz"),"abc\xE4xyz"); + VERIFY_EQUAL(L"abc\u00E4xyz",mpt::ToWide(mpt::Charset::ISO8859_1,"abc\xE4xyz")); // utf8 - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetUTF8,L"abc\u00E4xyz"),"abc\xC3\xA4xyz"); - VERIFY_EQUAL(L"abc\u00E4xyz",mpt::ToWide(mpt::CharsetUTF8,"abc\xC3\xA4xyz")); - VERIFY_EQUAL(mpt::ToCharset(mpt::CharsetUTF8,L"abc\u5BB6xyz"),"abc\xE5\xAE\xB6xyz"); - VERIFY_EQUAL(L"abc\u5BB6xyz",mpt::ToWide(mpt::CharsetUTF8,"abc\xE5\xAE\xB6xyz")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::UTF8,L"abc\u00E4xyz"),"abc\xC3\xA4xyz"); + VERIFY_EQUAL(L"abc\u00E4xyz",mpt::ToWide(mpt::Charset::UTF8,"abc\xC3\xA4xyz")); + VERIFY_EQUAL(mpt::ToCharset(mpt::Charset::UTF8,L"abc\u5BB6xyz"),"abc\xE5\xAE\xB6xyz"); + VERIFY_EQUAL(L"abc\u5BB6xyz",mpt::ToWide(mpt::Charset::UTF8,"abc\xE5\xAE\xB6xyz")); #if MPT_COMPILER_MSVC #pragma warning(pop) @@ -3030,7 +3058,7 @@ static void TestLoadXMFile(const CSoundFile &sndFile) // Global Variables VERIFY_EQUAL_NONCONT(sndFile.GetTitle(), "Test Module"); - VERIFY_EQUAL_NONCONT(sndFile.m_songMessage.at(0), 'O'); + VERIFY_EQUAL_NONCONT(sndFile.m_songMessage.substr(0, 32), "OpenMPT Module Loader Test Suite"); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultTempo, TEMPO(139, 0)); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultSpeed, 5); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultGlobalVolume, 128); @@ -3045,7 +3073,7 @@ static void TestLoadXMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(sndFile.m_nTempoMode, tempoModeModern); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultRowsPerBeat, 6); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultRowsPerMeasure, 12); - VERIFY_EQUAL_NONCONT(sndFile.m_dwCreatedWithVersion, MAKE_VERSION_NUMERIC(1, 19, 02, 05)); + VERIFY_EQUAL_NONCONT(sndFile.m_dwCreatedWithVersion, MPT_V("1.19.02.05")); VERIFY_EQUAL_NONCONT(sndFile.Order().GetRestartPos(), 1); // Macros @@ -3055,21 +3083,21 @@ static void TestLoadXMFile(const CSoundFile &sndFile) // Channels VERIFY_EQUAL_NONCONT(sndFile.GetNumChannels(), 2); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.ChnSettings[0].szName, "First Channel"), 0); + VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[0].szName == "First Channel"), true); #ifndef NO_PLUGINS VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].nMixPlugin, 0); #endif // NO_PLUGINS - VERIFY_EQUAL_NONCONT(strcmp(sndFile.ChnSettings[1].szName, "Second Channel"), 0); + VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[1].szName == "Second Channel"), true); #ifndef NO_PLUGINS VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].nMixPlugin, 1); #endif // NO_PLUGINS // Samples VERIFY_EQUAL_NONCONT(sndFile.GetNumSamples(), 3); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[1], "Pulse Sample"), 0); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[2], "Empty Sample"), 0); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[3], "Unassigned Sample"), 0); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[1] == "Pulse Sample"), true); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[2] == "Empty Sample"), true); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[3] == "Unassigned Sample"), true); #ifdef MODPLUG_TRACKER VERIFY_EQUAL_NONCONT(pModDoc->FindSampleParent(1), 1); VERIFY_EQUAL_NONCONT(pModDoc->FindSampleParent(2), 1); @@ -3122,7 +3150,7 @@ static void TestLoadXMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(pIns->GetCutoff(), 0); VERIFY_EQUAL_NONCONT(pIns->IsResonanceEnabled(), false); VERIFY_EQUAL_NONCONT(pIns->GetResonance(), 0); - VERIFY_EQUAL_NONCONT(pIns->nFilterMode, FLTMODE_UNCHANGED); + VERIFY_EQUAL_NONCONT(pIns->filterMode, FilterMode::Unchanged); VERIFY_EQUAL_NONCONT(pIns->nVolSwing, 0); VERIFY_EQUAL_NONCONT(pIns->nPanSwing, 0); @@ -3230,7 +3258,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) // Global Variables VERIFY_EQUAL_NONCONT(sndFile.GetTitle(), "Test Module_____________X"); - VERIFY_EQUAL_NONCONT(sndFile.m_songMessage.at(0), 'O'); + VERIFY_EQUAL_NONCONT(sndFile.m_songMessage.substr(0, 32), "OpenMPT Module Loader Test Suite"); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultTempo, TEMPO(139, 999)); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultSpeed, 5); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultGlobalVolume, 128); @@ -3245,7 +3273,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(sndFile.m_nTempoMode, tempoModeModern); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultRowsPerBeat, 6); VERIFY_EQUAL_NONCONT(sndFile.m_nDefaultRowsPerMeasure, 12); - VERIFY_EQUAL_NONCONT(sndFile.m_dwCreatedWithVersion, MAKE_VERSION_NUMERIC(1, 19, 02, 05)); + VERIFY_EQUAL_NONCONT(sndFile.m_dwCreatedWithVersion, MPT_V("1.19.02.05")); VERIFY_EQUAL_NONCONT(sndFile.m_nResampling, SRCMODE_SINC8LP); VERIFY_EQUAL_NONCONT(sndFile.m_songArtist, U_("Tester")); VERIFY_EQUAL_NONCONT(sndFile.m_tempoSwing.size(), 6); @@ -3258,7 +3286,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) // Edit history VERIFY_EQUAL_NONCONT(sndFile.GetFileHistory().size() > 0, true); - const FileHistory &fh = sndFile.GetFileHistory().at(0); + const FileHistory &fh = sndFile.GetFileHistory().front(); VERIFY_EQUAL_NONCONT(fh.loadDate.tm_year, 111); VERIFY_EQUAL_NONCONT(fh.loadDate.tm_mon, 5); VERIFY_EQUAL_NONCONT(fh.loadDate.tm_mday, 14); @@ -3275,7 +3303,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) // Channels VERIFY_EQUAL_NONCONT(sndFile.GetNumChannels(), 70); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.ChnSettings[0].szName, "First Channel"), 0); + VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[0].szName == "First Channel"), true); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].nPan, 32); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].nVolume, 32); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].dwFlags, CHN_MUTE); @@ -3283,7 +3311,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[0].nMixPlugin, 0); #endif // NO_PLUGINS - VERIFY_EQUAL_NONCONT(strcmp(sndFile.ChnSettings[1].szName, "Second Channel"), 0); + VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[1].szName == "Second Channel"), true); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].nPan, 128); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].nVolume, 16); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].dwFlags, CHN_SURROUND); @@ -3291,7 +3319,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[1].nMixPlugin, 1); #endif // NO_PLUGINS - VERIFY_EQUAL_NONCONT(strcmp(sndFile.ChnSettings[69].szName, "Last Channel______X"), 0); + VERIFY_EQUAL_NONCONT((sndFile.ChnSettings[69].szName == "Last Channel______X"), true); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[69].nPan, 256); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[69].nVolume, 7); VERIFY_EQUAL_NONCONT(sndFile.ChnSettings[69].dwFlags, ChannelFlags(0)); @@ -3338,7 +3366,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) { const ModSample &sample = sndFile.GetSample(2); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[2], "Stereo / 16-Bit"), 0); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[2] == "Stereo / 16-Bit"), true); VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 4); VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 2); VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 2); @@ -3359,8 +3387,8 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) // External sample { const ModSample &sample = sndFile.GetSample(4); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[4], "Overridden Name"), 0); - VERIFY_EQUAL_NONCONT(strcmp(sample.filename, "External"), 0); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[4] == "Overridden Name"), true); + VERIFY_EQUAL_NONCONT((sample.filename == "External"), true); #ifdef MPT_EXTERNAL_SAMPLES VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 1); VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 1); @@ -3415,7 +3443,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(pIns->GetCutoff(), 0x32); VERIFY_EQUAL_NONCONT(pIns->IsResonanceEnabled(), true); VERIFY_EQUAL_NONCONT(pIns->GetResonance(), 0x64); - VERIFY_EQUAL_NONCONT(pIns->nFilterMode, FLTMODE_HIGHPASS); + VERIFY_EQUAL_NONCONT(pIns->filterMode, FilterMode::HighPass); VERIFY_EQUAL_NONCONT(pIns->nVolSwing, 0x30); VERIFY_EQUAL_NONCONT(pIns->nPanSwing, 0x18); @@ -3470,14 +3498,14 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(sndFile.Order.GetNumSequences(), 2); VERIFY_EQUAL_NONCONT(sndFile.Order(0).GetLengthTailTrimmed(), 3); - VERIFY_EQUAL_NONCONT(sndFile.Order(0).GetName(), "First Sequence"); + VERIFY_EQUAL_NONCONT(sndFile.Order(0).GetName(), U_("First Sequence")); VERIFY_EQUAL_NONCONT(sndFile.Order(0)[0], sndFile.Order.GetIgnoreIndex()); VERIFY_EQUAL_NONCONT(sndFile.Order(0)[1], 0); VERIFY_EQUAL_NONCONT(sndFile.Order(0)[2], sndFile.Order.GetIgnoreIndex()); VERIFY_EQUAL_NONCONT(sndFile.Order(0).GetRestartPos(), 1); VERIFY_EQUAL_NONCONT(sndFile.Order(1).GetLengthTailTrimmed(), 3); - VERIFY_EQUAL_NONCONT(sndFile.Order(1).GetName(), "Second Sequence"); + VERIFY_EQUAL_NONCONT(sndFile.Order(1).GetName(), U_("Second Sequence")); VERIFY_EQUAL_NONCONT(sndFile.Order(1)[0], 1); VERIFY_EQUAL_NONCONT(sndFile.Order(1)[1], 2); VERIFY_EQUAL_NONCONT(sndFile.Order(1)[2], 3); @@ -3537,8 +3565,11 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile) VERIFY_EQUAL_NONCONT(plug.IsMasterEffect(), true); VERIFY_EQUAL_NONCONT(plug.GetGain(), 11); VERIFY_EQUAL_NONCONT(plug.pMixPlugin != nullptr, true); - VERIFY_EQUAL_NONCONT(plug.pMixPlugin->GetParameter(1), 0.5f); - VERIFY_EQUAL_NONCONT(plug.pMixPlugin->IsInstrument(), false); + if(plug.pMixPlugin) + { + VERIFY_EQUAL_NONCONT(plug.pMixPlugin->GetParameter(1), 0.5f); + VERIFY_EQUAL_NONCONT(plug.pMixPlugin->IsInstrument(), false); + } #endif // NO_PLUGINS #ifdef MODPLUG_TRACKER @@ -3576,7 +3607,7 @@ static void TestLoadS3MFile(const CSoundFile &sndFile, bool resaved) VERIFY_EQUAL_NONCONT((sndFile.m_SongFlags & SONG_FILE_FLAGS), SONG_FASTVOLSLIDES); VERIFY_EQUAL_NONCONT(sndFile.GetMixLevels(), mixLevelsCompatible); VERIFY_EQUAL_NONCONT(sndFile.m_nTempoMode, tempoModeClassic); - VERIFY_EQUAL_NONCONT(sndFile.m_dwLastSavedWithVersion, resaved ? Version(Version::Current().GetRawVersion() & 0xFFFF0000u) : MAKE_VERSION_NUMERIC(1, 27, 00, 00)); + VERIFY_EQUAL_NONCONT(sndFile.m_dwLastSavedWithVersion, resaved ? Version(Version::Current().GetRawVersion() & 0xFFFF0000u) : MPT_V("1.27.00.00")); VERIFY_EQUAL_NONCONT(sndFile.Order().GetRestartPos(), 0); // Channels @@ -3597,8 +3628,8 @@ static void TestLoadS3MFile(const CSoundFile &sndFile, bool resaved) VERIFY_EQUAL_NONCONT(sndFile.GetNumSamples(), 4); { const ModSample &sample = sndFile.GetSample(1); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[1], "Sample_1__________________X"), 0); - VERIFY_EQUAL_NONCONT(strcmp(sample.filename, "Filename_1_X"), 0); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[1] == "Sample_1__________________X"), true); + VERIFY_EQUAL_NONCONT((sample.filename == "Filename_1_X"), true); VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 1); VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 1); VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 1); @@ -3624,15 +3655,15 @@ static void TestLoadS3MFile(const CSoundFile &sndFile, bool resaved) { const ModSample &sample = sndFile.GetSample(2); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[2], "Empty"), 0); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[2] == "Empty"), true); VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_S3M), 16384); VERIFY_EQUAL_NONCONT(sample.nVolume, 2 * 4); } { const ModSample &sample = sndFile.GetSample(3); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[3], "Stereo / 16-Bit"), 0); - VERIFY_EQUAL_NONCONT(strcmp(sample.filename, "Filename_3_X"), 0); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[3] == "Stereo / 16-Bit"), true); + VERIFY_EQUAL_NONCONT((sample.filename == "Filename_3_X"), true); VERIFY_EQUAL_NONCONT(sample.GetBytesPerSample(), 4); VERIFY_EQUAL_NONCONT(sample.GetNumChannels(), 2); VERIFY_EQUAL_NONCONT(sample.GetElementarySampleSize(), 2); @@ -3653,8 +3684,8 @@ static void TestLoadS3MFile(const CSoundFile &sndFile, bool resaved) { const ModSample &sample = sndFile.GetSample(4); - VERIFY_EQUAL_NONCONT(strcmp(sndFile.m_szNames[4], "adlib"), 0); - VERIFY_EQUAL_NONCONT(strcmp(sample.filename, ""), 0); + VERIFY_EQUAL_NONCONT((sndFile.m_szNames[4] == "adlib"), true); + VERIFY_EQUAL_NONCONT((sample.filename == ""), true); VERIFY_EQUAL_NONCONT(sample.GetSampleRate(MOD_TYPE_S3M), 8363); VERIFY_EQUAL_NONCONT(sample.nVolume, 58 * 4); VERIFY_EQUAL_NONCONT(sample.uFlags, CHN_ADLIB); @@ -3699,38 +3730,20 @@ static void TestLoadS3MFile(const CSoundFile &sndFile, bool resaved) static bool ShouldRunTests() { - mpt::PathString theFile = theApp.GetAppDirPath(); - // Only run the tests when we're in the project directory structure. - std::size_t pathComponents = mpt::String::Split(theFile.ToUnicode(), U_("\\")).size() - 1; - for(std::size_t i = 0; i < pathComponents; ++i) + mpt::PathString theFile = theApp.GetInstallPath(); + if(theFile.IsDirectory() && (theFile + P_("test")).IsDirectory()) { - if(theFile.IsDirectory() && (theFile + P_("test")).IsDirectory()) + if((theFile + P_("test\\test.mptm")).IsFile()) { - if((theFile + P_("test\\test.mptm")).IsFile()) - { - return true; - } + return true; } - theFile += P_("..\\"); } return false; } static mpt::PathString GetTestFilenameBase() { - mpt::PathString theFile = theApp.GetAppDirPath(); - std::size_t pathComponents = mpt::String::Split(theFile.ToUnicode(), U_("\\")).size() - 1; - for(std::size_t i = 0; i < pathComponents; ++i) - { - if(theFile.IsDirectory() && (theFile + P_("test")).IsDirectory()) - { - if((theFile + P_("test\\test.mptm")).IsFile()) - { - break; - } - } - theFile += P_("..\\"); - } + mpt::PathString theFile = theApp.GetInstallPath(); theFile += P_("test/test."); return theFile; } @@ -3975,7 +3988,7 @@ static MPT_NOINLINE void TestLoadSaveFile() // General file I/O tests { - mpt::ostringstream f; + std::ostringstream f; size_t bytesWritten; mpt::IO::WriteVarInt(f, uint16(0), &bytesWritten); VERIFY_EQUAL_NONCONT(bytesWritten, 1); mpt::IO::WriteVarInt(f, uint16(127), &bytesWritten); VERIFY_EQUAL_NONCONT(bytesWritten, 1); @@ -4000,15 +4013,16 @@ static MPT_NOINLINE void TestLoadSaveFile() // This is both, compile-time and run-time cheking. // Run-time in case some weird compiler gets confused by our templates // and only writes the first array element. - mpt::ostringstream f; + std::ostringstream f; uint16be data[2]; + Clear(data); data[0] = 0x1234; data[1] = 0x5678; mpt::IO::Write(f, data); VERIFY_EQUAL(f.str(), std::string("\x12\x34\x56\x78")); } { - mpt::ostringstream f; + std::ostringstream f; std::vector data; data.resize(3); data[0] = 0x1234; @@ -4018,8 +4032,9 @@ static MPT_NOINLINE void TestLoadSaveFile() VERIFY_EQUAL(f.str(), std::string("\x12\x34\x56\x78\x12\x34")); } { - mpt::ostringstream f; + std::ostringstream f; int16be data[3]; + Clear(data); data[0] = 0x1234; data[1] = 0x5678; data[2] = 0x1234; @@ -4070,29 +4085,29 @@ static MPT_NOINLINE void TestEditing() // Rearrange samples sndFile.m_nSamples = 2; - mpt::String::Copy(sndFile.GetSample(1).filename, "1"); - mpt::String::Copy(sndFile.m_szNames[1], "1"); - mpt::String::Copy(sndFile.GetSample(2).filename, "2"); - mpt::String::Copy(sndFile.m_szNames[2], "2"); + sndFile.GetSample(1).filename = "1"; + sndFile.m_szNames[1] = "1"; + sndFile.GetSample(2).filename = "2"; + sndFile.m_szNames[2] = "2"; sndFile.GetSample(2).nLength = 16; sndFile.GetSample(2).AllocateSample(); modDoc->ReArrangeSamples({ 2, SAMPLEINDEX_INVALID, 1 }); VERIFY_EQUAL_NONCONT(sndFile.GetSample(1).HasSampleData(), true); - VERIFY_EQUAL_NONCONT(sndFile.GetSample(1).filename, std::string("2")); - VERIFY_EQUAL_NONCONT(sndFile.m_szNames[1], std::string("2")); - VERIFY_EQUAL_NONCONT(sndFile.GetSample(2).filename, std::string()); - VERIFY_EQUAL_NONCONT(sndFile.m_szNames[2], std::string()); - VERIFY_EQUAL_NONCONT(sndFile.GetSample(3).filename, std::string("1")); - VERIFY_EQUAL_NONCONT(sndFile.m_szNames[3], std::string("1")); + VERIFY_EQUAL_NONCONT(sndFile.GetSample(1).filename, "2"); + VERIFY_EQUAL_NONCONT(sndFile.m_szNames[1], "2"); + VERIFY_EQUAL_NONCONT(sndFile.GetSample(2).filename, ""); + VERIFY_EQUAL_NONCONT(sndFile.m_szNames[2], ""); + VERIFY_EQUAL_NONCONT(sndFile.GetSample(3).filename, "1"); + VERIFY_EQUAL_NONCONT(sndFile.m_szNames[3], "1"); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 4)->instr, 3); // Convert / rearrange instruments modDoc->ConvertSamplesToInstruments(); modDoc->ReArrangeInstruments({ INSTRUMENTINDEX_INVALID, 2, 1, 3 }); - VERIFY_EQUAL_NONCONT(sndFile.Instruments[1]->name, std::string()); - VERIFY_EQUAL_NONCONT(sndFile.Instruments[2]->name, std::string()); - VERIFY_EQUAL_NONCONT(sndFile.Instruments[3]->name, std::string("2")); - VERIFY_EQUAL_NONCONT(sndFile.Instruments[4]->name, std::string("1")); + VERIFY_EQUAL_NONCONT(sndFile.Instruments[1]->name, ""); + VERIFY_EQUAL_NONCONT(sndFile.Instruments[2]->name, ""); + VERIFY_EQUAL_NONCONT(sndFile.Instruments[3]->name, "2"); + VERIFY_EQUAL_NONCONT(sndFile.Instruments[4]->name, "1"); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 4)->instr, 4); modDoc->ConvertInstrumentsToSamples(); VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(37, 4)->instr, 3); @@ -4119,7 +4134,7 @@ static void RunITCompressionTest(const std::vector &sampleData, FlagSet pSndFile = mpt::make_unique(); + std::unique_ptr pSndFile = std::make_unique(); CSoundFile &sndFile = *pSndFile.get(); sndFile.m_nType = MOD_TYPE_MPT; sndFile.Patterns.DestroyPatterns(); @@ -4260,7 +4275,7 @@ static MPT_NOINLINE void TestPCnoteSerialization() // Copy pattern data for comparison. CPatternContainer patterns{ sndFile.Patterns }; - mpt::stringstream mem; + std::stringstream mem; WriteModPatterns(mem, sndFile.Patterns); VERIFY_EQUAL_NONCONT( mem.good(), true ); @@ -4284,6 +4299,27 @@ static MPT_NOINLINE void TestPCnoteSerialization() } +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 +} + + // Test String I/O functionality static MPT_NOINLINE void TestStringIO() { @@ -4297,27 +4333,17 @@ static MPT_NOINLINE void TestStringIO() #define ReadTest(mode, dst, src, expectedResult) \ std::memset(dst, 0x7f, sizeof(dst)); \ - mpt::String::Read(dst, src); \ - VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, mpt::size(dst)), 0); /* Ensure that the strings are identical */ \ - for(size_t i = strlen(dst); i < mpt::size(dst); i++) \ - VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ - std::memset(dst, 0x7f, sizeof(dst)); \ mpt::String::WriteAutoBuf(dst) = mpt::String::ReadBuf(mpt::String:: mode , src); \ - VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, mpt::size(dst)), 0); /* Ensure that the strings are identical */ \ - for(size_t i = strlen(dst); i < mpt::size(dst); i++) \ - /* VERIFY_EQUAL_NONCONT(dst[i], '\0'); */ /* Ensure that rest of the buffer is completely nulled */ \ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = strlen(dst); i < std::size(dst); i++) \ + VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ /**/ #define WriteTest(mode, dst, src, expectedResult) \ std::memset(dst, 0x7f, sizeof(dst)); \ - mpt::String::Write(dst, src); \ - VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, mpt::size(dst)), 0); /* Ensure that the strings are identical */ \ - for(size_t i = mpt::strnlen(dst, mpt::size(dst)); i < mpt::size(dst); i++) \ - VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ - std::memset(dst, 0x7f, sizeof(dst)); \ mpt::String::WriteBuf(mpt::String:: mode , dst) = mpt::String::ReadAutoBuf(src); \ - VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, mpt::size(dst)), 0); /* Ensure that the strings are identical */ \ - for(size_t i = mpt::strnlen(dst, mpt::size(dst)); i < mpt::size(dst); i++) \ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = Test::strnlen(dst, std::size(dst)); i < std::size(dst); i++) \ VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ /**/ @@ -4437,28 +4463,21 @@ static MPT_NOINLINE void TestStringIO() { std::string dststring; - std::string src0string = std::string(src0, mpt::size(src0)); - std::string src1string = std::string(src1, mpt::size(src1)); - std::string src2string = std::string(src2, mpt::size(src2)); - std::string src3string = std::string(src3, mpt::size(src3)); + std::string src0string = std::string(src0, std::size(src0)); + std::string src1string = std::string(src1, std::size(src1)); + std::string src2string = std::string(src2, std::size(src2)); + std::string src3string = std::string(src3, std::size(src3)); #define ReadTest(mode, dst, src, expectedResult) \ - mpt::String::Read(dst, src); \ - VERIFY_EQUAL_NONCONT(dst, expectedResult); /* Ensure that the strings are identical */ \ dst = mpt::String::ReadBuf(mpt::String:: mode , src); \ VERIFY_EQUAL_NONCONT(dst, expectedResult); /* Ensure that the strings are identical */ \ /**/ #define WriteTest(mode, dst, src, expectedResult) \ std::memset(dst, 0x7f, sizeof(dst)); \ - mpt::String::Write(dst, src); \ - VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, mpt::size(dst)), 0); /* Ensure that the strings are identical */ \ - for(size_t i = mpt::strnlen(dst, mpt::size(dst)); i < mpt::size(dst); i++) \ - VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ - std::memset(dst, 0x7f, sizeof(dst)); \ mpt::String::WriteBuf(mpt::String:: mode , dst) = src; \ - VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, mpt::size(dst)), 0); /* Ensure that the strings are identical */ \ - for(size_t i = mpt::strnlen(dst, mpt::size(dst)); i < mpt::size(dst); i++) \ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = Test::strnlen(dst, std::size(dst)); i < std::size(dst); i++) \ VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ /**/ @@ -4553,9 +4572,120 @@ static MPT_NOINLINE void TestStringIO() mpt::String::FixNullString(src1); mpt::String::FixNullString(src2); mpt::String::FixNullString(src3); - VERIFY_EQUAL_NONCONT(strncmp(src1, "X ", mpt::size(src1)), 0); - VERIFY_EQUAL_NONCONT(strncmp(src2, "XYZ", mpt::size(src2)), 0); - VERIFY_EQUAL_NONCONT(strncmp(src3, "XYZ", mpt::size(src3)), 0); + VERIFY_EQUAL_NONCONT(strncmp(src1, "X ", std::size(src1)), 0); + VERIFY_EQUAL_NONCONT(strncmp(src2, "XYZ", std::size(src2)), 0); + VERIFY_EQUAL_NONCONT(strncmp(src3, "XYZ", std::size(src3)), 0); + + { + + char s0[4] = {'\0', 'X', ' ', 'X' }; + char s2[4] = { 'X', ' ','\0', 'X' }; + char s4[4] = { 'X', 'Y', 'Z', ' ' }; + + char d2[2] = {'\0','\0'}; + char d3[3] = {'\0','\0','\0'}; + char d4[4] = {'\0','\0','\0','\0'}; + char d5[5] = {'\0','\0','\0','\0','\0'}; + + #define CopyTest(dst, src, expectedResult) \ + std::memset(dst, 0x7f, sizeof(dst)); \ + mpt::String::WriteAutoBuf(dst) = mpt::String::ReadAutoBuf(src); \ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = strlen(dst); i < std::size(dst); i++) \ + VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ + /**/ + + CopyTest(d2, s0, ""); + CopyTest(d2, s2, "X"); + CopyTest(d2, s4, "X"); + CopyTest(d3, s0, ""); + CopyTest(d3, s2, "X "); + CopyTest(d3, s4, "XY"); + CopyTest(d4, s0, ""); + CopyTest(d4, s2, "X "); + CopyTest(d4, s4, "XYZ"); + CopyTest(d5, s0, ""); + CopyTest(d5, s2, "X "); + CopyTest(d5, s4, "XYZ "); + + #undef CopyTest + + #define CopyTestN(dst, src, len, expectedResult) \ + std::memset(dst, 0x7f, sizeof(dst)); \ + mpt::String::WriteAutoBuf(dst) = mpt::String::ReadAutoBuf(src, std::min(std::size(src), static_cast(len))); \ + VERIFY_EQUAL_NONCONT(strncmp(dst, expectedResult, std::size(dst)), 0); /* Ensure that the strings are identical */ \ + for(size_t i = strlen(dst); i < std::size(dst); i++) \ + VERIFY_EQUAL_NONCONT(dst[i], '\0'); /* Ensure that rest of the buffer is completely nulled */ \ + /**/ + + CopyTestN(d2, s0, 1, ""); + CopyTestN(d2, s2, 1, "X"); + CopyTestN(d2, s4, 1, "X"); + CopyTestN(d3, s0, 1, ""); + CopyTestN(d3, s2, 1, "X"); + CopyTestN(d3, s4, 1, "X"); + CopyTestN(d4, s0, 1, ""); + CopyTestN(d4, s2, 1, "X"); + CopyTestN(d4, s4, 1, "X"); + CopyTestN(d5, s0, 1, ""); + CopyTestN(d5, s2, 1, "X"); + CopyTestN(d5, s4, 1, "X"); + + CopyTestN(d2, s0, 2, ""); + CopyTestN(d2, s2, 2, "X"); + CopyTestN(d2, s4, 2, "X"); + CopyTestN(d3, s0, 2, ""); + CopyTestN(d3, s2, 2, "X "); + CopyTestN(d3, s4, 2, "XY"); + CopyTestN(d4, s0, 2, ""); + CopyTestN(d4, s2, 2, "X "); + CopyTestN(d4, s4, 2, "XY"); + CopyTestN(d5, s0, 2, ""); + CopyTestN(d5, s2, 2, "X "); + CopyTestN(d5, s4, 2, "XY"); + + CopyTestN(d2, s0, 3, ""); + CopyTestN(d2, s2, 3, "X"); + CopyTestN(d2, s4, 3, "X"); + CopyTestN(d3, s0, 3, ""); + CopyTestN(d3, s2, 3, "X "); + CopyTestN(d3, s4, 3, "XY"); + CopyTestN(d4, s0, 3, ""); + CopyTestN(d4, s2, 3, "X "); + CopyTestN(d4, s4, 3, "XYZ"); + CopyTestN(d5, s0, 3, ""); + CopyTestN(d5, s2, 3, "X "); + CopyTestN(d5, s4, 3, "XYZ"); + + CopyTestN(d2, s0, 4, ""); + CopyTestN(d2, s2, 4, "X"); + CopyTestN(d2, s4, 4, "X"); + CopyTestN(d3, s0, 4, ""); + CopyTestN(d3, s2, 4, "X "); + CopyTestN(d3, s4, 4, "XY"); + CopyTestN(d4, s0, 4, ""); + CopyTestN(d4, s2, 4, "X "); + CopyTestN(d4, s4, 4, "XYZ"); + CopyTestN(d5, s0, 4, ""); + CopyTestN(d5, s2, 4, "X "); + CopyTestN(d5, s4, 4, "XYZ "); + + CopyTestN(d2, s0, 5, ""); + CopyTestN(d2, s2, 5, "X"); + CopyTestN(d2, s4, 5, "X"); + CopyTestN(d3, s0, 5, ""); + CopyTestN(d3, s2, 5, "X "); + CopyTestN(d3, s4, 5, "XY"); + CopyTestN(d4, s0, 5, ""); + CopyTestN(d4, s2, 5, "X "); + CopyTestN(d4, s4, 5, "XYZ"); + CopyTestN(d5, s0, 5, ""); + CopyTestN(d5, s2, 5, "X "); + CopyTestN(d5, s4, 5, "XYZ "); + + #undef CopyTest + + } } @@ -4582,9 +4712,9 @@ static MPT_NOINLINE void TestSampleConversion() uint8 *unsigned8 = static_cast(targetBuf) + 256; int8 *delta8 = static_cast(targetBuf) + 512; int8 delta = 0; - CopySample(signed8, 256, 1, mpt::byte_cast(source8), 256, 1); - CopySample(reinterpret_cast(unsigned8), 256, 1, mpt::byte_cast(source8), 256, 1); - CopySample(delta8, 256, 1, mpt::byte_cast(source8), 256, 1); + CopySample(signed8, 256, 1, mpt::byte_cast(source8), 256, 1); + CopySample(reinterpret_cast(unsigned8), 256, 1, mpt::byte_cast(source8), 256, 1); + CopySample(delta8, 256, 1, mpt::byte_cast(source8), 256, 1); for(size_t i = 0; i < 256; i++) { @@ -4612,9 +4742,9 @@ static MPT_NOINLINE void TestSampleConversion() uint16 *unsigned16 = static_cast(targetBuf) + 65536; int16 *delta16 = static_cast(targetBuf) + 65536 * 2; int16 delta = 0; - CopySample >(signed16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); - CopySample >(reinterpret_cast(unsigned16), 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); - CopySample >(delta16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); + CopySample >(signed16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); + CopySample >(reinterpret_cast(unsigned16), 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); + CopySample >(delta16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); for(size_t i = 0; i < 65536; i++) { @@ -4632,9 +4762,9 @@ static MPT_NOINLINE void TestSampleConversion() source16[i * 2 + 1] = static_cast(i & 0xFF); } - CopySample >(signed16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); - CopySample >(reinterpret_cast(unsigned16), 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); - CopySample >(delta16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); + CopySample >(signed16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); + CopySample >(reinterpret_cast(unsigned16), 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); + CopySample >(delta16, 65536, 1, mpt::byte_cast(source16), 65536 * 2, 1); delta = 0; for(size_t i = 0; i < 65536; i++) @@ -4663,8 +4793,8 @@ static MPT_NOINLINE void TestSampleConversion() sample.nLength = 65536; sample.uFlags.set(CHN_16BIT); sample.pData.pSample = (static_cast(targetBuf) + 65536); - CopyAndNormalizeSample, SC::DecodeInt24<0, littleEndian24> > >(sample, mpt::byte_cast(source24), 3*65536); - CopySample, SC::DecodeInt24<0, littleEndian24> > >(truncated16, 65536, 1, mpt::byte_cast(source24), 65536 * 3, 1); + CopyAndNormalizeSample, SC::DecodeInt24<0, littleEndian24> > >(sample, mpt::byte_cast(source24), 3*65536); + CopySample, SC::DecodeInt24<0, littleEndian24> > >(truncated16, 65536, 1, mpt::byte_cast(source24), 65536 * 3, 1); for(size_t i = 0; i < 65536; i++) { @@ -4691,16 +4821,13 @@ static MPT_NOINLINE void TestSampleConversion() sample.nLength = 65536; sample.uFlags.set(CHN_16BIT); sample.pData.pSample = static_cast(targetBuf) + 65536; - CopyAndNormalizeSample, SC::DecodeFloat32 > >(sample, mpt::byte_cast(source32), 4*65536); - CopySample, SC::DecodeFloat32 > >(truncated16, 65536, 1, mpt::byte_cast(source32), 65536 * 4, 1); + CopyAndNormalizeSample, SC::DecodeFloat32 > >(sample, mpt::byte_cast(source32), 4*65536); + CopySample, SC::DecodeFloat32 > >(truncated16, 65536, 1, mpt::byte_cast(source32), 65536 * 4, 1); for(size_t i = 0; i < 65536; i++) { VERIFY_EQUAL_QUIET_NONCONT(sample.sample16()[i], static_cast(i - 0x8000u)); - if(mpt::abs(truncated16[i] - static_cast((i - 0x8000u) / 2)) > 1) - { - VERIFY_EQUAL_QUIET_NONCONT(true, false); - } + VERIFY_EQUAL_QUIET_NONCONT(std::abs(truncated16[i] - static_cast((i - 0x8000u) / 2)) <= 1, true); } } @@ -4709,7 +4836,7 @@ static MPT_NOINLINE void TestSampleConversion() int8 oneSample = 1; char *signed8 = reinterpret_cast(targetBuf); memset(signed8, 0, 4); - CopySample(reinterpret_cast(targetBuf), 4, 1, reinterpret_cast(&oneSample), sizeof(oneSample), 1); + CopySample(reinterpret_cast(targetBuf), 4, 1, reinterpret_cast(&oneSample), sizeof(oneSample), 1); VERIFY_EQUAL_NONCONT(signed8[0], 1); VERIFY_EQUAL_NONCONT(signed8[1], 0); VERIFY_EQUAL_NONCONT(signed8[2], 0); @@ -4718,11 +4845,14 @@ static MPT_NOINLINE void TestSampleConversion() // Dither { - std::vector buffer(64); + std::vector buffer(64); Dither dither(mpt::global_random_device()); dither.SetMode(DitherModPlug); - dither.Process(buffer.data(), 64, 1, 16); - std::vector expected = { + for(std::size_t i = 0; i < 64; ++i) + { + buffer[i] = dither.ModPlugDither().process<16>(0, buffer[i]); + } + std::vector expected = { 727, -557, -552, diff --git a/Frameworks/OpenMPT/libOpenMPT.xcodeproj/project.pbxproj b/Frameworks/OpenMPT/libOpenMPT.xcodeproj/project.pbxproj index 0ae5bff98..0e711dac9 100644 --- a/Frameworks/OpenMPT/libOpenMPT.xcodeproj/project.pbxproj +++ b/Frameworks/OpenMPT/libOpenMPT.xcodeproj/project.pbxproj @@ -25,6 +25,16 @@ 831132EA21F9565F001F678F /* OPL.h in Headers */ = {isa = PBXBuildFile; fileRef = 831132E521F9565F001F678F /* OPL.h */; }; 831132EB21F9565F001F678F /* Load_c67.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 831132E621F9565F001F678F /* Load_c67.cpp */; }; 831132EC21F9565F001F678F /* OPL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 831132E721F9565F001F678F /* OPL.cpp */; }; + 83AA7D1D2519B619004C5298 /* mptOSError.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7D1B2519B618004C5298 /* mptOSError.h */; }; + 83AA7D1E2519B619004C5298 /* mptOSException.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7D1C2519B618004C5298 /* mptOSException.h */; }; + 83AA7D252519B643004C5298 /* Dither.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7D212519B643004C5298 /* Dither.cpp */; }; + 83AA7D262519B643004C5298 /* SampleBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7D222519B643004C5298 /* SampleBuffer.h */; }; + 83AA7D272519B643004C5298 /* SampleTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7D232519B643004C5298 /* SampleTypes.h */; }; + 83AA7D282519B643004C5298 /* Dither.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7D242519B643004C5298 /* Dither.h */; }; + 83AA7D322519B694004C5298 /* TinyFFT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7D2E2519B694004C5298 /* TinyFFT.cpp */; }; + 83AA7D332519B694004C5298 /* SampleFormatSFZ.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7D2F2519B694004C5298 /* SampleFormatSFZ.cpp */; }; + 83AA7D342519B694004C5298 /* SampleFormatBRR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7D302519B694004C5298 /* SampleFormatBRR.cpp */; }; + 83AA7D352519B694004C5298 /* TinyFFT.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7D312519B694004C5298 /* TinyFFT.h */; }; 83E5EFD01FFEF9D200659F0F /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5EFCE1FFEF9D200659F0F /* config.h */; }; 83E5F8801FFEF9E400659F0F /* minimp3.c in Sources */ = {isa = PBXBuildFile; fileRef = 83E5F2461FFEF9E100659F0F /* minimp3.c */; }; 83E5F8811FFEF9E400659F0F /* minimp3.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E5F2471FFEF9E100659F0F /* minimp3.h */; }; @@ -86,10 +96,8 @@ 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 */; }; @@ -131,7 +139,6 @@ 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 */; }; @@ -179,7 +186,6 @@ 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 */; }; @@ -246,7 +252,6 @@ 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 */; }; @@ -299,6 +304,16 @@ 831132E521F9565F001F678F /* OPL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OPL.h; sourceTree = ""; }; 831132E621F9565F001F678F /* Load_c67.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Load_c67.cpp; sourceTree = ""; }; 831132E721F9565F001F678F /* OPL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OPL.cpp; sourceTree = ""; }; + 83AA7D1B2519B618004C5298 /* mptOSError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptOSError.h; sourceTree = ""; }; + 83AA7D1C2519B618004C5298 /* mptOSException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mptOSException.h; sourceTree = ""; }; + 83AA7D212519B643004C5298 /* Dither.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Dither.cpp; sourceTree = ""; }; + 83AA7D222519B643004C5298 /* SampleBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SampleBuffer.h; sourceTree = ""; }; + 83AA7D232519B643004C5298 /* SampleTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SampleTypes.h; sourceTree = ""; }; + 83AA7D242519B643004C5298 /* Dither.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Dither.h; sourceTree = ""; }; + 83AA7D2E2519B694004C5298 /* TinyFFT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TinyFFT.cpp; sourceTree = ""; }; + 83AA7D2F2519B694004C5298 /* SampleFormatSFZ.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleFormatSFZ.cpp; sourceTree = ""; }; + 83AA7D302519B694004C5298 /* SampleFormatBRR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleFormatBRR.cpp; sourceTree = ""; }; + 83AA7D312519B694004C5298 /* TinyFFT.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TinyFFT.h; sourceTree = ""; }; 83E5EFBD1FFEF7CC00659F0F /* libOpenMPT.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = libOpenMPT.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; }; @@ -364,10 +379,8 @@ 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 = ""; }; @@ -409,7 +422,6 @@ 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 = ""; }; @@ -457,7 +469,6 @@ 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 = ""; }; @@ -524,7 +535,6 @@ 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 = ""; }; @@ -671,6 +681,8 @@ 83E5FC371FFEFA0D00659F0F /* mptMutex.h */, 83E5FC441FFEFA0D00659F0F /* mptOS.cpp */, 83E5FC5C1FFEFA0D00659F0F /* mptOS.h */, + 83AA7D1B2519B618004C5298 /* mptOSError.h */, + 83AA7D1C2519B618004C5298 /* mptOSException.h */, 83E5FC4D1FFEFA0D00659F0F /* mptPathString.cpp */, 83E5FC591FFEFA0D00659F0F /* mptPathString.h */, 83E5FC5B1FFEFA0D00659F0F /* mptRandom.cpp */, @@ -716,8 +728,6 @@ 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 */, @@ -732,9 +742,13 @@ 83E5FCF61FFEFA7400659F0F /* soundbase */ = { isa = PBXGroup; children = ( + 83AA7D212519B643004C5298 /* Dither.cpp */, + 83AA7D242519B643004C5298 /* Dither.h */, + 83AA7D222519B643004C5298 /* SampleBuffer.h */, 83E5FCF71FFEFA7400659F0F /* SampleFormat.h */, - 83E5FCF81FFEFA7400659F0F /* SampleFormatCopy.h */, 83E5FCF91FFEFA7400659F0F /* SampleFormatConverters.h */, + 83E5FCF81FFEFA7400659F0F /* SampleFormatCopy.h */, + 83AA7D232519B643004C5298 /* SampleTypes.h */, ); path = soundbase; sourceTree = ""; @@ -767,8 +781,6 @@ 83E5FD881FFEFA8400659F0F /* ContainerPP20.cpp */, 83E5FD581FFEFA8400659F0F /* ContainerUMX.cpp */, 83E5FD5D1FFEFA8400659F0F /* ContainerXPK.cpp */, - 83E5FD9A1FFEFA8400659F0F /* Dither.cpp */, - 83E5FD241FFEFA8400659F0F /* Dither.h */, 83E5FD7A1FFEFA8400659F0F /* Dlsbank.cpp */, 83E5FD911FFEFA8400659F0F /* Dlsbank.h */, 83E5FDAE1FFEFA8400659F0F /* Fastmix.cpp */, @@ -862,11 +874,13 @@ 83E5FD321FFEFA8400659F0F /* RowVisitor.h */, 83E5FD901FFEFA8400659F0F /* S3MTools.cpp */, 83E5FD251FFEFA8400659F0F /* S3MTools.h */, + 83AA7D302519B694004C5298 /* SampleFormatBRR.cpp */, 83E5FD751FFEFA8400659F0F /* SampleFormatFLAC.cpp */, 83E5FDB51FFEFA8400659F0F /* SampleFormatMediaFoundation.cpp */, 83E5FD5E1FFEFA8400659F0F /* SampleFormatMP3.cpp */, 83E5FDAC1FFEFA8400659F0F /* SampleFormatOpus.cpp */, 83E5FDA11FFEFA8400659F0F /* SampleFormats.cpp */, + 83AA7D2F2519B694004C5298 /* SampleFormatSFZ.cpp */, 83E5FD8B1FFEFA8400659F0F /* SampleFormatVorbis.cpp */, 83E5FD991FFEFA8400659F0F /* SampleIO.cpp */, 83E5FD5A1FFEFA8400659F0F /* SampleIO.h */, @@ -882,9 +896,10 @@ 83E5FD811FFEFA8400659F0F /* Tables.h */, 83E5FDB01FFEFA8400659F0F /* Tagging.cpp */, 83E5FD7B1FFEFA8400659F0F /* Tagging.h */, + 83AA7D2E2519B694004C5298 /* TinyFFT.cpp */, + 83AA7D312519B694004C5298 /* TinyFFT.h */, 83E5FD5F1FFEFA8400659F0F /* tuning.cpp */, 83E5FDA51FFEFA8400659F0F /* tuning.h */, - 83E5FD571FFEFA8400659F0F /* tuningbase.cpp */, 83E5FD831FFEFA8400659F0F /* tuningbase.h */, 83E5FD981FFEFA8400659F0F /* tuningCollection.cpp */, 83E5FD7C1FFEFA8400659F0F /* tuningcollection.h */, @@ -996,8 +1011,9 @@ 83E5FC6C1FFEFA0D00659F0F /* mptStringFormat.h in Headers */, 83E5FDBA1FFEFA8500659F0F /* MixerInterface.h in Headers */, 83E5FDD81FFEFA8500659F0F /* ITTools.h in Headers */, + 83AA7D272519B643004C5298 /* SampleTypes.h in Headers */, 83E5FE241FFEFA8500659F0F /* FloatMixer.h in Headers */, - 83E5FDCC1FFEFA8500659F0F /* Dither.h in Headers */, + 83AA7D262519B643004C5298 /* SampleBuffer.h in Headers */, 83E5FC681FFEFA0D00659F0F /* stdafx.h in Headers */, 83E5FDD21FFEFA8500659F0F /* MPEGFrame.h in Headers */, 83E5FDCA1FFEFA8500659F0F /* ITCompression.h in Headers */, @@ -1065,7 +1081,10 @@ 83E5FDC91FFEFA8500659F0F /* ChunkReader.h in Headers */, 83E5FDF91FFEFA8500659F0F /* Distortion.h in Headers */, 83E5FE261FFEFA8500659F0F /* AudioCriticalSection.h in Headers */, + 83AA7D352519B694004C5298 /* TinyFFT.h in Headers */, 83E5FCD01FFEFA1A00659F0F /* libopenmpt_internal.h in Headers */, + 83AA7D1E2519B619004C5298 /* mptOSException.h in Headers */, + 83AA7D1D2519B619004C5298 /* mptOSError.h in Headers */, 83E5FC991FFEFA0D00659F0F /* versionNumber.h in Headers */, 83E5FC771FFEFA0D00659F0F /* mptTime.h in Headers */, 83E5FC8C1FFEFA0D00659F0F /* mptString.h in Headers */, @@ -1095,6 +1114,7 @@ 83E5FE391FFEFA8500659F0F /* IntMixer.h in Headers */, 83E5FDF01FFEFA8500659F0F /* WavesReverb.h in Headers */, 83E5FDED1FFEFA8500659F0F /* I3DL2Reverb.h in Headers */, + 83AA7D282519B643004C5298 /* Dither.h in Headers */, 83E5FC701FFEFA0D00659F0F /* mptMutex.h in Headers */, 83E5FD0C1FFEFA7D00659F0F /* DSP.h in Headers */, 83E5FC631FFEFA0D00659F0F /* mptCRC.h in Headers */, @@ -1141,7 +1161,7 @@ TargetAttributes = { 83E5EFBC1FFEF7CC00659F0F = { CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Manual; + ProvisioningStyle = Automatic; }; }; }; @@ -1219,6 +1239,7 @@ 83E5FC6D1FFEFA0D00659F0F /* Logging.cpp in Sources */, 83E5FE151FFEFA8500659F0F /* Sndmix.cpp in Sources */, 83E5FDB71FFEFA8500659F0F /* WAVTools.cpp in Sources */, + 83AA7D322519B694004C5298 /* TinyFFT.cpp in Sources */, 83E5FDEE1FFEFA8500659F0F /* I3DL2Reverb.cpp in Sources */, 83E5FC9A1FFEFA0D00659F0F /* misc_util.cpp in Sources */, 83E5FE0C1FFEFA8500659F0F /* Load_mtm.cpp in Sources */, @@ -1240,15 +1261,16 @@ 83E5FE451FFEFA8500659F0F /* Load_mdl.cpp in Sources */, 831132EC21F9565F001F678F /* OPL.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 */, + 83AA7D252519B643004C5298 /* Dither.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 */, 831132D621F955B2001F678F /* mptAlloc.cpp in Sources */, + 83AA7D332519B694004C5298 /* SampleFormatSFZ.cpp in Sources */, 831132D821F955B2001F678F /* mptStringBuffer.cpp in Sources */, 83E5FDD61FFEFA8500659F0F /* modcommand.cpp in Sources */, 83E5FD091FFEFA7D00659F0F /* DSP.cpp in Sources */, @@ -1270,9 +1292,9 @@ 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 */, + 83AA7D342519B694004C5298 /* SampleFormatBRR.cpp in Sources */, 83E5FE141FFEFA8500659F0F /* Load_sfx.cpp in Sources */, 83E5FE571FFEFA8500659F0F /* ITCompression.cpp in Sources */, 83E5FE591FFEFA8500659F0F /* MPEGFrame.cpp in Sources */, @@ -1280,7 +1302,6 @@ 83E5FC941FFEFA0D00659F0F /* mptRandom.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 */, @@ -1296,7 +1317,6 @@ 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 */, @@ -1327,7 +1347,7 @@ ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; @@ -1379,7 +1399,7 @@ OpenMPT/include/modplug/include, OpenMPT/include, ); - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -1395,7 +1415,7 @@ ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; @@ -1443,7 +1463,7 @@ OpenMPT/include/modplug/include, OpenMPT/include, ); - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; USE_HEADERMAP = YES; @@ -1455,10 +1475,10 @@ 83E5EFC61FFEF7CC00659F0F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -1479,10 +1499,10 @@ 83E5EFC71FFEF7CC00659F0F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; diff --git a/Frameworks/Opus/Opus.xcodeproj/project.pbxproj b/Frameworks/Opus/Opus.xcodeproj/project.pbxproj index 4fc846e64..30c956b7e 100644 --- a/Frameworks/Opus/Opus.xcodeproj/project.pbxproj +++ b/Frameworks/Opus/Opus.xcodeproj/project.pbxproj @@ -9,6 +9,22 @@ /* Begin PBXBuildFile section */ 830F884C19C9107E00420FB0 /* Ogg.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 830F884719C9105E00420FB0 /* Ogg.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 830F885F19C9145E00420FB0 /* Ogg.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 830F884719C9105E00420FB0 /* Ogg.framework */; }; + 8374715B2509BC1A00A6D7CA /* fft_arm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8374714A2509BC1A00A6D7CA /* fft_arm.h */; }; + 8374715C2509BC1A00A6D7CA /* kiss_fft_armv4.h in Headers */ = {isa = PBXBuildFile; fileRef = 8374714B2509BC1A00A6D7CA /* kiss_fft_armv4.h */; }; + 8374715F2509BC1A00A6D7CA /* fixed_armv4.h in Headers */ = {isa = PBXBuildFile; fileRef = 8374714E2509BC1A00A6D7CA /* fixed_armv4.h */; }; + 837471612509BC1A00A6D7CA /* kiss_fft_armv5e.h in Headers */ = {isa = PBXBuildFile; fileRef = 837471502509BC1A00A6D7CA /* kiss_fft_armv5e.h */; }; + 837471622509BC1A00A6D7CA /* pitch_arm.h in Headers */ = {isa = PBXBuildFile; fileRef = 837471512509BC1A00A6D7CA /* pitch_arm.h */; }; + 837471632509BC1A00A6D7CA /* mdct_arm.h in Headers */ = {isa = PBXBuildFile; fileRef = 837471522509BC1A00A6D7CA /* mdct_arm.h */; }; + 837471642509BC1A00A6D7CA /* armcpu.c in Sources */ = {isa = PBXBuildFile; fileRef = 837471532509BC1A00A6D7CA /* armcpu.c */; }; + 837471652509BC1A00A6D7CA /* fixed_armv5e.h in Headers */ = {isa = PBXBuildFile; fileRef = 837471542509BC1A00A6D7CA /* fixed_armv5e.h */; }; + 837471662509BC1A00A6D7CA /* arm_celt_map.c in Sources */ = {isa = PBXBuildFile; fileRef = 837471552509BC1A00A6D7CA /* arm_celt_map.c */; }; + 837471692509BC1A00A6D7CA /* celt_neon_intr.c in Sources */ = {isa = PBXBuildFile; fileRef = 837471582509BC1A00A6D7CA /* celt_neon_intr.c */; }; + 8374716A2509BC1A00A6D7CA /* armopts.s.in in Resources */ = {isa = PBXBuildFile; fileRef = 837471592509BC1A00A6D7CA /* armopts.s.in */; }; + 8374716B2509BC1A00A6D7CA /* armcpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8374715A2509BC1A00A6D7CA /* armcpu.h */; }; + 837471712509C5D100A6D7CA /* macros_armv4.h in Headers */ = {isa = PBXBuildFile; fileRef = 8374716D2509C5D100A6D7CA /* macros_armv4.h */; }; + 837471722509C5D100A6D7CA /* SigProc_FIX_armv4.h in Headers */ = {isa = PBXBuildFile; fileRef = 8374716E2509C5D100A6D7CA /* SigProc_FIX_armv4.h */; }; + 837471732509C5D100A6D7CA /* macros_armv5e.h in Headers */ = {isa = PBXBuildFile; fileRef = 8374716F2509C5D100A6D7CA /* macros_armv5e.h */; }; + 837471742509C5D100A6D7CA /* SigProc_FIX_armv5e.h in Headers */ = {isa = PBXBuildFile; fileRef = 837471702509C5D100A6D7CA /* SigProc_FIX_armv5e.h */; }; 8375B07017FFEABB0092A79F /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8375B06E17FFEABB0092A79F /* InfoPlist.strings */; }; 8375B20B17FFEB2C0092A79F /* _kiss_fft_guts.h in Headers */ = {isa = PBXBuildFile; fileRef = 8375B0A317FFEB2C0092A79F /* _kiss_fft_guts.h */; }; 8375B20C17FFEB2C0092A79F /* arch.h in Headers */ = {isa = PBXBuildFile; fileRef = 8375B0A417FFEB2C0092A79F /* arch.h */; }; @@ -273,6 +289,22 @@ /* Begin PBXFileReference section */ 830F884119C9105E00420FB0 /* Ogg.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Ogg.xcodeproj; path = ../Ogg/macosx/Ogg.xcodeproj; sourceTree = ""; }; 833F68421CDBCABC00AFB9F0 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; + 8374714A2509BC1A00A6D7CA /* fft_arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fft_arm.h; sourceTree = ""; }; + 8374714B2509BC1A00A6D7CA /* kiss_fft_armv4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kiss_fft_armv4.h; sourceTree = ""; }; + 8374714E2509BC1A00A6D7CA /* fixed_armv4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fixed_armv4.h; sourceTree = ""; }; + 837471502509BC1A00A6D7CA /* kiss_fft_armv5e.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kiss_fft_armv5e.h; sourceTree = ""; }; + 837471512509BC1A00A6D7CA /* pitch_arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pitch_arm.h; sourceTree = ""; }; + 837471522509BC1A00A6D7CA /* mdct_arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mdct_arm.h; sourceTree = ""; }; + 837471532509BC1A00A6D7CA /* armcpu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = armcpu.c; sourceTree = ""; }; + 837471542509BC1A00A6D7CA /* fixed_armv5e.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fixed_armv5e.h; sourceTree = ""; }; + 837471552509BC1A00A6D7CA /* arm_celt_map.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = arm_celt_map.c; sourceTree = ""; }; + 837471582509BC1A00A6D7CA /* celt_neon_intr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = celt_neon_intr.c; sourceTree = ""; }; + 837471592509BC1A00A6D7CA /* armopts.s.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = armopts.s.in; sourceTree = ""; }; + 8374715A2509BC1A00A6D7CA /* armcpu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = armcpu.h; sourceTree = ""; }; + 8374716D2509C5D100A6D7CA /* macros_armv4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macros_armv4.h; sourceTree = ""; }; + 8374716E2509C5D100A6D7CA /* SigProc_FIX_armv4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SigProc_FIX_armv4.h; sourceTree = ""; }; + 8374716F2509C5D100A6D7CA /* macros_armv5e.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macros_armv5e.h; sourceTree = ""; }; + 837471702509C5D100A6D7CA /* SigProc_FIX_armv5e.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SigProc_FIX_armv5e.h; sourceTree = ""; }; 8375B06217FFEABB0092A79F /* Opus.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Opus.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8375B06D17FFEABB0092A79F /* Opus-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Opus-Info.plist"; sourceTree = ""; }; 8375B06F17FFEABB0092A79F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -520,6 +552,36 @@ name = Products; sourceTree = ""; }; + 837471492509BC1A00A6D7CA /* arm */ = { + isa = PBXGroup; + children = ( + 837471552509BC1A00A6D7CA /* arm_celt_map.c */, + 837471532509BC1A00A6D7CA /* armcpu.c */, + 8374715A2509BC1A00A6D7CA /* armcpu.h */, + 837471592509BC1A00A6D7CA /* armopts.s.in */, + 837471582509BC1A00A6D7CA /* celt_neon_intr.c */, + 8374714A2509BC1A00A6D7CA /* fft_arm.h */, + 8374714E2509BC1A00A6D7CA /* fixed_armv4.h */, + 837471542509BC1A00A6D7CA /* fixed_armv5e.h */, + 8374714B2509BC1A00A6D7CA /* kiss_fft_armv4.h */, + 837471502509BC1A00A6D7CA /* kiss_fft_armv5e.h */, + 837471522509BC1A00A6D7CA /* mdct_arm.h */, + 837471512509BC1A00A6D7CA /* pitch_arm.h */, + ); + path = arm; + sourceTree = ""; + }; + 8374716C2509C5D100A6D7CA /* arm */ = { + isa = PBXGroup; + children = ( + 8374716D2509C5D100A6D7CA /* macros_armv4.h */, + 8374716E2509C5D100A6D7CA /* SigProc_FIX_armv4.h */, + 8374716F2509C5D100A6D7CA /* macros_armv5e.h */, + 837471702509C5D100A6D7CA /* SigProc_FIX_armv5e.h */, + ); + path = arm; + sourceTree = ""; + }; 8375B05817FFEABB0092A79F = { isa = PBXGroup; children = ( @@ -588,6 +650,7 @@ 8375B0A217FFEB2C0092A79F /* celt */ = { isa = PBXGroup; children = ( + 837471492509BC1A00A6D7CA /* arm */, 837928B81C293C78005DBCEA /* x86 */, 837928AF1C293C6F005DBCEA /* cpu_support.h */, 837928DA1C29406E005DBCEA /* celt_encoder.c */, @@ -656,6 +719,7 @@ 8375B10917FFEB2C0092A79F /* silk */ = { isa = PBXGroup; children = ( + 8374716C2509C5D100A6D7CA /* arm */, 837928CB1C293CA1005DBCEA /* x86 */, 8375B10A17FFEB2C0092A79F /* A2NLSF.c */, 8375B10B17FFEB2C0092A79F /* ana_filt_bank_1.c */, @@ -900,11 +964,14 @@ 8375B30417FFEB2C0092A79F /* tables.h in Headers */, 8375B2B817FFEB2C0092A79F /* main_FLP.h in Headers */, 8375B2D817FFEB2C0092A79F /* macros.h in Headers */, + 837471712509C5D100A6D7CA /* macros_armv4.h in Headers */, 837928D61C293CA1005DBCEA /* SigProc_FIX_sse.h in Headers */, 837928C51C293C78005DBCEA /* pitch_sse.h in Headers */, 8375B27617FFEB2C0092A79F /* control.h in Headers */, 8375B2E417FFEB2C0092A79F /* pitch_est_defines.h in Headers */, + 837471732509C5D100A6D7CA /* macros_armv5e.h in Headers */, 8375B2D917FFEB2C0092A79F /* main.h in Headers */, + 837471612509BC1A00A6D7CA /* kiss_fft_armv5e.h in Headers */, 8375B22417FFEB2C0092A79F /* laplace.h in Headers */, 8375B23117FFEB2C0092A79F /* quant_bands.h in Headers */, 8375B22F17FFEB2C0092A79F /* pitch.h in Headers */, @@ -917,12 +984,15 @@ 8375B30117FFEB2C0092A79F /* structs.h in Headers */, 8375B31C17FFEB2C0092A79F /* opus_private.h in Headers */, 8375B25417FFEB2C0092A79F /* opus_custom.h in Headers */, + 837471652509BC1A00A6D7CA /* fixed_armv5e.h in Headers */, 8375B20C17FFEB2C0092A79F /* arch.h in Headers */, 8375B22817FFEB2C0092A79F /* mdct.h in Headers */, 8375B30C17FFEB2C0092A79F /* tuning_parameters.h in Headers */, 8375B2E717FFEB2C0092A79F /* PLC.h in Headers */, 8375B2ED17FFEB2C0092A79F /* resampler_private.h in Headers */, + 837471622509BC1A00A6D7CA /* pitch_arm.h in Headers */, 8375B27B17FFEB2C0092A79F /* debug.h in Headers */, + 837471742509C5D100A6D7CA /* SigProc_FIX_armv5e.h in Headers */, 8375B21417FFEB2C0092A79F /* celt_lpc.h in Headers */, 8375B36917FFEFFF0092A79F /* version.h in Headers */, 8375B2F717FFEB2C0092A79F /* SigProc_FIX.h in Headers */, @@ -934,6 +1004,7 @@ 8375B23317FFEB2C0092A79F /* rate.h in Headers */, 8375B22017FFEB2C0092A79F /* float_cast.h in Headers */, 8375B20B17FFEB2C0092A79F /* _kiss_fft_guts.h in Headers */, + 8374715C2509BC1A00A6D7CA /* kiss_fft_armv4.h in Headers */, 8375B21717FFEB2C0092A79F /* ecintrin.h in Headers */, 837928B41C293C6F005DBCEA /* static_modes_fixed_arm_ne10.h in Headers */, 8375B30D17FFEB2C0092A79F /* typedef.h in Headers */, @@ -941,14 +1012,18 @@ 8375B22217FFEB2C0092A79F /* kiss_fft.h in Headers */, 8375B23517FFEB2C0092A79F /* static_modes_fixed.h in Headers */, 8375B22917FFEB2C0092A79F /* mfrngcod.h in Headers */, + 8374715F2509BC1A00A6D7CA /* fixed_armv4.h in Headers */, + 837471632509BC1A00A6D7CA /* mdct_arm.h in Headers */, 8375B22617FFEB2C0092A79F /* mathops.h in Headers */, 837928B51C293C6F005DBCEA /* static_modes_float_arm_ne10.h in Headers */, 8375B21D17FFEB2C0092A79F /* entenc.h in Headers */, 8375B36817FFEFFF0092A79F /* config.h in Headers */, 8375B21F17FFEB2C0092A79F /* fixed_generic.h in Headers */, 8375B2D617FFEB2C0092A79F /* MacroCount.h in Headers */, + 8374715B2509BC1A00A6D7CA /* fft_arm.h in Headers */, 8375B21617FFEB2C0092A79F /* cwrs.h in Headers */, 837928D31C293CA1005DBCEA /* main_sse.h in Headers */, + 8374716B2509BC1A00A6D7CA /* armcpu.h in Headers */, 8375B2D717FFEB2C0092A79F /* MacroDebug.h in Headers */, 837928E01C2940A9005DBCEA /* analysis.h in Headers */, 8375B35217FFEB2C0092A79F /* internal.h in Headers */, @@ -956,6 +1031,7 @@ 8375B2C717FFEB2C0092A79F /* structs_FLP.h in Headers */, 8375B28417FFEB2C0092A79F /* define.h in Headers */, 8375B21B17FFEB2C0092A79F /* entdec.h in Headers */, + 837471722509C5D100A6D7CA /* SigProc_FIX_armv4.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -992,8 +1068,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 8375B06117FFEABB0092A79F = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -1045,6 +1121,7 @@ buildActionMask = 2147483647; files = ( 8375B07017FFEABB0092A79F /* InfoPlist.strings in Resources */, + 8374716A2509BC1A00A6D7CA /* armopts.s.in in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1096,6 +1173,7 @@ 8375B27C17FFEB2C0092A79F /* dec_API.c in Sources */, 8375B2EA17FFEB2C0092A79F /* resampler.c in Sources */, 837928D81C293CA1005DBCEA /* VQ_WMat_EC_sse.c in Sources */, + 837471662509BC1A00A6D7CA /* arm_celt_map.c in Sources */, 837928C41C293C78005DBCEA /* pitch_sse.c in Sources */, 8375B2F517FFEB2C0092A79F /* shell_coder.c in Sources */, 8375B2CC17FFEB2C0092A79F /* init_decoder.c in Sources */, @@ -1107,6 +1185,7 @@ 8375B28317FFEB2C0092A79F /* decoder_set_fs.c in Sources */, 8375B2E817FFEB2C0092A79F /* process_NLSFs.c in Sources */, 8375B26D17FFEB2C0092A79F /* A2NLSF.c in Sources */, + 837471692509BC1A00A6D7CA /* celt_neon_intr.c in Sources */, 8375B2E217FFEB2C0092A79F /* NSQ.c in Sources */, 8375B2AE17FFEB2C0092A79F /* find_LTP_FLP.c in Sources */, 8375B2B917FFEB2C0092A79F /* noise_shape_analysis_FLP.c in Sources */, @@ -1184,6 +1263,7 @@ 8375B2E017FFEB2C0092A79F /* NLSF_VQ.c in Sources */, 8375B30E17FFEB2C0092A79F /* VAD.c in Sources */, 8375B23017FFEB2C0092A79F /* quant_bands.c in Sources */, + 837471642509BC1A00A6D7CA /* armcpu.c in Sources */, 8375B21A17FFEB2C0092A79F /* entdec.c in Sources */, 8375B27E17FFEB2C0092A79F /* decode_frame.c in Sources */, 8375B2A817FFEB2C0092A79F /* burg_modified_FLP.c in Sources */, @@ -1278,7 +1358,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -1325,7 +1405,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; }; name = Release; @@ -1333,7 +1413,10 @@ 8375B08B17FFEABB0092A79F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_SEARCH_PATHS = ( @@ -1364,6 +1447,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = org.xiph.libopus; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; @@ -1373,7 +1457,10 @@ 8375B08C17FFEABB0092A79F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_SEARCH_PATHS = ( @@ -1401,6 +1488,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = org.xiph.libopus; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; diff --git a/Frameworks/Opus/Opus/opus/celt/arm/armcpu.c b/Frameworks/Opus/Opus/opus/celt/arm/armcpu.c index 5e5d10c34..4aad638d9 100644 --- a/Frameworks/Opus/Opus/opus/celt/arm/armcpu.c +++ b/Frameworks/Opus/Opus/opus/celt/arm/armcpu.c @@ -31,7 +31,7 @@ #include "config.h" #endif -#ifdef OPUS_HAVE_RTCD +#if defined(OPUS_HAVE_RTCD) && defined(__aarch64__) #include "armcpu.h" #include "cpu_support.h" diff --git a/Frameworks/Opus/Opus/opus/celt/arm/celt_neon_intr.c b/Frameworks/Opus/Opus/opus/celt/arm/celt_neon_intr.c index 47dce15ba..692190142 100644 --- a/Frameworks/Opus/Opus/opus/celt/arm/celt_neon_intr.c +++ b/Frameworks/Opus/Opus/opus/celt/arm/celt_neon_intr.c @@ -34,10 +34,11 @@ #include "config.h" #endif +#if !defined(FIXED_POINT) && defined(OPUS_ARM_MAY_HAVE_NEON_INTR) + #include #include "../pitch.h" -#if !defined(FIXED_POINT) /* * Function: xcorr_kernel_neon_float * --------------------------------- diff --git a/Frameworks/Opus/Opus/opus/celt/arm/celt_pitch_xcorr_arm-gnu.S b/Frameworks/Opus/Opus/opus/celt/arm/celt_pitch_xcorr_arm-gnu.S index 5b2ee55a1..81321091f 100644 --- a/Frameworks/Opus/Opus/opus/celt/arm/celt_pitch_xcorr_arm-gnu.S +++ b/Frameworks/Opus/Opus/opus/celt/arm/celt_pitch_xcorr_arm-gnu.S @@ -549,3 +549,4 @@ celt_pitch_xcorr_edsp_done: @ END: .section .note.GNU-stack,"",%progbits + diff --git a/Frameworks/Opus/Opus/opus/celt/x86/celt_lpc_sse.c b/Frameworks/Opus/Opus/opus/celt/x86/celt_lpc_sse.c index 9c5258169..bfa4ed80d 100644 --- a/Frameworks/Opus/Opus/opus/celt/x86/celt_lpc_sse.c +++ b/Frameworks/Opus/Opus/opus/celt/x86/celt_lpc_sse.c @@ -29,7 +29,7 @@ #include "config.h" #endif -#if defined(FIXED_POINT) +#if defined(FIXED_POINT) && defined(OPUS_X86_MAY_HAVE_SSE4_1) #include #include diff --git a/Frameworks/Opus/Opus/opus/celt/x86/pitch_sse.c b/Frameworks/Opus/Opus/opus/celt/x86/pitch_sse.c index 20e73126b..defa8b7aa 100644 --- a/Frameworks/Opus/Opus/opus/celt/x86/pitch_sse.c +++ b/Frameworks/Opus/Opus/opus/celt/x86/pitch_sse.c @@ -29,6 +29,8 @@ #include "config.h" #endif +#ifdef __x86_64__ + #include "macros.h" #include "celt_lpc.h" #include "stack_alloc.h" @@ -182,4 +184,6 @@ void comb_filter_const_sse(opus_val32 *y, opus_val32 *x, int T, int N, } +#endif + #endif diff --git a/Frameworks/Opus/Opus/opus/celt/x86/pitch_sse2.c b/Frameworks/Opus/Opus/opus/celt/x86/pitch_sse2.c index a0e7d1bea..16629b5d6 100644 --- a/Frameworks/Opus/Opus/opus/celt/x86/pitch_sse2.c +++ b/Frameworks/Opus/Opus/opus/celt/x86/pitch_sse2.c @@ -29,6 +29,8 @@ #include "config.h" #endif +#ifdef __x86_64__ + #include #include @@ -93,3 +95,5 @@ opus_val32 celt_inner_prod_sse2(const opus_val16 *x, const opus_val16 *y, return sum; } #endif + +#endif diff --git a/Frameworks/Opus/Opus/opus/celt/x86/pitch_sse4_1.c b/Frameworks/Opus/Opus/opus/celt/x86/pitch_sse4_1.c index a092c68b2..9fd5792ea 100644 --- a/Frameworks/Opus/Opus/opus/celt/x86/pitch_sse4_1.c +++ b/Frameworks/Opus/Opus/opus/celt/x86/pitch_sse4_1.c @@ -29,6 +29,8 @@ #include "config.h" #endif +#if defined(OPUS_X86_MAY_HAVE_SSE4_1) && defined(FIXED_POINT) + #include #include @@ -38,7 +40,6 @@ #include "mathops.h" #include "pitch.h" -#if defined(OPUS_X86_MAY_HAVE_SSE4_1) && defined(FIXED_POINT) #include #include "x86cpu.h" diff --git a/Frameworks/Opus/Opus/opus/celt/x86/x86_celt_map.c b/Frameworks/Opus/Opus/opus/celt/x86/x86_celt_map.c index 1ed2acbcf..03a6ef924 100644 --- a/Frameworks/Opus/Opus/opus/celt/x86/x86_celt_map.c +++ b/Frameworks/Opus/Opus/opus/celt/x86/x86_celt_map.c @@ -29,11 +29,17 @@ #include "config.h" #endif +#ifdef __x86_64__ + +#include "cpu_support.h" +#include "arch.h" + #include "x86/x86cpu.h" #include "celt_lpc.h" #include "pitch.h" #include "pitch_sse.h" + #if defined(OPUS_HAVE_RTCD) # if defined(FIXED_POINT) @@ -146,3 +152,5 @@ void (*const COMB_FILTER_CONST_IMPL[OPUS_ARCHMASK + 1])( #endif #endif + +#endif diff --git a/Frameworks/Opus/Opus/opus/celt/x86/x86cpu.c b/Frameworks/Opus/Opus/opus/celt/x86/x86cpu.c index f850715e1..33131cd3e 100644 --- a/Frameworks/Opus/Opus/opus/celt/x86/x86cpu.c +++ b/Frameworks/Opus/Opus/opus/celt/x86/x86cpu.c @@ -29,17 +29,16 @@ #include "config.h" #endif +#if (defined(OPUS_X86_MAY_HAVE_SSE) && !defined(OPUS_X86_PRESUME_SSE)) || \ + (defined(OPUS_X86_MAY_HAVE_SSE2) && !defined(OPUS_X86_PRESUME_SSE2)) || \ + (defined(OPUS_X86_MAY_HAVE_SSE4_1) && !defined(OPUS_X86_PRESUME_SSE4_1)) + #include "cpu_support.h" #include "macros.h" #include "main.h" #include "pitch.h" #include "x86cpu.h" -#if (defined(OPUS_X86_MAY_HAVE_SSE) && !defined(OPUS_X86_PRESUME_SSE)) || \ - (defined(OPUS_X86_MAY_HAVE_SSE2) && !defined(OPUS_X86_PRESUME_SSE2)) || \ - (defined(OPUS_X86_MAY_HAVE_SSE4_1) && !defined(OPUS_X86_PRESUME_SSE4_1)) - - #if defined(_MSC_VER) #include diff --git a/Frameworks/Opus/Opus/opus/config.h b/Frameworks/Opus/Opus/opus/config.h index d9d405ace..2aa962100 100644 --- a/Frameworks/Opus/Opus/opus/config.h +++ b/Frameworks/Opus/Opus/opus/config.h @@ -41,9 +41,19 @@ POSSIBILITY OF SUCH DAMAGE. #define __MACOSX__ +#ifndef __aarch64__ #define OPUS_HAVE_RTCD 1 #define OPUS_X86_MAY_HAVE_SSE 1 #define OPUS_X86_MAY_HAVE_SSE2 1 #define OPUS_X86_MAY_HAVE_SSE4_1 1 +#else + +#undef OPUS_HAVE_RTCD +#define OPUS_ARM_MAY_HAVE_NEON 1 +#define OPUS_ARM_MAY_HAVE_NEON_INTR 1 +#define OPUS_ARM_PRESUME_NEON 1 +#define OPUS_ARM_PRESUME_NEON_INTR 1 +#endif + #endif CONFIG_H diff --git a/Frameworks/Opus/Opus/opus/silk/x86/NSQ_del_dec_sse.c b/Frameworks/Opus/Opus/opus/silk/x86/NSQ_del_dec_sse.c index f5245dc17..441d66c19 100644 --- a/Frameworks/Opus/Opus/opus/silk/x86/NSQ_del_dec_sse.c +++ b/Frameworks/Opus/Opus/opus/silk/x86/NSQ_del_dec_sse.c @@ -29,6 +29,8 @@ #include "config.h" #endif +#if defined(OPUS_X86_MAY_HAVE_SSE4_1) + #include #include #include @@ -855,3 +857,5 @@ static OPUS_INLINE void silk_nsq_del_dec_scale_states_sse4_1( } } } + +#endif diff --git a/Frameworks/Opus/Opus/opus/silk/x86/NSQ_sse.c b/Frameworks/Opus/Opus/opus/silk/x86/NSQ_sse.c index 1d2965612..7fbeadd7a 100644 --- a/Frameworks/Opus/Opus/opus/silk/x86/NSQ_sse.c +++ b/Frameworks/Opus/Opus/opus/silk/x86/NSQ_sse.c @@ -29,6 +29,8 @@ #include "config.h" #endif +#ifdef __x86_64__ + #include #include #include @@ -718,3 +720,5 @@ static OPUS_INLINE void silk_nsq_scale_states_sse4_1( } } } + +#endif diff --git a/Frameworks/Opus/Opus/opus/silk/x86/VAD_sse.c b/Frameworks/Opus/Opus/opus/silk/x86/VAD_sse.c index 0f4fb7a62..0683b1b0e 100644 --- a/Frameworks/Opus/Opus/opus/silk/x86/VAD_sse.c +++ b/Frameworks/Opus/Opus/opus/silk/x86/VAD_sse.c @@ -29,6 +29,8 @@ #include "config.h" #endif +#ifdef __x86_64__ + #include #include #include @@ -275,3 +277,5 @@ opus_int silk_VAD_GetSA_Q8_sse4_1( /* O Return value, 0 if s RESTORE_STACK; return( ret ); } + +#endif diff --git a/Frameworks/Opus/Opus/opus/silk/x86/VQ_WMat_EC_sse.c b/Frameworks/Opus/Opus/opus/silk/x86/VQ_WMat_EC_sse.c index 50b71ade4..691fa5efe 100644 --- a/Frameworks/Opus/Opus/opus/silk/x86/VQ_WMat_EC_sse.c +++ b/Frameworks/Opus/Opus/opus/silk/x86/VQ_WMat_EC_sse.c @@ -29,6 +29,8 @@ #include "config.h" #endif +#ifdef __x86_64__ + #include #include #include @@ -140,3 +142,5 @@ void silk_VQ_WMat_EC_sse4_1( cb_row_Q7 += LTP_ORDER; } } + +#endif diff --git a/Frameworks/Opus/Opus/opus/silk/x86/x86_silk_map.c b/Frameworks/Opus/Opus/opus/silk/x86/x86_silk_map.c index 6e79675a8..eae1e8028 100644 --- a/Frameworks/Opus/Opus/opus/silk/x86/x86_silk_map.c +++ b/Frameworks/Opus/Opus/opus/silk/x86/x86_silk_map.c @@ -35,7 +35,7 @@ #include "pitch.h" #include "main.h" -#if !defined(OPUS_X86_PRESUME_SSE4_1) +#if !defined(OPUS_X86_PRESUME_SSE4_1) && defined(__x86_64__) #if defined(FIXED_POINT) diff --git a/Frameworks/SSEQPlayer/SSEQPlayer.xcodeproj/project.pbxproj b/Frameworks/SSEQPlayer/SSEQPlayer.xcodeproj/project.pbxproj index b69cdd9e3..8e6be9a37 100644 --- a/Frameworks/SSEQPlayer/SSEQPlayer.xcodeproj/project.pbxproj +++ b/Frameworks/SSEQPlayer/SSEQPlayer.xcodeproj/project.pbxproj @@ -241,8 +241,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 83848FB71807623F00E7332D = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -358,7 +358,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -403,7 +403,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; }; name = Release; @@ -411,7 +411,10 @@ 83848FE11807623F00E7332D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -419,6 +422,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.sseqplayer; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; @@ -428,7 +432,10 @@ 83848FE21807623F00E7332D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -436,6 +443,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.sseqplayer; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; diff --git a/Frameworks/Shorten/Shorten.xcodeproj/project.pbxproj b/Frameworks/Shorten/Shorten.xcodeproj/project.pbxproj index 5da0d063b..6954c5be5 100644 --- a/Frameworks/Shorten/Shorten.xcodeproj/project.pbxproj +++ b/Frameworks/Shorten/Shorten.xcodeproj/project.pbxproj @@ -176,8 +176,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8DC2EF4F0486A6940098B216 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -245,8 +245,11 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -260,6 +263,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.lib.shorten; PRODUCT_NAME = Shorten; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; @@ -271,7 +275,10 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -285,6 +292,7 @@ OTHER_CFLAGS = "-DHAVE_CONFIG_H"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.lib.shorten; PRODUCT_NAME = Shorten; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SHARED_PRECOMPS_DIR = "$(CACHE_ROOT)/SharedPrecompiledHeaders"; SKIP_INSTALL = YES; @@ -333,7 +341,7 @@ ../../include, include, ); - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYMROOT = ../../build; @@ -378,7 +386,7 @@ ../../include, include, ); - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SYMROOT = ../../build; }; diff --git a/Frameworks/Syntrax-c/Syntrax_c.xcodeproj/project.pbxproj b/Frameworks/Syntrax-c/Syntrax_c.xcodeproj/project.pbxproj index 357addf4e..0977ca7ea 100644 --- a/Frameworks/Syntrax-c/Syntrax_c.xcodeproj/project.pbxproj +++ b/Frameworks/Syntrax-c/Syntrax_c.xcodeproj/project.pbxproj @@ -112,8 +112,8 @@ TargetAttributes = { 832FF31A1C96511E0076D662 = { CreatedOnToolsVersion = 7.2.1; - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -205,7 +205,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -254,7 +254,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; VERSIONING_SYSTEM = "apple-generic"; @@ -265,7 +265,10 @@ 832FF3241C96511E0076D662 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -274,6 +277,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.cogx.Syntrax-c"; PRODUCT_NAME = Syntrax_c; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; }; name = Debug; @@ -281,7 +285,10 @@ 832FF3251C96511E0076D662 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -290,6 +297,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.cogx.Syntrax-c"; PRODUCT_NAME = Syntrax_c; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; }; name = Release; diff --git a/Frameworks/TagLib/TagLib.xcodeproj/project.pbxproj b/Frameworks/TagLib/TagLib.xcodeproj/project.pbxproj index e1cb7d9f0..32a394891 100644 --- a/Frameworks/TagLib/TagLib.xcodeproj/project.pbxproj +++ b/Frameworks/TagLib/TagLib.xcodeproj/project.pbxproj @@ -900,8 +900,8 @@ LastUpgradeCheck = 0940; TargetAttributes = { 8DC2EF4F0486A6940098B216 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -1045,7 +1045,10 @@ 1DEB91AE08733DA50010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -1062,6 +1065,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.lib.taglib; PRODUCT_NAME = TagLib; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; @@ -1071,7 +1075,10 @@ 1DEB91AF08733DA50010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -1086,6 +1093,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.lib.taglib; PRODUCT_NAME = TagLib; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; WRAPPER_EXTENSION = framework; }; @@ -1126,7 +1134,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "-DHAVE_CONFIG_H"; SDKROOT = macosx; @@ -1168,7 +1176,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; OTHER_CFLAGS = "-DHAVE_CONFIG_H"; SDKROOT = macosx; SKIP_INSTALL = YES; diff --git a/Frameworks/Vorbis/macosx/Vorbis.xcodeproj/project.pbxproj b/Frameworks/Vorbis/macosx/Vorbis.xcodeproj/project.pbxproj index 66f184906..7f1a2444c 100644 --- a/Frameworks/Vorbis/macosx/Vorbis.xcodeproj/project.pbxproj +++ b/Frameworks/Vorbis/macosx/Vorbis.xcodeproj/project.pbxproj @@ -578,8 +578,8 @@ LastUpgradeCheck = 1130; TargetAttributes = { 730F23A1091827B100AB638C = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; 738835E30B18F870005C7A69 = { DevelopmentTeam = N6E749HJ2X; @@ -757,11 +757,11 @@ 730F23ED091827B100AB638C /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - DEVELOPMENT_TEAM = N6E749HJ2X; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_SEARCH_PATHS = /Library/Frameworks; @@ -772,7 +772,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = ""; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = org.xiph.vorbis; PRODUCT_NAME = Vorbis; @@ -792,11 +792,11 @@ 730F23EE091827B100AB638C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = YES; - DEVELOPMENT_TEAM = N6E749HJ2X; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_SEARCH_PATHS = /Library/Frameworks; @@ -805,7 +805,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = ""; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = org.xiph.vorbis; PRODUCT_NAME = Vorbis; diff --git a/Frameworks/WavPack/WavPack.xcodeproj/project.pbxproj b/Frameworks/WavPack/WavPack.xcodeproj/project.pbxproj index 9edef38b6..9aa70f979 100644 --- a/Frameworks/WavPack/WavPack.xcodeproj/project.pbxproj +++ b/Frameworks/WavPack/WavPack.xcodeproj/project.pbxproj @@ -248,8 +248,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8DC2EF4F0486A6940098B216 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -336,8 +336,11 @@ 1DEB91AE08733DA50010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -367,6 +370,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.lib.wavpack; PRODUCT_NAME = WavPack; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; @@ -377,7 +381,10 @@ 1DEB91AF08733DA50010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -404,6 +411,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.lib.wavpack; PRODUCT_NAME = WavPack; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; @@ -444,7 +452,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYMROOT = ../../build; @@ -483,7 +491,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SYMROOT = ../../build; }; diff --git a/Frameworks/g719/g719.xcodeproj/project.pbxproj b/Frameworks/g719/g719.xcodeproj/project.pbxproj index ba8b15b16..e7c6fd981 100644 --- a/Frameworks/g719/g719.xcodeproj/project.pbxproj +++ b/Frameworks/g719/g719.xcodeproj/project.pbxproj @@ -349,7 +349,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -399,7 +399,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; VERSIONING_SYSTEM = "apple-generic"; diff --git a/Frameworks/lazyusf2/lazyusf2.xcodeproj/project.pbxproj b/Frameworks/lazyusf2/lazyusf2.xcodeproj/project.pbxproj index 1f08cd081..5724a4150 100644 --- a/Frameworks/lazyusf2/lazyusf2.xcodeproj/project.pbxproj +++ b/Frameworks/lazyusf2/lazyusf2.xcodeproj/project.pbxproj @@ -964,8 +964,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 83C8B62118AF57770071B040 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -1126,7 +1126,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -1171,7 +1171,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; }; name = Release; @@ -1179,13 +1179,16 @@ 83C8B64B18AF57770071B040 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; - GCC_PREPROCESSOR_DEFINITIONS = ( + GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1"; + "GCC_PREPROCESSOR_DEFINITIONS[arch=i386]" = ( "DEBUG=1", - ARCH_MIN_SSE2, DYNAREC, ); GCC_WARN_PEDANTIC = YES; @@ -1193,6 +1196,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.lazyusf2; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; USER_HEADER_SEARCH_PATHS = lazyusf2; @@ -1203,14 +1207,17 @@ 83C8B64C18AF57770071B040 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; GCC_OPTIMIZATION_LEVEL = fast; - GCC_PREPROCESSOR_DEFINITIONS = ( + GCC_PREPROCESSOR_DEFINITIONS = "$(inherit)"; + "GCC_PREPROCESSOR_DEFINITIONS[arch=i386]" = ( "$(inherit)", - ARCH_MIN_SSE2, DYNAREC, ); GCC_WARN_PEDANTIC = YES; @@ -1218,6 +1225,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.lazyusf2; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; USER_HEADER_SEARCH_PATHS = lazyusf2; diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/cached_interp.c b/Frameworks/lazyusf2/lazyusf2/r4300/cached_interp.c index ccdc87fbd..7f2acbf37 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/cached_interp.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/cached_interp.c @@ -154,7 +154,9 @@ static void osal_fastcall FIN_BLOCK(usf_state_t * state) Used by dynarec only, check should be unnecessary */ state->PC->ops(state); +#ifdef DYNAREC if (state->r4300emu == CORE_DYNAREC) dyna_jump(state); +#endif } else { @@ -176,7 +178,9 @@ Used by dynarec only, check should be unnecessary else state->PC->ops(state); +#ifdef DYNAREC if (state->r4300emu == CORE_DYNAREC) dyna_jump(state); +#endif } } @@ -196,8 +200,10 @@ The preceeding update_debugger SHOULD be unnecessary since it should have been called before NOTCOMPILED would have been executed */ state->PC->ops(state); +#ifdef DYNAREC if (state->r4300emu == CORE_DYNAREC) dyna_jump(state); +#endif } static void osal_fastcall NOTCOMPILED2(usf_state_t * state) @@ -537,7 +543,9 @@ void osal_fastcall jump_to_func(usf_state_t * state) } state->PC=state->actual->block+((addr-state->actual->start)>>2); +#ifdef DYNAREC if (state->r4300emu == CORE_DYNAREC) dyna_jump(state); +#endif } #undef addr diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/exception.c b/Frameworks/lazyusf2/lazyusf2/r4300/exception.c index 41c185726..47e3e198c 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/exception.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/exception.c @@ -96,11 +96,13 @@ void TLB_refill_exception(usf_state_t * state, unsigned int address, int w) state->last_addr = state->PC->addr; +#ifdef DYNAREC if (state->r4300emu == CORE_DYNAREC) { dyna_jump(state); if (!state->dyna_interp) state->delay_slot = 0; } +#endif if (state->r4300emu != CORE_DYNAREC || state->dyna_interp) { @@ -131,11 +133,13 @@ void osal_fastcall exception_general(usf_state_t * state) } generic_jump_to(state, 0x80000180); state->last_addr = state->PC->addr; +#ifdef DYNAREC if (state->r4300emu == CORE_DYNAREC) { dyna_jump(state); if (!state->dyna_interp) state->delay_slot = 0; } +#endif if (state->r4300emu != CORE_DYNAREC || state->dyna_interp) { state->dyna_interp = 0; diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/interupt.c b/Frameworks/lazyusf2/lazyusf2/r4300/interupt.c index ce05e92d4..f740b537d 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/interupt.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/interupt.c @@ -506,7 +506,9 @@ void osal_fastcall gen_interupt(usf_state_t * state) if (state->stop == 1) { state->g_gs_vi_counter = 0; // debug +#ifdef DYNAREC dyna_stop(state); +#endif } if (!state->interupt_unsafe_state) diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/r4300.c b/Frameworks/lazyusf2/lazyusf2/r4300/r4300.c index 9ccc09964..790088ba8 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/r4300.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/r4300.c @@ -206,6 +206,7 @@ void r4300_reset_soft(usf_state_t * state) /* ready to execute IPL3 */ } +#ifdef DYNAREC #if !defined(NO_ASM) static void dynarec_setup_code() { @@ -236,6 +237,7 @@ static void dynarec_setup_code() dyna_stop(state); } #endif +#endif void r4300_begin(usf_state_t * state) { diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/recomp.c b/Frameworks/lazyusf2/lazyusf2/r4300/recomp.c index 8e92344bb..a192daac5 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/recomp.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/recomp.c @@ -49,27 +49,34 @@ #include "ops.h" #include "tlb.h" +#ifdef DYNAREC static void *malloc_exec(usf_state_t *, size_t size); static void free_exec(void *ptr, size_t length); - +#endif static void RSV(usf_state_t * state) { state->dst->ops = state->current_instruction_table.RESERVED; - state->recomp_func = genreserved; +#ifdef DYNAREC + state->recomp_func = genreserved; +#endif } static void RFIN_BLOCK(usf_state_t * state) { state->dst->ops = state->current_instruction_table.FIN_BLOCK; +#ifdef DYNAREC state->recomp_func = genfin_block; +#endif } static void RNOTCOMPILED(usf_state_t * state) { state->dst->ops = state->current_instruction_table.NOTCOMPILED; +#ifdef DYNAREC state->recomp_func = gennotcompiled; +#endif } static void recompile_standard_i_type(usf_state_t * state) @@ -113,13 +120,17 @@ static void recompile_standard_cf_type(usf_state_t * state) static void RNOP(usf_state_t * state) { state->dst->ops = state->current_instruction_table.NOP; +#ifdef DYNAREC state->recomp_func = gennop; +#endif } static void RSLL(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SLL; +#ifdef DYNAREC state->recomp_func = gensll; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -127,7 +138,9 @@ static void RSLL(usf_state_t * state) static void RSRL(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SRL; +#ifdef DYNAREC state->recomp_func = gensrl; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -135,7 +148,9 @@ static void RSRL(usf_state_t * state) static void RSRA(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SRA; +#ifdef DYNAREC state->recomp_func = gensra; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -143,7 +158,9 @@ static void RSRA(usf_state_t * state) static void RSLLV(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SLLV; +#ifdef DYNAREC state->recomp_func = gensllv; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -151,7 +168,9 @@ static void RSLLV(usf_state_t * state) static void RSRLV(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SRLV; +#ifdef DYNAREC state->recomp_func = gensrlv; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -159,7 +178,9 @@ static void RSRLV(usf_state_t * state) static void RSRAV(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SRAV; +#ifdef DYNAREC state->recomp_func = gensrav; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -167,40 +188,52 @@ static void RSRAV(usf_state_t * state) static void RJR(usf_state_t * state) { state->dst->ops = state->current_instruction_table.JR; +#ifdef DYNAREC state->recomp_func = genjr; +#endif recompile_standard_i_type(state); } static void RJALR(usf_state_t * state) { state->dst->ops = state->current_instruction_table.JALR; +#ifdef DYNAREC state->recomp_func = genjalr; +#endif recompile_standard_r_type(state); } static void RSYSCALL(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SYSCALL; +#ifdef DYNAREC state->recomp_func = gensyscall; +#endif } /* Idle loop hack from 64th Note */ static void RBREAK(usf_state_t * state) { state->dst->ops = state->current_instruction_table.BREAK; +#ifdef DYNAREC state->recomp_func = genbreak; +#endif } static void RSYNC(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SYNC; +#ifdef DYNAREC state->recomp_func = gensync; +#endif } static void RMFHI(usf_state_t * state) { state->dst->ops = state->current_instruction_table.MFHI; +#ifdef DYNAREC state->recomp_func = genmfhi; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -208,14 +241,18 @@ static void RMFHI(usf_state_t * state) static void RMTHI(usf_state_t * state) { state->dst->ops = state->current_instruction_table.MTHI; +#ifdef DYNAREC state->recomp_func = genmthi; +#endif recompile_standard_r_type(state); } static void RMFLO(usf_state_t * state) { state->dst->ops = state->current_instruction_table.MFLO; +#ifdef DYNAREC state->recomp_func = genmflo; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -223,14 +260,18 @@ static void RMFLO(usf_state_t * state) static void RMTLO(usf_state_t * state) { state->dst->ops = state->current_instruction_table.MTLO; +#ifdef DYNAREC state->recomp_func = genmtlo; +#endif recompile_standard_r_type(state); } static void RDSLLV(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DSLLV; +#ifdef DYNAREC state->recomp_func = gendsllv; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -238,7 +279,9 @@ static void RDSLLV(usf_state_t * state) static void RDSRLV(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DSRLV; +#ifdef DYNAREC state->recomp_func = gendsrlv; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -246,7 +289,9 @@ static void RDSRLV(usf_state_t * state) static void RDSRAV(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DSRAV; +#ifdef DYNAREC state->recomp_func = gendsrav; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -254,63 +299,81 @@ static void RDSRAV(usf_state_t * state) static void RMULT(usf_state_t * state) { state->dst->ops = state->current_instruction_table.MULT; +#ifdef DYNAREC state->recomp_func = genmult; +#endif recompile_standard_r_type(state); } static void RMULTU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.MULTU; +#ifdef DYNAREC state->recomp_func = genmultu; +#endif recompile_standard_r_type(state); } static void RDIV(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DIV; +#ifdef DYNAREC state->recomp_func = gendiv; +#endif recompile_standard_r_type(state); } static void RDIVU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DIVU; +#ifdef DYNAREC state->recomp_func = gendivu; +#endif recompile_standard_r_type(state); } static void RDMULT(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DMULT; +#ifdef DYNAREC state->recomp_func = gendmult; +#endif recompile_standard_r_type(state); } static void RDMULTU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DMULTU; +#ifdef DYNAREC state->recomp_func = gendmultu; +#endif recompile_standard_r_type(state); } static void RDDIV(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DDIV; +#ifdef DYNAREC state->recomp_func = genddiv; +#endif recompile_standard_r_type(state); } static void RDDIVU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DDIVU; +#ifdef DYNAREC state->recomp_func = genddivu; +#endif recompile_standard_r_type(state); } static void RADD(usf_state_t * state) { state->dst->ops = state->current_instruction_table.ADD; +#ifdef DYNAREC state->recomp_func = genadd; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -318,7 +381,9 @@ static void RADD(usf_state_t * state) static void RADDU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.ADDU; +#ifdef DYNAREC state->recomp_func = genaddu; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -326,7 +391,9 @@ static void RADDU(usf_state_t * state) static void RSUB(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SUB; +#ifdef DYNAREC state->recomp_func = gensub; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -334,7 +401,9 @@ static void RSUB(usf_state_t * state) static void RSUBU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SUBU; +#ifdef DYNAREC state->recomp_func = gensubu; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -342,7 +411,9 @@ static void RSUBU(usf_state_t * state) static void RAND(usf_state_t * state) { state->dst->ops = state->current_instruction_table.AND; +#ifdef DYNAREC state->recomp_func = genand; +#endif recompile_standard_r_type(state); if(state->dst->f.r.rd == state->reg) RNOP(state); } @@ -350,7 +421,9 @@ static void RAND(usf_state_t * state) static void ROR(usf_state_t * state) { state->dst->ops = state->current_instruction_table.OR; +#ifdef DYNAREC state->recomp_func = genor; +#endif recompile_standard_r_type(state); if(state->dst->f.r.rd == state->reg) RNOP(state); } @@ -358,7 +431,9 @@ static void ROR(usf_state_t * state) static void RXOR(usf_state_t * state) { state->dst->ops = state->current_instruction_table.XOR; +#ifdef DYNAREC state->recomp_func = genxor; +#endif recompile_standard_r_type(state); if(state->dst->f.r.rd == state->reg) RNOP(state); } @@ -366,7 +441,9 @@ static void RXOR(usf_state_t * state) static void RNOR(usf_state_t * state) { state->dst->ops = state->current_instruction_table.NOR; +#ifdef DYNAREC state->recomp_func = gennor; +#endif recompile_standard_r_type(state); if(state->dst->f.r.rd == state->reg) RNOP(state); } @@ -374,7 +451,9 @@ static void RNOR(usf_state_t * state) static void RSLT(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SLT; +#ifdef DYNAREC state->recomp_func = genslt; +#endif recompile_standard_r_type(state); if(state->dst->f.r.rd == state->reg) RNOP(state); } @@ -382,7 +461,9 @@ static void RSLT(usf_state_t * state) static void RSLTU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SLTU; +#ifdef DYNAREC state->recomp_func = gensltu; +#endif recompile_standard_r_type(state); if(state->dst->f.r.rd == state->reg) RNOP(state); } @@ -390,7 +471,9 @@ static void RSLTU(usf_state_t * state) static void RDADD(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DADD; +#ifdef DYNAREC state->recomp_func = gendadd; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -398,7 +481,9 @@ static void RDADD(usf_state_t * state) static void RDADDU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DADDU; +#ifdef DYNAREC state->recomp_func = gendaddu; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -406,7 +491,9 @@ static void RDADDU(usf_state_t * state) static void RDSUB(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DSUB; +#ifdef DYNAREC state->recomp_func = gendsub; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -414,7 +501,9 @@ static void RDSUB(usf_state_t * state) static void RDSUBU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DSUBU; +#ifdef DYNAREC state->recomp_func = gendsubu; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -422,44 +511,58 @@ static void RDSUBU(usf_state_t * state) static void RTGE(usf_state_t * state) { state->dst->ops = state->current_instruction_table.NI; +#ifdef DYNAREC state->recomp_func = genni; +#endif } static void RTGEU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.NI; +#ifdef DYNAREC state->recomp_func = genni; +#endif } static void RTLT(usf_state_t * state) { state->dst->ops = state->current_instruction_table.NI; +#ifdef DYNAREC state->recomp_func = genni; +#endif } static void RTLTU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.NI; +#ifdef DYNAREC state->recomp_func = genni; +#endif } static void RTEQ(usf_state_t * state) { state->dst->ops = state->current_instruction_table.TEQ; +#ifdef DYNAREC state->recomp_func = genteq; +#endif recompile_standard_r_type(state); } static void RTNE(usf_state_t * state) { state->dst->ops = state->current_instruction_table.NI; +#ifdef DYNAREC state->recomp_func = genni; +#endif } static void RDSLL(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DSLL; +#ifdef DYNAREC state->recomp_func = gendsll; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -467,7 +570,9 @@ static void RDSLL(usf_state_t * state) static void RDSRL(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DSRL; +#ifdef DYNAREC state->recomp_func = gendsrl; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -475,7 +580,9 @@ static void RDSRL(usf_state_t * state) static void RDSRA(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DSRA; +#ifdef DYNAREC state->recomp_func = gendsra; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -483,7 +590,9 @@ static void RDSRA(usf_state_t * state) static void RDSLL32(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DSLL32; +#ifdef DYNAREC state->recomp_func = gendsll32; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -491,7 +600,9 @@ static void RDSLL32(usf_state_t * state) static void RDSRL32(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DSRL32; +#ifdef DYNAREC state->recomp_func = gendsrl32; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -499,7 +610,9 @@ static void RDSRL32(usf_state_t * state) static void RDSRA32(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DSRA32; +#ifdef DYNAREC state->recomp_func = gendsra32; +#endif recompile_standard_r_type(state); if (state->dst->f.r.rd == state->reg) RNOP(state); } @@ -524,7 +637,9 @@ static void RBLTZ(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BLTZ; +#ifdef DYNAREC state->recomp_func = genbltz; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -532,13 +647,17 @@ static void RBLTZ(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BLTZ_IDLE; +#ifdef DYNAREC state->recomp_func = genbltz_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BLTZ_OUT; +#ifdef DYNAREC state->recomp_func = genbltz_out; +#endif } } @@ -546,7 +665,9 @@ static void RBGEZ(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BGEZ; +#ifdef DYNAREC state->recomp_func = genbgez; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -554,13 +675,17 @@ static void RBGEZ(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BGEZ_IDLE; +#ifdef DYNAREC state->recomp_func = genbgez_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BGEZ_OUT; +#ifdef DYNAREC state->recomp_func = genbgez_out; +#endif } } @@ -568,7 +693,9 @@ static void RBLTZL(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BLTZL; +#ifdef DYNAREC state->recomp_func = genbltzl; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -576,13 +703,17 @@ static void RBLTZL(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BLTZL_IDLE; +#ifdef DYNAREC state->recomp_func = genbltzl_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BLTZL_OUT; +#ifdef DYNAREC state->recomp_func = genbltzl_out; +#endif } } @@ -590,7 +721,9 @@ static void RBGEZL(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BGEZL; +#ifdef DYNAREC state->recomp_func = genbgezl; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -598,57 +731,75 @@ static void RBGEZL(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BGEZL_IDLE; +#ifdef DYNAREC state->recomp_func = genbgezl_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BGEZL_OUT; +#ifdef DYNAREC state->recomp_func = genbgezl_out; +#endif } } static void RTGEI(usf_state_t * state) { state->dst->ops = state->current_instruction_table.NI; +#ifdef DYNAREC state->recomp_func = genni; +#endif } static void RTGEIU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.NI; +#ifdef DYNAREC state->recomp_func = genni; +#endif } static void RTLTI(usf_state_t * state) { state->dst->ops = state->current_instruction_table.NI; +#ifdef DYNAREC state->recomp_func = genni; +#endif } static void RTLTIU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.NI; +#ifdef DYNAREC state->recomp_func = genni; +#endif } static void RTEQI(usf_state_t * state) { state->dst->ops = state->current_instruction_table.NI; +#ifdef DYNAREC state->recomp_func = genni; +#endif } static void RTNEI(usf_state_t * state) { state->dst->ops = state->current_instruction_table.NI; +#ifdef DYNAREC state->recomp_func = genni; +#endif } static void RBLTZAL(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BLTZAL; +#ifdef DYNAREC state->recomp_func = genbltzal; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -656,13 +807,17 @@ static void RBLTZAL(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BLTZAL_IDLE; +#ifdef DYNAREC state->recomp_func = genbltzal_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BLTZAL_OUT; +#ifdef DYNAREC state->recomp_func = genbltzal_out; +#endif } } @@ -670,7 +825,9 @@ static void RBGEZAL(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BGEZAL; +#ifdef DYNAREC state->recomp_func = genbgezal; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -678,13 +835,17 @@ static void RBGEZAL(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BGEZAL_IDLE; +#ifdef DYNAREC state->recomp_func = genbgezal_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BGEZAL_OUT; +#ifdef DYNAREC state->recomp_func = genbgezal_out; +#endif } } @@ -692,7 +853,9 @@ static void RBLTZALL(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BLTZALL; +#ifdef DYNAREC state->recomp_func = genbltzall; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -700,13 +863,17 @@ static void RBLTZALL(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BLTZALL_IDLE; +#ifdef DYNAREC state->recomp_func = genbltzall_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BLTZALL_OUT; +#ifdef DYNAREC state->recomp_func = genbltzall_out; +#endif } } @@ -714,7 +881,9 @@ static void RBGEZALL(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BGEZALL; +#ifdef DYNAREC state->recomp_func = genbgezall; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -722,13 +891,17 @@ static void RBGEZALL(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BGEZALL_IDLE; +#ifdef DYNAREC state->recomp_func = genbgezall_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BGEZALL_OUT; +#ifdef DYNAREC state->recomp_func = genbgezall_out; +#endif } } @@ -747,31 +920,41 @@ static void (*recomp_regimm[32])(usf_state_t *) = static void RTLBR(usf_state_t * state) { state->dst->ops = state->current_instruction_table.TLBR; +#ifdef DYNAREC state->recomp_func = gentlbr; +#endif } static void RTLBWI(usf_state_t * state) { state->dst->ops = state->current_instruction_table.TLBWI; +#ifdef DYNAREC state->recomp_func = gentlbwi; +#endif } static void RTLBWR(usf_state_t * state) { state->dst->ops = state->current_instruction_table.TLBWR; +#ifdef DYNAREC state->recomp_func = gentlbwr; +#endif } static void RTLBP(usf_state_t * state) { state->dst->ops = state->current_instruction_table.TLBP; +#ifdef DYNAREC state->recomp_func = gentlbp; +#endif } static void RERET(usf_state_t * state) { state->dst->ops = state->current_instruction_table.ERET; +#ifdef DYNAREC state->recomp_func = generet; +#endif } static void (*recomp_tlb[64])(usf_state_t *) = @@ -793,7 +976,9 @@ static void (*recomp_tlb[64])(usf_state_t *) = static void RMFC0(usf_state_t * state) { state->dst->ops = state->current_instruction_table.MFC0; +#ifdef DYNAREC state->recomp_func = genmfc0; +#endif recompile_standard_r_type(state); state->dst->f.r.rd = (long long*)(state->g_cp0_regs + ((state->src >> 11) & 0x1F)); state->dst->f.r.nrd = (state->src >> 11) & 0x1F; @@ -803,7 +988,9 @@ static void RMFC0(usf_state_t * state) static void RMTC0(usf_state_t * state) { state->dst->ops = state->current_instruction_table.MTC0; +#ifdef DYNAREC state->recomp_func = genmtc0; +#endif recompile_standard_r_type(state); state->dst->f.r.nrd = (state->src >> 11) & 0x1F; } @@ -829,7 +1016,9 @@ static void RBC1F(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BC1F; +#ifdef DYNAREC state->recomp_func = genbc1f; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -837,13 +1026,17 @@ static void RBC1F(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BC1F_IDLE; +#ifdef DYNAREC state->recomp_func = genbc1f_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BC1F_OUT; +#ifdef DYNAREC state->recomp_func = genbc1f_out; +#endif } } @@ -851,7 +1044,9 @@ static void RBC1T(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BC1T; +#ifdef DYNAREC state->recomp_func = genbc1t; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -859,13 +1054,17 @@ static void RBC1T(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BC1T_IDLE; +#ifdef DYNAREC state->recomp_func = genbc1t_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BC1T_OUT; +#ifdef DYNAREC state->recomp_func = genbc1t_out; +#endif } } @@ -873,7 +1072,9 @@ static void RBC1FL(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BC1FL; +#ifdef DYNAREC state->recomp_func = genbc1fl; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -881,13 +1082,17 @@ static void RBC1FL(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BC1FL_IDLE; +#ifdef DYNAREC state->recomp_func = genbc1fl_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BC1FL_OUT; +#ifdef DYNAREC state->recomp_func = genbc1fl_out; +#endif } } @@ -895,7 +1100,9 @@ static void RBC1TL(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BC1TL; +#ifdef DYNAREC state->recomp_func = genbc1tl; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -903,13 +1110,17 @@ static void RBC1TL(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BC1TL_IDLE; +#ifdef DYNAREC state->recomp_func = genbc1tl_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BC1TL_OUT; +#ifdef DYNAREC state->recomp_func = genbc1tl_out; +#endif } } @@ -926,245 +1137,315 @@ static void (*recomp_bc[4])(usf_state_t *) = static void RADD_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.ADD_S; +#ifdef DYNAREC state->recomp_func = genadd_s; +#endif recompile_standard_cf_type(state); } static void RSUB_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SUB_S; +#ifdef DYNAREC state->recomp_func = gensub_s; +#endif recompile_standard_cf_type(state); } static void RMUL_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.MUL_S; +#ifdef DYNAREC state->recomp_func = genmul_s; +#endif recompile_standard_cf_type(state); } static void RDIV_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DIV_S; +#ifdef DYNAREC state->recomp_func = gendiv_s; +#endif recompile_standard_cf_type(state); } static void RSQRT_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SQRT_S; +#ifdef DYNAREC state->recomp_func = gensqrt_s; +#endif recompile_standard_cf_type(state); } static void RABS_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.ABS_S; +#ifdef DYNAREC state->recomp_func = genabs_s; +#endif recompile_standard_cf_type(state); } static void RMOV_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.MOV_S; +#ifdef DYNAREC state->recomp_func = genmov_s; +#endif recompile_standard_cf_type(state); } static void RNEG_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.NEG_S; +#ifdef DYNAREC state->recomp_func = genneg_s; +#endif recompile_standard_cf_type(state); } static void RROUND_L_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.ROUND_L_S; +#ifdef DYNAREC state->recomp_func = genround_l_s; +#endif recompile_standard_cf_type(state); } static void RTRUNC_L_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.TRUNC_L_S; +#ifdef DYNAREC state->recomp_func = gentrunc_l_s; +#endif recompile_standard_cf_type(state); } static void RCEIL_L_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.CEIL_L_S; +#ifdef DYNAREC state->recomp_func = genceil_l_s; +#endif recompile_standard_cf_type(state); } static void RFLOOR_L_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.FLOOR_L_S; +#ifdef DYNAREC state->recomp_func = genfloor_l_s; +#endif recompile_standard_cf_type(state); } static void RROUND_W_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.ROUND_W_S; +#ifdef DYNAREC state->recomp_func = genround_w_s; +#endif recompile_standard_cf_type(state); } static void RTRUNC_W_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.TRUNC_W_S; +#ifdef DYNAREC state->recomp_func = gentrunc_w_s; +#endif recompile_standard_cf_type(state); } static void RCEIL_W_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.CEIL_W_S; +#ifdef DYNAREC state->recomp_func = genceil_w_s; +#endif recompile_standard_cf_type(state); } static void RFLOOR_W_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.FLOOR_W_S; +#ifdef DYNAREC state->recomp_func = genfloor_w_s; +#endif recompile_standard_cf_type(state); } static void RCVT_D_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.CVT_D_S; +#ifdef DYNAREC state->recomp_func = gencvt_d_s; +#endif recompile_standard_cf_type(state); } static void RCVT_W_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.CVT_W_S; +#ifdef DYNAREC state->recomp_func = gencvt_w_s; +#endif recompile_standard_cf_type(state); } static void RCVT_L_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.CVT_L_S; +#ifdef DYNAREC state->recomp_func = gencvt_l_s; +#endif recompile_standard_cf_type(state); } static void RC_F_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_F_S; +#ifdef DYNAREC state->recomp_func = genc_f_s; +#endif recompile_standard_cf_type(state); } static void RC_UN_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_UN_S; +#ifdef DYNAREC state->recomp_func = genc_un_s; +#endif recompile_standard_cf_type(state); } static void RC_EQ_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_EQ_S; +#ifdef DYNAREC state->recomp_func = genc_eq_s; +#endif recompile_standard_cf_type(state); } static void RC_UEQ_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_UEQ_S; +#ifdef DYNAREC state->recomp_func = genc_ueq_s; +#endif recompile_standard_cf_type(state); } static void RC_OLT_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_OLT_S; +#ifdef DYNAREC state->recomp_func = genc_olt_s; +#endif recompile_standard_cf_type(state); } static void RC_ULT_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_ULT_S; +#ifdef DYNAREC state->recomp_func = genc_ult_s; +#endif recompile_standard_cf_type(state); } static void RC_OLE_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_OLE_S; +#ifdef DYNAREC state->recomp_func = genc_ole_s; +#endif recompile_standard_cf_type(state); } static void RC_ULE_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_ULE_S; +#ifdef DYNAREC state->recomp_func = genc_ule_s; +#endif recompile_standard_cf_type(state); } static void RC_SF_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_SF_S; +#ifdef DYNAREC state->recomp_func = genc_sf_s; +#endif recompile_standard_cf_type(state); } static void RC_NGLE_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_NGLE_S; +#ifdef DYNAREC state->recomp_func = genc_ngle_s; +#endif recompile_standard_cf_type(state); } static void RC_SEQ_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_SEQ_S; +#ifdef DYNAREC state->recomp_func = genc_seq_s; +#endif recompile_standard_cf_type(state); } static void RC_NGL_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_NGL_S; +#ifdef DYNAREC state->recomp_func = genc_ngl_s; +#endif recompile_standard_cf_type(state); } static void RC_LT_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_LT_S; +#ifdef DYNAREC state->recomp_func = genc_lt_s; +#endif recompile_standard_cf_type(state); } static void RC_NGE_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_NGE_S; +#ifdef DYNAREC state->recomp_func = genc_nge_s; +#endif recompile_standard_cf_type(state); } static void RC_LE_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_LE_S; +#ifdef DYNAREC state->recomp_func = genc_le_s; +#endif recompile_standard_cf_type(state); } static void RC_NGT_S(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_NGT_S; +#ifdef DYNAREC state->recomp_func = genc_ngt_s; +#endif recompile_standard_cf_type(state); } @@ -1187,245 +1468,315 @@ static void (*recomp_s[64])(usf_state_t *) = static void RADD_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.ADD_D; +#ifdef DYNAREC state->recomp_func = genadd_d; +#endif recompile_standard_cf_type(state); } static void RSUB_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SUB_D; +#ifdef DYNAREC state->recomp_func = gensub_d; +#endif recompile_standard_cf_type(state); } static void RMUL_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.MUL_D; +#ifdef DYNAREC state->recomp_func = genmul_d; +#endif recompile_standard_cf_type(state); } static void RDIV_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DIV_D; +#ifdef DYNAREC state->recomp_func = gendiv_d; +#endif recompile_standard_cf_type(state); } static void RSQRT_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SQRT_D; +#ifdef DYNAREC state->recomp_func = gensqrt_d; +#endif recompile_standard_cf_type(state); } static void RABS_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.ABS_D; +#ifdef DYNAREC state->recomp_func = genabs_d; +#endif recompile_standard_cf_type(state); } static void RMOV_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.MOV_D; +#ifdef DYNAREC state->recomp_func = genmov_d; +#endif recompile_standard_cf_type(state); } static void RNEG_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.NEG_D; +#ifdef DYNAREC state->recomp_func = genneg_d; +#endif recompile_standard_cf_type(state); } static void RROUND_L_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.ROUND_L_D; +#ifdef DYNAREC state->recomp_func = genround_l_d; +#endif recompile_standard_cf_type(state); } static void RTRUNC_L_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.TRUNC_L_D; +#ifdef DYNAREC state->recomp_func = gentrunc_l_d; +#endif recompile_standard_cf_type(state); } static void RCEIL_L_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.CEIL_L_D; +#ifdef DYNAREC state->recomp_func = genceil_l_d; +#endif recompile_standard_cf_type(state); } static void RFLOOR_L_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.FLOOR_L_D; +#ifdef DYNAREC state->recomp_func = genfloor_l_d; +#endif recompile_standard_cf_type(state); } static void RROUND_W_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.ROUND_W_D; +#ifdef DYNAREC state->recomp_func = genround_w_d; +#endif recompile_standard_cf_type(state); } static void RTRUNC_W_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.TRUNC_W_D; +#ifdef DYNAREC state->recomp_func = gentrunc_w_d; +#endif recompile_standard_cf_type(state); } static void RCEIL_W_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.CEIL_W_D; +#ifdef DYNAREC state->recomp_func = genceil_w_d; +#endif recompile_standard_cf_type(state); } static void RFLOOR_W_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.FLOOR_W_D; +#ifdef DYNAREC state->recomp_func = genfloor_w_d; +#endif recompile_standard_cf_type(state); } static void RCVT_S_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.CVT_S_D; +#ifdef DYNAREC state->recomp_func = gencvt_s_d; +#endif recompile_standard_cf_type(state); } static void RCVT_W_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.CVT_W_D; +#ifdef DYNAREC state->recomp_func = gencvt_w_d; +#endif recompile_standard_cf_type(state); } static void RCVT_L_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.CVT_L_D; +#ifdef DYNAREC state->recomp_func = gencvt_l_d; +#endif recompile_standard_cf_type(state); } static void RC_F_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_F_D; +#ifdef DYNAREC state->recomp_func = genc_f_d; +#endif recompile_standard_cf_type(state); } static void RC_UN_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_UN_D; +#ifdef DYNAREC state->recomp_func = genc_un_d; +#endif recompile_standard_cf_type(state); } static void RC_EQ_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_EQ_D; +#ifdef DYNAREC state->recomp_func = genc_eq_d; +#endif recompile_standard_cf_type(state); } static void RC_UEQ_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_UEQ_D; +#ifdef DYNAREC state->recomp_func = genc_ueq_d; +#endif recompile_standard_cf_type(state); } static void RC_OLT_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_OLT_D; +#ifdef DYNAREC state->recomp_func = genc_olt_d; +#endif recompile_standard_cf_type(state); } static void RC_ULT_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_ULT_D; +#ifdef DYNAREC state->recomp_func = genc_ult_d; +#endif recompile_standard_cf_type(state); } static void RC_OLE_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_OLE_D; +#ifdef DYNAREC state->recomp_func = genc_ole_d; +#endif recompile_standard_cf_type(state); } static void RC_ULE_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_ULE_D; +#ifdef DYNAREC state->recomp_func = genc_ule_d; +#endif recompile_standard_cf_type(state); } static void RC_SF_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_SF_D; +#ifdef DYNAREC state->recomp_func = genc_sf_d; +#endif recompile_standard_cf_type(state); } static void RC_NGLE_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_NGLE_D; +#ifdef DYNAREC state->recomp_func = genc_ngle_d; +#endif recompile_standard_cf_type(state); } static void RC_SEQ_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_SEQ_D; +#ifdef DYNAREC state->recomp_func = genc_seq_d; +#endif recompile_standard_cf_type(state); } static void RC_NGL_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_NGL_D; +#ifdef DYNAREC state->recomp_func = genc_ngl_d; +#endif recompile_standard_cf_type(state); } static void RC_LT_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_LT_D; +#ifdef DYNAREC state->recomp_func = genc_lt_d; +#endif recompile_standard_cf_type(state); } static void RC_NGE_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_NGE_D; +#ifdef DYNAREC state->recomp_func = genc_nge_d; +#endif recompile_standard_cf_type(state); } static void RC_LE_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_LE_D; +#ifdef DYNAREC state->recomp_func = genc_le_d; +#endif recompile_standard_cf_type(state); } static void RC_NGT_D(usf_state_t * state) { state->dst->ops = state->current_instruction_table.C_NGT_D; +#ifdef DYNAREC state->recomp_func = genc_ngt_d; +#endif recompile_standard_cf_type(state); } @@ -1448,14 +1799,18 @@ static void (*recomp_d[64])(usf_state_t *) = static void RCVT_S_W(usf_state_t * state) { state->dst->ops = state->current_instruction_table.CVT_S_W; +#ifdef DYNAREC state->recomp_func = gencvt_s_w; +#endif recompile_standard_cf_type(state); } static void RCVT_D_W(usf_state_t * state) { state->dst->ops = state->current_instruction_table.CVT_D_W; +#ifdef DYNAREC state->recomp_func = gencvt_d_w; +#endif recompile_standard_cf_type(state); } @@ -1478,14 +1833,18 @@ static void (*recomp_w[64])(usf_state_t *) = static void RCVT_S_L(usf_state_t * state) { state->dst->ops = state->current_instruction_table.CVT_S_L; +#ifdef DYNAREC state->recomp_func = gencvt_s_l; +#endif recompile_standard_cf_type(state); } static void RCVT_D_L(usf_state_t * state) { state->dst->ops = state->current_instruction_table.CVT_D_L; +#ifdef DYNAREC state->recomp_func = gencvt_d_l; +#endif recompile_standard_cf_type(state); } @@ -1508,7 +1867,9 @@ static void (*recomp_l[64])(usf_state_t *) = static void RMFC1(usf_state_t * state) { state->dst->ops = state->current_instruction_table.MFC1; +#ifdef DYNAREC state->recomp_func = genmfc1; +#endif recompile_standard_r_type(state); state->dst->f.r.nrd = (state->src >> 11) & 0x1F; if (state->dst->f.r.rt == state->reg) RNOP(state); @@ -1517,7 +1878,9 @@ static void RMFC1(usf_state_t * state) static void RDMFC1(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DMFC1; +#ifdef DYNAREC state->recomp_func = gendmfc1; +#endif recompile_standard_r_type(state); state->dst->f.r.nrd = (state->src >> 11) & 0x1F; if (state->dst->f.r.rt == state->reg) RNOP(state); @@ -1526,7 +1889,9 @@ static void RDMFC1(usf_state_t * state) static void RCFC1(usf_state_t * state) { state->dst->ops = state->current_instruction_table.CFC1; +#ifdef DYNAREC state->recomp_func = gencfc1; +#endif recompile_standard_r_type(state); state->dst->f.r.nrd = (state->src >> 11) & 0x1F; if (state->dst->f.r.rt == state->reg) RNOP(state); @@ -1536,7 +1901,9 @@ static void RMTC1(usf_state_t * state) { state->dst->ops = state->current_instruction_table.MTC1; recompile_standard_r_type(state); +#ifdef DYNAREC state->recomp_func = genmtc1; +#endif state->dst->f.r.nrd = (state->src >> 11) & 0x1F; } @@ -1544,7 +1911,9 @@ static void RDMTC1(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DMTC1; recompile_standard_r_type(state); +#ifdef DYNAREC state->recomp_func = gendmtc1; +#endif state->dst->f.r.nrd = (state->src >> 11) & 0x1F; } @@ -1552,7 +1921,9 @@ static void RCTC1(usf_state_t * state) { state->dst->ops = state->current_instruction_table.CTC1; recompile_standard_r_type(state); +#ifdef DYNAREC state->recomp_func = genctc1; +#endif state->dst->f.r.nrd = (state->src >> 11) & 0x1F; } @@ -1607,7 +1978,9 @@ static void RJ(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.J; +#ifdef DYNAREC state->recomp_func = genj; +#endif recompile_standard_j_type(state); target = (state->dst->f.j.inst_index<<2) | (state->dst->addr & 0xF0000000); if (target == state->dst->addr) @@ -1615,13 +1988,17 @@ static void RJ(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.J_IDLE; +#ifdef DYNAREC state->recomp_func = genj_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.J_OUT; +#ifdef DYNAREC state->recomp_func = genj_out; +#endif } } @@ -1629,7 +2006,9 @@ static void RJAL(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.JAL; +#ifdef DYNAREC state->recomp_func = genjal; +#endif recompile_standard_j_type(state); target = (state->dst->f.j.inst_index<<2) | (state->dst->addr & 0xF0000000); if (target == state->dst->addr) @@ -1637,13 +2016,17 @@ static void RJAL(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.JAL_IDLE; +#ifdef DYNAREC state->recomp_func = genjal_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.JAL_OUT; +#ifdef DYNAREC state->recomp_func = genjal_out; +#endif } } @@ -1651,7 +2034,9 @@ static void RBEQ(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BEQ; +#ifdef DYNAREC state->recomp_func = genbeq; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -1659,13 +2044,17 @@ static void RBEQ(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BEQ_IDLE; +#ifdef DYNAREC state->recomp_func = genbeq_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BEQ_OUT; +#ifdef DYNAREC state->recomp_func = genbeq_out; +#endif } } @@ -1673,7 +2062,9 @@ static void RBNE(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BNE; +#ifdef DYNAREC state->recomp_func = genbne; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -1681,13 +2072,17 @@ static void RBNE(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BNE_IDLE; +#ifdef DYNAREC state->recomp_func = genbne_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BNE_OUT; +#ifdef DYNAREC state->recomp_func = genbne_out; +#endif } } @@ -1695,7 +2090,9 @@ static void RBLEZ(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BLEZ; +#ifdef DYNAREC state->recomp_func = genblez; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -1703,13 +2100,17 @@ static void RBLEZ(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BLEZ_IDLE; +#ifdef DYNAREC state->recomp_func = genblez_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BLEZ_OUT; +#ifdef DYNAREC state->recomp_func = genblez_out; +#endif } } @@ -1717,7 +2118,9 @@ static void RBGTZ(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BGTZ; +#ifdef DYNAREC state->recomp_func = genbgtz; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -1725,20 +2128,26 @@ static void RBGTZ(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BGTZ_IDLE; +#ifdef DYNAREC state->recomp_func = genbgtz_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BGTZ_OUT; +#ifdef DYNAREC state->recomp_func = genbgtz_out; +#endif } } static void RADDI(usf_state_t * state) { state->dst->ops = state->current_instruction_table.ADDI; +#ifdef DYNAREC state->recomp_func = genaddi; +#endif recompile_standard_i_type(state); if(state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1746,7 +2155,9 @@ static void RADDI(usf_state_t * state) static void RADDIU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.ADDIU; +#ifdef DYNAREC state->recomp_func = genaddiu; +#endif recompile_standard_i_type(state); if(state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1754,7 +2165,9 @@ static void RADDIU(usf_state_t * state) static void RSLTI(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SLTI; +#ifdef DYNAREC state->recomp_func = genslti; +#endif recompile_standard_i_type(state); if(state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1762,7 +2175,9 @@ static void RSLTI(usf_state_t * state) static void RSLTIU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SLTIU; +#ifdef DYNAREC state->recomp_func = gensltiu; +#endif recompile_standard_i_type(state); if(state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1770,7 +2185,9 @@ static void RSLTIU(usf_state_t * state) static void RANDI(usf_state_t * state) { state->dst->ops = state->current_instruction_table.ANDI; +#ifdef DYNAREC state->recomp_func = genandi; +#endif recompile_standard_i_type(state); if(state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1778,7 +2195,9 @@ static void RANDI(usf_state_t * state) static void RORI(usf_state_t * state) { state->dst->ops = state->current_instruction_table.ORI; +#ifdef DYNAREC state->recomp_func = genori; +#endif recompile_standard_i_type(state); if (state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1786,7 +2205,9 @@ static void RORI(usf_state_t * state) static void RXORI(usf_state_t * state) { state->dst->ops = state->current_instruction_table.XORI; +#ifdef DYNAREC state->recomp_func = genxori; +#endif recompile_standard_i_type(state); if (state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1794,7 +2215,9 @@ static void RXORI(usf_state_t * state) static void RLUI(usf_state_t * state) { state->dst->ops = state->current_instruction_table.LUI; +#ifdef DYNAREC state->recomp_func = genlui; +#endif recompile_standard_i_type(state); if (state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1813,7 +2236,9 @@ static void RBEQL(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BEQL; +#ifdef DYNAREC state->recomp_func = genbeql; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -1821,13 +2246,17 @@ static void RBEQL(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BEQL_IDLE; +#ifdef DYNAREC state->recomp_func = genbeql_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BEQL_OUT; +#ifdef DYNAREC state->recomp_func = genbeql_out; +#endif } } @@ -1835,7 +2264,9 @@ static void RBNEL(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BNEL; +#ifdef DYNAREC state->recomp_func = genbnel; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -1843,13 +2274,17 @@ static void RBNEL(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BNEL_IDLE; +#ifdef DYNAREC state->recomp_func = genbnel_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BNEL_OUT; +#ifdef DYNAREC state->recomp_func = genbnel_out; +#endif } } @@ -1857,7 +2292,9 @@ static void RBLEZL(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BLEZL; +#ifdef DYNAREC state->recomp_func = genblezl; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -1865,13 +2302,17 @@ static void RBLEZL(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BLEZL_IDLE; +#ifdef DYNAREC state->recomp_func = genblezl_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BLEZL_OUT; +#ifdef DYNAREC state->recomp_func = genblezl_out; +#endif } } @@ -1879,7 +2320,9 @@ static void RBGTZL(usf_state_t * state) { unsigned int target; state->dst->ops = state->current_instruction_table.BGTZL; +#ifdef DYNAREC state->recomp_func = genbgtzl; +#endif recompile_standard_i_type(state); target = state->dst->addr + state->dst->f.i.immediate*4 + 4; if (target == state->dst->addr) @@ -1887,20 +2330,26 @@ static void RBGTZL(usf_state_t * state) if (state->check_nop) { state->dst->ops = state->current_instruction_table.BGTZL_IDLE; +#ifdef DYNAREC state->recomp_func = genbgtzl_idle; +#endif } } else if (target < state->dst_block->start || target >= state->dst_block->end || state->dst->addr == (state->dst_block->end-4)) { state->dst->ops = state->current_instruction_table.BGTZL_OUT; +#ifdef DYNAREC state->recomp_func = genbgtzl_out; +#endif } } static void RDADDI(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DADDI; +#ifdef DYNAREC state->recomp_func = gendaddi; +#endif recompile_standard_i_type(state); if(state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1908,7 +2357,9 @@ static void RDADDI(usf_state_t * state) static void RDADDIU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.DADDIU; +#ifdef DYNAREC state->recomp_func = gendaddiu; +#endif recompile_standard_i_type(state); if(state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1916,7 +2367,9 @@ static void RDADDIU(usf_state_t * state) static void RLDL(usf_state_t * state) { state->dst->ops = state->current_instruction_table.LDL; +#ifdef DYNAREC state->recomp_func = genldl; +#endif recompile_standard_i_type(state); if(state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1924,7 +2377,9 @@ static void RLDL(usf_state_t * state) static void RLDR(usf_state_t * state) { state->dst->ops = state->current_instruction_table.LDR; +#ifdef DYNAREC state->recomp_func = genldr; +#endif recompile_standard_i_type(state); if(state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1932,7 +2387,9 @@ static void RLDR(usf_state_t * state) static void RLB(usf_state_t * state) { state->dst->ops = state->current_instruction_table.LB; +#ifdef DYNAREC state->recomp_func = genlb; +#endif recompile_standard_i_type(state); if (state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1940,7 +2397,9 @@ static void RLB(usf_state_t * state) static void RLH(usf_state_t * state) { state->dst->ops = state->current_instruction_table.LH; +#ifdef DYNAREC state->recomp_func = genlh; +#endif recompile_standard_i_type(state); if (state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1948,7 +2407,9 @@ static void RLH(usf_state_t * state) static void RLWL(usf_state_t * state) { state->dst->ops = state->current_instruction_table.LWL; +#ifdef DYNAREC state->recomp_func = genlwl; +#endif recompile_standard_i_type(state); if (state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1956,7 +2417,9 @@ static void RLWL(usf_state_t * state) static void RLW(usf_state_t * state) { state->dst->ops = state->current_instruction_table.LW; +#ifdef DYNAREC state->recomp_func = genlw; +#endif recompile_standard_i_type(state); if (state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1964,7 +2427,9 @@ static void RLW(usf_state_t * state) static void RLBU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.LBU; +#ifdef DYNAREC state->recomp_func = genlbu; +#endif recompile_standard_i_type(state); if(state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1972,7 +2437,9 @@ static void RLBU(usf_state_t * state) static void RLHU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.LHU; +#ifdef DYNAREC state->recomp_func = genlhu; +#endif recompile_standard_i_type(state); if(state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1980,7 +2447,9 @@ static void RLHU(usf_state_t * state) static void RLWR(usf_state_t * state) { state->dst->ops = state->current_instruction_table.LWR; +#ifdef DYNAREC state->recomp_func = genlwr; +#endif recompile_standard_i_type(state); if(state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1988,7 +2457,9 @@ static void RLWR(usf_state_t * state) static void RLWU(usf_state_t * state) { state->dst->ops = state->current_instruction_table.LWU; +#ifdef DYNAREC state->recomp_func = genlwu; +#endif recompile_standard_i_type(state); if(state->dst->f.i.rt == state->reg) RNOP(state); } @@ -1996,61 +2467,79 @@ static void RLWU(usf_state_t * state) static void RSB(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SB; +#ifdef DYNAREC state->recomp_func = gensb; +#endif recompile_standard_i_type(state); } static void RSH(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SH; +#ifdef DYNAREC state->recomp_func = gensh; +#endif recompile_standard_i_type(state); } static void RSWL(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SWL; +#ifdef DYNAREC state->recomp_func = genswl; +#endif recompile_standard_i_type(state); } static void RSW(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SW; +#ifdef DYNAREC state->recomp_func = gensw; +#endif recompile_standard_i_type(state); } static void RSDL(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SDL; +#ifdef DYNAREC state->recomp_func = gensdl; +#endif recompile_standard_i_type(state); } static void RSDR(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SDR; +#ifdef DYNAREC state->recomp_func = gensdr; +#endif recompile_standard_i_type(state); } static void RSWR(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SWR; +#ifdef DYNAREC state->recomp_func = genswr; +#endif recompile_standard_i_type(state); } static void RCACHE(usf_state_t * state) { +#ifdef DYNAREC state->recomp_func = gencache; +#endif state->dst->ops = state->current_instruction_table.CACHE; } static void RLL(usf_state_t * state) { +#ifdef DYNAREC state->recomp_func = genll; +#endif state->dst->ops = state->current_instruction_table.LL; recompile_standard_i_type(state); if(state->dst->f.i.rt == state->reg) RNOP(state); @@ -2059,28 +2548,36 @@ static void RLL(usf_state_t * state) static void RLWC1(usf_state_t * state) { state->dst->ops = state->current_instruction_table.LWC1; +#ifdef DYNAREC state->recomp_func = genlwc1; +#endif recompile_standard_lf_type(state); } static void RLLD(usf_state_t * state) { state->dst->ops = state->current_instruction_table.NI; +#ifdef DYNAREC state->recomp_func = genni; +#endif recompile_standard_i_type(state); } static void RLDC1(usf_state_t * state) { state->dst->ops = state->current_instruction_table.LDC1; +#ifdef DYNAREC state->recomp_func = genldc1; +#endif recompile_standard_lf_type(state); } static void RLD(usf_state_t * state) { state->dst->ops = state->current_instruction_table.LD; +#ifdef DYNAREC state->recomp_func = genld; +#endif recompile_standard_i_type(state); if (state->dst->f.i.rt == state->reg) RNOP(state); } @@ -2088,7 +2585,9 @@ static void RLD(usf_state_t * state) static void RSC(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SC; +#ifdef DYNAREC state->recomp_func = gensc; +#endif recompile_standard_i_type(state); if (state->dst->f.i.rt == state->reg) RNOP(state); } @@ -2096,28 +2595,36 @@ static void RSC(usf_state_t * state) static void RSWC1(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SWC1; +#ifdef DYNAREC state->recomp_func = genswc1; +#endif recompile_standard_lf_type(state); } static void RSCD(usf_state_t * state) { state->dst->ops = state->current_instruction_table.NI; +#ifdef DYNAREC state->recomp_func = genni; +#endif recompile_standard_i_type(state); } static void RSDC1(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SDC1; +#ifdef DYNAREC state->recomp_func = gensdc1; +#endif recompile_standard_lf_type(state); } static void RSD(usf_state_t * state) { state->dst->ops = state->current_instruction_table.SD; +#ifdef DYNAREC state->recomp_func = gensd; +#endif recompile_standard_i_type(state); } @@ -2156,6 +2663,7 @@ void init_block(usf_state_t * state, precomp_block *block) if (!block->block) { size_t memsize = get_block_memsize(block); +#ifdef DYNAREC if (state->r4300emu == CORE_DYNAREC) { block->block = (precomp_instr *) malloc_exec(state, memsize); if (!block->block) { @@ -2163,7 +2671,9 @@ void init_block(usf_state_t * state, precomp_block *block) return; } } - else { + else +#endif + { block->block = (precomp_instr *) malloc(memsize); if (!block->block) { DebugMessage(state, M64MSG_ERROR, "Memory error: couldn't allocate memory for cached interpreter."); @@ -2175,6 +2685,7 @@ void init_block(usf_state_t * state, precomp_block *block) already_exist = 0; } +#ifdef DYNAREC if (state->r4300emu == CORE_DYNAREC) { if (!block->code) @@ -2202,6 +2713,7 @@ void init_block(usf_state_t * state, precomp_block *block) init_assembler(state, NULL, 0, NULL, 0); init_cache(state, block->block); } +#endif if (!already_exist) { @@ -2209,10 +2721,14 @@ void init_block(usf_state_t * state, precomp_block *block) { state->dst = block->block + i; state->dst->addr = block->start + i*4; +#ifdef DYNAREC state->dst->reg_cache_infos.need_map = 0; +#endif state->dst->local_addr = state->code_length; RNOTCOMPILED(state); +#ifdef DYNAREC if (state->r4300emu == CORE_DYNAREC) state->recomp_func(state); +#endif } state->init_length = state->code_length; } @@ -2222,12 +2738,15 @@ void init_block(usf_state_t * state, precomp_block *block) for (i=0; idst = block->block + i; +#ifdef DYNAREC state->dst->reg_cache_infos.need_map = 0; +#endif state->dst->local_addr = i * (state->init_length / length); state->dst->ops = state->current_instruction_table.NOTCOMPILED; } } +#ifdef DYNAREC if (state->r4300emu == CORE_DYNAREC) { free_all_registers(state); @@ -2237,6 +2756,7 @@ void init_block(usf_state_t * state, precomp_block *block) block->max_code_length = state->max_code_length; free_assembler(state, &block->jumps_table, &block->jumps_number, &block->riprel_table, &block->riprel_number); } +#endif /* here we're marking the block as a valid code even if it's not compiled * yet as the game should have already set up the code correctly. @@ -2309,16 +2829,22 @@ void init_block(usf_state_t * state, precomp_block *block) void free_block(usf_state_t * state, precomp_block *block) { +#ifdef DYNAREC size_t memsize = get_block_memsize(block); +#endif if (block->block) { +#ifdef DYNAREC if (state->r4300emu == CORE_DYNAREC) free_exec(block->block, memsize); else +#endif free(block->block); block->block = NULL; } +#ifdef DYNAREC if (block->code) { free_exec(block->code, block->max_code_length); block->code = NULL; } +#endif if (block->jumps_table) { free(block->jumps_table); block->jumps_table = NULL; } if (block->riprel_table) { free(block->riprel_table); block->riprel_table = NULL; } } @@ -2335,6 +2861,7 @@ void recompile_block(usf_state_t * state, int *source, precomp_block *block, uns //for (i=0; i<16; i++) block->md5[i] = 0; block->adler32 = 0; +#ifdef DYNAREC if (state->r4300emu == CORE_DYNAREC) { state->code_length = block->code_length; @@ -2343,6 +2870,7 @@ void recompile_block(usf_state_t * state, int *source, precomp_block *block, uns init_assembler(state, block->jumps_table, block->jumps_number, block->riprel_table, block->riprel_number); init_cache(state, block->block + (func & 0xFFF) / 4); } +#endif for (i = (func & 0xFFF) / 4; finished != 2; i++) { @@ -2359,11 +2887,15 @@ void recompile_block(usf_state_t * state, int *source, precomp_block *block, uns state->check_nop = source[i+1] == 0; state->dst = block->block + i; state->dst->addr = block->start + i*4; +#ifdef DYNAREC state->dst->reg_cache_infos.need_map = 0; +#endif state->dst->local_addr = state->code_length; state->recomp_func = NULL; recomp_ops[((state->src >> 26) & 0x3F)](state); +#ifdef DYNAREC if (state->r4300emu == CORE_DYNAREC) state->recomp_func(state); +#endif state->dst = block->block + i; /*if ((dst+1)->ops != NOTCOMPILED && !delay_slot_compiled && @@ -2375,7 +2907,9 @@ void recompile_block(usf_state_t * state, int *source, precomp_block *block, uns if (state->delay_slot_compiled) { state->delay_slot_compiled--; +#ifdef DYNAREC free_all_registers(state); +#endif } if (i >= length-2+(length>>2)) finished = 2; @@ -2396,7 +2930,9 @@ void recompile_block(usf_state_t * state, int *source, precomp_block *block, uns { state->dst = block->block + i; state->dst->addr = block->start + i*4; +#ifdef DYNAREC state->dst->reg_cache_infos.need_map = 0; +#endif state->dst->local_addr = state->code_length; RFIN_BLOCK(state); if (state->r4300emu == CORE_DYNAREC) state->recomp_func(state); @@ -2405,13 +2941,18 @@ void recompile_block(usf_state_t * state, int *source, precomp_block *block, uns { state->dst = block->block + i; state->dst->addr = block->start + i*4; +#ifdef DYNAREC state->dst->reg_cache_infos.need_map = 0; +#endif state->dst->local_addr = state->code_length; RFIN_BLOCK(state); +#ifdef DYNAREC if (state->r4300emu == CORE_DYNAREC) state->recomp_func(state); +#endif i++; } } +#ifdef DYNAREC else if (state->r4300emu == CORE_DYNAREC) genlink_subblock(state); if (state->r4300emu == CORE_DYNAREC) @@ -2422,6 +2963,7 @@ void recompile_block(usf_state_t * state, int *source, precomp_block *block, uns block->max_code_length = state->max_code_length; free_assembler(state, &block->jumps_table, &block->jumps_number, &block->riprel_table, &block->riprel_number); } +#endif } static int is_jump(usf_state_t * state) @@ -2507,21 +3049,28 @@ void recompile_opcode(usf_state_t * state) state->src = *state->SRC; state->dst++; state->dst->addr = (state->dst-1)->addr + 4; +#ifdef DYNAREC state->dst->reg_cache_infos.need_map = 0; +#endif if(!is_jump(state)) { state->recomp_func = NULL; recomp_ops[((state->src >> 26) & 0x3F)](state); +#ifdef DYNAREC if (state->r4300emu == CORE_DYNAREC) state->recomp_func(state); +#endif } else { RNOP(state); +#ifdef DYNAREC if (state->r4300emu == CORE_DYNAREC) state->recomp_func(state); +#endif } state->delay_slot_compiled = 2; } +#ifdef DYNAREC #if defined(__APPLE__) static inline int macos_release() { @@ -2605,3 +3154,4 @@ static void free_exec(void *ptr, size_t length) free(ptr); #endif } +#endif diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/recomp.h b/Frameworks/lazyusf2/lazyusf2/r4300/recomp.h index 1bd9b1689..31ce7ecb7 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/recomp.h +++ b/Frameworks/lazyusf2/lazyusf2/r4300/recomp.h @@ -73,7 +73,9 @@ typedef struct _precomp_instr } f; unsigned int addr; /* word-aligned instruction address in r4300 address space */ unsigned int local_addr; /* byte offset to start of corresponding x86_64 instructions, from start of code block */ +#ifdef DYNAREC reg_cache_struct reg_cache_infos; +#endif } precomp_instr; typedef struct _precomp_block diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/assemble.c b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/assemble.c index 26d388a5d..58f69a443 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/assemble.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/assemble.c @@ -44,6 +44,7 @@ /* Static Functions */ +#ifdef DYNAREC void add_jump(usf_state_t * state, unsigned int pc_addr, unsigned int mi_addr, unsigned int absolute64) { if (state->jumps_number == state->max_jumps_number) @@ -199,3 +200,6 @@ void jump_end_rel32(usf_state_t * state) put32(state, jump_vec); state->code_length = jump_end; } + +#endif + diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/assemble.h b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/assemble.h index 074757974..d2aec168c 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/assemble.h +++ b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/assemble.h @@ -77,6 +77,7 @@ #define RP3 RCX #endif +#ifdef DYNAREC void jump_start_rel8(usf_state_t *); void jump_end_rel8(usf_state_t *); void jump_start_rel32(usf_state_t *); @@ -1192,6 +1193,7 @@ static inline void ffree_fpreg(usf_state_t * state, int fpreg) put8(state, 0xDD); put8(state, 0xC0 + fpreg); } +#endif #endif /* M64P_R4300_ASSEMBLE_H */ diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/assemble_struct.h b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/assemble_struct.h index 46e59ff53..7eb4151c5 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/assemble_struct.h +++ b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/assemble_struct.h @@ -23,6 +23,7 @@ #ifndef M64P_R4300_ASSEMBLE_STRUCT_H #define M64P_R4300_ASSEMBLE_STRUCT_H +#ifdef DYNAREC typedef struct _reg_cache_struct { int need_map; @@ -30,5 +31,6 @@ typedef struct _reg_cache_struct unsigned char jump_wrapper[84]; int need_cop1_check; } reg_cache_struct; +#endif #endif /* M64P_R4300_ASSEMBLE_STRUCT_H */ diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gbc.c b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gbc.c index 001627109..fb013e809 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gbc.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gbc.c @@ -39,6 +39,7 @@ #include "r4300/instr_counters.h" #endif +#ifdef DYNAREC static void genbc1f_test(usf_state_t * state) { test_m32rel_imm32(state, (unsigned int*)&state->FCR31, 0x800000); @@ -303,3 +304,4 @@ void genbc1tl_idle(usf_state_t * state) #endif } +#endif diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop0.c b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop0.c index 86be49e00..82944c974 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop0.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop0.c @@ -38,6 +38,7 @@ #include "r4300/instr_counters.h" #endif +#ifdef DYNAREC void genmfc0(usf_state_t * state) { #if defined(COUNT_INSTR) @@ -53,4 +54,4 @@ void genmtc0(usf_state_t * state) #endif gencallinterp(state, (unsigned long long)state->current_instruction_table.MTC0, 0); } - +#endif diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1.c b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1.c index 14d1f7fe8..5823dc0ec 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1.c @@ -42,6 +42,7 @@ #include "r4300/instr_counters.h" #endif +#ifdef DYNAREC void genmfc1(usf_state_t * state) { #if defined(COUNT_INSTR) @@ -160,4 +161,4 @@ void genctc1(usf_state_t * state) fldcw_m16rel(state, (unsigned short*)&state->rounding_mode); #endif } - +#endif diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1_d.c b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1_d.c index b3215822f..5aa77d7a4 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1_d.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1_d.c @@ -38,6 +38,7 @@ #include "r4300/instr_counters.h" #endif +#ifdef DYNAREC void genadd_d(usf_state_t * state) { #if defined(COUNT_INSTR) @@ -716,4 +717,4 @@ void genc_ngt_d(usf_state_t * state) and_m32rel_imm32(state, (unsigned int*)&state->FCR31, ~0x800000); // 11 #endif } - +#endif diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1_l.c b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1_l.c index ac81153f0..224ccd104 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1_l.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1_l.c @@ -36,6 +36,7 @@ #include "r4300/instr_counters.h" #endif +#ifdef DYNAREC void gencvt_s_l(usf_state_t * state) { #if defined(COUNT_INSTR) @@ -67,4 +68,4 @@ void gencvt_d_l(usf_state_t * state) fstp_preg64_qword(state, RAX); #endif } - +#endif diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1_s.c b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1_s.c index 75f8617f8..9582e6b08 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1_s.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1_s.c @@ -39,6 +39,7 @@ #include "r4300/instr_counters.h" #endif +#ifdef DYNAREC void genadd_s(usf_state_t * state) { #if defined(COUNT_INSTR) @@ -715,4 +716,4 @@ void genc_ngt_s(usf_state_t * state) and_m32rel_imm32(state, (unsigned int*)&state->FCR31, ~0x800000); // 11 #endif } - +#endif diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1_w.c b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1_w.c index 55efd2dc4..5ee2106ef 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1_w.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gcop1_w.c @@ -38,6 +38,7 @@ #include "r4300/instr_counters.h" #endif +#ifdef DYNAREC void gencvt_s_w(usf_state_t * state) { #if defined(COUNT_INSTR) @@ -69,4 +70,4 @@ void gencvt_d_w(usf_state_t * state) fstp_preg64_qword(state, RAX); #endif } - +#endif diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gr4300.c b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gr4300.c index 6449150b6..7e517adb6 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gr4300.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gr4300.c @@ -49,6 +49,7 @@ /* static functions */ +#ifdef DYNAREC static void genupdate_count(usf_state_t * state, unsigned int addr) { mov_reg32_imm32(state, EAX, addr); @@ -2257,4 +2258,4 @@ void gensc(usf_state_t * state) #endif gencallinterp(state, (unsigned long long)state->current_instruction_table.SC, 0); } - +#endif diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gregimm.c b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gregimm.c index bb74308bf..a6512773d 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gregimm.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gregimm.c @@ -42,6 +42,7 @@ #include "r4300/instr_counters.h" #endif +#ifdef DYNAREC static void genbltz_test(usf_state_t * state) { int rs_64bit = is64(state, (unsigned int *)state->dst->f.i.rs); @@ -608,4 +609,4 @@ void genbgezall_idle(usf_state_t * state) genbgezall(state); #endif } - +#endif diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gspecial.c b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gspecial.c index 3289449ef..e11c2f024 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gspecial.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gspecial.c @@ -45,6 +45,7 @@ # define offsetof(TYPE,MEMBER) ((unsigned int) &((TYPE*)0)->MEMBER) #endif +#ifdef DYNAREC void gensll(usf_state_t * state) { #if defined(COUNT_INSTR) @@ -1075,4 +1076,4 @@ void genbreak(usf_state_t * state) { gencallinterp(state, (unsigned long long)state->current_instruction_table.BREAK, 0); } - +#endif diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gtlb.c b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gtlb.c index 3dab44bd0..b2a68324a 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gtlb.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/gtlb.c @@ -37,6 +37,7 @@ #include "r4300/instr_counters.h" #endif +#ifdef DYNAREC void gentlbwi(usf_state_t * state) { #if defined(COUNT_INSTR) @@ -98,4 +99,4 @@ void gentlbwr(usf_state_t * state) #endif gencallinterp(state, (unsigned long long)state->current_instruction_table.TLBWR, 0); } - +#endif diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/regcache.c b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/regcache.c index aaa78fd27..d3be7eb66 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/regcache.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/regcache.c @@ -34,6 +34,7 @@ #include "r4300/r4300.h" #include "r4300/recomph.h" +#ifdef DYNAREC void init_cache(usf_state_t * state, precomp_instr* start) { int i; @@ -622,4 +623,4 @@ void build_wrappers(usf_state_t * state, precomp_instr *instr, int start, int en } } } - +#endif diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/regcache.h b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/regcache.h index 892580c5f..202e73f2c 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/regcache.h +++ b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/regcache.h @@ -25,6 +25,7 @@ #include "r4300/recomp.h" +#ifdef DYNAREC void init_cache(usf_state_t *, precomp_instr* start); void free_registers_move_start(usf_state_t *); void free_all_registers(usf_state_t *); @@ -42,6 +43,7 @@ int allocate_register_64_w(usf_state_t *, unsigned long long *addr); void allocate_register_32_manually(usf_state_t *, int reg, unsigned int *addr); void allocate_register_32_manually_w(usf_state_t *, int reg, unsigned int *addr); void build_wrappers(usf_state_t *, precomp_instr*, int, int, precomp_block*); +#endif #endif /* M64P_R4300_REGCACHE_H */ diff --git a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/rjump.c b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/rjump.c index 4007b2d2e..8112ea712 100644 --- a/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/rjump.c +++ b/Frameworks/lazyusf2/lazyusf2/r4300/x86_64/rjump.c @@ -20,6 +20,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#ifdef DYNAREC + #include #include "usf/usf.h" @@ -108,4 +110,4 @@ void dyna_stop(usf_state_t * state) *state->return_address = (unsigned long long) state->save_rip; } } - +#endif diff --git a/Frameworks/lazyusf2/lazyusf2/rsp_lle/rsp.h b/Frameworks/lazyusf2/lazyusf2/rsp_lle/rsp.h index 5c7aeb19e..0422ee3d9 100644 --- a/Frameworks/lazyusf2/lazyusf2/rsp_lle/rsp.h +++ b/Frameworks/lazyusf2/lazyusf2/rsp_lle/rsp.h @@ -27,6 +27,12 @@ /* * Streaming SIMD Extensions version import management */ +#ifdef __aarch64__ +#define ARCH_MIN_ARM_NEON 1 +#else +#define ARCH_MIN_SSE2 1 +#endif + #ifdef ARCH_MIN_SSSE3 #define ARCH_MIN_SSE2 #include diff --git a/Frameworks/lazyusf2/lazyusf2/rsp_lle/vu/vsaw.h b/Frameworks/lazyusf2/lazyusf2/rsp_lle/vu/vsaw.h index a6f4b28f1..db682c110 100644 --- a/Frameworks/lazyusf2/lazyusf2/rsp_lle/vu/vsaw.h +++ b/Frameworks/lazyusf2/lazyusf2/rsp_lle/vu/vsaw.h @@ -34,22 +34,22 @@ static void VSAR(usf_state_t * state, int vd, int vs, int vt, int e) if (e > 2) { message(state, "VSAR\nInvalid mask.", 2); - #if ARCH_MIN_ARM_NEON +#if ARCH_MIN_ARM_NEON int16x8_t zero = vdupq_n_s16(0); vst1q_s16(VR[vd], zero); - #else +#else for (i = 0; i < N; i++) state->VR[vd][i] = 0x0000; /* override behavior (zilmar) */ - #endif +#endif } else { - #if ARCH_MIN_ARM_NEON +#if ARCH_MIN_ARM_NEON vector_copy(VR[vd], VACC[e]); - #else +#else for (i = 0; i < N; i++) state->VR[vd][i] = state->VACC[e][i]; - #endif +#endif } for (i = 0; i < N; i++) @@ -72,18 +72,18 @@ static void VSAW(usf_state_t * state, int vd, int vs, int vt, int e) { /* branch very unlikely...never seen a game do VSAW illegally */ message(state, "VSAW\nIllegal mask.", 2); - #if ARCH_MIN_ARM_NEON +#if ARCH_MIN_ARM_NEON int16x8_t zero = vdupq_n_s16(0); vst1q_s16(state->VR[vd], zero); - #else +#else for (i = 0; i < N; i++) state->VR[vd][i] = 0x0000; /* override behavior (zilmar) */ return; - #endif +#endif } vector_copy(state->VR[vd], state->VACC[e]); return; diff --git a/Frameworks/lazyusf2/lazyusf2/usf/usf_internal.h b/Frameworks/lazyusf2/lazyusf2/usf/usf_internal.h index 6aa2889cb..3fe2755ff 100644 --- a/Frameworks/lazyusf2/lazyusf2/usf/usf_internal.h +++ b/Frameworks/lazyusf2/lazyusf2/usf/usf_internal.h @@ -167,7 +167,9 @@ typedef struct _precomp_instr } f; unsigned int addr; /* word-aligned instruction address in r4300 address space */ unsigned int local_addr; /* byte offset to start of corresponding x86_64 instructions, from start of code block */ +#ifdef DYNAREC reg_cache_struct reg_cache_infos; +#endif } precomp_instr; typedef struct _precomp_block diff --git a/Frameworks/libatrac9/libatrac9.xcodeproj/project.pbxproj b/Frameworks/libatrac9/libatrac9.xcodeproj/project.pbxproj index e4a028a50..af58ce7f1 100644 --- a/Frameworks/libatrac9/libatrac9.xcodeproj/project.pbxproj +++ b/Frameworks/libatrac9/libatrac9.xcodeproj/project.pbxproj @@ -296,7 +296,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -351,7 +351,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; diff --git a/Frameworks/libsidplay/libsidplay/config.h b/Frameworks/libsidplay/libsidplay/config.h index 5705fed4b..08c4cfd86 100644 --- a/Frameworks/libsidplay/libsidplay/config.h +++ b/Frameworks/libsidplay/libsidplay/config.h @@ -5,7 +5,9 @@ #define HAVE_BOOL 1 +#if !defined(__aarch64__) #define HAVE_MMINTRIN_H 1 +#endif #define HAVE_STRCASECMP 1 diff --git a/Frameworks/libsidplay/sidplayfp.xcodeproj/project.pbxproj b/Frameworks/libsidplay/sidplayfp.xcodeproj/project.pbxproj index 49da03f7e..5166212bf 100644 --- a/Frameworks/libsidplay/sidplayfp.xcodeproj/project.pbxproj +++ b/Frameworks/libsidplay/sidplayfp.xcodeproj/project.pbxproj @@ -877,8 +877,8 @@ TargetAttributes = { 8314D6541A354E7800EEE8E6 = { CreatedOnToolsVersion = 6.1.1; - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -1034,7 +1034,7 @@ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "sidplay-residfp-code/libsidplayfp/src", ); - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -1090,7 +1090,7 @@ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "sidplay-residfp-code/libsidplayfp/src", ); - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; VERSIONING_SYSTEM = "apple-generic"; @@ -1101,8 +1101,11 @@ 8314D66C1A354E7800EEE8E6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -1119,6 +1122,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.cogx.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = sidplayfp; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; USE_HEADERMAP = NO; @@ -1128,8 +1132,11 @@ 8314D66D1A354E7800EEE8E6 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -1146,6 +1153,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.cogx.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = sidplayfp; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; USE_HEADERMAP = NO; diff --git a/Frameworks/mGBA/mGBA.xcodeproj/project.pbxproj b/Frameworks/mGBA/mGBA.xcodeproj/project.pbxproj index a4e2374fa..4a12c4396 100644 --- a/Frameworks/mGBA/mGBA.xcodeproj/project.pbxproj +++ b/Frameworks/mGBA/mGBA.xcodeproj/project.pbxproj @@ -105,7 +105,6 @@ 83299FA31E765789003A3242 /* menu.h in Headers */ = {isa = PBXBuildFile; fileRef = 83299F251E765789003A3242 /* menu.h */; }; 83299FA41E765789003A3242 /* gui.h in Headers */ = {isa = PBXBuildFile; fileRef = 83299F261E765789003A3242 /* gui.h */; }; 83299FA51E765789003A3242 /* hash.h in Headers */ = {isa = PBXBuildFile; fileRef = 83299F271E765789003A3242 /* hash.h */; }; - 83299FA61E765789003A3242 /* math.h in Headers */ = {isa = PBXBuildFile; fileRef = 83299F281E765789003A3242 /* math.h */; }; 83299FA71E765789003A3242 /* memory.h in Headers */ = {isa = PBXBuildFile; fileRef = 83299F291E765789003A3242 /* memory.h */; }; 83299FA81E765789003A3242 /* fast.h in Headers */ = {isa = PBXBuildFile; fileRef = 83299F2B1E765789003A3242 /* fast.h */; }; 83299FA91E765789003A3242 /* ips.h in Headers */ = {isa = PBXBuildFile; fileRef = 83299F2C1E765789003A3242 /* ips.h */; }; @@ -121,7 +120,6 @@ 83299FB31E765789003A3242 /* png-io.h in Headers */ = {isa = PBXBuildFile; fileRef = 83299F3B1E765789003A3242 /* png-io.h */; }; 83299FB41E765789003A3242 /* ring-fifo.h in Headers */ = {isa = PBXBuildFile; fileRef = 83299F3C1E765789003A3242 /* ring-fifo.h */; }; 83299FB51E765789003A3242 /* socket.h in Headers */ = {isa = PBXBuildFile; fileRef = 83299F3D1E765789003A3242 /* socket.h */; }; - 83299FB61E765789003A3242 /* string.h in Headers */ = {isa = PBXBuildFile; fileRef = 83299F3E1E765789003A3242 /* string.h */; }; 83299FB71E765789003A3242 /* table.h in Headers */ = {isa = PBXBuildFile; fileRef = 83299F3F1E765789003A3242 /* table.h */; }; 83299FB81E765789003A3242 /* text-codec.h in Headers */ = {isa = PBXBuildFile; fileRef = 83299F401E765789003A3242 /* text-codec.h */; }; 83299FB91E765789003A3242 /* threading.h in Headers */ = {isa = PBXBuildFile; fileRef = 83299F411E765789003A3242 /* threading.h */; }; @@ -189,6 +187,8 @@ 83CA2E331D7BCB6700F2EA53 /* ini.h in Headers */ = {isa = PBXBuildFile; fileRef = 83CA2E2F1D7BCB6700F2EA53 /* ini.h */; }; 83CA2E3B1D7BCBD300F2EA53 /* memory.c in Sources */ = {isa = PBXBuildFile; fileRef = 83CA2E391D7BCBD300F2EA53 /* memory.c */; }; 83CA2E431D7BD82100F2EA53 /* core.c in Sources */ = {isa = PBXBuildFile; fileRef = 83CA2E421D7BD82100F2EA53 /* core.c */; }; + 83D0381124A40FB9004CF90F /* mgba-string.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D0381024A40FB9004CF90F /* mgba-string.h */; }; + 83D0381324A4125D004CF90F /* mgba-math.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D0381224A4125D004CF90F /* mgba-math.h */; }; 83D7282220A673A600076FC5 /* matrix.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D7282120A673A500076FC5 /* matrix.h */; }; 83D7282420A673BB00076FC5 /* matrix.c in Sources */ = {isa = PBXBuildFile; fileRef = 83D7282320A673BB00076FC5 /* matrix.c */; }; /* End PBXBuildFile section */ @@ -292,7 +292,6 @@ 83299F251E765789003A3242 /* menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = menu.h; sourceTree = ""; }; 83299F261E765789003A3242 /* gui.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gui.h; sourceTree = ""; }; 83299F271E765789003A3242 /* hash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hash.h; sourceTree = ""; }; - 83299F281E765789003A3242 /* math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = math.h; sourceTree = ""; }; 83299F291E765789003A3242 /* memory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = memory.h; sourceTree = ""; }; 83299F2B1E765789003A3242 /* fast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fast.h; sourceTree = ""; }; 83299F2C1E765789003A3242 /* ips.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ips.h; sourceTree = ""; }; @@ -308,7 +307,6 @@ 83299F3B1E765789003A3242 /* png-io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "png-io.h"; sourceTree = ""; }; 83299F3C1E765789003A3242 /* ring-fifo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ring-fifo.h"; sourceTree = ""; }; 83299F3D1E765789003A3242 /* socket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = socket.h; sourceTree = ""; }; - 83299F3E1E765789003A3242 /* string.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string.h; sourceTree = ""; }; 83299F3F1E765789003A3242 /* table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = table.h; sourceTree = ""; }; 83299F401E765789003A3242 /* text-codec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "text-codec.h"; sourceTree = ""; }; 83299F411E765789003A3242 /* threading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = threading.h; sourceTree = ""; }; @@ -383,6 +381,8 @@ 83CA2E2F1D7BCB6700F2EA53 /* ini.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ini.h; sourceTree = ""; }; 83CA2E391D7BCBD300F2EA53 /* memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = memory.c; path = platform/posix/memory.c; sourceTree = ""; }; 83CA2E421D7BD82100F2EA53 /* core.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = core.c; sourceTree = ""; }; + 83D0381024A40FB9004CF90F /* mgba-string.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mgba-string.h"; sourceTree = ""; }; + 83D0381224A4125D004CF90F /* mgba-math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mgba-math.h"; sourceTree = ""; }; 83D7282120A673A500076FC5 /* matrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = matrix.h; sourceTree = ""; }; 83D7282320A673BB00076FC5 /* matrix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = matrix.c; sourceTree = ""; }; /* End PBXFileReference section */ @@ -658,15 +658,15 @@ 83299F211E765789003A3242 /* gui */, 83299F261E765789003A3242 /* gui.h */, 83299F271E765789003A3242 /* hash.h */, - 83299F281E765789003A3242 /* math.h */, 83299F291E765789003A3242 /* memory.h */, + 83D0381224A4125D004CF90F /* mgba-math.h */, + 83D0381024A40FB9004CF90F /* mgba-string.h */, 83299F2A1E765789003A3242 /* patch */, 83299F2E1E765789003A3242 /* patch.h */, 83299F2F1E765789003A3242 /* platform */, 83299F3B1E765789003A3242 /* png-io.h */, 83299F3C1E765789003A3242 /* ring-fifo.h */, 83299F3D1E765789003A3242 /* socket.h */, - 83299F3E1E765789003A3242 /* string.h */, 83299F3F1E765789003A3242 /* table.h */, 83299F401E765789003A3242 /* text-codec.h */, 83299F411E765789003A3242 /* threading.h */, @@ -999,7 +999,6 @@ 83299F5A1E765789003A3242 /* arm.h in Headers */, 83299F811E765789003A3242 /* input.h in Headers */, 83299F761E765789003A3242 /* lockstep.h in Headers */, - 83299FA61E765789003A3242 /* math.h in Headers */, 83299F991E765789003A3242 /* arm-algo.h in Headers */, 83299F6E1E765789003A3242 /* gb.h in Headers */, 83299FB91E765789003A3242 /* threading.h in Headers */, @@ -1068,7 +1067,6 @@ 83299F9F1E765789003A3242 /* formatting.h in Headers */, 83CA2DBA1D7BC9BC00F2EA53 /* debugger.h in Headers */, 83299F781E765789003A3242 /* timer.h in Headers */, - 83299FB61E765789003A3242 /* string.h in Headers */, 83299F8C1E765789003A3242 /* serialize.h in Headers */, 83CA2DBC1D7BC9BC00F2EA53 /* memory-debugger.h in Headers */, 83299F741E765789003A3242 /* tile-cache.h in Headers */, @@ -1079,6 +1077,7 @@ 83299F901E765789003A3242 /* timer.h in Headers */, 83299F451E765789003A3242 /* cheats.h in Headers */, 83CA29AE1D7BC4EE00F2EA53 /* hle-bios.h in Headers */, + 83D0381324A4125D004CF90F /* mgba-math.h in Headers */, 83299F7D1E765789003A3242 /* dma.h in Headers */, 83299F601E765789003A3242 /* emitter-arm.h in Headers */, 83299F651E765789003A3242 /* isa-thumb.h in Headers */, @@ -1097,6 +1096,7 @@ 83299F881E765789003A3242 /* mgm.h in Headers */, 83299F4F1E765789003A3242 /* rewind.h in Headers */, 83299F611E765789003A3242 /* emitter-inlines.h in Headers */, + 83D0381124A40FB9004CF90F /* mgba-string.h in Headers */, 83299F691E765789003A3242 /* gdb-stub.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1133,8 +1133,8 @@ TargetAttributes = { 83CA24121D7BC47C00F2EA53 = { CreatedOnToolsVersion = 8.0; - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -1285,7 +1285,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -1338,7 +1338,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; VERSIONING_SYSTEM = "apple-generic"; @@ -1349,7 +1349,10 @@ 83CA241C1D7BC47C00F2EA53 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -1372,6 +1375,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.mGBA; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; }; name = Debug; @@ -1379,7 +1383,10 @@ 83CA241D1D7BC47C00F2EA53 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -1401,6 +1408,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.mGBA; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; }; name = Release; diff --git a/Frameworks/mGBA/mGBA/mgba b/Frameworks/mGBA/mGBA/mgba index 93cb7b0dc..523515c16 160000 --- a/Frameworks/mGBA/mGBA/mgba +++ b/Frameworks/mGBA/mGBA/mgba @@ -1 +1 @@ -Subproject commit 93cb7b0dc8aab92d1b88604609acaf971cb3c5e0 +Subproject commit 523515c16efd5d4abb8aaebdb124fdc38d3aa071 diff --git a/Frameworks/midi_processing/midi_processing.xcodeproj/project.pbxproj b/Frameworks/midi_processing/midi_processing.xcodeproj/project.pbxproj index abe12e89f..a129defe7 100644 --- a/Frameworks/midi_processing/midi_processing.xcodeproj/project.pbxproj +++ b/Frameworks/midi_processing/midi_processing.xcodeproj/project.pbxproj @@ -162,8 +162,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 83B066AB180D56B9008E3612 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -278,7 +278,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -324,7 +324,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; }; name = Release; @@ -332,7 +332,10 @@ 83B066D5180D56B9008E3612 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -340,6 +343,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "NoWork-Inc.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; @@ -349,7 +353,10 @@ 83B066D6180D56B9008E3612 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -357,6 +364,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "NoWork-Inc.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; diff --git a/Frameworks/mpg123/mpg123.xcodeproj/project.pbxproj b/Frameworks/mpg123/mpg123.xcodeproj/project.pbxproj index 6ee223b58..ab9f9f247 100644 --- a/Frameworks/mpg123/mpg123.xcodeproj/project.pbxproj +++ b/Frameworks/mpg123/mpg123.xcodeproj/project.pbxproj @@ -452,7 +452,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INSTALL_PATH = "@loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SKIP_INSTALL = YES; }; @@ -500,7 +500,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INSTALL_PATH = "@loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SKIP_INSTALL = YES; }; name = Release; diff --git a/Frameworks/psflib/psflib.xcodeproj/project.pbxproj b/Frameworks/psflib/psflib.xcodeproj/project.pbxproj index 2b427e5de..d49e5dfd3 100644 --- a/Frameworks/psflib/psflib.xcodeproj/project.pbxproj +++ b/Frameworks/psflib/psflib.xcodeproj/project.pbxproj @@ -136,8 +136,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 8343781B17F93CB500584396 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -242,7 +242,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -287,7 +287,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; }; name = Release; @@ -295,7 +295,10 @@ 8343784517F93CB500584396 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -304,6 +307,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.psflib; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; @@ -313,7 +317,10 @@ 8343784617F93CB500584396 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -322,6 +329,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.psflib; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index bd9b71b28..fcefda657 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -68,7 +68,6 @@ 8306B0E420984590000302D4 /* wave.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0C92098458E000302D4 /* wave.c */; }; 8306B0E520984590000302D4 /* ubi_lyn.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0CA2098458E000302D4 /* ubi_lyn.c */; }; 8306B0E620984590000302D4 /* msb_msh.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0CB2098458E000302D4 /* msb_msh.c */; }; - 8306B0E720984590000302D4 /* opus_ppp.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0CC2098458E000302D4 /* opus_ppp.c */; }; 8306B0E820984590000302D4 /* opus_interleave_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8306B0CD2098458E000302D4 /* opus_interleave_streamfile.h */; }; 8306B0E920984590000302D4 /* opus.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0CE2098458E000302D4 /* opus.c */; }; 8306B0EA20984590000302D4 /* caf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8306B0CF2098458F000302D4 /* caf.c */; }; @@ -93,7 +92,6 @@ 831BA61B1EAC61A500CF89B0 /* sgxd.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA6111EAC61A500CF89B0 /* sgxd.c */; }; 831BA61C1EAC61A500CF89B0 /* sxd.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA6121EAC61A500CF89B0 /* sxd.c */; }; 831BA61D1EAC61A500CF89B0 /* ubi_raki.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA6131EAC61A500CF89B0 /* ubi_raki.c */; }; - 831BA61E1EAC61A500CF89B0 /* vawx.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA6141EAC61A500CF89B0 /* vawx.c */; }; 831BA61F1EAC61A500CF89B0 /* x360_cxs.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA6151EAC61A500CF89B0 /* x360_cxs.c */; }; 831BA6211EAC61A500CF89B0 /* x360_pasx.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA6171EAC61A500CF89B0 /* x360_pasx.c */; }; 831BA6281EAC61CB00CF89B0 /* coding_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA6221EAC61CB00CF89B0 /* coding_utils.c */; }; @@ -141,7 +139,6 @@ 8349A8E91FE6253900E26435 /* blocked_ea_1snh.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8E31FE6253800E26435 /* blocked_ea_1snh.c */; }; 8349A8EA1FE6253900E26435 /* blocked_ea_schl.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8E41FE6253800E26435 /* blocked_ea_schl.c */; }; 8349A8EB1FE6253900E26435 /* blocked_ivaud.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8E51FE6253800E26435 /* blocked_ivaud.c */; }; - 8349A8EC1FE6253900E26435 /* blocked_vawx.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8E61FE6253900E26435 /* blocked_vawx.c */; }; 8349A8ED1FE6253900E26435 /* blocked_ea_sns.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8E71FE6253900E26435 /* blocked_ea_sns.c */; }; 8349A9071FE6258200E26435 /* dec.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8EE1FE6257C00E26435 /* dec.c */; }; 8349A9081FE6258200E26435 /* ezw.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8EF1FE6257C00E26435 /* ezw.c */; }; @@ -218,7 +215,6 @@ 834FE111215C79ED000A5D3D /* ck.c in Sources */ = {isa = PBXBuildFile; fileRef = 834FE0E8215C79EC000A5D3D /* ck.c */; }; 835027131ED119E000C25929 /* mta2_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 835027121ED119E000C25929 /* mta2_decoder.c */; }; 8350C0551E071881009E0A93 /* xma.c in Sources */ = {isa = PBXBuildFile; fileRef = 8350C0541E071881009E0A93 /* xma.c */; }; - 8350C05A1E071990009E0A93 /* ps2_svag_snk.c in Sources */ = {isa = PBXBuildFile; fileRef = 8350C0591E071990009E0A93 /* ps2_svag_snk.c */; }; 8351F3292212B53400A606E4 /* dsa_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8351F3252212B53300A606E4 /* dsa_decoder.c */; }; 8351F32D2212B57000A606E4 /* 208.c in Sources */ = {isa = PBXBuildFile; fileRef = 8351F32A2212B57000A606E4 /* 208.c */; }; 8351F32E2212B57000A606E4 /* ubi_bao_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8351F32B2212B57000A606E4 /* ubi_bao_streamfile.h */; }; @@ -313,7 +309,6 @@ 836F6FA418BDC2190095E648 /* nds_hwas.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6818BDC2180095E648 /* nds_hwas.c */; }; 836F6FA518BDC2190095E648 /* nds_rrds.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6918BDC2180095E648 /* nds_rrds.c */; }; 836F6FA718BDC2190095E648 /* nds_strm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6B18BDC2180095E648 /* nds_strm.c */; }; - 836F6FA818BDC2190095E648 /* nds_swav.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6C18BDC2180095E648 /* nds_swav.c */; }; 836F6FA918BDC2190095E648 /* ngc_adpdtk.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6D18BDC2180095E648 /* ngc_adpdtk.c */; }; 836F6FAA18BDC2190095E648 /* ngc_bh2pcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6E18BDC2180095E648 /* ngc_bh2pcm.c */; }; 836F6FAD18BDC2190095E648 /* ngc_dsp_konami.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E7118BDC2180095E648 /* ngc_dsp_konami.c */; }; @@ -376,7 +371,6 @@ 836F6FFA18BDC2190095E648 /* ps2_spm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBE18BDC2190095E648 /* ps2_spm.c */; }; 836F6FFB18BDC2190095E648 /* ps2_sps.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EBF18BDC2190095E648 /* ps2_sps.c */; }; 836F6FFC18BDC2190095E648 /* ps2_ster.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC018BDC2190095E648 /* ps2_ster.c */; }; - 836F700018BDC2190095E648 /* ps2_svag.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC418BDC2190095E648 /* ps2_svag.c */; }; 836F700118BDC2190095E648 /* ps2_tec.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC518BDC2190095E648 /* ps2_tec.c */; }; 836F700218BDC2190095E648 /* ps2_tk5.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC618BDC2190095E648 /* ps2_tk5.c */; }; 836F700418BDC2190095E648 /* ps2_vas.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EC818BDC2190095E648 /* ps2_vas.c */; }; @@ -439,7 +433,7 @@ 836F705418BDC2190095E648 /* streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 836F6F1818BDC2190095E648 /* streamfile.h */; settings = {ATTRIBUTES = (Public, ); }; }; 836F705518BDC2190095E648 /* streamtypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 836F6F1918BDC2190095E648 /* streamtypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; 836F705618BDC2190095E648 /* util.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F1A18BDC2190095E648 /* util.c */; }; - 836F705718BDC2190095E648 /* util.h in Headers */ = {isa = PBXBuildFile; fileRef = 836F6F1B18BDC2190095E648 /* util.h */; }; + 836F705718BDC2190095E648 /* util.h in Headers */ = {isa = PBXBuildFile; fileRef = 836F6F1B18BDC2190095E648 /* util.h */; settings = {ATTRIBUTES = (Public, ); }; }; 836F705818BDC2190095E648 /* vgmstream.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6F1C18BDC2190095E648 /* vgmstream.c */; }; 836F705918BDC2190095E648 /* vgmstream.h in Headers */ = {isa = PBXBuildFile; fileRef = 836F6F1D18BDC2190095E648 /* vgmstream.h */; settings = {ATTRIBUTES = (Public, ); }; }; 83709E051ECBC1A4005C03D3 /* gtd.c in Sources */ = {isa = PBXBuildFile; fileRef = 83709DFF1ECBC1A4005C03D3 /* gtd.c */; }; @@ -498,9 +492,6 @@ 837CEB0523487F2C00E62A4A /* sqex_sead_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEAF023487F2C00E62A4A /* sqex_sead_streamfile.h */; }; 837CEB072348809400E62A4A /* seb.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEB062348809400E62A4A /* seb.c */; }; 8385D4E6245174C700FF8E67 /* diva.c in Sources */ = {isa = PBXBuildFile; fileRef = 8385D4E2245174C600FF8E67 /* diva.c */; }; - 838BDB641D3AF08C0022CA6F /* libavcodec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB611D3AF08C0022CA6F /* libavcodec.a */; }; - 838BDB651D3AF08C0022CA6F /* libavformat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB621D3AF08C0022CA6F /* libavformat.a */; }; - 838BDB661D3AF08C0022CA6F /* libavutil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB631D3AF08C0022CA6F /* libavutil.a */; }; 838BDB681D3AF70D0022CA6F /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB671D3AF70D0022CA6F /* libz.tbd */; }; 838BDB6A1D3AF7140022CA6F /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 838BDB691D3AF7140022CA6F /* libiconv.tbd */; }; 838BDB6C1D3AFAB10022CA6F /* ffmpeg_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 838BDB6B1D3AFAB10022CA6F /* ffmpeg_decoder.c */; }; @@ -539,6 +530,13 @@ 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 */; }; + 83A8BADD256679C5000F5F3F /* wady_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A8BADC256679C5000F5F3F /* wady_decoder.c */; }; + 83A8BADF256679E3000F5F3F /* blocked_xwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A8BADE256679E3000F5F3F /* blocked_xwav.c */; }; + 83A8BAE525667AA8000F5F3F /* wady.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A8BAE025667AA7000F5F3F /* wady.c */; }; + 83A8BAE625667AA8000F5F3F /* ps2_enth_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A8BAE125667AA7000F5F3F /* ps2_enth_streamfile.h */; }; + 83A8BAE725667AA8000F5F3F /* xse.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A8BAE225667AA7000F5F3F /* xse.c */; }; + 83A8BAE825667AA8000F5F3F /* xwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A8BAE325667AA7000F5F3F /* xwav.c */; }; + 83A8BAE925667AA8000F5F3F /* cpk.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A8BAE425667AA7000F5F3F /* cpk.c */; }; 83AA5D161F6E2F600020821C /* ea_xa_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D0E1F6E2F5F0020821C /* ea_xa_decoder.c */; }; 83AA5D171F6E2F600020821C /* mpeg_custom_utils_ealayer3.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D131F6E2F5F0020821C /* mpeg_custom_utils_ealayer3.c */; }; 83AA5D181F6E2F600020821C /* mpeg_custom_utils_awc.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D141F6E2F600020821C /* mpeg_custom_utils_awc.c */; }; @@ -548,6 +546,21 @@ 83AA5D241F6E2F9C0020821C /* awc.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D201F6E2F9B0020821C /* awc.c */; }; 83AA5D251F6E2F9C0020821C /* hca_keys.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA5D211F6E2F9C0020821C /* hca_keys.h */; }; 83AA5D271F6E2F9C0020821C /* stm.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA5D231F6E2F9C0020821C /* stm.c */; }; + 83AA7F722519BFEA004C5298 /* vorbis_bitreader.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7F6D2519BFEA004C5298 /* vorbis_bitreader.h */; }; + 83AA7F732519BFEA004C5298 /* mpeg_bitreader.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7F712519BFEA004C5298 /* mpeg_bitreader.h */; }; + 83AA7F7D2519C042004C5298 /* dsb.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F742519C041004C5298 /* dsb.c */; }; + 83AA7F7E2519C042004C5298 /* svag_kcet.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F752519C041004C5298 /* svag_kcet.c */; }; + 83AA7F7F2519C042004C5298 /* bsf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F762519C042004C5298 /* bsf.c */; }; + 83AA7F802519C042004C5298 /* sab_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7F772519C042004C5298 /* sab_streamfile.h */; }; + 83AA7F812519C042004C5298 /* silence.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F782519C042004C5298 /* silence.c */; }; + 83AA7F822519C042004C5298 /* ktsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F792519C042004C5298 /* ktsc.c */; }; + 83AA7F832519C042004C5298 /* adp_konami.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F7A2519C042004C5298 /* adp_konami.c */; }; + 83AA7F842519C042004C5298 /* svag_snk.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F7B2519C042004C5298 /* svag_snk.c */; }; + 83AA7F852519C042004C5298 /* zwv.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F7C2519C042004C5298 /* zwv.c */; }; + 83AA7F8A2519C076004C5298 /* decode.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7F862519C076004C5298 /* decode.h */; }; + 83AA7F8B2519C076004C5298 /* render.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AA7F872519C076004C5298 /* render.h */; }; + 83AA7F8C2519C076004C5298 /* render.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F882519C076004C5298 /* render.c */; }; + 83AA7F8D2519C076004C5298 /* decode.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AA7F892519C076004C5298 /* decode.c */; }; 83AB8C761E8072A100086084 /* x360_ast.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AB8C741E8072A100086084 /* x360_ast.c */; }; 83AFABBC23795202002F3947 /* xssb.c in Sources */ = {isa = PBXBuildFile; fileRef = 83AFABB923795201002F3947 /* xssb.c */; }; 83AFABBD23795202002F3947 /* ea_eaac_opus_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AFABBA23795202002F3947 /* ea_eaac_opus_streamfile.h */; }; @@ -577,7 +590,11 @@ 83C7282822BC8C1500678B4A /* mixing.h in Headers */ = {isa = PBXBuildFile; fileRef = 83C7282422BC8C1400678B4A /* mixing.h */; }; 83C7282922BC8C1500678B4A /* mixing.c in Sources */ = {isa = PBXBuildFile; fileRef = 83C7282522BC8C1400678B4A /* mixing.c */; }; 83C7282A22BC8C1500678B4A /* plugins.c in Sources */ = {isa = PBXBuildFile; fileRef = 83C7282622BC8C1400678B4A /* plugins.c */; }; - 83CD428A1F787879000F77BE /* libswresample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 83CD42851F787878000F77BE /* libswresample.a */; }; + 83D0381824A4129A004CF90F /* swav.c in Sources */ = {isa = PBXBuildFile; fileRef = 83D0381724A4129A004CF90F /* swav.c */; }; + 83D11A17256CE2E900565AD3 /* libavcodec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D11A0F256CE08D00565AD3 /* libavcodec.a */; }; + 83D11A18256CE2EE00565AD3 /* libavformat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D11A10256CE08D00565AD3 /* libavformat.a */; }; + 83D11A19256CE2FA00565AD3 /* libavutil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D11A11256CE08D00565AD3 /* libavutil.a */; }; + 83D11A1B256CE30300565AD3 /* libswresample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D11A12256CE08D00565AD3 /* libswresample.a */; }; 83D2007A248DDB770048BD24 /* fsb_encrypted_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D20072248DDB760048BD24 /* fsb_encrypted_streamfile.h */; }; 83D2007B248DDB770048BD24 /* mups_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83D20073248DDB760048BD24 /* mups_streamfile.h */; }; 83D2007C248DDB770048BD24 /* ktsr.c in Sources */ = {isa = PBXBuildFile; fileRef = 83D20074248DDB760048BD24 /* ktsr.c */; }; @@ -586,7 +603,6 @@ 83D2007F248DDB770048BD24 /* mups.c in Sources */ = {isa = PBXBuildFile; fileRef = 83D20077248DDB770048BD24 /* mups.c */; }; 83D20080248DDB770048BD24 /* sadf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83D20078248DDB770048BD24 /* sadf.c */; }; 83D20081248DDB770048BD24 /* sadl.c in Sources */ = {isa = PBXBuildFile; fileRef = 83D20079248DDB770048BD24 /* sadl.c */; }; - 83D2F58E2356B266007646ED /* libopus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D2F58A2356B266007646ED /* libopus.a */; }; 83D731891A749D1500CA1366 /* g719.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D7313E1A74968A00CA1366 /* g719.framework */; }; 83D7318A1A749D2200CA1366 /* g719.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83D7313E1A74968A00CA1366 /* g719.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 83D7318C1A749EEE00CA1366 /* g719_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83D7318B1A749EEE00CA1366 /* g719_decoder.c */; }; @@ -719,6 +735,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 83EC716624A98B8F00B807C1 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 6; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -783,7 +808,6 @@ 8306B0C92098458E000302D4 /* wave.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wave.c; sourceTree = ""; }; 8306B0CA2098458E000302D4 /* ubi_lyn.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_lyn.c; sourceTree = ""; }; 8306B0CB2098458E000302D4 /* msb_msh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = msb_msh.c; sourceTree = ""; }; - 8306B0CC2098458E000302D4 /* opus_ppp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = opus_ppp.c; sourceTree = ""; }; 8306B0CD2098458E000302D4 /* opus_interleave_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opus_interleave_streamfile.h; sourceTree = ""; }; 8306B0CE2098458E000302D4 /* opus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = opus.c; sourceTree = ""; }; 8306B0CF2098458F000302D4 /* caf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = caf.c; sourceTree = ""; }; @@ -807,7 +831,6 @@ 831BA6111EAC61A500CF89B0 /* sgxd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sgxd.c; sourceTree = ""; }; 831BA6121EAC61A500CF89B0 /* sxd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sxd.c; sourceTree = ""; }; 831BA6131EAC61A500CF89B0 /* ubi_raki.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_raki.c; sourceTree = ""; }; - 831BA6141EAC61A500CF89B0 /* vawx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vawx.c; sourceTree = ""; }; 831BA6151EAC61A500CF89B0 /* x360_cxs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_cxs.c; sourceTree = ""; }; 831BA6171EAC61A500CF89B0 /* x360_pasx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_pasx.c; sourceTree = ""; }; 831BA6221EAC61CB00CF89B0 /* coding_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = coding_utils.c; sourceTree = ""; }; @@ -855,7 +878,6 @@ 8349A8E31FE6253800E26435 /* blocked_ea_1snh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ea_1snh.c; sourceTree = ""; }; 8349A8E41FE6253800E26435 /* blocked_ea_schl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ea_schl.c; sourceTree = ""; }; 8349A8E51FE6253800E26435 /* blocked_ivaud.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ivaud.c; sourceTree = ""; }; - 8349A8E61FE6253900E26435 /* blocked_vawx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_vawx.c; sourceTree = ""; }; 8349A8E71FE6253900E26435 /* blocked_ea_sns.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_ea_sns.c; sourceTree = ""; }; 8349A8EE1FE6257C00E26435 /* dec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dec.c; sourceTree = ""; }; 8349A8EF1FE6257C00E26435 /* ezw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ezw.c; sourceTree = ""; }; @@ -931,7 +953,6 @@ 834FE0E8215C79EC000A5D3D /* ck.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ck.c; sourceTree = ""; }; 835027121ED119E000C25929 /* mta2_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mta2_decoder.c; sourceTree = ""; }; 8350C0541E071881009E0A93 /* xma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xma.c; sourceTree = ""; }; - 8350C0591E071990009E0A93 /* ps2_svag_snk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_svag_snk.c; sourceTree = ""; }; 8351F3252212B53300A606E4 /* dsa_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsa_decoder.c; sourceTree = ""; }; 8351F32A2212B57000A606E4 /* 208.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 208.c; sourceTree = ""; }; 8351F32B2212B57000A606E4 /* ubi_bao_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_bao_streamfile.h; sourceTree = ""; }; @@ -1027,7 +1048,6 @@ 836F6E6818BDC2180095E648 /* nds_hwas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nds_hwas.c; sourceTree = ""; }; 836F6E6918BDC2180095E648 /* nds_rrds.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nds_rrds.c; sourceTree = ""; }; 836F6E6B18BDC2180095E648 /* nds_strm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nds_strm.c; sourceTree = ""; }; - 836F6E6C18BDC2180095E648 /* nds_swav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nds_swav.c; sourceTree = ""; }; 836F6E6D18BDC2180095E648 /* ngc_adpdtk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ngc_adpdtk.c; sourceTree = ""; }; 836F6E6E18BDC2180095E648 /* ngc_bh2pcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ngc_bh2pcm.c; sourceTree = ""; }; 836F6E7118BDC2180095E648 /* ngc_dsp_konami.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ngc_dsp_konami.c; sourceTree = ""; }; @@ -1090,7 +1110,6 @@ 836F6EBE18BDC2190095E648 /* ps2_spm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_spm.c; sourceTree = ""; }; 836F6EBF18BDC2190095E648 /* ps2_sps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_sps.c; sourceTree = ""; }; 836F6EC018BDC2190095E648 /* ps2_ster.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_ster.c; sourceTree = ""; }; - 836F6EC418BDC2190095E648 /* ps2_svag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_svag.c; sourceTree = ""; }; 836F6EC518BDC2190095E648 /* ps2_tec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_tec.c; sourceTree = ""; }; 836F6EC618BDC2190095E648 /* ps2_tk5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_tk5.c; sourceTree = ""; }; 836F6EC818BDC2190095E648 /* ps2_vas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_vas.c; sourceTree = ""; }; @@ -1212,9 +1231,6 @@ 837CEAF023487F2C00E62A4A /* sqex_sead_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sqex_sead_streamfile.h; sourceTree = ""; }; 837CEB062348809400E62A4A /* seb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = seb.c; sourceTree = ""; }; 8385D4E2245174C600FF8E67 /* diva.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = diva.c; sourceTree = ""; }; - 838BDB611D3AF08C0022CA6F /* libavcodec.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavcodec.a; path = ../../ThirdParty/ffmpeg/lib/libavcodec.a; sourceTree = ""; }; - 838BDB621D3AF08C0022CA6F /* libavformat.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavformat.a; path = ../../ThirdParty/ffmpeg/lib/libavformat.a; sourceTree = ""; }; - 838BDB631D3AF08C0022CA6F /* libavutil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavutil.a; path = ../../ThirdParty/ffmpeg/lib/libavutil.a; sourceTree = ""; }; 838BDB671D3AF70D0022CA6F /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 838BDB691D3AF7140022CA6F /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; }; 838BDB6B1D3AFAB10022CA6F /* ffmpeg_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffmpeg_decoder.c; sourceTree = ""; }; @@ -1252,6 +1268,13 @@ 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 = ""; }; + 83A8BADC256679C5000F5F3F /* wady_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wady_decoder.c; sourceTree = ""; }; + 83A8BADE256679E3000F5F3F /* blocked_xwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_xwav.c; sourceTree = ""; }; + 83A8BAE025667AA7000F5F3F /* wady.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wady.c; sourceTree = ""; }; + 83A8BAE125667AA7000F5F3F /* ps2_enth_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ps2_enth_streamfile.h; sourceTree = ""; }; + 83A8BAE225667AA7000F5F3F /* xse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xse.c; sourceTree = ""; }; + 83A8BAE325667AA7000F5F3F /* xwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xwav.c; sourceTree = ""; }; + 83A8BAE425667AA7000F5F3F /* cpk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cpk.c; sourceTree = ""; }; 83AA5D0E1F6E2F5F0020821C /* ea_xa_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_xa_decoder.c; sourceTree = ""; }; 83AA5D131F6E2F5F0020821C /* mpeg_custom_utils_ealayer3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpeg_custom_utils_ealayer3.c; sourceTree = ""; }; 83AA5D141F6E2F600020821C /* mpeg_custom_utils_awc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpeg_custom_utils_awc.c; sourceTree = ""; }; @@ -1261,6 +1284,21 @@ 83AA5D201F6E2F9B0020821C /* awc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = awc.c; sourceTree = ""; }; 83AA5D211F6E2F9C0020821C /* hca_keys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hca_keys.h; sourceTree = ""; }; 83AA5D231F6E2F9C0020821C /* stm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = stm.c; sourceTree = ""; }; + 83AA7F6D2519BFEA004C5298 /* vorbis_bitreader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vorbis_bitreader.h; sourceTree = ""; }; + 83AA7F712519BFEA004C5298 /* mpeg_bitreader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mpeg_bitreader.h; sourceTree = ""; }; + 83AA7F742519C041004C5298 /* dsb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsb.c; sourceTree = ""; }; + 83AA7F752519C041004C5298 /* svag_kcet.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = svag_kcet.c; sourceTree = ""; }; + 83AA7F762519C042004C5298 /* bsf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bsf.c; sourceTree = ""; }; + 83AA7F772519C042004C5298 /* sab_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sab_streamfile.h; sourceTree = ""; }; + 83AA7F782519C042004C5298 /* silence.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = silence.c; sourceTree = ""; }; + 83AA7F792519C042004C5298 /* ktsc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ktsc.c; sourceTree = ""; }; + 83AA7F7A2519C042004C5298 /* adp_konami.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adp_konami.c; sourceTree = ""; }; + 83AA7F7B2519C042004C5298 /* svag_snk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = svag_snk.c; sourceTree = ""; }; + 83AA7F7C2519C042004C5298 /* zwv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = zwv.c; sourceTree = ""; }; + 83AA7F862519C076004C5298 /* decode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = decode.h; sourceTree = ""; }; + 83AA7F872519C076004C5298 /* render.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = render.h; sourceTree = ""; }; + 83AA7F882519C076004C5298 /* render.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = render.c; sourceTree = ""; }; + 83AA7F892519C076004C5298 /* decode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = decode.c; sourceTree = ""; }; 83AB8C741E8072A100086084 /* x360_ast.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x360_ast.c; sourceTree = ""; }; 83AFABB923795201002F3947 /* xssb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xssb.c; sourceTree = ""; }; 83AFABBA23795202002F3947 /* ea_eaac_opus_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ea_eaac_opus_streamfile.h; sourceTree = ""; }; @@ -1290,7 +1328,11 @@ 83C7282422BC8C1400678B4A /* mixing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mixing.h; sourceTree = ""; }; 83C7282522BC8C1400678B4A /* mixing.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mixing.c; sourceTree = ""; }; 83C7282622BC8C1400678B4A /* plugins.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = plugins.c; sourceTree = ""; }; - 83CD42851F787878000F77BE /* libswresample.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libswresample.a; path = ../../ThirdParty/ffmpeg/lib/libswresample.a; sourceTree = ""; }; + 83D0381724A4129A004CF90F /* swav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = swav.c; sourceTree = ""; }; + 83D11A0F256CE08D00565AD3 /* libavcodec.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavcodec.a; path = ../../ThirdParty/ffmpeg/lib/libavcodec.a; sourceTree = ""; }; + 83D11A10256CE08D00565AD3 /* libavformat.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavformat.a; path = ../../ThirdParty/ffmpeg/lib/libavformat.a; sourceTree = ""; }; + 83D11A11256CE08D00565AD3 /* libavutil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavutil.a; path = ../../ThirdParty/ffmpeg/lib/libavutil.a; sourceTree = ""; }; + 83D11A12256CE08D00565AD3 /* libswresample.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libswresample.a; path = ../../ThirdParty/ffmpeg/lib/libswresample.a; sourceTree = ""; }; 83D20072248DDB760048BD24 /* fsb_encrypted_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fsb_encrypted_streamfile.h; sourceTree = ""; }; 83D20073248DDB760048BD24 /* mups_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mups_streamfile.h; sourceTree = ""; }; 83D20074248DDB760048BD24 /* ktsr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ktsr.c; sourceTree = ""; }; @@ -1299,7 +1341,6 @@ 83D20077248DDB770048BD24 /* mups.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mups.c; sourceTree = ""; }; 83D20078248DDB770048BD24 /* sadf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sadf.c; sourceTree = ""; }; 83D20079248DDB770048BD24 /* sadl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sadl.c; sourceTree = ""; }; - 83D2F58A2356B266007646ED /* libopus.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopus.a; path = ../../ThirdParty/ffmpeg/lib/libopus.a; sourceTree = ""; }; 83D731381A74968900CA1366 /* g719.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = g719.xcodeproj; path = ../g719/g719.xcodeproj; sourceTree = ""; }; 83D7318B1A749EEE00CA1366 /* g719_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = g719_decoder.c; sourceTree = ""; }; 83E56BA01F2EE3500026BC60 /* vorbis_custom_utils_ogl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_ogl.c; sourceTree = ""; }; @@ -1339,12 +1380,11 @@ 838BDB711D3B1F990022CA6F /* CoreFoundation.framework in Frameworks */, 838BDB6A1D3AF7140022CA6F /* libiconv.tbd in Frameworks */, 838BDB681D3AF70D0022CA6F /* libz.tbd in Frameworks */, - 83D2F58E2356B266007646ED /* libopus.a in Frameworks */, - 83CD428A1F787879000F77BE /* libswresample.a in Frameworks */, + 83D11A17256CE2E900565AD3 /* libavcodec.a in Frameworks */, + 83D11A18256CE2EE00565AD3 /* libavformat.a in Frameworks */, + 83D11A19256CE2FA00565AD3 /* libavutil.a in Frameworks */, + 83D11A1B256CE30300565AD3 /* libswresample.a in Frameworks */, 83D731891A749D1500CA1366 /* g719.framework in Frameworks */, - 838BDB641D3AF08C0022CA6F /* libavcodec.a in Frameworks */, - 838BDB651D3AF08C0022CA6F /* libavformat.a in Frameworks */, - 838BDB661D3AF08C0022CA6F /* libavutil.a in Frameworks */, 8313E3E61902020400B4B6F1 /* mpg123.framework in Frameworks */, 834D795520E4F0D400C4A5CC /* Vorbis.framework in Frameworks */, 836C052B23F62F1A00FA07C7 /* libatrac9.framework in Frameworks */, @@ -1400,7 +1440,6 @@ 836F6B3B18BDB8880095E648 /* Frameworks */ = { isa = PBXGroup; children = ( - 83D2F58A2356B266007646ED /* libopus.a */, 838BDB7E1D3B1FD10022CA6F /* Cocoa.framework */, 838BDB7C1D3B1FCC0022CA6F /* CoreVideo.framework */, 838BDB7A1D3B1FC20022CA6F /* CoreMedia.framework */, @@ -1419,11 +1458,11 @@ 836F6B3E18BDB8880095E648 /* Other Frameworks */ = { isa = PBXGroup; children = ( + 83D11A0F256CE08D00565AD3 /* libavcodec.a */, + 83D11A10256CE08D00565AD3 /* libavformat.a */, + 83D11A11256CE08D00565AD3 /* libavutil.a */, + 83D11A12256CE08D00565AD3 /* libswresample.a */, 835FC6C123F62AEE006960FA /* libatrac9.xcodeproj */, - 838BDB611D3AF08C0022CA6F /* libavcodec.a */, - 838BDB621D3AF08C0022CA6F /* libavformat.a */, - 838BDB631D3AF08C0022CA6F /* libavutil.a */, - 83CD42851F787878000F77BE /* libswresample.a */, 8313E33D1901FBDC00B4B6F1 /* mpg123.xcodeproj */, 83F412871E932F9A002E37D0 /* Vorbis.xcodeproj */, 83D731381A74968900CA1366 /* g719.xcodeproj */, @@ -1456,11 +1495,15 @@ 836F6DDF18BDC2180095E648 /* coding */, 836F6DFF18BDC2180095E648 /* layout */, 836F6E2718BDC2180095E648 /* meta */, + 83AA7F892519C076004C5298 /* decode.c */, + 83AA7F862519C076004C5298 /* decode.h */, 83A3F0711E3AD8B900D6A794 /* formats.c */, 83C7282522BC8C1400678B4A /* mixing.c */, 83C7282422BC8C1400678B4A /* mixing.h */, 83C7282622BC8C1400678B4A /* plugins.c */, 83C7282322BC8C1300678B4A /* plugins.h */, + 83AA7F882519C076004C5298 /* render.c */, + 83AA7F872519C076004C5298 /* render.h */, 83A3F0731E3AD8B900D6A794 /* stack_alloc.h */, 836F6F1718BDC2190095E648 /* streamfile.c */, 836F6F1818BDC2190095E648 /* streamfile.h */, @@ -1520,6 +1563,7 @@ 836F6DEC18BDC2180095E648 /* lsf_decoder.c */, 83709E0B1ECBC1C3005C03D3 /* mc3_decoder.c */, 836F6DEE18BDC2180095E648 /* mp4_aac_decoder.c */, + 83AA7F712519BFEA004C5298 /* mpeg_bitreader.h */, 839E21D91F2EDAF000EE54D7 /* mpeg_custom_utils_ahx.c */, 83AA5D141F6E2F600020821C /* mpeg_custom_utils_awc.c */, 83AA5D131F6E2F5F0020821C /* mpeg_custom_utils_ealayer3.c */, @@ -1549,6 +1593,7 @@ 8373341023F60C7A00DE14DC /* tgcadpcm_decoder.c */, 837CEA7623487E2400E62A4A /* ubi_adpcm_decoder.c */, 83F1EE2C245D4FB20076E182 /* vadpcm_decoder.c */, + 83AA7F6D2519BFEA004C5298 /* vorbis_bitreader.h */, 839E21D61F2EDAF000EE54D7 /* vorbis_custom_data_fsb.h */, 839E21DC1F2EDAF000EE54D7 /* vorbis_custom_data_wwise.h */, 839E21D71F2EDAF000EE54D7 /* vorbis_custom_decoder.c */, @@ -1558,6 +1603,7 @@ 839E21DF1F2EDAF000EE54D7 /* vorbis_custom_utils_sk.c */, 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */, 839E21D81F2EDAF000EE54D7 /* vorbis_custom_utils_wwise.c */, + 83A8BADC256679C5000F5F3F /* wady_decoder.c */, 836F6DFD18BDC2180095E648 /* ws_decoder.c */, 836F6DFE18BDC2180095E648 /* xa_decoder.c */, 834FE0B0215C798C000A5D3D /* xmd_decoder.c */, @@ -1597,7 +1643,6 @@ 8306B09820984550000302D4 /* blocked_thp.c */, 8306B09920984550000302D4 /* blocked_tra.c */, 83031ECA243C50CB00C3F3E0 /* blocked_ubi_sce.c */, - 8349A8E61FE6253900E26435 /* blocked_vawx.c */, 83AA5D1A1F6E2F7F0020821C /* blocked_vgs.c */, 83031ECB243C50CB00C3F3E0 /* blocked_vid1.c */, 832BF80321E050DC006F50F1 /* blocked_vs_square.c */, @@ -1609,6 +1654,7 @@ 8306B0912098454E000302D4 /* blocked_xa.c */, 83A21F7A201D895B000F04B9 /* blocked_xvag.c */, 8306B08C2098454D000302D4 /* blocked_xvas.c */, + 83A8BADE256679E3000F5F3F /* blocked_xwav.c */, 836F6E0418BDC2180095E648 /* blocked.c */, 834FE0BD215C79A9000A5D3D /* flat.c */, 836F6E0D18BDC2180095E648 /* interleave.c */, @@ -1630,6 +1676,7 @@ 836F6E2A18BDC2180095E648 /* aax.c */, 837CEAD623487E8300E62A4A /* acb.c */, 836F6E2B18BDC2180095E648 /* acm.c */, + 83AA7F7A2519C042004C5298 /* adp_konami.c */, 834FE0CF215C79E8000A5D3D /* adpcm_capcom.c */, 836F6E2C18BDC2180095E648 /* ads.c */, 8349A9021FE6258100E26435 /* adx_keys.h */, @@ -1669,11 +1716,13 @@ 8373342523F60CDC00DE14DC /* bnsf_keys.h */, 836F6E3918BDC2180095E648 /* bnsf.c */, 836F6E3A18BDC2180095E648 /* brstm.c */, + 83AA7F762519C042004C5298 /* bsf.c */, 83EDE5D71A70951A005F5D84 /* btsnd.c */, 835C883122CC17BD001B4B3F /* bwav.c */, 8306B0CF2098458F000302D4 /* caf.c */, 836F6E3B18BDC2180095E648 /* capdsp.c */, 834FE0E8215C79EC000A5D3D /* ck.c */, + 83A8BAE425667AA7000F5F3F /* cpk.c */, 83FC177023AC59A800E1025F /* cri_utf.c */, 83FC176C23AC58D100E1025F /* cri_utf.h */, 83FC176B23AC58D100E1025F /* csb.c */, @@ -1690,6 +1739,7 @@ 834FE0CD215C79E8000A5D3D /* derf.c */, 8385D4E2245174C600FF8E67 /* diva.c */, 836F6E4318BDC2180095E648 /* dmsg_segh.c */, + 83AA7F742519C041004C5298 /* dsb.c */, 8351F32C2212B57000A606E4 /* dsf.c */, 83299FCF1E7660C7003A3242 /* dsp_adx.c */, 836F6E4418BDC2180095E648 /* dsp_bdsp.c */, @@ -1753,6 +1803,7 @@ 834FE0C3215C79E6000A5D3D /* kma9_streamfile.h */, 83A21F83201D8981000F04B9 /* kma9.c */, 836F6E5918BDC2180095E648 /* kraw.c */, + 83AA7F792519C042004C5298 /* ktsc.c */, 83D20074248DDB760048BD24 /* ktsr.c */, 830EBE122004656E0023AA10 /* ktss.c */, 8373342423F60CDB00DE14DC /* kwb.c */, @@ -1797,7 +1848,6 @@ 836F6E6918BDC2180095E648 /* nds_rrds.c */, 830165991F256BD000CA0941 /* nds_strm_ffta2.c */, 836F6E6B18BDC2180095E648 /* nds_strm.c */, - 836F6E6C18BDC2180095E648 /* nds_swav.c */, 836F6E6D18BDC2180095E648 /* ngc_adpdtk.c */, 836F6E6E18BDC2180095E648 /* ngc_bh2pcm.c */, 836F6E7118BDC2180095E648 /* ngc_dsp_konami.c */, @@ -1831,7 +1881,6 @@ 831BA60F1EAC61A500CF89B0 /* ogl.c */, 8349A8FB1FE6257F00E26435 /* omu.c */, 8306B0CD2098458E000302D4 /* opus_interleave_streamfile.h */, - 8306B0CC2098458E000302D4 /* opus_ppp.c */, 8306B0CE2098458E000302D4 /* opus.c */, 836F6E8318BDC2180095E648 /* otm.c */, 836F6E8418BDC2180095E648 /* p3d.c */, @@ -1856,6 +1905,7 @@ 836F6E9618BDC2180095E648 /* ps2_bmdx.c */, 836F6E9718BDC2180095E648 /* ps2_ccc.c */, 836F6E9818BDC2180095E648 /* ps2_dxh.c */, + 83A8BAE125667AA7000F5F3F /* ps2_enth_streamfile.h */, 836F6E9918BDC2180095E648 /* ps2_enth.c */, 836F6E9A18BDC2180095E648 /* ps2_exst.c */, 836F6E9B18BDC2180095E648 /* ps2_filp.c */, @@ -1886,8 +1936,6 @@ 836F6EBE18BDC2190095E648 /* ps2_spm.c */, 836F6EBF18BDC2190095E648 /* ps2_sps.c */, 836F6EC018BDC2190095E648 /* ps2_ster.c */, - 8350C0591E071990009E0A93 /* ps2_svag_snk.c */, - 836F6EC418BDC2190095E648 /* ps2_svag.c */, 836F6EC518BDC2190095E648 /* ps2_tec.c */, 836F6EC618BDC2190095E648 /* ps2_tk5.c */, 832BF80D21E05148006F50F1 /* ps2_va3.c */, @@ -1925,6 +1973,7 @@ 836F6EE818BDC2190095E648 /* rwsd.c */, 836F6EE918BDC2190095E648 /* rwx.c */, 836F6EEA18BDC2190095E648 /* s14_sss.c */, + 83AA7F772519C042004C5298 /* sab_streamfile.h */, 8349A8F11FE6257D00E26435 /* sab.c */, 83D20078248DDB770048BD24 /* sadf.c */, 83D20079248DDB770048BD24 /* sadl.c */, @@ -1941,6 +1990,7 @@ 83C7280822BC893C00678B4A /* sfh.c */, 836F6EF118BDC2190095E648 /* sfl.c */, 831BA6111EAC61A500CF89B0 /* sgxd.c */, + 83AA7F782519C042004C5298 /* silence.c */, 839E21EA1F2EDB0500EE54D7 /* sk_aud.c */, 836F6EF218BDC2190095E648 /* sli.c */, 8306B0D32098458F000302D4 /* smc_smh.c */, @@ -1960,8 +2010,11 @@ 836F6EF718BDC2190095E648 /* str_snds.c */, 834FE0C2215C79E6000A5D3D /* str_wav.c */, 83C7280722BC893B00678B4A /* strm_abylight.c */, + 83AA7F752519C041004C5298 /* svag_kcet.c */, + 83AA7F7B2519C042004C5298 /* svag_snk.c */, 834FE0D7215C79EA000A5D3D /* svg.c */, 836F6EF918BDC2190095E648 /* svs.c */, + 83D0381724A4129A004CF90F /* swav.c */, 831BA6121EAC61A500CF89B0 /* sxd.c */, 83709E031ECBC1A4005C03D3 /* ta_aac.c */, 8373342E23F60D4100DE14DC /* tgc.c */, @@ -1984,7 +2037,6 @@ 834FE0DD215C79EB000A5D3D /* utk.c */, 834FE0E4215C79EC000A5D3D /* vag.c */, 834FE0D3215C79E9000A5D3D /* vai.c */, - 831BA6141EAC61A500CF89B0 /* vawx.c */, 836F6EFD18BDC2190095E648 /* vgs.c */, 83031ED7243C510400C3F3E0 /* vid1.c */, 834FE0CE215C79E8000A5D3D /* vis.c */, @@ -1997,6 +2049,7 @@ 83F0AA5D21E2028B004BBC04 /* vsv_streamfile.h */, 83F0AA5E21E2028C004BBC04 /* vsv.c */, 8349A9011FE6258000E26435 /* vxn.c */, + 83A8BAE025667AA7000F5F3F /* wady.c */, 8306B0C22098458C000302D4 /* waf.c */, 8306B0D02098458F000302D4 /* wave_segmented.c */, 8306B0C92098458E000302D4 /* wave.c */, @@ -2036,11 +2089,13 @@ 832BF81921E0514A006F50F1 /* xopus.c */, 832BF80A21E05148006F50F1 /* xpcm.c */, 832BF80C21E05148006F50F1 /* xps.c */, + 83A8BAE225667AA7000F5F3F /* xse.c */, 836F6F1218BDC2190095E648 /* xss.c */, 83AFABB923795201002F3947 /* xssb.c */, 834FE0C6215C79E7000A5D3D /* xvag_streamfile.h */, 83345A4E1F8AEB2800B2EAA4 /* xvag.c */, 837CEADC23487F2900E62A4A /* xvas.c */, + 83A8BAE325667AA7000F5F3F /* xwav.c */, 83C727FB22BC893800678B4A /* xwb_xsb.h */, 836F6F1318BDC2190095E648 /* xwb.c */, 83A21F7D201D8980000F04B9 /* xwc.c */, @@ -2052,6 +2107,7 @@ 832BF80E21E05149006F50F1 /* zsnd_streamfile.h */, 832BF80F21E05149006F50F1 /* zsnd.c */, 836F6F1618BDC2190095E648 /* zwdsp.c */, + 83AA7F7C2519C042004C5298 /* zwv.c */, ); path = meta; sourceTree = ""; @@ -2086,9 +2142,11 @@ 836F6F3518BDC2190095E648 /* nwa_decoder.h in Headers */, 83031EC9243C50A800C3F3E0 /* circus_decoder_lzxpcm.h in Headers */, 83C7282222BC893D00678B4A /* mta2_streamfile.h in Headers */, + 83AA7F802519C042004C5298 /* sab_streamfile.h in Headers */, 83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */, 834FE0EC215C79ED000A5D3D /* kma9_streamfile.h in Headers */, 8349A90F1FE6258200E26435 /* aix_streamfile.h in Headers */, + 836F705718BDC2190095E648 /* util.h in Headers */, 83C7282722BC8C1500678B4A /* plugins.h in Headers */, 834FE0ED215C79ED000A5D3D /* fsb_interleave_streamfile.h in Headers */, 8351F32E2212B57000A606E4 /* ubi_bao_streamfile.h in Headers */, @@ -2099,6 +2157,7 @@ 83C7282822BC8C1500678B4A /* mixing.h in Headers */, 832BF82C21E0514B006F50F1 /* hca_keys_awb.h in Headers */, 8306B0E020984590000302D4 /* ppst_streamfile.h in Headers */, + 83AA7F722519BFEA004C5298 /* vorbis_bitreader.h in Headers */, 834FE0BA215C798C000A5D3D /* ea_mt_decoder_utk.h in Headers */, 835C883722CC17BE001B4B3F /* ogg_vorbis_streamfile.h in Headers */, 837CEAFE23487F2C00E62A4A /* jstm_streamfile.h in Headers */, @@ -2112,6 +2171,7 @@ 8323894B1D22419B00482226 /* clHCA.h in Headers */, 83C7281122BC893D00678B4A /* 9tav_streamfile.h in Headers */, 83C7280F22BC893D00678B4A /* xwb_xsb.h in Headers */, + 83AA7F732519BFEA004C5298 /* mpeg_bitreader.h in Headers */, 83C7281622BC893D00678B4A /* xwma_konami_streamfile.h in Headers */, 839E21E81F2EDAF100EE54D7 /* mpeg_decoder.h in Headers */, 839E21E51F2EDAF100EE54D7 /* vorbis_custom_decoder.h in Headers */, @@ -2124,13 +2184,14 @@ 83031EC5243C50A800C3F3E0 /* circus_decoder_lib_data.h in Headers */, 834FE0B5215C798C000A5D3D /* acm_decoder_libacm.h in Headers */, 839E21E61F2EDAF100EE54D7 /* vorbis_custom_data_wwise.h in Headers */, + 83AA7F8A2519C076004C5298 /* decode.h in Headers */, 8373342D23F60CDC00DE14DC /* bnsf_keys.h in Headers */, 834FE103215C79ED000A5D3D /* ea_schl_streamfile.h in Headers */, 83F0AA6021E2028C004BBC04 /* vsv_streamfile.h in Headers */, 83FBD506235D31F800D35BCD /* riff_ogg_streamfile.h in Headers */, 48C2650F1A5D420800A0A3D6 /* vorbisfile.h in Headers */, - 836F705718BDC2190095E648 /* util.h in Headers */, 836F6F9A18BDC2190095E648 /* meta.h in Headers */, + 83A8BAE625667AA8000F5F3F /* ps2_enth_streamfile.h in Headers */, 8306B0D820984590000302D4 /* ea_eaac_streamfile.h in Headers */, 837CEB0523487F2C00E62A4A /* sqex_sead_streamfile.h in Headers */, 83031ED9243C510500C3F3E0 /* ubi_lyn_streamfile.h in Headers */, @@ -2138,6 +2199,7 @@ 8306B0E820984590000302D4 /* opus_interleave_streamfile.h in Headers */, 83031EDD243C510500C3F3E0 /* xnb_streamfile.h in Headers */, 83D2007B248DDB770048BD24 /* mups_streamfile.h in Headers */, + 83AA7F8B2519C076004C5298 /* render.h in Headers */, 8373341623F60C7B00DE14DC /* g7221_decoder_aes.h in Headers */, 8373341923F60C7B00DE14DC /* g7221_decoder_lib_data.h in Headers */, 83AFABBD23795202002F3947 /* ea_eaac_opus_streamfile.h in Headers */, @@ -2169,6 +2231,7 @@ 836F6B3618BDB8880095E648 /* Headers */, 836F6B3718BDB8880095E648 /* Resources */, 83979B0C18BDD93100192D2F /* CopyFiles */, + 83EC716624A98B8F00B807C1 /* CopyFiles */, ); buildRules = ( 48C265101A5D424500A0A3D6 /* PBXBuildRule */, @@ -2194,7 +2257,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 836F6B3818BDB8880095E648 = { - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -2313,6 +2377,7 @@ 839E21E41F2EDAF100EE54D7 /* vorbis_custom_utils_fsb.c in Sources */, 839E21E11F2EDAF100EE54D7 /* vorbis_custom_decoder.c in Sources */, 839E21E71F2EDAF100EE54D7 /* mpeg_custom_utils.c in Sources */, + 83A8BAE525667AA8000F5F3F /* wady.c in Sources */, 839E21E31F2EDAF100EE54D7 /* mpeg_custom_utils_ahx.c in Sources */, 8306B0AF20984552000302D4 /* blocked_rws.c in Sources */, 83C7281A22BC893D00678B4A /* mus_vc.c in Sources */, @@ -2367,7 +2432,6 @@ 836F6FD718BDC2190095E648 /* ps2_filp.c in Sources */, 83FF0EBC1E93282100C58054 /* wwise.c in Sources */, 836F6F7018BDC2190095E648 /* apple_caff.c in Sources */, - 836F700018BDC2190095E648 /* ps2_svag.c in Sources */, 836F6FB618BDC2190095E648 /* ngc_sck_dsp.c in Sources */, 836F6F2818BDC2190095E648 /* ima_decoder.c in Sources */, 837CEB072348809400E62A4A /* seb.c in Sources */, @@ -2418,11 +2482,13 @@ 836F703C18BDC2190095E648 /* wii_bns.c in Sources */, 830EBE132004656E0023AA10 /* xnb.c in Sources */, 835027131ED119E000C25929 /* mta2_decoder.c in Sources */, + 83A8BAE725667AA8000F5F3F /* xse.c in Sources */, 8306B0DB20984590000302D4 /* nxap.c in Sources */, 836F6FA718BDC2190095E648 /* nds_strm.c in Sources */, 8349A91A1FE6258200E26435 /* vxn.c in Sources */, 8349A8EB1FE6253900E26435 /* blocked_ivaud.c in Sources */, 83A3F0741E3AD8B900D6A794 /* formats.c in Sources */, + 83A8BAE925667AA8000F5F3F /* cpk.c in Sources */, 836F6F6E18BDC2190095E648 /* aix.c in Sources */, 836F6F8718BDC2190095E648 /* ffw.c in Sources */, 8349A9141FE6258200E26435 /* omu.c in Sources */, @@ -2430,6 +2496,7 @@ 836F705618BDC2190095E648 /* util.c in Sources */, 8306B0A820984552000302D4 /* blocked_ps2_iab.c in Sources */, 8306B0EA20984590000302D4 /* caf.c in Sources */, + 83AA7F842519C042004C5298 /* svag_snk.c in Sources */, 834FE107215C79ED000A5D3D /* mib_mih.c in Sources */, 836F703618BDC2190095E648 /* thp.c in Sources */, 836F6F7818BDC2190095E648 /* Cstr.c in Sources */, @@ -2440,7 +2507,7 @@ 836F6FF218BDC2190095E648 /* ps2_rnd.c in Sources */, 83709E0D1ECBC1C3005C03D3 /* mc3_decoder.c in Sources */, 832BF80521E050DC006F50F1 /* blocked_mul.c in Sources */, - 836F6FA818BDC2190095E648 /* nds_swav.c in Sources */, + 83A8BADD256679C5000F5F3F /* wady_decoder.c in Sources */, 8306B0D920984590000302D4 /* ngc_str_cauldron.c in Sources */, 834FE0FB215C79ED000A5D3D /* xau_konami.c in Sources */, 83F0AA6121E2028C004BBC04 /* vsv.c in Sources */, @@ -2458,7 +2525,7 @@ 836F701118BDC2190095E648 /* ps3_cps.c in Sources */, 8306B0DA20984590000302D4 /* ea_wve_au00.c in Sources */, 83A21F85201D8981000F04B9 /* atx.c in Sources */, - 8306B0E720984590000302D4 /* opus_ppp.c in Sources */, + 83A8BADF256679E3000F5F3F /* blocked_xwav.c in Sources */, 836F6F6518BDC2190095E648 /* 2dx9.c in Sources */, 830EBE102004655D0023AA10 /* atrac9_decoder.c in Sources */, 836F700818BDC2190095E648 /* ps2_vms.c in Sources */, @@ -2467,6 +2534,7 @@ 836F6F6618BDC2190095E648 /* aax.c in Sources */, 836F6FD618BDC2190095E648 /* ps2_exst.c in Sources */, 8306B0BC20984552000302D4 /* blocked_vs.c in Sources */, + 83AA7F812519C042004C5298 /* silence.c in Sources */, 834FE0B3215C798C000A5D3D /* acm_decoder_util.c in Sources */, 837CEA7A23487E2500E62A4A /* ffmpeg_decoder_utils.c in Sources */, 837CEAF823487F2C00E62A4A /* xmv_valve.c in Sources */, @@ -2483,7 +2551,10 @@ 836F6F6F18BDC2190095E648 /* akb.c in Sources */, 83EED5D6203A8BD7008BEB45 /* blocked_ea_swvr.c in Sources */, 8349A9181FE6258200E26435 /* ea_1snh.c in Sources */, + 83AA7F7E2519C042004C5298 /* svag_kcet.c in Sources */, 8373342C23F60CDC00DE14DC /* kwb.c in Sources */, + 83A8BAE825667AA8000F5F3F /* xwav.c in Sources */, + 83AA7F852519C042004C5298 /* zwv.c in Sources */, 83EED5D4203A8BC7008BEB45 /* aus.c in Sources */, 836F6F7F18BDC2190095E648 /* dmsg_segh.c in Sources */, 832BF81D21E0514B006F50F1 /* msf_tamasoft.c in Sources */, @@ -2556,7 +2627,7 @@ 836F705218BDC2190095E648 /* zwdsp.c in Sources */, 836F6FFB18BDC2190095E648 /* ps2_sps.c in Sources */, 836F6F2018BDC2190095E648 /* adx_decoder.c in Sources */, - 8349A8EC1FE6253900E26435 /* blocked_vawx.c in Sources */, + 83AA7F8D2519C076004C5298 /* decode.c in Sources */, 834FE0EA215C79ED000A5D3D /* aif_asobo.c in Sources */, 836F700418BDC2190095E648 /* ps2_vas.c in Sources */, 836F6F9818BDC2190095E648 /* mattel_hyperscan.c in Sources */, @@ -2577,6 +2648,7 @@ 836F6FAF18BDC2190095E648 /* ngc_dsp_std.c in Sources */, 836F6F7118BDC2190095E648 /* ast.c in Sources */, 834FE0BF215C79A9000A5D3D /* flat.c in Sources */, + 83AA7F7D2519C042004C5298 /* dsb.c in Sources */, 836F6F6B18BDC2190095E648 /* agsc.c in Sources */, 836F700E18BDC2190095E648 /* ps2_xa2.c in Sources */, 837CEB0023487F2C00E62A4A /* smk.c in Sources */, @@ -2602,6 +2674,7 @@ 8306B0E620984590000302D4 /* msb_msh.c in Sources */, 836F702918BDC2190095E648 /* sat_sap.c in Sources */, 8351F3292212B53400A606E4 /* dsa_decoder.c in Sources */, + 83D0381824A4129A004CF90F /* swav.c in Sources */, 836F6F3718BDC2190095E648 /* pcm_decoder.c in Sources */, 834FE106215C79ED000A5D3D /* utk.c in Sources */, 831BA6281EAC61CB00CF89B0 /* coding_utils.c in Sources */, @@ -2609,6 +2682,7 @@ 837CEAFF23487F2C00E62A4A /* raw_snds.c in Sources */, 83031EDC243C510500C3F3E0 /* vid1.c in Sources */, 836F700218BDC2190095E648 /* ps2_tk5.c in Sources */, + 83AA7F8C2519C076004C5298 /* render.c in Sources */, 83AA5D271F6E2F9C0020821C /* stm.c in Sources */, 831BA61D1EAC61A500CF89B0 /* ubi_raki.c in Sources */, 8306B0A520984552000302D4 /* blocked_ea_wve_au00.c in Sources */, @@ -2657,7 +2731,6 @@ 8306B0AB20984552000302D4 /* layered.c in Sources */, 8306B0EC20984590000302D4 /* pcm_sre.c in Sources */, 836F6FC818BDC2190095E648 /* pos.c in Sources */, - 8350C05A1E071990009E0A93 /* ps2_svag_snk.c in Sources */, 836F6F8918BDC2190095E648 /* gca.c in Sources */, 836F6FA418BDC2190095E648 /* nds_hwas.c in Sources */, 83A21F8A201D8982000F04B9 /* fsb_encrypted.c in Sources */, @@ -2679,7 +2752,6 @@ 8349A9161FE6258200E26435 /* flx.c in Sources */, 832BF82921E0514B006F50F1 /* msf_banpresto.c in Sources */, 834FE0BE215C79A9000A5D3D /* blocked_xa_aiff.c in Sources */, - 831BA61E1EAC61A500CF89B0 /* vawx.c in Sources */, 836F702A18BDC2190095E648 /* sd9.c in Sources */, 836F6FB418BDC2190095E648 /* ngc_nst_dsp.c in Sources */, 836F6FDB18BDC2190095E648 /* ps2_hsf.c in Sources */, @@ -2732,6 +2804,7 @@ 83AFABBC23795202002F3947 /* xssb.c in Sources */, 836F704818BDC2190095E648 /* xbox_ims.c in Sources */, 837CEAF623487F2C00E62A4A /* mzrt.c in Sources */, + 83AA7F832519C042004C5298 /* adp_konami.c in Sources */, 836F6F7518BDC2190095E648 /* bnsf.c in Sources */, 836F704318BDC2190095E648 /* wpd.c in Sources */, 8349A9081FE6258200E26435 /* ezw.c in Sources */, @@ -2745,6 +2818,7 @@ 83AA5D1D1F6E2F800020821C /* blocked_vgs.c in Sources */, 8323894A1D22419B00482226 /* clHCA.c in Sources */, 836F702E18BDC2190095E648 /* sli.c in Sources */, + 83AA7F822519C042004C5298 /* ktsc.c in Sources */, 836F6FDE18BDC2190095E648 /* ps2_ild.c in Sources */, 836F703E18BDC2190095E648 /* wii_ras.c in Sources */, 834FE0EE215C79ED000A5D3D /* ue4opus.c in Sources */, @@ -2782,6 +2856,7 @@ 83D2007C248DDB770048BD24 /* ktsr.c in Sources */, 836F704E18BDC2190095E648 /* xss.c in Sources */, 836F6FD118BDC2190095E648 /* ps2_bg00.c in Sources */, + 83AA7F7F2519C042004C5298 /* bsf.c in Sources */, 836F6F4118BDC2190095E648 /* blocked.c in Sources */, 836F6F3B18BDC2190095E648 /* ws_decoder.c in Sources */, 838BDB6E1D3B043C0022CA6F /* ffmpeg.c in Sources */, @@ -2910,7 +2985,7 @@ ); INSTALL_PATH = "@loader_path/../Frameworks"; LIBRARY_SEARCH_PATHS = ../../ThirdParty/ffmpeg/lib; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -2975,7 +3050,7 @@ ); INSTALL_PATH = "@loader_path/../Frameworks"; LIBRARY_SEARCH_PATHS = ../../ThirdParty/ffmpeg/lib; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SKIP_INSTALL = YES; }; @@ -2984,10 +3059,10 @@ 836F6B6218BDB8880095E648 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_SEARCH_PATHS = ( @@ -3014,10 +3089,10 @@ 836F6B6318BDB8880095E648 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_SEARCH_PATHS = ( diff --git a/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.c index 9814752ab..d87fe6fa1 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/acm_decoder.c @@ -5,17 +5,17 @@ /* libacm 1.2 (despite what libacm.h says) from: https://github.com/markokr/libacm */ typedef struct { - STREAMFILE *streamfile; + STREAMFILE* streamfile; /* reference */ int offset; } acm_io_config; -static int acm_read_streamfile(void *ptr, int size, int n, void *arg); -static int acm_seek_streamfile(void *arg, int offset, int whence); -static int acm_get_length_streamfile(void *arg); -acm_codec_data *init_acm(STREAMFILE *streamFile, int force_channel_number) { +static int acm_read_streamfile(void* ptr, int size, int n, void* arg); +static int acm_seek_streamfile(void* arg, int offset, int whence); +static int acm_get_length_streamfile(void* arg); + +acm_codec_data* init_acm(STREAMFILE* sf, int force_channel_number) { acm_codec_data* data = NULL; - char filename[PATH_LIMIT]; data = calloc(1,sizeof(acm_codec_data)); @@ -24,15 +24,14 @@ acm_codec_data *init_acm(STREAMFILE *streamFile, int force_channel_number) { data->io_config = calloc(1,sizeof(acm_io_config)); if (!data->io_config) goto fail; - streamFile->get_name(streamFile,filename,sizeof(filename)); - data->streamfile = open_streamfile(streamFile,filename); + data->streamfile = reopen_streamfile(sf, 0); if (!data->streamfile) goto fail; /* Setup libacm decoder, needs read callbacks and a parameter for said callbacks */ { - ACMStream *handle = NULL; + ACMStream* handle = NULL; int res; - acm_io_config *io_config = data->io_config; + acm_io_config* io_config = data->io_config; acm_io_callbacks io_callbacks = {0}; io_config->offset = 0; @@ -60,8 +59,8 @@ fail: return NULL; } -void decode_acm(acm_codec_data *data, sample * outbuf, int32_t samples_to_do, int channelspacing) { - ACMStream * acm = data->handle; +void decode_acm(acm_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channelspacing) { + ACMStream* acm = data->handle; int32_t samples_read = 0; while (samples_read < samples_to_do) { @@ -79,14 +78,14 @@ void decode_acm(acm_codec_data *data, sample * outbuf, int32_t samples_to_do, in } } -void reset_acm(acm_codec_data *data) { +void reset_acm(acm_codec_data* data) { if (!data || !data->handle) return; acm_seek_pcm(data->handle, 0); } -void free_acm(acm_codec_data *data) { +void free_acm(acm_codec_data* data) { if (!data) return; @@ -96,6 +95,11 @@ void free_acm(acm_codec_data *data) { free(data); } +STREAMFILE* acm_get_streamfile(acm_codec_data* data) { + if (!data) return NULL; + return data->streamfile; +} + /* ******************************* */ static int acm_read_streamfile(void *ptr, int size, int n, void *arg) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/adx_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/adx_decoder.c index f906ad0f8..36e2099ec 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/adx_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/adx_decoder.c @@ -1,7 +1,7 @@ #include "coding.h" #include "../util.h" -void decode_adx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int32_t frame_size, coding_t coding_type) { +void decode_adx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int32_t frame_size, coding_t coding_type) { uint8_t frame[0x12] = {0}; off_t frame_offset; int i, frames_in, sample_count = 0; @@ -21,12 +21,22 @@ void decode_adx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing frame_offset = stream->offset + bytes_per_frame * frames_in; read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ - scale = get_16bitBE(frame+0x00); + + scale = get_s16be(frame+0x00); switch(coding_type) { case coding_CRI_ADX: scale = scale + 1; coef1 = stream->adpcm_coef[0]; coef2 = stream->adpcm_coef[1]; + + /* Detect EOF scale (0x8001) found in some ADX of any type, signals "stop decoding" (without this frame?). + * Normally num_samples stops right before it, but ADXPLAY will honor it even in the middle on a file + * (may repeat last sample buffer). Some Baroque (SAT) videos set it on file end, but num_samples goes beyond. + * Just the upper bit triggers it even in encrypted ADX (max is 0x7FFF), but the check only here just in case. */ + if (frame[0] == 0x80 && frame[1] == 0x01) { + scale = 0; /* fix scaled click, maybe should just exit */ + VGM_LOG("ADX: reached EOF scale\n"); + } break; case coding_CRI_ADX_exp: scale = 1 << (12 - scale); @@ -79,6 +89,6 @@ void decode_adx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing } } -void adx_next_key(VGMSTREAMCHANNEL * stream) { +void adx_next_key(VGMSTREAMCHANNEL* stream) { stream->adx_xor = (stream->adx_xor * stream->adx_mult + stream->adx_add) & 0x7fff; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/at3plus_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/at3plus_decoder.c index ff00f8bbf..9347d8a73 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/at3plus_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/at3plus_decoder.c @@ -4,6 +4,14 @@ #ifdef VGM_USE_MAIATRAC3PLUS #include "maiatrac3plus.h" +struct maiatrac3plus_codec_data { + sample_t* buffer; + int channels; + int samples_discard; + void* handle; +}; + + maiatrac3plus_codec_data *init_at3plus() { maiatrac3plus_codec_data *data = malloc(sizeof(maiatrac3plus_codec_data)); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c index 045f33011..d609f12cc 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/atrac9_decoder.c @@ -1,19 +1,15 @@ #include "coding.h" #ifdef VGM_USE_ATRAC9 -#ifdef __MACOSX__ -#include -#else -#include "libatrac9.h" -#endif +#include "libatrac9/libatrac9.h" /* opaque struct */ struct atrac9_codec_data { - uint8_t *data_buffer; + uint8_t* data_buffer; size_t data_buffer_size; - sample_t *sample_buffer; + sample_t* sample_buffer; size_t samples_filled; /* number of samples in the buffer */ size_t samples_used; /* number of samples extracted from the buffer */ @@ -21,15 +17,15 @@ struct atrac9_codec_data { atrac9_config config; - void *handle; /* decoder handle */ + void* handle; /* decoder handle */ Atrac9CodecInfo info; /* decoder info */ }; -atrac9_codec_data *init_atrac9(atrac9_config *cfg) { +atrac9_codec_data* init_atrac9(atrac9_config* cfg) { int status; uint8_t config_data[4]; - atrac9_codec_data *data = NULL; + atrac9_codec_data* data = NULL; data = calloc(1, sizeof(atrac9_codec_data)); if (!data) goto fail; @@ -69,9 +65,9 @@ fail: return NULL; } -void decode_atrac9(VGMSTREAM *vgmstream, sample_t * outbuf, int32_t samples_to_do, int channels) { - VGMSTREAMCHANNEL *stream = &vgmstream->ch[0]; - atrac9_codec_data * data = vgmstream->codec_data; +void decode_atrac9(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) { + VGMSTREAMCHANNEL* stream = &vgmstream->ch[0]; + atrac9_codec_data* data = vgmstream->codec_data; int samples_done = 0; @@ -138,8 +134,7 @@ decode_fail: 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; +void reset_atrac9(atrac9_codec_data* data) { if (!data) return; if (!data->handle) @@ -171,11 +166,11 @@ fail: return; /* decode calls should fail... */ } -void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample) { - atrac9_codec_data *data = vgmstream->codec_data; +void seek_atrac9(VGMSTREAM* vgmstream, int32_t num_sample) { + atrac9_codec_data* data = vgmstream->codec_data; if (!data) return; - reset_atrac9(vgmstream); + reset_atrac9(data); /* find closest offset to desired sample, and samples to discard after that offset to reach loop */ { @@ -216,7 +211,7 @@ void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample) { } -void free_atrac9(atrac9_codec_data *data) { +void free_atrac9(atrac9_codec_data* data) { if (!data) return; if (data->handle) Atrac9ReleaseHandle(data->handle); @@ -269,7 +264,7 @@ fail: return 0; } -size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data *data) { +size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data* data) { return bytes / data->info.superframeSize * (data->info.frameSamples * data->info.framesInSuperframe); } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/celt_fsb_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/celt_fsb_decoder.c index b2805ccce..402c9b1d6 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/celt_fsb_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/celt_fsb_decoder.c @@ -1,231 +1,230 @@ -#include "coding.h" - -#ifdef VGM_USE_CELT -#include "celt/celt_fsb.h" - -#define FSB_CELT_0_06_1_VERSION 0x80000009 /* libcelt-0.6.1 */ -#define FSB_CELT_0_11_0_VERSION 0x80000010 /* libcelt-0.6.1 */ -#define FSB_CELT_SAMPLES_PER_FRAME 512 -#define FSB_CELT_INTERNAL_SAMPLE_RATE 44100 -#define FSB_CELT_MAX_DATA_SIZE 0x200 /* from 0x2e~0x172/1d0, all files are CBR though */ - -/* opaque struct */ -struct celt_codec_data { - sample *buffer; - - 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; - - int channel_mode; - celt_lib_t version; - void *mode_handle; - void *decoder_handle; -}; - - -/* FSB CELT, frames with custom header and standard data (API info from FMOD DLLs). - * FMOD used various libcelt versions, thus some tweaks are needed for them to coexist. */ - -celt_codec_data *init_celt_fsb(int channels, celt_lib_t version) { - int error = 0, lib_version = 0; - celt_codec_data *data = NULL; - - - data = calloc(1, sizeof(celt_codec_data)); - if (!data) goto fail; - - data->channel_mode = channels; /* should be 1/2, or rejected by libcelt */ - data->version = version; - - switch(data->version) { - case CELT_0_06_1: /* older FSB4 (FMOD ~4.33) */ - data->mode_handle = celt_0061_mode_create(FSB_CELT_INTERNAL_SAMPLE_RATE, data->channel_mode, FSB_CELT_SAMPLES_PER_FRAME, &error); - if (!data->mode_handle || error != CELT_OK) goto fail; - - error = celt_0061_mode_info(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version); - if (error != CELT_OK || lib_version != FSB_CELT_0_06_1_VERSION) goto fail; - - data->decoder_handle = celt_0061_decoder_create(data->mode_handle); - if (!data->decoder_handle) goto fail; - break; - - case CELT_0_11_0: /* newer FSB4 (FMOD ~4.34), FSB5 */ - data->mode_handle = celt_0110_mode_create(FSB_CELT_INTERNAL_SAMPLE_RATE, FSB_CELT_SAMPLES_PER_FRAME, &error); /* "custom" and not ok? */ - if (!data->mode_handle || error != CELT_OK) goto fail; - - error = celt_0110_mode_info(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version); - if (error != CELT_OK || lib_version != FSB_CELT_0_11_0_VERSION) goto fail; - - data->decoder_handle = celt_0110_decoder_create_custom(data->mode_handle, data->channel_mode, &error); - if (!data->decoder_handle || error != CELT_OK) goto fail; - break; - - default: - goto fail; - } - - data->sample_buffer = calloc(sizeof(sample), data->channel_mode * FSB_CELT_SAMPLES_PER_FRAME); - if (!data->sample_buffer) goto fail; - /* there is ~128 samples of encoder delay, but FMOD DLLs don't discard it? */ - - return data; - -fail: - free_celt_fsb(data); - return NULL; -} - - -void decode_celt_fsb(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int channels) { - VGMSTREAMCHANNEL *stream = &vgmstream->ch[0]; - celt_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 status; - uint8_t data_buffer[FSB_CELT_MAX_DATA_SIZE] = {0}; - size_t bytes, frame_size; - - - data->samples_used = 0; - - /* FSB DLLs do seem to check this fixed value */ - if (read_32bitBE(stream->offset+0x00,stream->streamfile) != 0x17C30DF3) { - goto decode_fail; - } - - frame_size = read_32bitLE(stream->offset+0x04,stream->streamfile); - if (frame_size > FSB_CELT_MAX_DATA_SIZE) { - goto decode_fail; - } - - /* read and decode one raw block and advance offsets */ - bytes = read_streamfile(data_buffer,stream->offset+0x08, frame_size,stream->streamfile); - if (bytes != frame_size) goto decode_fail; - - switch(data->version) { - case CELT_0_06_1: - status = celt_0061_decode(data->decoder_handle, data_buffer,bytes, data->sample_buffer); - break; - - case CELT_0_11_0: - status = celt_0110_decode(data->decoder_handle, data_buffer,bytes, data->sample_buffer, FSB_CELT_SAMPLES_PER_FRAME); - break; - - default: - goto decode_fail; - } - if (status != CELT_OK) goto decode_fail; - - stream->offset += 0x04+0x04+frame_size; - data->samples_filled += FSB_CELT_SAMPLES_PER_FRAME; - } - } - - return; - -decode_fail: - /* on error just put some 0 samples */ - VGM_LOG("CELT: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, (samples_to_do - samples_done)); - memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels); -} - -void reset_celt_fsb(VGMSTREAM *vgmstream) { - celt_codec_data *data = vgmstream->codec_data; - if (!data) return; - - /* recreate decoder (mode should not change) */ - switch(data->version) { - case CELT_0_06_1: - if (data->decoder_handle) celt_0061_decoder_destroy(data->decoder_handle); - - data->decoder_handle = celt_0061_decoder_create(data->mode_handle); - if (!data->decoder_handle) goto fail; - break; - - case CELT_0_11_0: - if (data->decoder_handle) celt_0110_decoder_destroy(data->decoder_handle); - - data->decoder_handle = celt_0110_decoder_create_custom(data->mode_handle, data->channel_mode, NULL); - if (!data->decoder_handle) goto fail; - break; - - default: - goto fail; - } - - data->samples_used = 0; - data->samples_filled = 0; - data->samples_to_discard = 0; - - return; -fail: - return; /* decode calls should fail... */ -} - -void seek_celt_fsb(VGMSTREAM *vgmstream, int32_t num_sample) { - celt_codec_data *data = vgmstream->codec_data; - if (!data) return; - - reset_celt_fsb(vgmstream); - - data->samples_to_discard = num_sample; - - /* 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_celt_fsb(celt_codec_data *data) { - if (!data) return; - - switch(data->version) { - case CELT_0_06_1: - if (data->decoder_handle) celt_0061_decoder_destroy(data->decoder_handle); - if (data->mode_handle) celt_0061_mode_destroy(data->mode_handle); - break; - - case CELT_0_11_0: - if (data->decoder_handle) celt_0110_decoder_destroy(data->decoder_handle); - if (data->mode_handle) celt_0110_mode_destroy(data->mode_handle); - break; - - default: - break; - } - - free(data->sample_buffer); - free(data); -} -#endif +#include "coding.h" + +#ifdef VGM_USE_CELT +#include "celt/celt_fsb.h" + +#define FSB_CELT_0_06_1_VERSION 0x80000009 /* libcelt-0.6.1 */ +#define FSB_CELT_0_11_0_VERSION 0x80000010 /* libcelt-0.11.0 */ +#define FSB_CELT_SAMPLES_PER_FRAME 512 +#define FSB_CELT_INTERNAL_SAMPLE_RATE 44100 +#define FSB_CELT_MAX_DATA_SIZE 0x200 /* from 0x2e~0x172/1d0, all files are CBR though */ + +/* opaque struct */ +struct celt_codec_data { + sample_t* buffer; + + sample_t* 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; + + int channel_mode; + celt_lib_t version; + void *mode_handle; + void *decoder_handle; +}; + + +/* FSB CELT, frames with custom header and standard data (API info from FMOD DLLs). + * FMOD used various libcelt versions, thus some tweaks are needed for them to coexist. */ + +celt_codec_data *init_celt_fsb(int channels, celt_lib_t version) { + int error = 0, lib_version = 0; + celt_codec_data* data = NULL; + + + data = calloc(1, sizeof(celt_codec_data)); + if (!data) goto fail; + + data->channel_mode = channels; /* should be 1/2, or rejected by libcelt */ + data->version = version; + + switch(data->version) { + case CELT_0_06_1: /* older FSB4 (FMOD ~4.33) */ + data->mode_handle = celt_0061_mode_create(FSB_CELT_INTERNAL_SAMPLE_RATE, data->channel_mode, FSB_CELT_SAMPLES_PER_FRAME, &error); + if (!data->mode_handle || error != CELT_OK) goto fail; + + error = celt_0061_mode_info(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version); + if (error != CELT_OK || lib_version != FSB_CELT_0_06_1_VERSION) goto fail; + + data->decoder_handle = celt_0061_decoder_create(data->mode_handle); + if (!data->decoder_handle) goto fail; + break; + + case CELT_0_11_0: /* newer FSB4 (FMOD ~4.34), FSB5 */ + data->mode_handle = celt_0110_mode_create(FSB_CELT_INTERNAL_SAMPLE_RATE, FSB_CELT_SAMPLES_PER_FRAME, &error); /* "custom" and not ok? */ + if (!data->mode_handle || error != CELT_OK) goto fail; + + error = celt_0110_mode_info(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version); + if (error != CELT_OK || lib_version != FSB_CELT_0_11_0_VERSION) goto fail; + + data->decoder_handle = celt_0110_decoder_create_custom(data->mode_handle, data->channel_mode, &error); + if (!data->decoder_handle || error != CELT_OK) goto fail; + break; + + default: + goto fail; + } + + data->sample_buffer = calloc(sizeof(sample), data->channel_mode * FSB_CELT_SAMPLES_PER_FRAME); + if (!data->sample_buffer) goto fail; + /* there is ~128 samples of encoder delay, but FMOD DLLs don't discard it? */ + + return data; + +fail: + free_celt_fsb(data); + return NULL; +} + + +void decode_celt_fsb(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) { + VGMSTREAMCHANNEL* stream = &vgmstream->ch[0]; + celt_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 status; + uint8_t data_buffer[FSB_CELT_MAX_DATA_SIZE] = {0}; + size_t bytes, frame_size; + + + data->samples_used = 0; + + /* FSB DLLs do seem to check this fixed value */ + if (read_32bitBE(stream->offset+0x00,stream->streamfile) != 0x17C30DF3) { + goto decode_fail; + } + + frame_size = read_32bitLE(stream->offset+0x04,stream->streamfile); + if (frame_size > FSB_CELT_MAX_DATA_SIZE) { + goto decode_fail; + } + + /* read and decode one raw block and advance offsets */ + bytes = read_streamfile(data_buffer,stream->offset+0x08, frame_size,stream->streamfile); + if (bytes != frame_size) goto decode_fail; + + switch(data->version) { + case CELT_0_06_1: + status = celt_0061_decode(data->decoder_handle, data_buffer,bytes, data->sample_buffer); + break; + + case CELT_0_11_0: + status = celt_0110_decode(data->decoder_handle, data_buffer,bytes, data->sample_buffer, FSB_CELT_SAMPLES_PER_FRAME); + break; + + default: + goto decode_fail; + } + if (status != CELT_OK) goto decode_fail; + + stream->offset += 0x04+0x04+frame_size; + data->samples_filled += FSB_CELT_SAMPLES_PER_FRAME; + } + } + + return; + +decode_fail: + /* on error just put some 0 samples */ + VGM_LOG("CELT: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, (samples_to_do - samples_done)); + memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels); +} + +void reset_celt_fsb(celt_codec_data* data) { + if (!data) return; + + /* recreate decoder (mode should not change) */ + switch(data->version) { + case CELT_0_06_1: + if (data->decoder_handle) celt_0061_decoder_destroy(data->decoder_handle); + + data->decoder_handle = celt_0061_decoder_create(data->mode_handle); + if (!data->decoder_handle) goto fail; + break; + + case CELT_0_11_0: + if (data->decoder_handle) celt_0110_decoder_destroy(data->decoder_handle); + + data->decoder_handle = celt_0110_decoder_create_custom(data->mode_handle, data->channel_mode, NULL); + if (!data->decoder_handle) goto fail; + break; + + default: + goto fail; + } + + data->samples_used = 0; + data->samples_filled = 0; + data->samples_to_discard = 0; + + return; +fail: + return; /* decode calls should fail... */ +} + +void seek_celt_fsb(VGMSTREAM *vgmstream, int32_t num_sample) { + celt_codec_data* data = vgmstream->codec_data; + if (!data) return; + + reset_celt_fsb(data); + + data->samples_to_discard = num_sample; + + /* 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_celt_fsb(celt_codec_data* data) { + if (!data) return; + + switch(data->version) { + case CELT_0_06_1: + if (data->decoder_handle) celt_0061_decoder_destroy(data->decoder_handle); + if (data->mode_handle) celt_0061_mode_destroy(data->mode_handle); + break; + + case CELT_0_11_0: + if (data->decoder_handle) celt_0110_decoder_destroy(data->decoder_handle); + if (data->mode_handle) celt_0110_mode_destroy(data->mode_handle); + break; + + default: + break; + } + + free(data->sample_buffer); + free(data); +} +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index f12fd89a3..82619ec6d 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -4,187 +4,230 @@ #include "../vgmstream.h" /* adx_decoder */ -void decode_adx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int32_t frame_bytes, coding_t coding_type); -void adx_next_key(VGMSTREAMCHANNEL * stream); +void decode_adx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int32_t frame_bytes, coding_t coding_type); +void adx_next_key(VGMSTREAMCHANNEL* stream); + /* g721_decoder */ -void decode_g721(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void g72x_init_state(struct g72x_state *state_ptr); +void decode_g721(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void g72x_init_state(struct g72x_state* state_ptr); + /* ima_decoder */ -void decode_standard_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo, int is_high_first); -void decode_3ds_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_wv6_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_alp_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_ffta2_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_blitz_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_mtf_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); +void decode_standard_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo, int is_high_first); +void decode_3ds_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_snds_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_otns_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_wv6_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_alp_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_ffta2_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_blitz_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_mtf_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); -void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); -void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); +void decode_ms_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); +void decode_ref_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); -void decode_xbox_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); -void decode_xbox_ima_mch(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_nds_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_dat4_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_rad_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); -void decode_rad_ima_mono(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int codec_config); -void decode_h4m_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, uint16_t frame_format); +void decode_xbox_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); +void decode_xbox_ima_mch(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_nds_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_dat4_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_rad_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); +void decode_rad_ima_mono(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_apple_ima4(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_fsb_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_wwise_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_awc_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_ubi_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int codec_config); +void decode_h4m_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, uint16_t frame_format); +void decode_cd_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); size_t ima_bytes_to_samples(size_t bytes, int channels); size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels); size_t xbox_ima_bytes_to_samples(size_t bytes, int channels); size_t dat4_ima_bytes_to_samples(size_t bytes, int channels); size_t apple_ima4_bytes_to_samples(size_t bytes, int channels); + /* ngc_dsp_decoder */ -void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_ngc_dsp_subint(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int interleave); +void decode_ngc_dsp(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_ngc_dsp_subint(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int interleave); size_t dsp_bytes_to_samples(size_t bytes, int channels); int32_t dsp_nibbles_to_samples(int32_t nibbles); -void dsp_read_coefs_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing); -void dsp_read_coefs_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing); -void dsp_read_coefs(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be); -void dsp_read_hist_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing); -void dsp_read_hist_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing); -void dsp_read_hist(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be); +void dsp_read_coefs_be(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset, off_t spacing); +void dsp_read_coefs_le(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset, off_t spacing); +void dsp_read_coefs(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset, off_t spacing, int be); +void dsp_read_hist_be(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset, off_t spacing); +void dsp_read_hist_le(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset, off_t spacing); +void dsp_read_hist(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset, off_t spacing, int be); + /* ngc_dtk_decoder */ -void decode_ngc_dtk(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ngc_dtk(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); + /* ngc_afc_decoder */ -void decode_ngc_afc(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_ngc_afc(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* vadpcm_decoder */ -void decode_vadpcm(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int order); +void decode_vadpcm(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int order); //int32_t vadpcm_bytes_to_samples(size_t bytes, int channels); void vadpcm_read_coefs_be(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset, int order, int entries, int ch); + /* pcm_decoder */ -void decode_pcm16le(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm16be(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm16_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian); -void decode_pcm8(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm8_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm8_unsigned(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm8_sb(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm4(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_pcm4_unsigned(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_ulaw(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_ulaw_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_alaw(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcmfloat(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian); +void decode_pcm16le(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm16be(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm16_int(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian); +void decode_pcm8(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm8_int(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm8_unsigned(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm8_sb(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm4(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_pcm4_unsigned(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ulaw(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_ulaw_int(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_alaw(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcmfloat(VGMSTREAMCHANNEL* stream, sample_t* 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); + /* psx_decoder */ void decode_psx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags, int config); void decode_psx_configurable(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size, int config); -void decode_psx_pivotal(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size); -int ps_find_loop_offsets(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end); -int ps_find_loop_offsets_full(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end); -size_t ps_find_padding(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int discard_empty); +void decode_psx_pivotal(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size); +int ps_find_loop_offsets(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* out_loop_start, int32_t* out_loop_end); +int ps_find_loop_offsets_full(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t* out_loop_start, int32_t* out_loop_end); +size_t ps_find_padding(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave, int discard_empty); size_t ps_bytes_to_samples(size_t bytes, int channels); size_t ps_cfg_bytes_to_samples(size_t bytes, size_t frame_size, int channels); -int ps_check_format(STREAMFILE *streamFile, off_t offset, size_t max); +int ps_check_format(STREAMFILE* sf, off_t offset, size_t max); + /* psv_decoder */ -void decode_hevag(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_hevag(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* xa_decoder */ -void decode_xa(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2); + /* ea_xa_decoder */ -void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_ea_xa_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); -void decode_maxis_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ea_xa(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); +void decode_ea_xa_v2(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); +void decode_maxis_xa(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +int32_t ea_xa_bytes_to_samples(size_t bytes, int channels); + /* ea_xas_decoder */ -void decode_ea_xas_v0(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_ea_xas_v1(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ea_xas_v0(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ea_xas_v1(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); + /* sdx2_decoder */ -void decode_sdx2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_sdx2_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_cbd2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_cbd2_int(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_sdx2(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_sdx2_int(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_cbd2(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_cbd2_int(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* ws_decoder */ -void decode_ws(VGMSTREAM * vgmstream, int channel, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_ws(VGMSTREAM* vgmstream, int channel, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* acm_decoder */ -acm_codec_data *init_acm(STREAMFILE *streamFile, int force_channel_number); -void decode_acm(acm_codec_data *data, sample * outbuf, int32_t samples_to_do, int channelspacing); -void reset_acm(acm_codec_data *data); -void free_acm(acm_codec_data *data); +acm_codec_data* init_acm(STREAMFILE* sf, int force_channel_number); +void decode_acm(acm_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channelspacing); +void reset_acm(acm_codec_data* data); +void free_acm(acm_codec_data* data); +STREAMFILE* acm_get_streamfile(acm_codec_data* data); + /* nwa_decoder */ -void decode_nwa(NWAData *nwa, sample *outbuf, int32_t samples_to_do); +typedef struct nwa_codec_data nwa_codec_data; + +nwa_codec_data* init_nwa(STREAMFILE* sf); +void decode_nwa(nwa_codec_data* data, sample_t* outbuf, int32_t samples_to_do); +void seek_nwa(nwa_codec_data *data, int32_t sample); +void reset_nwa(nwa_codec_data *data); +void free_nwa(nwa_codec_data* data); +STREAMFILE* nwa_get_streamfile(nwa_codec_data* data); /* msadpcm_decoder */ -void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t first_sample, int32_t samples_to_do); -void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_msadpcm_stereo(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t first_sample, int32_t samples_to_do); +void decode_msadpcm_mono(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_msadpcm_ck(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); long msadpcm_bytes_to_samples(long bytes, int block_size, int channels); -int msadpcm_check_coefs(STREAMFILE *sf, off_t offset); +int msadpcm_check_coefs(STREAMFILE* sf, off_t offset); + /* yamaha_decoder */ -void decode_aica(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); -void decode_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_nxap(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_aica(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); +void decode_aska(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, size_t frame_size); +void decode_nxap(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); size_t yamaha_bytes_to_samples(size_t bytes, int channels); -size_t aska_bytes_to_samples(size_t bytes, int channels); +size_t aska_bytes_to_samples(size_t bytes, size_t frame_size, int channels); + /* tgcadpcm_decoder */ -void decode_tgc(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int32_t first_sample, int32_t samples_to_do); +void decode_tgc(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int32_t first_sample, int32_t samples_to_do); + /* nds_procyon_decoder */ -void decode_nds_procyon(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_nds_procyon(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* l5_555_decoder */ -void decode_l5_555(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_l5_555(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* sassc_decoder */ -void decode_sassc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_sassc(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* lsf_decode */ -void decode_lsf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_lsf(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* mtaf_decoder */ -void decode_mtaf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_mtaf(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); + /* mta2_decoder */ -void decode_mta2(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_mta2(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); + /* mc3_decoder */ -void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_mc3(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); + /* fadpcm_decoder */ -void decode_fadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_fadpcm(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* asf_decoder */ -void decode_asf(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_asf(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); int32_t asf_bytes_to_samples(size_t bytes, int channels); + /* dsa_decoder */ -void decode_dsa(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_dsa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* xmd_decoder */ -void decode_xmd(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size); +void decode_xmd(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size); + /* derf_decoder */ -void decode_derf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_derf(VGMSTREAMCHANNEL* stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + +/* wady_decoder */ +void decode_wady(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); /* circus_decoder */ typedef struct circus_codec_data circus_codec_data; + circus_codec_data* init_circus_vq(STREAMFILE* sf, off_t start, uint8_t codec, uint8_t flags); void decode_circus_vq(circus_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels); void reset_circus_vq(circus_codec_data* data); @@ -192,93 +235,205 @@ void seek_circus_vq(circus_codec_data* data, int32_t num_sample); void free_circus_vq(circus_codec_data* data); void decode_circus_adpcm(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + /* oki_decoder */ -void decode_pcfx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode); -void decode_oki16(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_oki4s(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_pcfx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode); +void decode_oki16(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_oki4s(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); size_t oki_bytes_to_samples(size_t bytes, int channels); + /* ptadpcm_decoder */ -void decode_ptadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size); +void decode_ptadpcm(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size); size_t ptadpcm_bytes_to_samples(size_t bytes, int channels, size_t frame_size); + /* ubi_adpcm_decoder */ -ubi_adpcm_codec_data *init_ubi_adpcm(STREAMFILE *streamFile, off_t offset, int channels); -void decode_ubi_adpcm(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t samples_to_do); -void reset_ubi_adpcm(ubi_adpcm_codec_data *data); -void seek_ubi_adpcm(ubi_adpcm_codec_data *data, int32_t num_sample); -void free_ubi_adpcm(ubi_adpcm_codec_data *data); -int ubi_adpcm_get_samples(ubi_adpcm_codec_data *data); +typedef struct ubi_adpcm_codec_data ubi_adpcm_codec_data; + +ubi_adpcm_codec_data* init_ubi_adpcm(STREAMFILE* sf, off_t offset, int channels); +void decode_ubi_adpcm(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do); +void reset_ubi_adpcm(ubi_adpcm_codec_data* data); +void seek_ubi_adpcm(ubi_adpcm_codec_data* data, int32_t num_sample); +void free_ubi_adpcm(ubi_adpcm_codec_data* data); +int ubi_adpcm_get_samples(ubi_adpcm_codec_data* data); + /* imuse_decoder */ typedef struct imuse_codec_data imuse_codec_data; -imuse_codec_data *init_imuse(STREAMFILE* sf, int channels); + +imuse_codec_data* init_imuse(STREAMFILE* sf, int channels); void decode_imuse(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do); void reset_imuse(imuse_codec_data* data); void seek_imuse(imuse_codec_data* data, int32_t num_sample); void free_imuse(imuse_codec_data* data); + /* ea_mt_decoder*/ -ea_mt_codec_data *init_ea_mt(int channels, int type); -ea_mt_codec_data *init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t *loop_offsets); -void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel); -void reset_ea_mt(VGMSTREAM * vgmstream); -void flush_ea_mt(VGMSTREAM *vgmstream); -void seek_ea_mt(VGMSTREAM * vgmstream, int32_t num_sample); -void free_ea_mt(ea_mt_codec_data *data, int channels); +typedef struct ea_mt_codec_data ea_mt_codec_data; + +ea_mt_codec_data* init_ea_mt(int channels, int type); +ea_mt_codec_data* init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t* loop_offsets); +void decode_ea_mt(VGMSTREAM* vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel); +void reset_ea_mt(VGMSTREAM* vgmstream); +void flush_ea_mt(VGMSTREAM* vgmstream); +void seek_ea_mt(VGMSTREAM* vgmstream, int32_t num_sample); +void free_ea_mt(ea_mt_codec_data* data, int channels); + /* relic_decoder */ +typedef struct relic_codec_data relic_codec_data; + relic_codec_data* init_relic(int channels, int bitrate, int codec_rate); void decode_relic(VGMSTREAMCHANNEL* stream, relic_codec_data* data, sample_t* outbuf, int32_t samples_to_do); void reset_relic(relic_codec_data* data); void seek_relic(relic_codec_data* data, int32_t num_sample); void free_relic(relic_codec_data* data); + /* hca_decoder */ -hca_codec_data *init_hca(STREAMFILE *streamFile); -void decode_hca(hca_codec_data * data, sample_t * outbuf, int32_t samples_to_do); -void reset_hca(hca_codec_data * data); -void loop_hca(hca_codec_data * data, int32_t num_sample); -void free_hca(hca_codec_data * data); -int test_hca_key(hca_codec_data * data, unsigned long long keycode); +typedef struct hca_codec_data hca_codec_data; + +hca_codec_data* init_hca(STREAMFILE* sf); +void decode_hca(hca_codec_data* data, sample_t* outbuf, int32_t samples_to_do); +void reset_hca(hca_codec_data* data); +void loop_hca(hca_codec_data* data, int32_t num_sample); +void free_hca(hca_codec_data* data); +int test_hca_key(hca_codec_data* data, unsigned long long keycode); +void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode); +clHCA_stInfo* hca_get_info(hca_codec_data* data); +STREAMFILE* hca_get_streamfile(hca_codec_data* data); + #ifdef VGM_USE_VORBIS /* ogg_vorbis_decoder */ -ogg_vorbis_codec_data* init_ogg_vorbis(STREAMFILE *sf, off_t start, off_t size, ogg_vorbis_io *io); -void decode_ogg_vorbis(ogg_vorbis_codec_data *data, sample_t *outbuf, int32_t samples_to_do, int channels); -void reset_ogg_vorbis(VGMSTREAM *vgmstream); -void seek_ogg_vorbis(VGMSTREAM *vgmstream, int32_t num_sample); -void free_ogg_vorbis(ogg_vorbis_codec_data *data); +typedef struct ogg_vorbis_codec_data ogg_vorbis_codec_data; +typedef struct { //todo simplify + STREAMFILE *streamfile; + 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 */ + + /* 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 xor_value; +} ogg_vorbis_io; + +ogg_vorbis_codec_data* init_ogg_vorbis(STREAMFILE* sf, off_t start, off_t size, ogg_vorbis_io* io); +void decode_ogg_vorbis(ogg_vorbis_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels); +void reset_ogg_vorbis(VGMSTREAM* vgmstream); +void seek_ogg_vorbis(ogg_vorbis_codec_data* data, int32_t num_sample); +void free_ogg_vorbis(ogg_vorbis_codec_data* data); + +int ogg_vorbis_get_comment(ogg_vorbis_codec_data* data, const char** comment); +void ogg_vorbis_get_info(ogg_vorbis_codec_data* data, int* p_channels, int* p_sample_rate); +void ogg_vorbis_get_samples(ogg_vorbis_codec_data* data, int* p_samples); +void ogg_vorbis_set_disable_reordering(ogg_vorbis_codec_data* data, int set); +STREAMFILE* ogg_vorbis_get_streamfile(ogg_vorbis_codec_data* data); -int ogg_vorbis_get_comment(ogg_vorbis_codec_data *data, const char **comment); -void ogg_vorbis_get_info(ogg_vorbis_codec_data *data, int *p_channels, int *p_sample_rate); -void ogg_vorbis_get_samples(ogg_vorbis_codec_data *data, int *p_samples); -void ogg_vorbis_set_disable_reordering(ogg_vorbis_codec_data *data, int set); -STREAMFILE* ogg_vorbis_get_streamfile(ogg_vorbis_codec_data *data); /* vorbis_custom_decoder */ -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_t * outbuf, int32_t samples_to_do, int channels); -void reset_vorbis_custom(VGMSTREAM *vgmstream); -void seek_vorbis_custom(VGMSTREAM *vgmstream, int32_t num_sample); -void free_vorbis_custom(vorbis_custom_codec_data *data); +typedef struct vorbis_custom_codec_data vorbis_custom_codec_data; + +typedef enum { + VORBIS_FSB, /* FMOD FSB: simplified/external setup packets, custom packet headers */ + VORBIS_WWISE, /* Wwise WEM: many variations (custom setup, headers and data) */ + VORBIS_OGL, /* Shin'en OGL: custom packet headers */ + VORBIS_SK, /* Silicon Knights AUD: "OggS" replaced by "SK" */ + VORBIS_VID1, /* Neversoft VID1: custom packet blocks/headers */ +} vorbis_custom_t; + +/* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */ +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 */ + int channels; + int sample_rate; + int blocksize_0_exp; + int blocksize_1_exp; + + uint32_t setup_id; /* external setup */ + int big_endian; /* flag */ + + /* Wwise Vorbis config */ + wwise_setup_t setup_type; + wwise_header_t header_type; + wwise_packet_t packet_type; + + /* output (kinda ugly here but to simplify) */ + off_t data_start_offset; + +} vorbis_custom_config; + +vorbis_custom_codec_data* init_vorbis_custom(STREAMFILE* sf, off_t start_offset, vorbis_custom_t type, vorbis_custom_config* config); +void decode_vorbis_custom(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels); +void reset_vorbis_custom(VGMSTREAM* vgmstream); +void seek_vorbis_custom(VGMSTREAM* vgmstream, int32_t num_sample); +void free_vorbis_custom(vorbis_custom_codec_data* data); #endif + #ifdef VGM_USE_MPEG /* mpeg_decoder */ -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_t * outbuf, int32_t samples_to_do, int channels); -void reset_mpeg(VGMSTREAM *vgmstream); -void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample); -void free_mpeg(mpeg_codec_data *data); -void flush_mpeg(mpeg_codec_data * data); +typedef struct mpeg_codec_data mpeg_codec_data; -long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data *data); +/* Custom MPEG modes, mostly differing in the data layout */ +typedef enum { + MPEG_STANDARD, /* 1 stream */ + MPEG_AHX, /* 1 stream with false frame headers */ + MPEG_XVAG, /* N streams of fixed interleave (frame-aligned, several data-frames of fixed size) */ + MPEG_FSB, /* N streams of 1 data-frame+padding (=interleave) */ + MPEG_P3D, /* N streams of fixed interleave (not frame-aligned) */ + MPEG_SCD, /* N streams of fixed interleave (not frame-aligned) */ + MPEG_EA, /* 1 stream (maybe N streams in absolute offsets?) */ + MPEG_EAL31, /* EALayer3 v1 (SCHl), custom frames with v1 header */ + MPEG_EAL31b, /* EALayer3 v1 (SNS), custom frames with v1 header + minor changes */ + MPEG_EAL32P, /* EALayer3 v2 "PCM", custom frames with v2 header + bigger PCM blocks? */ + MPEG_EAL32S, /* EALayer3 v2 "Spike", custom frames with v2 header + smaller PCM blocks? */ + MPEG_LYN, /* N streams of fixed interleave */ + MPEG_AWC, /* N streams in block layout (music) or absolute offsets (sfx) */ + MPEG_EAMP3 /* custom frame header + MPEG frame + PCM blocks */ +} mpeg_custom_t; + +/* config for the above modes */ +typedef struct { + int channels; /* max channels */ + int fsb_padding; /* fsb padding mode */ + int chunk_size; /* size of a data portion */ + int data_size; /* playable size */ + int interleave; /* size of stream interleave */ + int encryption; /* encryption mode */ + int big_endian; + int skip_samples; + /* for AHX */ + int cri_type; + uint16_t cri_key1; + uint16_t cri_key2; + uint16_t cri_key3; +} mpeg_custom_config; + +mpeg_codec_data* init_mpeg(STREAMFILE* sf, off_t start_offset, coding_t *coding_type, int channels); +mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, 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_t* outbuf, int32_t samples_to_do, int channels); +void reset_mpeg(mpeg_codec_data* data); +void seek_mpeg(VGMSTREAM* vgmstream, int32_t num_sample); +void free_mpeg(mpeg_codec_data* data); +void flush_mpeg(mpeg_codec_data* data); + +int mpeg_get_sample_rate(mpeg_codec_data* data); +long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data); #endif + #ifdef VGM_USE_G7221 /* g7221_decoder */ +typedef struct g7221_codec_data g7221_codec_data; + g7221_codec_data* init_g7221(int channel_count, int frame_size); void decode_g7221(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t samples_to_do, int channel); void reset_g7221(g7221_codec_data* data); @@ -287,73 +442,93 @@ void set_key_g7221(g7221_codec_data* data, const uint8_t* key); int test_key_g7221(g7221_codec_data* data, off_t start, STREAMFILE* sf); #endif + #ifdef VGM_USE_G719 /* g719_decoder */ -g719_codec_data *init_g719(int channel_count, int frame_size); -void decode_g719(VGMSTREAM *vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel); -void reset_g719(g719_codec_data * data, int channels); -void free_g719(g719_codec_data * data, int channels); +typedef struct g719_codec_data g719_codec_data; + +g719_codec_data* init_g719(int channel_count, int frame_size); +void decode_g719(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t samples_to_do, int channel); +void reset_g719(g719_codec_data* data, int channels); +void free_g719(g719_codec_data* data, int channels); #endif + #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) /* mp4_aac_decoder */ -void decode_mp4_aac(mp4_aac_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels); -void reset_mp4_aac(VGMSTREAM *vgmstream); -void seek_mp4_aac(VGMSTREAM *vgmstream, int32_t num_sample); -void free_mp4_aac(mp4_aac_codec_data * data); +void decode_mp4_aac(mp4_aac_codec_data* data, sample * outbuf, int32_t samples_to_do, int channels); +void reset_mp4_aac(VGMSTREAM* vgmstream); +void seek_mp4_aac(VGMSTREAM* vgmstream, int32_t num_sample); +void free_mp4_aac(mp4_aac_codec_data* data); #endif + #ifdef VGM_USE_MAIATRAC3PLUS /* 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); +typedef struct maiatrac3plus_codec_data maiatrac3plus_codec_data; + +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_t * 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); +typedef struct { + int channels; /* to detect weird multichannel */ + uint32_t config_data; /* ATRAC9 config header */ + int encoder_delay; /* initial samples to discard */ +} atrac9_config; +typedef struct atrac9_codec_data atrac9_codec_data; + +atrac9_codec_data* init_atrac9(atrac9_config* cfg); +void decode_atrac9(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels); +void reset_atrac9(atrac9_codec_data* data); +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); size_t atrac9_bytes_to_samples_cfg(size_t bytes, uint32_t atrac9_config); #endif + #ifdef VGM_USE_CELT /* celt_fsb_decoder */ -celt_codec_data *init_celt_fsb(int channels, celt_lib_t version); -void decode_celt_fsb(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int channels); -void reset_celt_fsb(VGMSTREAM *vgmstream); -void seek_celt_fsb(VGMSTREAM *vgmstream, int32_t num_sample); -void free_celt_fsb(celt_codec_data *data); +typedef enum { CELT_0_06_1,CELT_0_11_0} celt_lib_t; +typedef struct celt_codec_data celt_codec_data; + +celt_codec_data* init_celt_fsb(int channels, celt_lib_t version); +void decode_celt_fsb(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels); +void reset_celt_fsb(celt_codec_data* data); +void seek_celt_fsb(VGMSTREAM* vgmstream, int32_t num_sample); +void free_celt_fsb(celt_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_header_offset_subsong(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong); +ffmpeg_codec_data* init_ffmpeg_offset(STREAMFILE* sf, uint64_t start, uint64_t size); +ffmpeg_codec_data* init_ffmpeg_header_offset(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size); +ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong); -void decode_ffmpeg(VGMSTREAM *stream, sample_t * outbuf, int32_t samples_to_do, int channels); -void reset_ffmpeg(VGMSTREAM *vgmstream); -void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample); -void free_ffmpeg(ffmpeg_codec_data *data); +void decode_ffmpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels); +void reset_ffmpeg(ffmpeg_codec_data* data); +void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample); +void free_ffmpeg(ffmpeg_codec_data* data); -void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples); -uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data * data); -void ffmpeg_set_channel_remapping(ffmpeg_codec_data * data, int *channels_remap); -const char* ffmpeg_get_codec_name(ffmpeg_codec_data * data); -void ffmpeg_set_force_seek(ffmpeg_codec_data * data); +void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples); +uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data); +void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int* channels_remap); +const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data); +void ffmpeg_set_force_seek(ffmpeg_codec_data* data); const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key); - +STREAMFILE* ffmpeg_get_streamfile(ffmpeg_codec_data* data); /* ffmpeg_decoder_utils.c (helper-things) */ -ffmpeg_codec_data * init_ffmpeg_atrac3_raw(STREAMFILE *sf, off_t offset, size_t data_size, int sample_count, int channels, int sample_rate, int block_align, int encoder_delay); -ffmpeg_codec_data * init_ffmpeg_atrac3_riff(STREAMFILE *sf, off_t offset, int* out_samples); +ffmpeg_codec_data* init_ffmpeg_atrac3_raw(STREAMFILE* sf, off_t offset, size_t data_size, int sample_count, int channels, int sample_rate, int block_align, int encoder_delay); +ffmpeg_codec_data* init_ffmpeg_atrac3_riff(STREAMFILE* sf, off_t offset, int* out_samples); /* ffmpeg_decoder_custom_opus.c (helper-things) */ @@ -365,30 +540,36 @@ typedef struct { int coupled_count; int stream_count; int channel_mapping[8]; + /* frame table */ + off_t table_offset; + int table_count; } opus_config; -ffmpeg_codec_data * init_ffmpeg_switch_opus_config(STREAMFILE *streamFile, off_t start_offset, size_t data_size, opus_config* cfg); -ffmpeg_codec_data * init_ffmpeg_switch_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); -ffmpeg_codec_data * init_ffmpeg_ue4_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); -ffmpeg_codec_data * init_ffmpeg_ea_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); -ffmpeg_codec_data * init_ffmpeg_x_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); +ffmpeg_codec_data* init_ffmpeg_switch_opus_config(STREAMFILE* sf, off_t start_offset, size_t data_size, opus_config* cfg); +ffmpeg_codec_data* init_ffmpeg_switch_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); +ffmpeg_codec_data* init_ffmpeg_ue4_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); +ffmpeg_codec_data* init_ffmpeg_ea_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); +ffmpeg_codec_data* init_ffmpeg_x_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip); +ffmpeg_codec_data* init_ffmpeg_fsb_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate); +ffmpeg_codec_data* init_ffmpeg_wwise_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip); -size_t switch_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE *streamFile); +size_t switch_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE* sf); -size_t switch_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile); -size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile); -size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile); +size_t switch_opus_get_encoder_delay(off_t offset, STREAMFILE* sf); +size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE* sf); +size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE* sf); +size_t fsb_opus_get_encoder_delay(off_t offset, STREAMFILE* sf); #endif /* coding_utils */ -int ffmpeg_fmt_chunk_swap_endian(uint8_t * chunk, size_t chunk_size, uint16_t codec); -int ffmpeg_make_riff_atrac3plus(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int encoder_delay); -int ffmpeg_make_riff_xma1(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int stream_mode); -int ffmpeg_make_riff_xma2(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_count, int block_size); -int ffmpeg_make_riff_xma_from_fmt_chunk(uint8_t * buf, size_t buf_size, off_t fmt_offset, size_t fmt_size, size_t data_size, STREAMFILE *streamFile, int big_endian); -int ffmpeg_make_riff_xma2_from_xma2_chunk(uint8_t * buf, size_t buf_size, off_t xma2_offset, size_t xma2_size, size_t data_size, STREAMFILE *streamFile); -int ffmpeg_make_riff_xwma(uint8_t * buf, size_t buf_size, int codec, size_t data_size, int channels, int sample_rate, int avg_bps, int block_align); +int ffmpeg_fmt_chunk_swap_endian(uint8_t* chunk, size_t chunk_size, uint16_t codec); +int ffmpeg_make_riff_atrac3plus(uint8_t* buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int encoder_delay); +int ffmpeg_make_riff_xma1(uint8_t* buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int stream_mode); +int ffmpeg_make_riff_xma2(uint8_t* buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_count, int block_size); +int ffmpeg_make_riff_xma_from_fmt_chunk(uint8_t* buf, size_t buf_size, off_t fmt_offset, size_t fmt_size, size_t data_size, STREAMFILE* sf, int big_endian); +int ffmpeg_make_riff_xma2_from_xma2_chunk(uint8_t* buf, size_t buf_size, off_t xma2_offset, size_t xma2_size, size_t data_size, STREAMFILE* sf); +int ffmpeg_make_riff_xwma(uint8_t* buf, size_t buf_size, int codec, size_t data_size, int channels, int sample_rate, int avg_bps, int block_align); /* MS audio format's sample info (struct to avoid passing so much stuff, separate for reusing) */ typedef struct { @@ -412,39 +593,27 @@ typedef struct { int32_t loop_start_sample; int32_t loop_end_sample; } ms_sample_data; -void xma_get_samples(ms_sample_data * msd, STREAMFILE *streamFile); -void wmapro_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, int block_align, int sample_rate, uint32_t decode_flags); -void wma_get_samples(ms_sample_data * msd, STREAMFILE *streamFile, int block_align, int sample_rate, uint32_t decode_flags); +void xma_get_samples(ms_sample_data* msd, STREAMFILE* sf); +void wmapro_get_samples(ms_sample_data* msd, STREAMFILE* sf, int block_align, int sample_rate, uint32_t decode_flags); +void wma_get_samples(ms_sample_data* msd, STREAMFILE* sf, int block_align, int sample_rate, uint32_t decode_flags); -void xma1_parse_fmt_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * channels, int * sample_rate, int * loop_flag, int32_t * loop_start_b, int32_t * loop_end_b, int32_t * loop_subframe, int be); -void xma2_parse_fmt_chunk_extra(STREAMFILE *streamFile, off_t chunk_offset, int * loop_flag, int32_t * out_num_samples, int32_t * out_loop_start_sample, int32_t * out_loop_end_sample, int be); -void xma2_parse_xma2_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * channels, int * sample_rate, int * loop_flag, int32_t * num_samples, int32_t * loop_start_sample, int32_t * loop_end_sample); +void xma1_parse_fmt_chunk(STREAMFILE* sf, off_t chunk_offset, int* channels, int* sample_rate, int* loop_flag, int32_t* loop_start_b, int32_t* loop_end_b, int32_t* loop_subframe, int be); +void xma2_parse_fmt_chunk_extra(STREAMFILE* sf, off_t chunk_offset, int* loop_flag, int32_t* out_num_samples, int32_t* out_loop_start_sample, int32_t* out_loop_end_sample, int be); +void xma2_parse_xma2_chunk(STREAMFILE* sf, off_t chunk_offset, int* channels, int* sample_rate, int* loop_flag, int32_t* num_samples, int32_t* loop_start_sample, int32_t* loop_end_sample); -void xma_fix_raw_samples(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples); -void xma_fix_raw_samples_hb(VGMSTREAM *vgmstream, STREAMFILE *headerFile, STREAMFILE *bodyFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples); -void xma_fix_raw_samples_ch(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, int channel_per_stream, int fix_num_samples, int fix_loop_samples); +void xma_fix_raw_samples(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples); +void xma_fix_raw_samples_hb(VGMSTREAM* vgmstream, STREAMFILE* sf_head, STREAMFILE* sf_body, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples); +void xma_fix_raw_samples_ch(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t stream_offset, size_t stream_size, int channel_per_stream, int fix_num_samples, int fix_loop_samples); size_t atrac3_bytes_to_samples(size_t bytes, int full_block_align); size_t atrac3plus_bytes_to_samples(size_t bytes, int full_block_align); size_t ac3_bytes_to_samples(size_t bytes, int full_block_align, int channels); -size_t aac_get_samples(STREAMFILE *streamFile, off_t start_offset, size_t bytes); -size_t mpeg_get_samples(STREAMFILE *streamFile, off_t start_offset, size_t bytes); - - -/* An internal struct to pass around and simulate a bitstream. */ -typedef enum { BITSTREAM_MSF, BITSTREAM_VORBIS } vgm_bitstream_t; -typedef struct { - uint8_t * buf; /* buffer to read/write*/ - size_t bufsize; /* max size of the buffer */ - off_t b_off; /* current offset in bits inside the buffer */ - vgm_bitstream_t mode; /* read/write modes */ -} vgm_bitstream; - -int r_bits(vgm_bitstream * ib, int num_bits, uint32_t * value); -int w_bits(vgm_bitstream * ob, int num_bits, uint32_t value); +size_t aac_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes); +size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes); +int32_t mpeg_get_samples_clean(STREAMFILE* sf, off_t start, size_t size, size_t* p_loop_start, size_t* p_loop_end, int is_vbr); /* helper to pass a wrapped, clamped, fake extension-ed, SF to another meta */ -STREAMFILE* setup_subfile_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* extension); +STREAMFILE* setup_subfile_streamfile(STREAMFILE* sf, off_t subfile_offset, size_t subfile_size, const char* extension); #endif /*_CODING_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c index 0b6b3bfa5..faddd023e 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding_utils.c @@ -155,7 +155,8 @@ int ffmpeg_make_riff_xma1(uint8_t * buf, size_t buf_size, size_t sample_count, s put_16bitLE(buf+off+0x12, speakers); } - /* xmaencode decoding rejects XMA1 without "seek" chunk, though it doesn't seem to use it */ + /* xmaencode decoding rejects XMA1 without "seek" chunk, though it doesn't seem to use it + * (needs to be have entries but can be bogus, also generates seek for even small sounds) */ memcpy(buf+riff_size-4-4, "data", 4); put_32bitLE(buf+riff_size-4, data_size); /* data size */ @@ -1011,152 +1012,6 @@ size_t aac_get_samples(STREAMFILE *streamFile, off_t start_offset, size_t bytes) return frames * samples_per_frame; } - -/* ******************************************** */ -/* BITSTREAM */ -/* ******************************************** */ - - -/* Read bits (max 32) from buf and update the bit offset. Vorbis packs values in LSB order and byte by byte. - * (ex. from 2 bytes 00100111 00000001 we can could read 4b=0111 and 6b=010010, 6b=remainder (second value is split into the 2nd byte) */ -static int r_bits_vorbis(vgm_bitstream * ib, int num_bits, uint32_t * value) { - off_t off, pos; - int i, bit_buf, bit_val; - if (num_bits == 0) return 1; - if (num_bits > 32 || num_bits < 0 || ib->b_off + num_bits > ib->bufsize*8) goto fail; - - *value = 0; /* set all bits to 0 */ - off = ib->b_off / 8; /* byte offset */ - pos = ib->b_off % 8; /* bit sub-offset */ - for (i = 0; i < num_bits; i++) { - bit_buf = (1U << pos) & 0xFF; /* bit check for buf */ - bit_val = (1U << i); /* bit to set in value */ - - if (ib->buf[off] & bit_buf) /* is bit in buf set? */ - *value |= bit_val; /* set bit */ - - pos++; /* new byte starts */ - if (pos%8 == 0) { - pos = 0; - off++; - } - } - - ib->b_off += num_bits; - return 1; -fail: - return 0; -} - -/* Write bits (max 32) to buf and update the bit offset. Vorbis packs values in LSB order and byte by byte. - * (ex. writing 1101011010 from b_off 2 we get 01101011 00001101 (value split, and 11 in the first byte skipped)*/ -static int w_bits_vorbis(vgm_bitstream * ob, int num_bits, uint32_t value) { - off_t off, pos; - int i, bit_val, bit_buf; - if (num_bits == 0) return 1; - if (num_bits > 32 || num_bits < 0 || ob->b_off + num_bits > ob->bufsize*8) goto fail; - - - off = ob->b_off / 8; /* byte offset */ - pos = ob->b_off % 8; /* bit sub-offset */ - for (i = 0; i < num_bits; i++) { - bit_val = (1U << i); /* bit check for value */ - bit_buf = (1U << pos) & 0xFF; /* bit to set in buf */ - - if (value & bit_val) /* is bit in val set? */ - ob->buf[off] |= bit_buf; /* set bit */ - else - ob->buf[off] &= ~bit_buf; /* unset bit */ - - pos++; /* new byte starts */ - if (pos%8 == 0) { - pos = 0; - off++; - } - } - - ob->b_off += num_bits; - return 1; -fail: - return 0; -} - - -/* Read bits (max 32) from buf and update the bit offset. Order is BE (MSF). */ -static int r_bits_msf(vgm_bitstream * ib, int num_bits, uint32_t * value) { - off_t off, pos; - int i, bit_buf, bit_val; - if (num_bits == 0) return 1; - if (num_bits > 32 || num_bits < 0 || ib->b_off + num_bits > ib->bufsize*8) goto fail; - - *value = 0; /* set all bits to 0 */ - off = ib->b_off / 8; /* byte offset */ - pos = ib->b_off % 8; /* bit sub-offset */ - for (i = 0; i < num_bits; i++) { - bit_buf = (1U << (8-1-pos)) & 0xFF; /* bit check for buf */ - bit_val = (1U << (num_bits-1-i)); /* bit to set in value */ - - if (ib->buf[off] & bit_buf) /* is bit in buf set? */ - *value |= bit_val; /* set bit */ - - pos++; - if (pos%8 == 0) { /* new byte starts */ - pos = 0; - off++; - } - } - - ib->b_off += num_bits; - return 1; -fail: - return 0; -} - -/* Write bits (max 32) to buf and update the bit offset. Order is BE (MSF). */ -static int w_bits_msf(vgm_bitstream * ob, int num_bits, uint32_t value) { - off_t off, pos; - int i, bit_val, bit_buf; - if (num_bits == 0) return 1; - if (num_bits > 32 || num_bits < 0 || ob->b_off + num_bits > ob->bufsize*8) goto fail; - - - off = ob->b_off / 8; /* byte offset */ - pos = ob->b_off % 8; /* bit sub-offset */ - for (i = 0; i < num_bits; i++) { - bit_val = (1U << (num_bits-1-i)); /* bit check for value */ - bit_buf = (1U << (8-1-pos)) & 0xFF; /* bit to set in buf */ - - if (value & bit_val) /* is bit in val set? */ - ob->buf[off] |= bit_buf; /* set bit */ - else - ob->buf[off] &= ~bit_buf; /* unset bit */ - - pos++; - if (pos%8 == 0) { /* new byte starts */ - pos = 0; - off++; - } - } - - ob->b_off += num_bits; - return 1; -fail: - return 0; -} - -int r_bits(vgm_bitstream * ib, int num_bits, uint32_t * value) { - if (ib->mode == BITSTREAM_VORBIS) - return r_bits_vorbis(ib,num_bits,value); - else - return r_bits_msf(ib,num_bits,value); -} -int w_bits(vgm_bitstream * ob, int num_bits, uint32_t value) { - if (ob->mode == BITSTREAM_VORBIS) - return w_bits_vorbis(ob,num_bits,value); - else - return w_bits_msf(ob,num_bits,value); -} - /* ******************************************** */ /* CUSTOM STREAMFILES */ /* ******************************************** */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ea_xa_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ea_xa_decoder.c index 9178d3ae1..857dd6df7 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ea_xa_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ea_xa_decoder.c @@ -29,7 +29,7 @@ static const int EA_XA_TABLE[20] = { 0, -1, -3, -4 }; -/* EA XA v2 (always mono); like ea_xa_int but with "PCM samples" flag and doesn't add 128 on expand or clamp (pre-adjusted by the encoder?) */ +/* EA XA v2 (always mono); like v1 but with "PCM samples" flag and doesn't add 128 on expand or clamp (pre-adjusted by the encoder?) */ void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { uint8_t frame_info; int32_t coef1, coef2; @@ -183,71 +183,39 @@ void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac } #endif -/* EA XA v1 stereo */ -void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { +/* EA XA v1 (mono/stereo) */ +void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel, int is_stereo) { uint8_t frame_info; int32_t coef1, coef2; int i, sample_count, shift; int hn = (channel==0); /* high nibble marker for stereo subinterleave, ch0/L=high nibble, ch1/R=low nibble */ - int frame_size = 0x1e; + int frame_size = is_stereo ? 0x0f*2 : 0x0f; int frame_samples = 28; first_sample = first_sample % frame_samples; - /* header (coefs ch0+ch1 + shift ch0+ch1) */ - frame_info = read_8bit(stream->offset+0x00,stream->streamfile); - coef1 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 0]; - coef2 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 4]; - shift = (frame_info & 0x0F) + 8; + if (is_stereo) { + /* header (coefs ch0+ch1 + shift ch0+ch1) */ + frame_info = read_8bit(stream->offset + 0x00, stream->streamfile); + coef1 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 0]; + coef2 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 4]; - frame_info = read_8bit(stream->offset+0x01,stream->streamfile); - shift = (hn ? frame_info >> 4 : frame_info & 0x0F) + 8; - - /* samples */ - for (i=first_sample,sample_count=0; ioffset + 0x02 + i); - int nibble_shift = (hn ? 4 : 0); /* high nibble first */ - - sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile); - sample_nibble = (sample_byte >> nibble_shift) & 0x0F; - new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */ - new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8; - new_sample = clamp16(new_sample); - - outbuf[sample_count] = new_sample; - stream->adpcm_history2_32 = stream->adpcm_history1_32; - stream->adpcm_history1_32 = new_sample; + frame_info = read_8bit(stream->offset + 0x01, stream->streamfile); + shift = (hn ? frame_info >> 4 : frame_info & 0x0F) + 8; + } else { + /* header (coefs + shift ch0) */ + frame_info = read_8bit(stream->offset + 0x00, stream->streamfile); + coef1 = EA_XA_TABLE[(frame_info >> 4) + 0]; + coef2 = EA_XA_TABLE[(frame_info >> 4) + 4]; + shift = (frame_info & 0x0F) + 8; } - /* only increment offset on complete frame */ - if (i == frame_samples) - stream->offset += frame_size; -} - -/* EA-XA v1 mono/interleave */ -void decode_ea_xa_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { - uint8_t frame_info; - int32_t coef1, coef2; - int i, sample_count, shift; - - int frame_size = 0x0f; - int frame_samples = 28; - first_sample = first_sample % frame_samples; - - /* header (coefs+shift ch0) */ - frame_info = read_8bit(stream->offset,stream->streamfile); - coef1 = EA_XA_TABLE[(frame_info >> 4) + 0]; - coef2 = EA_XA_TABLE[(frame_info >> 4) + 4]; - shift = (frame_info & 0x0F) + 8; - /* samples */ for (i=first_sample,sample_count=0; ioffset + 0x01 + i/2); - int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */ + off_t byte_offset = is_stereo ? (stream->offset + 0x02 + i) : (stream->offset + 0x01 + i/2); + int nibble_shift = is_stereo ? (hn ? 4 : 0) : ((!(i & 1)) ? 4 : 0); /* high nibble first */ sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile); sample_nibble = (sample_byte >> nibble_shift) & 0x0F; @@ -303,3 +271,8 @@ void decode_maxis_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac if (i == frame_samples) stream->offset += frame_size; } + +int32_t ea_xa_bytes_to_samples(size_t bytes, int channels) { + if (channels <= 0) return 0; + return bytes / channels / 0x0f * 28; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ea_xas_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ea_xas_decoder.c index 5fde33b54..37979dd1c 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ea_xas_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ea_xas_decoder.c @@ -1,181 +1,181 @@ -#include "coding.h" -#include "../util.h" - -#if 0 -/* known game code/platforms use float buffer and coefs, but some approximations around use this int math: - * ... - * coef1 = table[index + 0] - * coef2 = table[index + 4] - * sample = clamp16(((signed_nibble << (20 - shift)) + hist1 * coef1 + hist2 * coef2 + 128) >> 8); */ -static const int EA_XA_TABLE[20] = { - 0, 240, 460, 392, - 0, 0, -208, -220, - 0, 1, 3, 4, - 7, 8, 10, 11, - 0, -1, -3, -4 -}; -#endif - -/* standard CD-XA's K0/K1 filter pairs */ -static const float xa_coefs[16][2] = { - { 0.0, 0.0 }, - { 0.9375, 0.0 }, - { 1.796875, -0.8125 }, - { 1.53125, -0.859375 }, - /* only 4 pairs exist, assume 0s for bad indexes */ -}; - -/* EA-XAS v1, evolution of EA-XA/XAS and cousin of MTA2. Reverse engineered from various .exes/.so - * - * Layout: blocks of 0x4c per channel (128 samples), divided into 4 headers + 4 vertical groups of 15 bytes. - * Original code reads all headers first then processes all nibbles (for CPU cache/parallelism/SIMD optimizations). - * To simplify, always decodes the block and discards unneeded samples, so doesn't use external hist. */ -void decode_ea_xas_v1(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - uint8_t frame[0x4c] = {0}; - off_t frame_offset; - int group, row, i, samples_done = 0, sample_count = 0; - size_t bytes_per_frame, samples_per_frame; - - - /* internal interleave */ - bytes_per_frame = 0x4c; - samples_per_frame = 128; - first_sample = first_sample % samples_per_frame; - - frame_offset = stream->offset + bytes_per_frame * channel; - read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ - - //todo: original code uses float sample buffer: - //- header pcm-hist to float-hist: hist * (1/32768) - //- nibble to signed to float: (int32_t)(pnibble << 28) * SHIFT_MUL_LUT[shift_index] - // look-up table just simplifies ((nibble << 12 << 12) >> 12 + shift) * (1/32768) - // though maybe introduces rounding errors? - //- coefs apply normally, though hists are already floats - //- final float sample isn't clamped - - - /* parse group headers */ - for (group = 0; group < 4; group++) { - float coef1, coef2; - int16_t hist1, hist2; - uint8_t shift; - uint32_t group_header = (uint32_t)get_32bitLE(frame + group*0x4); /* always LE */ - - coef1 = xa_coefs[group_header & 0x0F][0]; - coef2 = xa_coefs[group_header & 0x0F][1]; - hist2 = (int16_t)((group_header >> 0) & 0xFFF0); - hist1 = (int16_t)((group_header >> 16) & 0xFFF0); - shift = (group_header >> 16) & 0x0F; - - /* write header samples (needed) */ - if (sample_count >= first_sample && samples_done < samples_to_do) { - outbuf[samples_done * channelspacing] = hist2; - samples_done++; - } - sample_count++; - if (sample_count >= first_sample && samples_done < samples_to_do) { - outbuf[samples_done * channelspacing] = hist1; - samples_done++; - } - sample_count++; - - /* process nibbles per group */ - for (row = 0; row < 15; row++) { - for (i = 0; i < 1*2; i++) { - uint8_t nibbles = frame[4*4 + row*0x04 + group + i/2]; - int sample; - - sample = i&1 ? /* high nibble first */ - (nibbles >> 0) & 0x0f : - (nibbles >> 4) & 0x0f; - sample = (int16_t)(sample << 12) >> shift; /* 16b sign extend + scale */ - sample = sample + hist1 * coef1 + hist2 * coef2; - sample = clamp16(sample); - - if (sample_count >= first_sample && samples_done < samples_to_do) { - outbuf[samples_done * channelspacing] = sample; - samples_done++; - } - sample_count++; - - hist2 = hist1; - hist1 = sample; - } - } - } - - - /* internal interleave (interleaved channels, but manually advances to co-exist with ea blocks) */ - if (first_sample + samples_done == samples_per_frame) { - stream->offset += bytes_per_frame * channelspacing; - } -} - - -/* EA-XAS v0, without complex layouts and closer to EA-XA. Somewhat based on daemon1's decoder */ -void decode_ea_xas_v0(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - uint8_t frame[0x13] = {0}; - off_t frame_offset; - int i, frames_in, samples_done = 0, sample_count = 0; - size_t bytes_per_frame, samples_per_frame; - - - /* external interleave (fixed size), mono */ - bytes_per_frame = 0x02 + 0x02 + 0x0f; - samples_per_frame = 1 + 1 + 0x0f*2; - frames_in = first_sample / samples_per_frame; - first_sample = first_sample % samples_per_frame; - - frame_offset = stream->offset + bytes_per_frame * frames_in; - read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ - - //todo see above - - /* process frame */ - { - float coef1, coef2; - int16_t hist1, hist2; - uint8_t shift; - uint32_t frame_header = (uint32_t)get_32bitLE(frame); /* always LE */ - - coef1 = xa_coefs[frame_header & 0x0F][0]; - coef2 = xa_coefs[frame_header & 0x0F][1]; - hist2 = (int16_t)((frame_header >> 0) & 0xFFF0); - hist1 = (int16_t)((frame_header >> 16) & 0xFFF0); - shift = (frame_header >> 16) & 0x0F; - - /* write header samples (needed) */ - if (sample_count >= first_sample && samples_done < samples_to_do) { - outbuf[samples_done * channelspacing] = hist2; - samples_done++; - } - sample_count++; - if (sample_count >= first_sample && samples_done < samples_to_do) { - outbuf[samples_done * channelspacing] = hist1; - samples_done++; - } - sample_count++; - - /* process nibbles */ - for (i = 0; i < 0x0f*2; i++) { - uint8_t nibbles = frame[0x02 + 0x02 + i/2]; - int sample; - - sample = i&1 ? /* high nibble first */ - (nibbles >> 0) & 0x0f : - (nibbles >> 4) & 0x0f; - sample = (int16_t)(sample << 12) >> shift; /* 16b sign extend + scale */ - sample = sample + hist1 * coef1 + hist2 * coef2; - sample = clamp16(sample); - - if (sample_count >= first_sample && samples_done < samples_to_do) { - outbuf[samples_done * channelspacing] = sample; - samples_done++; - } - sample_count++; - - hist2 = hist1; - hist1 = sample; - } - } -} +#include "coding.h" +#include "../util.h" + +#if 0 +/* known game code/platforms use float buffer and coefs, but some approximations around use this int math: + * ... + * coef1 = table[index + 0] + * coef2 = table[index + 4] + * sample = clamp16(((signed_nibble << (20 - shift)) + hist1 * coef1 + hist2 * coef2 + 128) >> 8); */ +static const int EA_XA_TABLE[20] = { + 0, 240, 460, 392, + 0, 0, -208, -220, + 0, 1, 3, 4, + 7, 8, 10, 11, + 0, -1, -3, -4 +}; +#endif + +/* standard CD-XA's K0/K1 filter pairs */ +static const float xa_coefs[16][2] = { + { 0.0, 0.0 }, + { 0.9375, 0.0 }, + { 1.796875, -0.8125 }, + { 1.53125, -0.859375 }, + /* only 4 pairs exist, assume 0s for bad indexes */ +}; + +/* EA-XAS (XA Seekable) Version 1, evolution of EA-XA/XAS and cousin of MTA2. Reverse engineered from various .exes/.so + * + * Layout: blocks of 0x4c per channel (128 samples), divided into 4 headers + 4 vertical groups of 15 bytes. + * Original code reads all headers first then processes all nibbles (for CPU cache/parallelism/SIMD optimizations). + * To simplify, always decodes the block and discards unneeded samples, so doesn't use external hist. */ +void decode_ea_xas_v1(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + uint8_t frame[0x4c] = {0}; + off_t frame_offset; + int group, row, i, samples_done = 0, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + + + /* internal interleave */ + bytes_per_frame = 0x4c; + samples_per_frame = 128; + first_sample = first_sample % samples_per_frame; + + frame_offset = stream->offset + bytes_per_frame * channel; + read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ + + //todo: original code uses float sample buffer: + //- header pcm-hist to float-hist: hist * (1/32768) + //- nibble to signed to float: (int32_t)(pnibble << 28) * SHIFT_MUL_LUT[shift_index] + // look-up table just simplifies ((nibble << 12 << 12) >> 12 + shift) * (1/32768) + // though maybe introduces rounding errors? + //- coefs apply normally, though hists are already floats + //- final float sample isn't clamped + + + /* parse group headers */ + for (group = 0; group < 4; group++) { + float coef1, coef2; + int16_t hist1, hist2; + uint8_t shift; + uint32_t group_header = (uint32_t)get_32bitLE(frame + group*0x4); /* always LE */ + + coef1 = xa_coefs[group_header & 0x0F][0]; + coef2 = xa_coefs[group_header & 0x0F][1]; + hist2 = (int16_t)((group_header >> 0) & 0xFFF0); + hist1 = (int16_t)((group_header >> 16) & 0xFFF0); + shift = (group_header >> 16) & 0x0F; + + /* write header samples (needed) */ + if (sample_count >= first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = hist2; + samples_done++; + } + sample_count++; + if (sample_count >= first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = hist1; + samples_done++; + } + sample_count++; + + /* process nibbles per group */ + for (row = 0; row < 15; row++) { + for (i = 0; i < 1*2; i++) { + uint8_t nibbles = frame[4*4 + row*0x04 + group + i/2]; + int sample; + + sample = i&1 ? /* high nibble first */ + (nibbles >> 0) & 0x0f : + (nibbles >> 4) & 0x0f; + sample = (int16_t)(sample << 12) >> shift; /* 16b sign extend + scale */ + sample = sample + hist1 * coef1 + hist2 * coef2; + sample = clamp16(sample); + + if (sample_count >= first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = sample; + samples_done++; + } + sample_count++; + + hist2 = hist1; + hist1 = sample; + } + } + } + + + /* internal interleave (interleaved channels, but manually advances to co-exist with ea blocks) */ + if (first_sample + samples_done == samples_per_frame) { + stream->offset += bytes_per_frame * channelspacing; + } +} + + +/* EA-XAS v0 (xas0), without complex layouts and closer to EA-XA. Somewhat based on daemon1's decoder. */ +void decode_ea_xas_v0(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + uint8_t frame[0x13] = {0}; + off_t frame_offset; + int i, frames_in, samples_done = 0, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + + + /* external interleave (fixed size), mono */ + bytes_per_frame = 0x02 + 0x02 + 0x0f; + samples_per_frame = 1 + 1 + 0x0f*2; + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; + + frame_offset = stream->offset + bytes_per_frame * frames_in; + read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ + + //todo see above + + /* process frame */ + { + float coef1, coef2; + int16_t hist1, hist2; + uint8_t shift; + uint32_t frame_header = (uint32_t)get_32bitLE(frame); /* always LE */ + + coef1 = xa_coefs[frame_header & 0x0F][0]; + coef2 = xa_coefs[frame_header & 0x0F][1]; + hist2 = (int16_t)((frame_header >> 0) & 0xFFF0); + hist1 = (int16_t)((frame_header >> 16) & 0xFFF0); + shift = (frame_header >> 16) & 0x0F; + + /* write header samples (needed) */ + if (sample_count >= first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = hist2; + samples_done++; + } + sample_count++; + if (sample_count >= first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = hist1; + samples_done++; + } + sample_count++; + + /* process nibbles */ + for (i = 0; i < 0x0f*2; i++) { + uint8_t nibbles = frame[0x02 + 0x02 + i/2]; + int sample; + + sample = i&1 ? /* high nibble first */ + (nibbles >> 0) & 0x0f : + (nibbles >> 4) & 0x0f; + sample = (int16_t)(sample << 12) >> shift; /* 16b sign extend + scale */ + sample = sample + hist1 * coef1 + hist2 * coef2; + sample = clamp16(sample); + + if (sample_count >= first_sample && samples_done < samples_to_do) { + outbuf[samples_done * channelspacing] = sample; + samples_done++; + } + sample_count++; + + hist2 = hist1; + hist1 = sample; + } + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c index 1070921fe..65385b133 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c @@ -8,11 +8,8 @@ static volatile int g_ffmpeg_initialized = 0; -static void free_ffmpeg_config(ffmpeg_codec_data *data); -static int init_ffmpeg_config(ffmpeg_codec_data * data, int target_subsong, int reset); - -static void reset_ffmpeg_internal(ffmpeg_codec_data *data); -static void seek_ffmpeg_internal(ffmpeg_codec_data *data, int32_t num_sample); +static void free_ffmpeg_config(ffmpeg_codec_data* data); +static int init_ffmpeg_config(ffmpeg_codec_data* data, int target_subsong, int reset); /* ******************************************** */ /* INTERNAL UTILS */ @@ -34,7 +31,7 @@ static void g_init_ffmpeg() { } } -static void remap_audio(sample_t *outbuf, int sample_count, int channels, int *channel_mappings) { +static void remap_audio(sample_t* outbuf, int sample_count, int channels, int* channel_mappings) { int ch_from,ch_to,s; sample_t temp; for (s = 0; s < sample_count; s++) { @@ -65,7 +62,7 @@ static void remap_audio(sample_t *outbuf, int sample_count, int channels, int *c * first frame/packet and sets up index to timestamp 0. This ensures faulty demuxers will seek to 0 correctly. * Some formats may not seek to 0 even with this, though. */ -static int init_seek(ffmpeg_codec_data * data) { +static int init_seek(ffmpeg_codec_data* data) { int ret, ts_index, packet_count = 0; int64_t ts = 0; /* seek timestamp */ int64_t pos = 0; /* data offset */ @@ -161,8 +158,8 @@ fail: /* ******************************************** */ /* AVIO callback: read stream, handling custom data */ -static int ffmpeg_read(void *opaque, uint8_t *buf, int read_size) { - ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque; +static int ffmpeg_read(void* opaque, uint8_t* buf, int read_size) { + ffmpeg_codec_data* data = opaque; int bytes = 0; int max_to_copy = 0; @@ -196,8 +193,8 @@ static int ffmpeg_read(void *opaque, uint8_t *buf, int read_size) { } /* AVIO callback: seek stream, handling custom data */ -static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { - ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque; +static int64_t ffmpeg_seek(void* opaque, int64_t offset, int whence) { + ffmpeg_codec_data* data = opaque; int ret = 0; /* get cache'd size */ @@ -243,12 +240,12 @@ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { /* MAIN INIT/DECODER */ /* ******************************************** */ -ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) { - return init_ffmpeg_header_offset(streamFile, NULL,0, start,size); +ffmpeg_codec_data* init_ffmpeg_offset(STREAMFILE* sf, uint64_t start, uint64_t size) { + return init_ffmpeg_header_offset(sf, NULL,0, start,size); } -ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size) { - return init_ffmpeg_header_offset_subsong(streamFile, header, header_size, start, size, 0); +ffmpeg_codec_data* init_ffmpeg_header_offset(STREAMFILE* sf, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size) { + return init_ffmpeg_header_offset_subsong(sf, header, header_size, start, size, 0); } @@ -261,8 +258,8 @@ ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * * NULL header can used given if the stream has internal data recognized by FFmpeg at offset. * Stream index can be passed if the file has multiple audio streams that FFmpeg can demux (1=first). */ -ffmpeg_codec_data * init_ffmpeg_header_offset_subsong(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong) { - ffmpeg_codec_data * data = NULL; +ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong) { + ffmpeg_codec_data* data = NULL; int errcode; @@ -270,9 +267,9 @@ ffmpeg_codec_data * init_ffmpeg_header_offset_subsong(STREAMFILE *streamFile, ui if ((header && !header_size) || (!header && header_size)) goto fail; - if (size == 0 || start + size > get_streamfile_size(streamFile)) { - VGM_LOG("FFMPEG: wrong start+size found: %x + %x > %x \n", (uint32_t)start, (uint32_t)size, get_streamfile_size(streamFile)); - size = get_streamfile_size(streamFile) - start; + if (size == 0 || start + size > get_streamfile_size(sf)) { + VGM_LOG("FFMPEG: wrong start+size found: %x + %x > %x \n", (uint32_t)start, (uint32_t)size, get_streamfile_size(sf)); + size = get_streamfile_size(sf) - start; } @@ -284,7 +281,7 @@ ffmpeg_codec_data * init_ffmpeg_header_offset_subsong(STREAMFILE *streamFile, ui data = calloc(1, sizeof(ffmpeg_codec_data)); if (!data) return NULL; - data->streamfile = reopen_streamfile(streamFile, 0); + data->streamfile = reopen_streamfile(sf, 0); if (!data->streamfile) goto fail; /* fake header to trick FFmpeg into demuxing/decoding the stream */ @@ -366,7 +363,7 @@ fail: return NULL; } -static int init_ffmpeg_config(ffmpeg_codec_data * data, int target_subsong, int reset) { +static int init_ffmpeg_config(ffmpeg_codec_data* data, int target_subsong, int reset) { int errcode = 0; /* basic IO/format setup */ @@ -455,7 +452,7 @@ fail: } /* decodes a new frame to internal data */ -static int decode_ffmpeg_frame(ffmpeg_codec_data *data) { +static int decode_ffmpeg_frame(ffmpeg_codec_data* data) { int errcode; int frame_error = 0; @@ -672,7 +669,7 @@ static void samples_dblp_to_s16(sample_t* obuf, double** inbuf, int ichs, int sa } } -static void copy_samples(ffmpeg_codec_data *data, sample_t *outbuf, int samples_to_do) { +static void copy_samples(ffmpeg_codec_data* data, sample_t* outbuf, int samples_to_do) { int channels = data->codecCtx->channels; int is_planar = av_sample_fmt_is_planar(data->codecCtx->sample_fmt) && (channels > 1); void* ibuf; @@ -709,8 +706,8 @@ static void copy_samples(ffmpeg_codec_data *data, sample_t *outbuf, int samples_ } /* decode samples of any kind of FFmpeg format */ -void decode_ffmpeg(VGMSTREAM *vgmstream, sample_t * outbuf, int32_t samples_to_do, int channels) { - ffmpeg_codec_data *data = vgmstream->codec_data; +void decode_ffmpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) { + ffmpeg_codec_data* data = vgmstream->codec_data; while (samples_to_do > 0) { @@ -757,14 +754,11 @@ decode_fail: /* UTILS */ /* ******************************************** */ -void reset_ffmpeg_internal(ffmpeg_codec_data *data) { - seek_ffmpeg_internal(data, 0); -} -void reset_ffmpeg(VGMSTREAM *vgmstream) { - reset_ffmpeg_internal(vgmstream->codec_data); +void reset_ffmpeg(ffmpeg_codec_data* data) { + seek_ffmpeg(data, 0); } -void seek_ffmpeg_internal(ffmpeg_codec_data *data, int32_t num_sample) { +void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample) { if (!data) return; /* Start from 0 and discard samples until sample (slower but not too noticeable). @@ -813,12 +807,8 @@ fail: data->bad_init = 1; /* internals were probably free'd */ } -void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample) { - seek_ffmpeg_internal(vgmstream->codec_data, num_sample); -} - -static void free_ffmpeg_config(ffmpeg_codec_data *data) { +static void free_ffmpeg_config(ffmpeg_codec_data* data) { if (data == NULL) return; @@ -858,7 +848,7 @@ static void free_ffmpeg_config(ffmpeg_codec_data *data) { //todo avformat_find_stream_info may cause some Win Handle leaks? related to certain option } -void free_ffmpeg(ffmpeg_codec_data *data) { +void free_ffmpeg(ffmpeg_codec_data* data) { if (data == NULL) return; @@ -883,7 +873,7 @@ void free_ffmpeg(ffmpeg_codec_data *data) { * This could be added per format in FFmpeg directly, but it's here for flexibility and due to bugs * (FFmpeg's stream->(start_)skip_samples causes glitches in XMA). */ -void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples) { +void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples) { AVStream *stream = NULL; if (!data || !data->formatCtx) return; @@ -902,7 +892,7 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples) { } /* returns channel layout if set */ -uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data * data) { +uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data) { if (!data || !data->codecCtx) return 0; return (uint32_t)data->codecCtx->channel_layout; /* uint64 but there ain't so many speaker mappings */ } @@ -910,7 +900,7 @@ uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data * data) { /* yet another hack to fix codecs that encode channels in different order and reorder on decoder * but FFmpeg doesn't do it automatically * (maybe should be done via mixing, but could clash with other stuff?) */ -void ffmpeg_set_channel_remapping(ffmpeg_codec_data * data, int *channel_remap) { +void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int *channel_remap) { int i; if (data->channels > 32) @@ -922,7 +912,7 @@ void ffmpeg_set_channel_remapping(ffmpeg_codec_data * data, int *channel_remap) data->channel_remap_set = 1; } -const char* ffmpeg_get_codec_name(ffmpeg_codec_data * data) { +const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data) { if (!data || !data->codec) return NULL; if (data->codec->long_name) @@ -932,12 +922,12 @@ const char* ffmpeg_get_codec_name(ffmpeg_codec_data * data) { return NULL; } -void ffmpeg_set_force_seek(ffmpeg_codec_data * data) { +void ffmpeg_set_force_seek(ffmpeg_codec_data* data) { /* some formats like Smacker are so buggy that any seeking is impossible (even on video players), * or MPC with an incorrectly parsed seek table (using as 0 some non-0 seek offset). * whatever, we'll just kill and reconstruct FFmpeg's config every time */ data->force_seek = 1; - reset_ffmpeg_internal(data); /* reset state from trying to seek */ + reset_ffmpeg(data); /* reset state from trying to seek */ //stream = data->formatCtx->streams[data->streamIndex]; } @@ -959,4 +949,8 @@ const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key) return avde->value; } +STREAMFILE* ffmpeg_get_streamfile(ffmpeg_codec_data* data) { + if (!data) return NULL; + return data->streamfile; +} #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c index 93eb023db..2d47a60b3 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_custom_opus.c @@ -17,14 +17,13 @@ * https://github.com/hcs64/ww2ogg */ -typedef enum { OPUS_SWITCH, OPUS_UE4_v1, OPUS_UE4_v2, OPUS_EA, OPUS_X } opus_type_t; +typedef enum { OPUS_SWITCH, OPUS_UE4_v1, OPUS_UE4_v2, OPUS_EA, OPUS_X, OPUS_FSB, OPUS_WWISE } opus_type_t; static size_t make_oggs_first(uint8_t *buf, int buf_size, opus_config *cfg); static size_t make_oggs_page(uint8_t *buf, int buf_size, size_t data_size, int page_sequence, int granule); static size_t opus_get_packet_samples(const uint8_t *buf, int len); -static size_t opus_get_packet_samples_sf(STREAMFILE *sf, off_t offset); -static size_t get_xopus_packet_size(int packet, STREAMFILE *streamfile); -static opus_type_t get_ue4opus_version(STREAMFILE *sf, off_t offset); +static size_t opus_get_packet_samples_sf(STREAMFILE* sf, off_t offset); +static opus_type_t get_ue4opus_version(STREAMFILE* sf, off_t offset); typedef struct { /* config */ @@ -32,6 +31,11 @@ typedef struct { off_t stream_offset; size_t stream_size; + /* list of OPUS frame sizes, for variations that preload this (must alloc/dealloc on init/close) */ + off_t table_offset; + int table_count; + uint16_t* frame_table; + /* state */ off_t logical_offset; /* offset that corresponds to physical_offset */ off_t physical_offset; /* actual file offset */ @@ -46,11 +50,14 @@ typedef struct { size_t head_size; /* OggS head page size */ size_t logical_size; + } opus_io_data; +static size_t get_table_frame_size(opus_io_data* data, int packet); + /* Convers custom Opus packets to Ogg Opus, so the resulting data is larger than physical data. */ -static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, opus_io_data* data) { +static size_t opus_io_read(STREAMFILE* sf, uint8_t *dest, off_t offset, size_t length, opus_io_data* data) { size_t total_read = 0; /* ignore bad reads */ @@ -59,12 +66,12 @@ static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, } /* previous offset: re-start as we can't map logical<>physical offsets */ - if (offset < data->logical_offset) { + if (offset < data->logical_offset || data->logical_offset < 0) { data->physical_offset = data->stream_offset; data->logical_offset = 0x00; data->page_size = 0; data->samples_done = 0; - data->sequence = 2; /* appended header is 0/1 */ + data->sequence = 2; /* appended header+comment is 0/1 */ if (offset >= data->head_size) data->logical_offset = data->head_size; @@ -101,24 +108,26 @@ static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, switch(data->type) { case OPUS_SWITCH: /* format seem to come from opus_test and not Nintendo-specific */ - data_size = read_u32be(data->physical_offset, streamfile); + data_size = read_u32be(data->physical_offset, sf); skip_size = 0x08; /* size + Opus state(?) */ break; case OPUS_UE4_v1: - data_size = read_u16le(data->physical_offset, streamfile); + case OPUS_FSB: + data_size = read_u16le(data->physical_offset, sf); skip_size = 0x02; break; case OPUS_UE4_v2: - data_size = read_u16le(data->physical_offset + 0x00, streamfile); - packet_samples = read_u16le(data->physical_offset + 0x02, streamfile); + data_size = read_u16le(data->physical_offset + 0x00, sf); + packet_samples = read_u16le(data->physical_offset + 0x02, sf); skip_size = 0x02 + 0x02; break; case OPUS_EA: - data_size = read_u16be(data->physical_offset, streamfile); + data_size = read_u16be(data->physical_offset, sf); skip_size = 0x02; break; case OPUS_X: - data_size = get_xopus_packet_size(data->sequence - 2, streamfile); + case OPUS_WWISE: + data_size = get_table_frame_size(data, data->sequence - 2); skip_size = 0; break; default: @@ -131,13 +140,13 @@ static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, data->page_size = oggs_size + data_size; if (data->page_size > sizeof(data->page_buffer)) { /* happens on bad reads/EOF too */ - VGM_LOG("OPUS: buffer can't hold OggS at %x\n", (uint32_t)data->physical_offset); + VGM_LOG("OPUS: buffer can't hold OggS at %x, size=%x\n", (uint32_t)data->physical_offset, data->page_size); data->page_size = 0; break; } /* create fake OggS page (full page for checksums) */ - read_streamfile(data->page_buffer+oggs_size, data->physical_offset + skip_size, data_size, streamfile); /* store page data */ + read_streamfile(data->page_buffer+oggs_size, data->physical_offset + skip_size, data_size, sf); /* store page data */ if (packet_samples == 0) packet_samples = opus_get_packet_samples(data->page_buffer + oggs_size, data_size); data->samples_done += packet_samples; @@ -178,7 +187,7 @@ static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, } -static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) { +static size_t opus_io_size(STREAMFILE* sf, opus_io_data* data) { off_t physical_offset, max_physical_offset; size_t logical_size = 0; int packet = 0; @@ -186,8 +195,8 @@ static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) { if (data->logical_size) return data->logical_size; - if (data->stream_offset + data->stream_size > get_streamfile_size(streamfile)) { - VGM_LOG("OPUS: wrong streamsize %x + %x vs %x\n", (uint32_t)data->stream_offset, data->stream_size, get_streamfile_size(streamfile)); + if (data->stream_offset + data->stream_size > get_streamfile_size(sf)) { + VGM_LOG("OPUS: wrong streamsize %x + %x vs %x\n", (uint32_t)data->stream_offset, data->stream_size, get_streamfile_size(sf)); return 0; } @@ -201,30 +210,37 @@ static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) { switch(data->type) { case OPUS_SWITCH: - data_size = read_u32be(physical_offset, streamfile); + data_size = read_u32be(physical_offset, sf); skip_size = 0x08; break; case OPUS_UE4_v1: - data_size = read_u16le(physical_offset, streamfile); + case OPUS_FSB: + data_size = read_u16le(physical_offset, sf); skip_size = 0x02; break; case OPUS_UE4_v2: - data_size = read_u16le(physical_offset, streamfile); + data_size = read_u16le(physical_offset, sf); skip_size = 0x02 + 0x02; break; case OPUS_EA: - data_size = read_u16be(physical_offset, streamfile); + data_size = read_u16be(physical_offset, sf); skip_size = 0x02; break; case OPUS_X: - data_size = get_xopus_packet_size(packet, streamfile); + case OPUS_WWISE: + data_size = get_table_frame_size(data, packet); skip_size = 0x00; break; default: return 0; } - if (data_size == 0 ) { + /* FSB pads data after end (total size without frame headers is given but not too useful here) */ + if (data->type == OPUS_FSB && data_size == 0) { + break; + } + + if (data_size == 0) { VGM_LOG("OPUS: data_size is 0 at %x\n", (uint32_t)physical_offset); return 0; /* bad rip? or could 'break' and truck along */ } @@ -237,7 +253,7 @@ static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) { } /* logical size can be bigger though */ - if (physical_offset > get_streamfile_size(streamfile)) { + if (physical_offset > get_streamfile_size(sf)) { VGM_LOG("OPUS: wrong size\n"); return 0; } @@ -246,39 +262,62 @@ static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) { return data->logical_size; } +static int opus_io_init(STREAMFILE* sf, opus_io_data* data) { + //;VGM_LOG("OPUS: init\n"); + + /* read table containing frame sizes */ + if (data->table_count) { + int i; + //;VGM_LOG("OPUS: reading table, offset=%lx, entries=%i\n", data->table_offset, data->table_count); + + data->frame_table = malloc(data->table_count * sizeof(uint16_t)); + if (!data->frame_table) goto fail; + + for (i = 0; i < data->table_count; i++) { + data->frame_table[i] = read_u16le(data->table_offset + i * 0x02, sf); + } + } + + data->logical_offset = -1; /* force reset in case old data was cloned when re-opening SFs */ + data->logical_size = opus_io_size(sf, data); /* force size */ + return 1; +fail: + free(data->frame_table); + return 0; +} + +static void opus_io_close(STREAMFILE* sf, opus_io_data* data) { + //;VGM_LOG("OPUS: closing\n"); + + free(data->frame_table); +} + + /* Prepares custom IO for custom Opus, that is converted to Ogg Opus on the fly */ -static STREAMFILE* setup_opus_streamfile(STREAMFILE *streamFile, opus_config *cfg, off_t stream_offset, size_t stream_size, opus_type_t type) { - STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; +static STREAMFILE* setup_opus_streamfile(STREAMFILE* sf, opus_config *cfg, off_t stream_offset, size_t stream_size, opus_type_t type) { + STREAMFILE* new_sf = NULL; opus_io_data io_data = {0}; - size_t io_data_size = sizeof(opus_io_data); + + if (!cfg->sample_rate) + cfg->sample_rate = 48000; /* default / only value for opus */ io_data.type = type; io_data.stream_offset = stream_offset; io_data.stream_size = stream_size; io_data.physical_offset = stream_offset; + io_data.table_offset = cfg->table_offset; + io_data.table_count = cfg->table_count; + io_data.head_size = make_oggs_first(io_data.head_buffer, sizeof(io_data.head_buffer), cfg); if (!io_data.head_size) goto fail; - io_data.sequence = 2; - io_data.logical_size = opus_io_size(streamFile, &io_data); /* force init */ /* 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, opus_io_read,opus_io_size); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - - new_streamFile = open_buffer_streamfile(new_streamFile,0); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; - - return temp_streamFile; - + new_sf = open_wrap_streamfile(sf); + new_sf = open_io_streamfile_ex_f(new_sf, &io_data, sizeof(opus_io_data), opus_io_read, opus_io_size, opus_io_init, opus_io_close); + new_sf = open_buffer_streamfile_f(new_sf, 0); + return new_sf; fail: - close_streamfile(temp_streamFile); return NULL; } @@ -321,7 +360,7 @@ static uint32_t crc_lookup[256]={ }; /* from ww2ogg */ -static uint32_t get_oggs_checksum(uint8_t * data, int bytes) { +static uint32_t get_oggs_checksum(uint8_t* data, int bytes) { uint32_t crc_reg=0; int i; @@ -332,7 +371,7 @@ static uint32_t get_oggs_checksum(uint8_t * data, int bytes) { } /* from opus_decoder.c's opus_packet_get_samples_per_frame */ -static uint32_t opus_packet_get_samples_per_frame(const uint8_t * data, int Fs) { +static uint32_t opus_packet_get_samples_per_frame(const uint8_t* data, int Fs) { int audiosize; if (data[0]&0x80) { @@ -352,7 +391,7 @@ static uint32_t opus_packet_get_samples_per_frame(const uint8_t * data, int Fs) } /* from opus_decoder.c's opus_packet_get_nb_frames */ -static int opus_packet_get_nb_frames(const uint8_t * packet, int len) { +static int opus_packet_get_nb_frames(const uint8_t* packet, int len) { int count; if (len<1) return 0; @@ -369,7 +408,7 @@ static int opus_packet_get_nb_frames(const uint8_t * packet, int len) { /* ******************************** */ -static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int page_sequence, int granule) { +static size_t make_oggs_page(uint8_t* buf, int buf_size, size_t data_size, int page_sequence, int granule) { size_t page_done, lacing_done = 0; uint64_t absolute_granule = granule; /* wrong values seem validated (0, less than real samples, etc) */ int header_type_flag = (page_sequence==0 ? 2 : 0); @@ -383,15 +422,15 @@ static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int } segment_count = (int)(data_size / 0xFF + 1); - put_32bitBE(buf+0x00, 0x4F676753); /* capture pattern ("OggS") */ - put_8bit (buf+0x04, 0); /* stream structure version, fixed */ - put_8bit (buf+0x05, header_type_flag); /* bitflags (0: normal, continued = 1, first = 2, last = 4) */ - put_32bitLE(buf+0x06, (uint32_t)(absolute_granule >> 0 & 0xFFFFFFFF)); /* lower */ - put_32bitLE(buf+0x0A, (uint32_t)(absolute_granule >> 32 & 0xFFFFFFFF)); /* upper */ - put_32bitLE(buf+0x0E, stream_serial_number); /* for interleaved multi-streams */ - put_32bitLE(buf+0x12, page_sequence); - put_32bitLE(buf+0x16, checksum); /* 0 for now, until all data is written */ - put_8bit (buf+0x1A, segment_count); /* count of all lacing values */ + put_u32be(buf+0x00, 0x4F676753); /* capture pattern ("OggS") */ + put_u8 (buf+0x04, 0); /* stream structure version, fixed */ + put_u8 (buf+0x05, header_type_flag); /* bitflags (0: normal, continued = 1, first = 2, last = 4) */ + put_u32le(buf+0x06, (uint32_t)(absolute_granule >> 0 & 0xFFFFFFFF)); /* lower */ + put_u32le(buf+0x0A, (uint32_t)(absolute_granule >> 32 & 0xFFFFFFFF)); /* upper */ + put_u32le(buf+0x0E, stream_serial_number); /* for interleaved multi-streams */ + put_u32le(buf+0x12, page_sequence); + put_u32le(buf+0x16, checksum); /* 0 for now, until all data is written */ + put_u8 (buf+0x1A, segment_count); /* count of all lacing values */ /* segment table: size N in "lacing values" (ex. 0x20E=0xFF+FF+10; 0xFF=0xFF+00) */ page_done = 0x1B; @@ -400,12 +439,12 @@ static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int if (bytes > 0xFF) bytes = 0xFF; - put_8bit(buf+page_done, bytes); + put_u8(buf+page_done, bytes); page_done++; lacing_done += bytes; if (lacing_done == data_size && bytes == 0xFF) { - put_8bit(buf+page_done, 0x00); + put_u8(buf+page_done, 0x00); page_done++; } } @@ -416,14 +455,14 @@ static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int /* final checksum */ checksum = get_oggs_checksum(buf, page_done); - put_32bitLE(buf+0x16, checksum); + put_u32le(buf+0x16, checksum); return page_done; fail: return 0; } -static size_t make_opus_header(uint8_t * buf, int buf_size, opus_config *cfg) { +static size_t make_opus_header(uint8_t* buf, int buf_size, opus_config *cfg) { size_t header_size = 0x13; int mapping_family = 0; @@ -439,25 +478,25 @@ static size_t make_opus_header(uint8_t * buf, int buf_size, opus_config *cfg) { goto fail; } - put_32bitBE(buf+0x00, 0x4F707573); /* "Opus" header magic */ - put_32bitBE(buf+0x04, 0x48656164); /* "Head" header magic */ - put_8bit (buf+0x08, 1); /* version */ - put_8bit (buf+0x09, cfg->channels); - put_16bitLE(buf+0x0A, cfg->skip); - put_32bitLE(buf+0x0c, cfg->sample_rate); - put_16bitLE(buf+0x10, 0); /* output gain */ - put_8bit (buf+0x12, mapping_family); + put_u32be(buf+0x00, 0x4F707573); /* "Opus" header magic */ + put_u32be(buf+0x04, 0x48656164); /* "Head" header magic */ + put_u8 (buf+0x08, 1); /* version */ + put_u8 (buf+0x09, cfg->channels); + put_s16le(buf+0x0A, cfg->skip); + put_u32le(buf+0x0c, cfg->sample_rate); + put_u16le(buf+0x10, 0); /* output gain */ + put_u8 (buf+0x12, mapping_family); if (mapping_family > 0) { int i; /* internal mono/stereo streams (N mono/stereo streams form M channels) */ - put_8bit(buf+0x13, cfg->stream_count); + put_u8(buf+0x13, cfg->stream_count); /* joint stereo streams (rest would be mono, so 6ch can be 2ch+2ch+1ch+1ch = 2 coupled */ - put_8bit(buf+0x14, cfg->coupled_count); + put_u8(buf+0x14, cfg->coupled_count); /* mapping bits per channel? */ for (i = 0; i < cfg->channels; i++) { - put_8bit(buf+0x15+i, cfg->channel_mapping[i]); + put_u8(buf+0x15+i, cfg->channel_mapping[i]); } } @@ -466,9 +505,9 @@ fail: return 0; } -static size_t make_opus_comment(uint8_t * buf, int buf_size) { - const char * vendor_string = "vgmstream"; - const char * user_comment_0_string = "vgmstream Opus converter"; +static size_t make_opus_comment(uint8_t* buf, int buf_size) { + const char* vendor_string = "vgmstream"; + const char* user_comment_0_string = "vgmstream Opus converter"; size_t comment_size; int vendor_string_length, user_comment_0_length; @@ -481,20 +520,20 @@ static size_t make_opus_comment(uint8_t * buf, int buf_size) { goto fail; } - put_32bitBE(buf+0x00, 0x4F707573); /* "Opus" header magic */ - put_32bitBE(buf+0x04, 0x54616773); /* "Tags" header magic */ - put_32bitLE(buf+0x08, vendor_string_length); - memcpy (buf+0x0c, vendor_string, vendor_string_length); - put_32bitLE(buf+0x0c + vendor_string_length+0x00, 1); /* user_comment_list_length */ - put_32bitLE(buf+0x0c + vendor_string_length+0x04, user_comment_0_length); - memcpy (buf+0x0c + vendor_string_length+0x08, user_comment_0_string, user_comment_0_length); + put_u32be(buf+0x00, 0x4F707573); /* "Opus" header magic */ + put_u32be(buf+0x04, 0x54616773); /* "Tags" header magic */ + put_u32le(buf+0x08, vendor_string_length); + memcpy (buf+0x0c, vendor_string, vendor_string_length); + put_u32le(buf+0x0c + vendor_string_length+0x00, 1); /* user_comment_list_length */ + put_u32le(buf+0x0c + vendor_string_length+0x04, user_comment_0_length); + memcpy (buf+0x0c + vendor_string_length+0x08, user_comment_0_string, user_comment_0_length); return comment_size; fail: return 0; } -static size_t make_oggs_first(uint8_t * buf, int buf_size, opus_config *cfg) { +static size_t make_oggs_first(uint8_t* buf, int buf_size, opus_config* cfg) { int buf_done = 0; size_t bytes; @@ -516,10 +555,10 @@ fail: return 0; } -static size_t opus_get_packet_samples(const uint8_t * buf, int len) { +static size_t opus_get_packet_samples(const uint8_t* buf, int len) { return opus_packet_get_nb_frames(buf, len) * opus_packet_get_samples_per_frame(buf, 48000); } -static size_t opus_get_packet_samples_sf(STREAMFILE *sf, off_t offset) { +static size_t opus_get_packet_samples_sf(STREAMFILE* sf, off_t offset) { uint8_t buf[0x04]; /* at least 0x02 */ read_streamfile(buf, offset, sizeof(buf), sf); return opus_get_packet_samples(buf, sizeof(buf)); @@ -527,15 +566,19 @@ static size_t opus_get_packet_samples_sf(STREAMFILE *sf, off_t offset) { /************************** */ -static size_t get_xopus_packet_size(int packet, STREAMFILE * streamfile) { - /* XOPUS has a packet size table at the beginning, get size from there. - * Maybe should copy the table during setup to avoid IO, but all XOPUS are - * quite small so it isn't very noticeable. */ - return read_u16le(0x20 + packet*0x02, streamfile); +/* some formats store all frames in a table, rather than right before the frame */ +static size_t get_table_frame_size(opus_io_data* data, int frame) { + if (frame < 0 || frame >= data->table_count) { + VGM_LOG("OPUS: wrong requested frame %i, count=%i\n", frame, data->table_count); + return 0; + } + + //;VGM_LOG("OPUS: frame %i size=%x\n", frame, data->frame_table[frame]); + return data->frame_table[frame]; } -static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE *sf, opus_type_t type) { +static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE* sf, opus_type_t type) { size_t num_samples = 0; off_t end_offset = offset + stream_size; int packet = 0; @@ -555,6 +598,7 @@ static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFI skip_size = 0x08; break; case OPUS_UE4_v1: + case OPUS_FSB: data_size = read_u16le(offset, sf); skip_size = 0x02; break; @@ -567,10 +611,14 @@ static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFI data_size = read_u16be(offset, sf); skip_size = 0x02; break; + +#if 0 // needs data* for frame table, but num_samples should exist on header case OPUS_X: - data_size = get_xopus_packet_size(packet, sf); + case OPUS_WWISE: + data_size = get_table_frame_size(data, packet); skip_size = 0x00; break; +#endif default: return 0; } @@ -586,12 +634,12 @@ static size_t custom_opus_get_samples(off_t offset, size_t stream_size, STREAMFI return num_samples; } -size_t switch_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE *streamFile) { - return custom_opus_get_samples(offset, stream_size, streamFile, OPUS_SWITCH); +size_t switch_opus_get_samples(off_t offset, size_t stream_size, STREAMFILE* sf) { + return custom_opus_get_samples(offset, stream_size, sf, OPUS_SWITCH); } -static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE *sf, opus_type_t type) { +static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE* sf, opus_type_t type) { size_t skip_size, packet_samples = 0; switch(type) { @@ -599,6 +647,7 @@ static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE *sf, opus_t skip_size = 0x08; break; case OPUS_UE4_v1: + case OPUS_FSB: skip_size = 0x02; break; case OPUS_UE4_v2: @@ -609,6 +658,7 @@ static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE *sf, opus_t skip_size = 0x02; break; case OPUS_X: + case OPUS_WWISE: skip_size = 0x00; break; default: @@ -620,30 +670,33 @@ static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE *sf, opus_t /* encoder delay seems fixed to 1/8 of samples per frame, but may need more testing */ return packet_samples / 8; } -size_t switch_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) { - return custom_opus_get_encoder_delay(offset, streamFile, OPUS_SWITCH); +size_t switch_opus_get_encoder_delay(off_t offset, STREAMFILE* sf) { + return custom_opus_get_encoder_delay(offset, sf, OPUS_SWITCH); } -size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) { - return custom_opus_get_encoder_delay(offset, streamFile, get_ue4opus_version(streamFile, offset)); +size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE* sf) { + return custom_opus_get_encoder_delay(offset, sf, get_ue4opus_version(sf, offset)); } -size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) { - return custom_opus_get_encoder_delay(offset, streamFile, OPUS_EA); +size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE* sf) { + return custom_opus_get_encoder_delay(offset, sf, OPUS_EA); +} +size_t fsb_opus_get_encoder_delay(off_t offset, STREAMFILE* sf) { + return custom_opus_get_encoder_delay(offset, sf, OPUS_FSB); } /* ******************************************************* */ -/* actual FFmpeg only-code starts here (the above is universal enough but no point to compile) */ +/* actual FFmpeg only-code starts here (the above is universal enough but no point to compile separatedly) */ //#ifdef VGM_USE_FFMPEG -static ffmpeg_codec_data * init_ffmpeg_custom_opus_config(STREAMFILE *streamFile, off_t start_offset, size_t data_size, opus_config *cfg, opus_type_t type) { - ffmpeg_codec_data * ffmpeg_data = NULL; - STREAMFILE *temp_streamFile = NULL; +static ffmpeg_codec_data* init_ffmpeg_custom_opus_config(STREAMFILE* sf, off_t start_offset, size_t data_size, opus_config *cfg, opus_type_t type) { + ffmpeg_codec_data* ffmpeg_data = NULL; + STREAMFILE* temp_sf = NULL; - temp_streamFile = setup_opus_streamfile(streamFile, cfg, start_offset, data_size, type); - if (!temp_streamFile) goto fail; + temp_sf = setup_opus_streamfile(sf, cfg, start_offset, data_size, type); + if (!temp_sf) goto fail; - ffmpeg_data = init_ffmpeg_offset(temp_streamFile, 0x00,get_streamfile_size(temp_streamFile)); + ffmpeg_data = init_ffmpeg_offset(temp_sf, 0x00, get_streamfile_size(temp_sf)); if (!ffmpeg_data) goto fail; /* FFmpeg + libopus: skips samples, notifies skip in codecCtx->delay (not in stream->skip_samples) @@ -653,39 +706,58 @@ static ffmpeg_codec_data * init_ffmpeg_custom_opus_config(STREAMFILE *streamFile // ffmpeg_set_skip_samples(ffmpeg_data, skip); //} - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); return ffmpeg_data; fail: - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); return NULL; } -static ffmpeg_codec_data * init_ffmpeg_custom_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate, opus_type_t type) { + +static ffmpeg_codec_data* init_ffmpeg_custom_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate, opus_type_t type) { opus_config cfg = {0}; cfg.channels = channels; cfg.skip = skip; cfg.sample_rate = sample_rate; - return init_ffmpeg_custom_opus_config(streamFile, start_offset, data_size, &cfg, type); + return init_ffmpeg_custom_opus_config(sf, start_offset, data_size, &cfg, type); } -ffmpeg_codec_data * init_ffmpeg_switch_opus_config(STREAMFILE *streamFile, off_t start_offset, size_t data_size, opus_config* cfg) { - return init_ffmpeg_custom_opus_config(streamFile, start_offset, data_size, cfg, OPUS_SWITCH); -} -ffmpeg_codec_data * init_ffmpeg_switch_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { - return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_SWITCH); -} -ffmpeg_codec_data * init_ffmpeg_ue4_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { - return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, get_ue4opus_version(streamFile, start_offset)); -} -ffmpeg_codec_data * init_ffmpeg_ea_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { - return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_EA); -} -ffmpeg_codec_data * init_ffmpeg_x_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { - return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_X); +ffmpeg_codec_data* init_ffmpeg_custom_table_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip, int sample_rate, opus_type_t type) { + opus_config cfg = {0}; + cfg.channels = channels; + cfg.skip = skip; + cfg.sample_rate = sample_rate; + cfg.table_offset = table_offset; + cfg.table_count = table_count; + + return init_ffmpeg_custom_opus_config(sf, data_offset, data_size, &cfg, type); } -static opus_type_t get_ue4opus_version(STREAMFILE *sf, off_t offset) { + +ffmpeg_codec_data* init_ffmpeg_switch_opus_config(STREAMFILE* sf, off_t start_offset, size_t data_size, opus_config* cfg) { + return init_ffmpeg_custom_opus_config(sf, start_offset, data_size, cfg, OPUS_SWITCH); +} +ffmpeg_codec_data* init_ffmpeg_switch_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { + return init_ffmpeg_custom_opus(sf, start_offset, data_size, channels, skip, sample_rate, OPUS_SWITCH); +} +ffmpeg_codec_data* init_ffmpeg_ue4_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { + return init_ffmpeg_custom_opus(sf, start_offset, data_size, channels, skip, sample_rate, get_ue4opus_version(sf, start_offset)); +} +ffmpeg_codec_data* init_ffmpeg_ea_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { + return init_ffmpeg_custom_opus(sf, start_offset, data_size, channels, skip, sample_rate, OPUS_EA); +} +ffmpeg_codec_data* init_ffmpeg_x_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip) { + return init_ffmpeg_custom_table_opus(sf, table_offset, table_count, data_offset, data_size, channels, skip, 0, OPUS_X); +} +ffmpeg_codec_data* init_ffmpeg_fsb_opus(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) { + return init_ffmpeg_custom_opus(sf, start_offset, data_size, channels, skip, sample_rate, OPUS_FSB); +} +ffmpeg_codec_data* init_ffmpeg_wwise_opus(STREAMFILE* sf, off_t table_offset, int table_count, off_t data_offset, size_t data_size, int channels, int skip) { + return init_ffmpeg_custom_table_opus(sf, table_offset, table_count, data_offset, data_size, channels, skip, 0, OPUS_WWISE); +} + +static opus_type_t get_ue4opus_version(STREAMFILE* sf, off_t offset) { int read_samples, calc_samples; /* UE4OPUS v2 has packet samples right after packet size, check if data follows this */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c index c2466630c..627926ca0 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/g719_decoder.c @@ -1,18 +1,17 @@ #include "coding.h" +#include #ifdef VGM_USE_G719 -#ifdef __MACOSX__ -#include -#else -#include -#endif - #define G719_MAX_CODES ((1280/8)) /* in int16, so max frame size is (value/8)*2 (0xF0=common, 0x140=decoder max 2560b, rare) */ +struct g719_codec_data { + sample_t buffer[960]; + void *handle; +}; -g719_codec_data *init_g719(int channel_count, int frame_size) { +g719_codec_data* init_g719(int channel_count, int frame_size) { int i; - g719_codec_data *data = NULL; + g719_codec_data* data = NULL; if (frame_size / sizeof(int16_t) > G719_MAX_CODES) goto fail; @@ -39,10 +38,10 @@ fail: } -void decode_g719(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel) { - VGMSTREAMCHANNEL *ch = &vgmstream->ch[channel]; - g719_codec_data *data = vgmstream->codec_data; - g719_codec_data *ch_data = &data[channel]; +void decode_g719(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t samples_to_do, int channel) { + VGMSTREAMCHANNEL* ch = &vgmstream->ch[channel]; + g719_codec_data* data = vgmstream->codec_data; + g719_codec_data* ch_data = &data[channel]; int i; if (0 == vgmstream->samples_into_block) { @@ -58,7 +57,7 @@ void decode_g719(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int } -void reset_g719(g719_codec_data * data, int channels) { +void reset_g719(g719_codec_data* data, int channels) { int i; if (!data) return; @@ -67,7 +66,7 @@ void reset_g719(g719_codec_data * data, int channels) { } } -void free_g719(g719_codec_data * data, int channels) { +void free_g719(g719_codec_data* data, int channels) { int i; if (!data) return; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c index 64b6cd477..48205b405 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c @@ -1,21 +1,36 @@ #include "coding.h" +struct hca_codec_data { + STREAMFILE* streamfile; + clHCA_stInfo info; + + signed short* sample_buffer; + size_t samples_filled; + size_t samples_consumed; + size_t samples_to_discard; + + void* data_buffer; + + unsigned int current_block; + + void* handle; +}; + /* init a HCA stream; STREAMFILE will be duplicated for internal use. */ -hca_codec_data * init_hca(STREAMFILE *streamFile) { - char filename[PATH_LIMIT]; +hca_codec_data* init_hca(STREAMFILE* sf) { uint8_t header_buffer[0x2000]; /* hca header buffer data (probable max ~0x400) */ - hca_codec_data * data = NULL; /* vgmstream HCA context */ + hca_codec_data* data = NULL; /* vgmstream HCA context */ int header_size; int status; /* test header */ - if (read_streamfile(header_buffer, 0x00, 0x08, streamFile) != 0x08) + if (read_streamfile(header_buffer, 0x00, 0x08, sf) != 0x08) goto fail; header_size = clHCA_isOurFile(header_buffer, 0x08); if (header_size < 0 || header_size > 0x1000) goto fail; - if (read_streamfile(header_buffer, 0x00, header_size, streamFile) != header_size) + if (read_streamfile(header_buffer, 0x00, header_size, sf) != header_size) goto fail; /* init vgmstream context */ @@ -39,8 +54,7 @@ hca_codec_data * init_hca(STREAMFILE *streamFile) { if (!data->sample_buffer) goto fail; /* load streamfile for reads */ - get_streamfile_name(streamFile,filename, sizeof(filename)); - data->streamfile = open_streamfile(streamFile,filename); + data->streamfile = reopen_streamfile(sf, 0); if (!data->streamfile) goto fail; /* set initial values */ @@ -53,7 +67,7 @@ fail: return NULL; } -void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do) { +void decode_hca(hca_codec_data* data, sample_t* outbuf, int32_t samples_to_do) { int samples_done = 0; const unsigned int channels = data->info.channelCount; const unsigned int blockSize = data->info.blockSize; @@ -120,7 +134,7 @@ void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do) { } } -void reset_hca(hca_codec_data * data) { +void reset_hca(hca_codec_data* data) { if (!data) return; clHCA_DecodeReset(data->handle); @@ -130,7 +144,7 @@ void reset_hca(hca_codec_data * data) { data->samples_to_discard = data->info.encoderDelay; } -void loop_hca(hca_codec_data * data, int32_t num_sample) { +void loop_hca(hca_codec_data* data, int32_t num_sample) { if (!data) return; /* manually calc loop values if not set (should only happen with installed/forced looping, @@ -149,7 +163,7 @@ void loop_hca(hca_codec_data * data, int32_t num_sample) { data->samples_to_discard = data->info.loopStartDelay; } -void free_hca(hca_codec_data * data) { +void free_hca(hca_codec_data* data) { if (!data) return; close_streamfile(data->streamfile); @@ -175,7 +189,7 @@ void free_hca(hca_codec_data * data) { /* Test a number of frames if key decrypts correctly. * Returns score: <0: error/wrong, 0: unknown/silent file, >0: good (the closest to 1 the better). */ -int test_hca_key(hca_codec_data * data, unsigned long long keycode) { +int test_hca_key(hca_codec_data* data, unsigned long long keycode) { size_t test_frames = 0, current_frame = 0, blank_frames = 0; int total_score = 0, found_regular_frame = 0; const unsigned int blockSize = data->info.blockSize; @@ -242,3 +256,16 @@ int test_hca_key(hca_codec_data * data, unsigned long long keycode) { clHCA_DecodeReset(data->handle); return total_score; } + +void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode) { + clHCA_SetKey(data->handle, (unsigned long long)keycode); +} + +clHCA_stInfo* hca_get_info(hca_codec_data* data) { + return &data->info; +} + +STREAMFILE* hca_get_streamfile(hca_codec_data* data) { + if (!data) return NULL; + return data->streamfile; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c index 9f96d575f..4721bbdb3 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c @@ -285,6 +285,60 @@ static void mtf_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, if (*step_index > 88) *step_index=88; } +/* IMA table pre-modified like this: + for i=0..89 + adpcm = clamp(adpcm[i], 0x1fff) * 4; +*/ +static const int16_t mul_adpcm_table[89] = { + 28, 32, 36, 40, 44, 48, 52, 56, + 64, 68, 76, 84, 92, 100, 112, 124, + 136, 148, 164, 180, 200, 220, 240, 264, + 292, 320, 352, 388, 428, 472, 520, 572, + 628, 692, 760, 836, 920, 1012, 1116, 1228, + 1348, 1484, 1632, 1796, 1976, 2176, 2392, 2632, + 2896, 3184, 3504, 3852, 4240, 4664, 5128, 5644, + 6208, 6828, 7512, 8264, 9088, 9996, 10996, 12096, + 13308, 14640, 16104, 17712, 19484, 21432, 23576, 25936, + 28528, 31380, 32764, 32764, 32764, 32764, 32764, 32764, + 32764, 32764, 32764, 32764, 32764, 32764, 32764, 32764, + 32764 +}; + +/* step table is the same */ + +/* ops per code, generated like this: + for i=0..15 + v = 0x800 + if (i & 1) v = 0x1800 + if (i & 2) v += 0x2000 + if (i & 4) v += 0x4000 + if (i & 8) v = -v; + mul_op_table[i] = v; +*/ +static const int16_t mul_delta_table[16] = { + 0x0800, 0x1800, 0x2800, 0x3800, 0x4800, 0x5800, 0x6800, 0x7800, + -0x0800,-0x1800,-0x2800,-0x3800,-0x4800,-0x5800,-0x6800,-0x7800 +}; + + +/* Crystal Dynamics IMA, reverse engineered from the exe, also info: https://github.com/sephiroth99/MulDeMu */ +static void cd_ima_expand_nibble(uint8_t byte, int shift, int32_t* hist1, int32_t* index) { + int code, sample, step, delta; + + /* could do the above table calcs during decode too */ + code = (byte >> shift) & 0xf; + sample = *hist1; + step = mul_adpcm_table[*index]; + + delta = (int16_t)((step * mul_delta_table[code]) >> 16); + sample += delta; + + *hist1 = clamp16(sample); + *index += IMA_IndexTable[code]; + if (*index < 0) *index=0; + if (*index > 88) *index=88; +} + /* ************************************ */ /* DVI/IMA */ /* ************************************ */ @@ -1156,6 +1210,62 @@ void decode_h4m_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa stream->adpcm_step_index = step_index; } +/* test... */ +static inline int _clamp_s32(int value, int min, int max) { + if (value < min) + return min; + else if (value > max) + return max; + else + return value; +} + +/* Crystal Dynamics IMA. Original code uses mind-bending intrinsics, so this may not be fully accurate. + * Has another table with delta_table MMX combos, and uses header sample (first nibble is always 0). */ +void decode_cd_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + uint8_t frame[0x24] = {0}; + int i, frames_in, sample_pos = 0, block_samples, frame_size; + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + off_t frame_offset; + + /* external interleave (fixed size), mono */ + frame_size = 0x24; + block_samples = (frame_size - 0x4) * 2; + frames_in = first_sample / block_samples; + first_sample = first_sample % block_samples; + + frame_offset = stream->offset + frame_size * frames_in; + read_streamfile(frame, frame_offset, frame_size, stream->streamfile); /* ignore EOF errors */ + + /* normal header (hist+step+reserved), mono */ + if (first_sample == 0) { + hist1 = get_s16le(frame + 0x00); + step_index = get_u8(frame + 0x02); + step_index = _clamp_s32(step_index, 0, 88); + + /* write header sample (even samples per block, skips first nibble) */ + outbuf[sample_pos] = (short)(hist1); + sample_pos += channelspacing; + first_sample += 1; + samples_to_do -= 1; + } + + /* decode nibbles (layout: straight in mono) */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + int pos = 0x04 + (i/2); + int shift = (i&1 ? 4:0); /* low first, but first low nibble is skipped */ + + cd_ima_expand_nibble(frame[pos], shift, &hist1, &step_index); + outbuf[sample_pos] = (short)(hist1); + sample_pos += channelspacing; + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} + + /* ************************************************************* */ size_t ima_bytes_to_samples(size_t bytes, int channels) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/l5_555_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/l5_555_decoder.c index 0a12e8372..7b8a7aa4a 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/l5_555_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/l5_555_decoder.c @@ -8,48 +8,59 @@ static const int32_t l5_scales[32] = { 0x00130B82, 0x00182B83, 0x001EAC92, 0x0026EDB2, 0x00316777, 0x003EB2E6, 0x004F9232, 0x0064FBD1 }; -void decode_l5_555(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i=first_sample; - int32_t sample_count; +void decode_l5_555(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + uint8_t frame[0x12] = {0}; + off_t frame_offset; + int i, frames_in, sample_count = 0; + size_t bytes_per_frame, samples_per_frame; + uint16_t header; + uint8_t coef_index; - int framesin = first_sample/32; - - uint16_t header = (uint16_t)read_16bitLE(framesin*0x12+stream->offset,stream->streamfile); - int32_t pos_scale = l5_scales[(header>>5)&0x1f]; - int32_t neg_scale = l5_scales[header&0x1f]; - - int coef_index = (header >> 10) & 0x1f; int16_t hist1 = stream->adpcm_history1_16; int16_t hist2 = stream->adpcm_history2_16; int16_t hist3 = stream->adpcm_history3_16; - int32_t coef1 = stream->adpcm_coef_3by32[coef_index*3]; - int32_t coef2 = stream->adpcm_coef_3by32[coef_index*3+1]; - int32_t coef3 = stream->adpcm_coef_3by32[coef_index*3+2]; - /*printf("offset: %x\nscale: %d\nindex: %d (%lf,%lf)\nhist: %d %d\n", - (unsigned)stream->offset,scale,coef_index,coef1/2048.0,coef2/2048.0,hist1,hist2);*/ + int32_t coef1, coef2, coef3; + int32_t pos_scale, neg_scale; - first_sample = first_sample%32; + /* external interleave (fixed size), mono */ + bytes_per_frame = 0x12; + samples_per_frame = (bytes_per_frame - 0x02) * 2; /* always 32 */ + frames_in = first_sample / samples_per_frame; + first_sample = first_sample % samples_per_frame; - for (i=first_sample,sample_count=0; ioffset+2+i/2,stream->streamfile); - int nibble = (i&1? - get_low_nibble_signed(sample_byte): - get_high_nibble_signed(sample_byte)); - int32_t prediction = - -(hist1 * coef1 + hist2 * coef2 + hist3 * coef3); + /* parse frame header */ + frame_offset = stream->offset + bytes_per_frame * frames_in; + read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ + header = get_u32le(frame); + coef_index = (header >> 10) & 0x1f; + pos_scale = l5_scales[(header >> 5) & 0x1f]; + neg_scale = l5_scales[(header >> 0) & 0x1f]; - if (nibble >= 0) - { - outbuf[sample_count] = clamp16((prediction + nibble * pos_scale) >> 12); - } + coef1 = stream->adpcm_coef_3by32[coef_index * 3 + 0]; + coef2 = stream->adpcm_coef_3by32[coef_index * 3 + 1]; + coef3 = stream->adpcm_coef_3by32[coef_index * 3 + 2]; + + for (i = first_sample; i < first_sample + samples_to_do; i++) { + int32_t prediction, sample = 0; + uint8_t nibbles = frame[0x02 + i/2]; + + sample = (i&1) ? + get_low_nibble_signed(nibbles): + get_high_nibble_signed(nibbles); + prediction = -(hist1 * coef1 + hist2 * coef2 + hist3 * coef3); + + if (sample >= 0) + sample = (prediction + sample * pos_scale) >> 12; else - { - outbuf[sample_count] = clamp16((prediction + nibble * neg_scale) >> 12); - } + sample = (prediction + sample * neg_scale) >> 12; + sample = clamp16(sample); + + outbuf[sample_count] = sample; + sample_count += channelspacing; hist3 = hist2; hist2 = hist1; - hist1 = outbuf[sample_count]; + hist1 = sample; } stream->adpcm_history1_16 = hist1; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_bitreader.h b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_bitreader.h new file mode 100644 index 000000000..345f8aba4 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_bitreader.h @@ -0,0 +1,91 @@ +#ifndef _MPEG_BITREADER_H +#define _MPEG_BITREADER_H + +/* Simple bitreader for MPEG/standard bit format. + * Kept in .h since it's slightly faster (compiler can optimize statics better) */ + + +typedef struct { + uint8_t* buf; /* buffer to read/write */ + size_t bufsize; /* max size of the buffer */ + uint32_t b_off; /* current offset in bits inside the buffer */ +} bitstream_t; + +/* convenience util */ +static void init_bitstream(bitstream_t* b, uint8_t* buf, size_t bufsize) { + b->buf = buf; + b->bufsize = bufsize; + b->b_off = 0; +} + +/* Read bits (max 32) from buf and update the bit offset. Order is BE (MSF). */ +static int rb_bits(bitstream_t* ib, uint32_t bits, uint32_t* value) { + uint32_t shift, pos, val; + int i, bit_buf, bit_val; + + if (bits > 32 || ib->b_off + bits > ib->bufsize * 8) + goto fail; + + pos = ib->b_off / 8; /* byte offset */ + shift = ib->b_off % 8; /* bit sub-offset */ + + val = 0; + for (i = 0; i < bits; i++) { + bit_buf = (1U << (8-1-shift)) & 0xFF; /* bit check for buf */ + bit_val = (1U << (bits-1-i)); /* bit to set in value */ + + if (ib->buf[pos] & bit_buf) /* is bit in buf set? */ + val |= bit_val; /* set bit */ + + shift++; + if (shift % 8 == 0) { /* new byte starts */ + shift = 0; + pos++; + } + } + + *value = val; + ib->b_off += bits; + return 1; +fail: + VGM_LOG("BITREADER: read fail\n"); + *value = 0; + return 0; +} + +#ifndef BITSTREAM_READ_ONLY +/* Write bits (max 32) to buf and update the bit offset. Order is BE (MSF). */ +static int wb_bits(bitstream_t* ob, uint32_t bits, uint32_t value) { + uint32_t shift, pos; + int i, bit_val, bit_buf; + + if (bits > 32 || ob->b_off + bits > ob->bufsize * 8) + goto fail; + + pos = ob->b_off / 8; /* byte offset */ + shift = ob->b_off % 8; /* bit sub-offset */ + + for (i = 0; i < bits; i++) { + bit_val = (1U << (bits-1-i)); /* bit check for value */ + bit_buf = (1U << (8-1-shift)) & 0xFF; /* bit to set in buf */ + + if (value & bit_val) /* is bit in val set? */ + ob->buf[pos] |= bit_buf; /* set bit */ + else + ob->buf[pos] &= ~bit_buf; /* unset bit */ + + shift++; + if (shift % 8 == 0) { /* new byte starts */ + shift = 0; + pos++; + } + } + + ob->b_off += bits; + return 1; +fail: + return 0; +} +#endif + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c index 88eb711ff..f066ab4fd 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c @@ -3,12 +3,12 @@ #ifdef VGM_USE_MPEG /* init config and validate per type */ -int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) { +int mpeg_custom_setup_init_default(STREAMFILE* sf, off_t start_offset, mpeg_codec_data* data, coding_t* coding_type) { mpeg_frame_info info; /* get frame info at offset */ - if ( !mpeg_get_frame_info(streamFile, start_offset, &info)) + if ( !mpeg_get_frame_info(sf, start_offset, &info)) goto fail; switch(info.layer) { case 1: *coding_type = coding_MPEG_layer1; break; @@ -113,7 +113,7 @@ fail: /* writes data to the buffer and moves offsets */ -int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) { +int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream) { mpeg_custom_stream *ms = data->streams[num_stream]; mpeg_frame_info info; size_t current_data_size = 0; @@ -242,7 +242,7 @@ fail: * Gets info from a MPEG frame header at offset. Normally you would use mpg123_info but somehow * it's wrong at times (maybe because we use an ancient version) so here we do our thing. */ -static int mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info *info) { +static int mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info* info) { /* index tables */ static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 }; static const int layers[4] = { -1,3,2,1 }; @@ -313,12 +313,12 @@ static int mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info *info) { fail: return 0; } -int mpeg_get_frame_info(STREAMFILE *sf, off_t offset, mpeg_frame_info *info) { +int mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info) { uint32_t header = read_u32be(offset, sf); return mpeg_get_frame_info_h(header, info); } -size_t mpeg_get_samples(STREAMFILE *sf, off_t start_offset, size_t bytes) { +size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes) { off_t offset = start_offset; off_t max_offset = start_offset + bytes; int frames = 0, samples = 0, encoder_delay = 0, encoder_padding = 0; @@ -428,4 +428,48 @@ size_t mpeg_get_samples(STREAMFILE *sf, off_t start_offset, size_t bytes) { return samples; } + +/* variation of the above, for clean streams = no ID3/VBR headers + * (maybe should be fused in a single thing with config, API is kinda messy too) */ +int32_t mpeg_get_samples_clean(STREAMFILE *sf, off_t start, size_t size, size_t* p_loop_start, size_t* p_loop_end, int is_vbr) { + mpeg_frame_info info; + off_t offset = start; + int32_t num_samples = 0, loop_start = 0, loop_end = 0; + + if (!is_vbr) { + /* CBR = quick calcs */ + if (!mpeg_get_frame_info(sf, offset, &info)) + goto fail; + + num_samples = size / info.frame_size * info.frame_samples; + loop_start = *p_loop_start / info.frame_size * info.frame_samples; + loop_end = *p_loop_end / info.frame_size * info.frame_samples; + } + else { + /* VBR (or unknown) = count frames */ + while (offset < start + size) { + if (!mpeg_get_frame_info(sf, offset, &info)) + goto fail; + + if (*p_loop_start + start == offset) + loop_start = num_samples; + + num_samples += info.frame_samples; + offset += info.frame_size; + + if (*p_loop_end + start == offset) + loop_end = num_samples; + } + } + + + *p_loop_start = loop_start; + *p_loop_end = loop_end; + + return num_samples; +fail: + VGM_LOG("MPEG: sample reader failed at %lx\n", offset); + return 0; +} + #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_awc.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_awc.c index 7bfd542ec..fafcad365 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_awc.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_awc.c @@ -1,184 +1,184 @@ -#include "mpeg_decoder.h" - -#ifdef VGM_USE_MPEG - -/** - * AWC music uses blocks (sfx doesn't), the fun part being each channel has different num_samples/frames - * per block, so it's unsuitable for the normal "blocked" layout and parsed here instead. - * Channel data is separate within the block (first all frames of ch0, then ch1, etc), padded, and sometimes - * the last few frames of a channel are repeated in the new block (marked with the "discard samples" field). - */ - -/* block header size, algined/padded to 0x800 */ -static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, mpeg_codec_data *data) { - size_t header_size = 0; - int i; - int entries = data->config.channels; - int32_t (*read_32bit)(off_t,STREAMFILE*) = data->config.big_endian ? read_32bitBE : read_32bitLE; - - for (i = 0; i < entries; i++) { - header_size += 0x18; - header_size += read_32bit(offset + 0x18*i + 0x04, streamFile) * 0x04; /* entries in the table */ - } - - if (header_size % 0x800) /* padded */ - header_size += 0x800 - (header_size % 0x800); - - return header_size; -} - -/* find data that repeats in the beginning of a new block at the end of last block */ -static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t new_offset, off_t last_offset) { - uint8_t new_frame[0x1000];/* buffer to avoid fseek back and forth */ - mpeg_frame_info info; - off_t off; - int i; - - /* read block first frame */ - if ( !mpeg_get_frame_info(streamFile, new_offset, &info)) - goto fail; - if (info.frame_size > 0x1000) - goto fail; - if (read_streamfile(new_frame,new_offset, info.frame_size,streamFile) != info.frame_size) - goto fail; - - /* find the frame in last bytes of prev block */ - off = last_offset - 0x4000; /* typical max is 5-10 frames of ~0x200, no way to know exact size */ - while (off < last_offset) { - /* compare frame vs prev block data */ - for (i = 0; i < info.frame_size; i++) { - if ((uint8_t)read_8bit(off+i,streamFile) != new_frame[i]) - break; - } - - /* frame fully compared? */ - if (i == info.frame_size) - return last_offset - off; - else - off += i+1; - } - -fail: - VGM_LOG("AWC: can't find repeat size, new=0x%08x, last=0x%08x\n", (uint32_t)new_offset, (uint32_t)last_offset); - return 0; /* keep on truckin' */ -} - - -/* init config and validate */ -int mpeg_custom_setup_init_awc(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) { - mpeg_frame_info info; - int is_music; - - /* start_offset can point to a block header that always starts with 0 (music) or normal data (sfx) */ - is_music = read_32bitBE(start_offset, streamFile) == 0x00000000; - if (is_music) - start_offset += get_block_header_size(streamFile, start_offset, data); - - /* get frame info at offset */ - if ( !mpeg_get_frame_info(streamFile, start_offset, &info)) - goto fail; - switch(info.layer) { - case 1: *coding_type = coding_MPEG_layer1; break; - case 2: *coding_type = coding_MPEG_layer2; break; - case 3: *coding_type = coding_MPEG_layer3; break; - default: goto fail; - } - data->channels_per_frame = info.channels; - data->samples_per_frame = info.frame_samples; - - - /* extra checks */ - if (is_music) { - if (data->config.chunk_size <= 0) - goto fail; /* needs block size */ - } - - /* no big encoder delay added (for sfx they can start in less than ~300 samples) */ - - return 1; -fail: - return 0; -} - - -/* writes data to the buffer and moves offsets, parsing AWC blocks */ -int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) { - mpeg_custom_stream *ms = data->streams[num_stream]; - mpeg_frame_info info; - size_t current_data_size = 0, data_offset; - size_t file_size = get_streamfile_size(stream->streamfile); - int i; - - - /* blocked layout used for music */ - if (data->config.chunk_size) { - off_t last_offset = stream->offset; /* when block end needs to be known */ - - /* block ended for this channel, move to next block start */ - if (ms->current_size_count > 0 && ms->current_size_count == ms->current_size_target) { - //mpg123_open_feed(ms->m); //todo reset maybe needed? - - data_offset = stream->offset - stream->channel_start_offset; /* ignoring header */ - data_offset -= data_offset % data->config.chunk_size; /* start of current block */ - stream->offset = stream->channel_start_offset + data_offset + data->config.chunk_size; - - ms->current_size_count = 0; - ms->current_size_target = 0; - } - - /* just in case, shouldn't happen */ - if (stream->offset >= file_size) { - goto fail; - } - - /* block starts for this channel, point to mpeg data */ - if (ms->current_size_count == 0) { - int32_t (*read_32bit)(off_t,STREAMFILE*) = data->config.big_endian ? read_32bitBE : read_32bitLE; - off_t channel_offset = 0; - - /* block has a header with base info per channel and table per channel (see blocked_awc.c) */ - ms->decode_to_discard = read_32bit(stream->offset + 0x18*num_stream + 0x08, stream->streamfile); - ms->current_size_target = read_32bit(stream->offset + 0x18*num_stream + 0x14, stream->streamfile); - - for (i = 0; i < num_stream; i++) { /* num_stream serves as channel */ - size_t channel_size = read_32bit(stream->offset + 0x18*i + 0x14, stream->streamfile); - if (channel_size % 0x10) /* 32b aligned */ - channel_size += 0x10 - channel_size % 0x10; - - channel_offset += channel_size; - } - - //;VGM_ASSERT(ms->decode_to_discard > 0, "AWC: s%i discard of %x found at chunk %lx\n", num_stream, ms->decode_to_discard, stream->offset); - stream->offset += channel_offset + get_block_header_size(stream->streamfile, stream->offset, data); - - /* A new block may repeat frame bytes from prev block, and decode_to_discard has the number of repeated samples. - * However in RDR PS3 (not GTA5?) the value can be off (ie. discards 1152 but the repeat decodes to ~1152*4). - * I can't figure out why, so just find and skip the repeat data manually (probably better for mpg123 too) */ - if (ms->decode_to_discard) { - size_t repeat = get_repeated_data_size(stream->streamfile, stream->offset, last_offset); - if (repeat > 0) - ms->decode_to_discard = 0; - stream->offset += repeat; - ms->current_size_target -= repeat; - } - } - } - - - /* update frame */ - if ( !mpeg_get_frame_info(stream->streamfile, stream->offset, &info) ) - goto fail; - current_data_size = info.frame_size; - - ms->bytes_in_buffer = read_streamfile(ms->buffer,stream->offset, current_data_size, stream->streamfile); - - stream->offset += current_data_size; - - ms->current_size_count += current_data_size; - - return 1; -fail: - return 0; -} - -#endif +#include "mpeg_decoder.h" + +#ifdef VGM_USE_MPEG + +/** + * AWC music uses blocks (sfx doesn't), the fun part being each channel has different num_samples/frames + * per block, so it's unsuitable for the normal "blocked" layout and parsed here instead. + * Channel data is separate within the block (first all frames of ch0, then ch1, etc), padded, and sometimes + * the last few frames of a channel are repeated in the new block (marked with the "discard samples" field). + */ + +/* block header size, algined/padded to 0x800 */ +static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, mpeg_codec_data *data) { + size_t header_size = 0; + int i; + int entries = data->config.channels; + int32_t (*read_32bit)(off_t,STREAMFILE*) = data->config.big_endian ? read_32bitBE : read_32bitLE; + + for (i = 0; i < entries; i++) { + header_size += 0x18; + header_size += read_32bit(offset + 0x18*i + 0x04, streamFile) * 0x04; /* entries in the table */ + } + + if (header_size % 0x800) /* padded */ + header_size += 0x800 - (header_size % 0x800); + + return header_size; +} + +/* find data that repeats in the beginning of a new block at the end of last block */ +static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t new_offset, off_t last_offset) { + uint8_t new_frame[0x1000];/* buffer to avoid fseek back and forth */ + mpeg_frame_info info; + off_t off; + int i; + + /* read block first frame */ + if ( !mpeg_get_frame_info(streamFile, new_offset, &info)) + goto fail; + if (info.frame_size > 0x1000) + goto fail; + if (read_streamfile(new_frame,new_offset, info.frame_size,streamFile) != info.frame_size) + goto fail; + + /* find the frame in last bytes of prev block */ + off = last_offset - 0x4000; /* typical max is 5-10 frames of ~0x200, no way to know exact size */ + while (off < last_offset) { + /* compare frame vs prev block data */ + for (i = 0; i < info.frame_size; i++) { + if ((uint8_t)read_8bit(off+i,streamFile) != new_frame[i]) + break; + } + + /* frame fully compared? */ + if (i == info.frame_size) + return last_offset - off; + else + off += i+1; + } + +fail: + VGM_LOG("AWC: can't find repeat size, new=0x%08x, last=0x%08x\n", (uint32_t)new_offset, (uint32_t)last_offset); + return 0; /* keep on truckin' */ +} + + +/* init config and validate */ +int mpeg_custom_setup_init_awc(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) { + mpeg_frame_info info; + int is_music; + + /* start_offset can point to a block header that always starts with 0 (music) or normal data (sfx) */ + is_music = read_32bitBE(start_offset, streamFile) == 0x00000000; + if (is_music) + start_offset += get_block_header_size(streamFile, start_offset, data); + + /* get frame info at offset */ + if ( !mpeg_get_frame_info(streamFile, start_offset, &info)) + goto fail; + switch(info.layer) { + case 1: *coding_type = coding_MPEG_layer1; break; + case 2: *coding_type = coding_MPEG_layer2; break; + case 3: *coding_type = coding_MPEG_layer3; break; + default: goto fail; + } + data->channels_per_frame = info.channels; + data->samples_per_frame = info.frame_samples; + + + /* extra checks */ + if (is_music) { + if (data->config.chunk_size <= 0) + goto fail; /* needs block size */ + } + + /* no big encoder delay added (for sfx they can start in less than ~300 samples) */ + + return 1; +fail: + return 0; +} + + +/* writes data to the buffer and moves offsets, parsing AWC blocks */ +int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) { + mpeg_custom_stream *ms = data->streams[num_stream]; + mpeg_frame_info info; + size_t current_data_size = 0, data_offset; + size_t file_size = get_streamfile_size(stream->streamfile); + int i; + + + /* blocked layout used for music */ + if (data->config.chunk_size) { + off_t last_offset = stream->offset; /* when block end needs to be known */ + + /* block ended for this channel, move to next block start */ + if (ms->current_size_count > 0 && ms->current_size_count == ms->current_size_target) { + //mpg123_open_feed(ms->m); //todo reset maybe needed? + + data_offset = stream->offset - stream->channel_start_offset; /* ignoring header */ + data_offset -= data_offset % data->config.chunk_size; /* start of current block */ + stream->offset = stream->channel_start_offset + data_offset + data->config.chunk_size; + + ms->current_size_count = 0; + ms->current_size_target = 0; + } + + /* just in case, shouldn't happen */ + if (stream->offset >= file_size) { + goto fail; + } + + /* block starts for this channel, point to mpeg data */ + if (ms->current_size_count == 0) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = data->config.big_endian ? read_32bitBE : read_32bitLE; + off_t channel_offset = 0; + + /* block has a header with base info per channel and table per channel (see blocked_awc.c) */ + ms->decode_to_discard = read_32bit(stream->offset + 0x18*num_stream + 0x08, stream->streamfile); + ms->current_size_target = read_32bit(stream->offset + 0x18*num_stream + 0x14, stream->streamfile); + + for (i = 0; i < num_stream; i++) { /* num_stream serves as channel */ + size_t channel_size = read_32bit(stream->offset + 0x18*i + 0x14, stream->streamfile); + if (channel_size % 0x10) /* 32b aligned */ + channel_size += 0x10 - channel_size % 0x10; + + channel_offset += channel_size; + } + + //;VGM_ASSERT(ms->decode_to_discard > 0, "AWC: s%i discard of %x found at chunk %lx\n", num_stream, ms->decode_to_discard, stream->offset); + stream->offset += channel_offset + get_block_header_size(stream->streamfile, stream->offset, data); + + /* A new block may repeat frame bytes from prev block, and decode_to_discard has the number of repeated samples. + * However in RDR PS3 (not GTA5?) the value can be off (ie. discards 1152 but the repeat decodes to ~1152*4). + * I can't figure out why, so just find and skip the repeat data manually (probably better for mpg123 too) */ + if (ms->decode_to_discard) { + size_t repeat = get_repeated_data_size(stream->streamfile, stream->offset, last_offset); + if (repeat > 0) + ms->decode_to_discard = 0; + stream->offset += repeat; + ms->current_size_target -= repeat; + } + } + } + + + /* update frame */ + if ( !mpeg_get_frame_info(stream->streamfile, stream->offset, &info) ) + goto fail; + current_data_size = info.frame_size; + + ms->bytes_in_buffer = read_streamfile(ms->buffer,stream->offset, current_data_size, stream->streamfile); + + stream->offset += current_data_size; + + ms->current_size_count += current_data_size; + + return 1; +fail: + return 0; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c index ecebf4998..4f0f19b47 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ealayer3.c @@ -1,4 +1,5 @@ #include "mpeg_decoder.h" +#include "mpeg_bitreader.h" #ifdef VGM_USE_MPEG @@ -27,9 +28,9 @@ /* helper to pass around */ typedef struct { - STREAMFILE *sf; + STREAMFILE* sf; off_t offset; - vgm_bitstream is; + bitstream_t is; uint8_t buf[EALAYER3_MAX_EA_FRAME_SIZE]; int leftover_bits; } ealayer3_buffer_t; @@ -82,21 +83,21 @@ typedef struct { } ealayer3_frame_t; -static int ealayer3_parse_frame(mpeg_codec_data *data, int num_stream, ealayer3_buffer_t *ib, ealayer3_frame_t *eaf); -static int ealayer3_parse_frame_v1(ealayer3_buffer_t *ib, ealayer3_frame_t *eaf, int channels_per_frame, int is_v1b); -static int ealayer3_parse_frame_v2(ealayer3_buffer_t *ib, ealayer3_frame_t *eaf); -static int ealayer3_parse_frame_common(ealayer3_buffer_t *ib, ealayer3_frame_t *eaf); -static int ealayer3_rebuild_mpeg_frame(vgm_bitstream *is_0, ealayer3_frame_t *eaf_0, vgm_bitstream *is_1, ealayer3_frame_t *eaf_1, vgm_bitstream *os); -static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, ealayer3_frame_t *eaf); -static int ealayer3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start); -static int ealayer3_is_empty_frame_v2p(STREAMFILE *sf, off_t offset); +static int ealayer3_parse_frame(mpeg_codec_data* data, int num_stream, ealayer3_buffer_t* ib, ealayer3_frame_t* eaf); +static int ealayer3_parse_frame_v1(ealayer3_buffer_t* ib, ealayer3_frame_t* eaf, int channels_per_frame, int is_v1b); +static int ealayer3_parse_frame_v2(ealayer3_buffer_t* ib, ealayer3_frame_t* eaf); +static int ealayer3_parse_frame_common(ealayer3_buffer_t* ib, ealayer3_frame_t* eaf); +static int ealayer3_rebuild_mpeg_frame(bitstream_t* is_0, ealayer3_frame_t* eaf_0, bitstream_t* is_1, ealayer3_frame_t* eaf_1, bitstream_t* os); +static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream, ealayer3_frame_t* eaf); +static int ealayer3_skip_data(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream, int at_start); +static int ealayer3_is_empty_frame_v2p(STREAMFILE* sf, off_t offset); /* **************************************************************************** */ /* EXTERNAL API */ /* **************************************************************************** */ /* init codec from an EALayer3 frame */ -int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamfile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) { +int mpeg_custom_setup_init_ealayer3(STREAMFILE* streamfile, off_t start_offset, mpeg_codec_data* data, coding_t *coding_type) { int ok; ealayer3_buffer_t ib = {0}; ealayer3_frame_t eaf; @@ -130,7 +131,7 @@ fail: } /* writes data to the buffer and moves offsets, transforming EALayer3 frames */ -int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) { +int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream) { mpeg_custom_stream *ms = data->streams[num_stream]; int ok, granule_found; ealayer3_buffer_t ib_0 = {0}, ib_1 = {0}; @@ -148,7 +149,7 @@ int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data * /* read first frame/granule, or PCM-only frame (found alone at the end of SCHl streams) */ - { + { //;VGM_LOG("s%i: get granule0 at %lx\n", num_stream,stream->offset); if (!ealayer3_skip_data(stream, data, num_stream, 1)) goto fail; @@ -168,15 +169,15 @@ int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data * if (!ealayer3_skip_data(stream, data, num_stream, 0)) goto fail; - } + } - /* In EAL3 V2P sometimes there is a new SNS/SPS block between granules. Instead of trying to fix it here - * or in blocked layout (too complex/patchy), SNS/SPS uses a custom streamfile that simply removes all - * block headers, so this parser only sees raw EALayer3 data. It also discards samples, which confuses - * blocked layout calculations - * - * Similarly (as V2P decodes and writes 1 granule at a time) stream can end in granule0. Since mpg123 - * decodes in pairs we detect and feed it a fake end granule1, to get the last granule0's 576 samples. */ + /* In EAL3 V2P sometimes there is a new SNS/SPS block between granules. Instead of trying to fix it here + * or in blocked layout (too complex/patchy), SNS/SPS uses a custom streamfile that simply removes all + * block headers, so this parser only sees raw EALayer3 data. It also discards samples, which confuses + * blocked layout calculations + * + * Similarly (as V2P decodes and writes 1 granule at a time) stream can end in granule0. Since mpg123 + * decodes in pairs we detect and feed it a fake end granule1, to get the last granule0's 576 samples. */ granule_found = 0; /* get second frame/granule (MPEG1 only) if first granule was found */ @@ -219,10 +220,9 @@ int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data * /* rebuild EALayer3 frame to MPEG frame */ { - vgm_bitstream os = {0}; + bitstream_t os = {0}; - os.buf = ms->buffer; - os.bufsize = ms->buffer_size; + init_bitstream(&os, ms->buffer, ms->buffer_size); ok = ealayer3_rebuild_mpeg_frame(&ib_0.is, &eaf_0, &ib_1.is, &eaf_1, &os); if (!ok) goto fail; @@ -243,7 +243,7 @@ fail: /* Read at most N bits from streamfile. This makes more smaller reads (not good) but * allows exact frame size reading (good), as reading over a frame then reading back * is expensive in EALayer3 since it uses custom streamfiles. */ -static void fill_buf(ealayer3_buffer_t *ib, int bits) { +static void fill_buf(ealayer3_buffer_t* ib, int bits) { size_t read_size, bytes_size; int mod; @@ -273,7 +273,7 @@ static void fill_buf(ealayer3_buffer_t *ib, int bits) { ib->leftover_bits = (mod > 0 ? 8 - mod : 0); } -static int ealayer3_parse_frame(mpeg_codec_data *data, int num_stream, ealayer3_buffer_t *ib, ealayer3_frame_t *eaf) { +static int ealayer3_parse_frame(mpeg_codec_data* data, int num_stream, ealayer3_buffer_t* ib, ealayer3_frame_t* eaf) { int ok; /* We must pass this from state, as not all EA-frames have channel info. @@ -302,14 +302,14 @@ fail: /* read V1"a" (in SCHl) and V1"b" (in SNS) EALayer3 frame */ -static int ealayer3_parse_frame_v1(ealayer3_buffer_t *ib, ealayer3_frame_t *eaf, int channels_per_frame, int is_v1b) { +static int ealayer3_parse_frame_v1(ealayer3_buffer_t* ib, ealayer3_frame_t* eaf, int channels_per_frame, int is_v1b) { int ok; - vgm_bitstream *is = &ib->is; + bitstream_t* is = &ib->is; /* read EA-frame V1 header */ fill_buf(ib, 8); - r_bits(is, 8,&eaf->v1_pcm_flag); + rb_bits(is, 8,&eaf->v1_pcm_flag); eaf->pre_size = 1; /* 8b */ @@ -331,15 +331,15 @@ static int ealayer3_parse_frame_v1(ealayer3_buffer_t *ib, ealayer3_frame_t *eaf, /* check PCM block */ if (eaf->v1_pcm_flag == 0xEE) { fill_buf(ib, 32); - r_bits(is, 16,&eaf->v1_offset_samples); /* PCM block offset in the buffer */ - r_bits(is, 16,&eaf->v1_pcm_samples); /* number of PCM samples, can be 0 */ + rb_bits(is, 16,&eaf->v1_offset_samples); /* PCM block offset in the buffer */ + rb_bits(is, 16,&eaf->v1_pcm_samples); /* number of PCM samples, can be 0 */ eaf->pre_size += 2+2; /* 16b+16b */ eaf->pcm_size = (2*eaf->v1_pcm_samples * channels_per_frame); if (is_v1b) { /* extra 32b in v1b */ fill_buf(ib, 32); - r_bits(is, 32,&eaf->v1_pcm_unknown); + rb_bits(is, 32,&eaf->v1_pcm_unknown); eaf->pre_size += 4; /* 32b */ @@ -356,26 +356,26 @@ fail: /* read V2"PCM" and V2"Spike" EALayer3 frame (exactly the same but V2P seems to have bigger * PCM blocks and maybe smaller frames) */ -static int ealayer3_parse_frame_v2(ealayer3_buffer_t *ib, ealayer3_frame_t *eaf) { +static int ealayer3_parse_frame_v2(ealayer3_buffer_t* ib, ealayer3_frame_t* eaf) { int ok; - vgm_bitstream *is = &ib->is; + bitstream_t* is = &ib->is; /* read EA-frame V2 header */ fill_buf(ib, 16); - r_bits(is, 1,&eaf->v2_extended_flag); - r_bits(is, 1,&eaf->v2_stereo_flag); - r_bits(is, 2,&eaf->v2_reserved); - r_bits(is, 12,&eaf->v2_frame_size); + rb_bits(is, 1,&eaf->v2_extended_flag); + rb_bits(is, 1,&eaf->v2_stereo_flag); + rb_bits(is, 2,&eaf->v2_reserved); + rb_bits(is, 12,&eaf->v2_frame_size); eaf->pre_size = 2; /* 16b */ if (eaf->v2_extended_flag) { fill_buf(ib, 32); - r_bits(is, 2,&eaf->v2_offset_mode); - r_bits(is, 10,&eaf->v2_offset_samples); - r_bits(is, 10,&eaf->v2_pcm_samples); - r_bits(is, 10,&eaf->v2_common_size); + rb_bits(is, 2,&eaf->v2_offset_mode); + rb_bits(is, 10,&eaf->v2_offset_samples); + rb_bits(is, 10,&eaf->v2_pcm_samples); + rb_bits(is, 10,&eaf->v2_common_size); eaf->pre_size += 4; /* 32b */ } @@ -406,7 +406,7 @@ fail: /* parses an EALayer3 frame (common part) */ -static int ealayer3_parse_frame_common(ealayer3_buffer_t *ib, ealayer3_frame_t *eaf) { +static int ealayer3_parse_frame_common(ealayer3_buffer_t* ib, ealayer3_frame_t* eaf) { /* index tables */ static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 }; static const int sample_rates[4][4] = { /* [version_index][sample rate index] */ @@ -417,17 +417,17 @@ static int ealayer3_parse_frame_common(ealayer3_buffer_t *ib, ealayer3_frame_t * }; static const int channels[4] = { 2,2,2, 1 }; /* [channel_mode] */ - vgm_bitstream *is = &ib->is; + bitstream_t* is = &ib->is; off_t start_b_off = is->b_off; int i, fill_bits, others_2_bits; /* read main header */ fill_buf(ib, 8); - r_bits(is, 2,&eaf->version_index); - r_bits(is, 2,&eaf->sample_rate_index); - r_bits(is, 2,&eaf->channel_mode); - r_bits(is, 2,&eaf->mode_extension); + rb_bits(is, 2,&eaf->version_index); + rb_bits(is, 2,&eaf->sample_rate_index); + rb_bits(is, 2,&eaf->channel_mode); + rb_bits(is, 2,&eaf->mode_extension); /* check empty frame */ @@ -455,7 +455,7 @@ static int ealayer3_parse_frame_common(ealayer3_buffer_t *ib, ealayer3_frame_t * /* read side info */ fill_buf(ib, 1); - r_bits(is, 1,&eaf->granule_index); + rb_bits(is, 1,&eaf->granule_index); fill_bits = (eaf->mpeg1 && eaf->granule_index == 1) ? 4 * eaf->channels : 0; fill_bits = fill_bits + (12 + 32 + others_2_bits) * eaf->channels; @@ -463,15 +463,15 @@ static int ealayer3_parse_frame_common(ealayer3_buffer_t *ib, ealayer3_frame_t * if (eaf->mpeg1 && eaf->granule_index == 1) { for (i = 0; i < eaf->channels; i++) { - r_bits(is, 4,&eaf->scfsi[i]); + rb_bits(is, 4,&eaf->scfsi[i]); } } for (i = 0; i < eaf->channels; i++) { - r_bits(is, 12,&eaf->main_data_size[i]); + rb_bits(is, 12,&eaf->main_data_size[i]); /* divided in 47b=32+15 (MPEG1) or 51b=32+19 (MPEG2), arbitrarily */ - r_bits(is, 32,&eaf->others_1[i]); - r_bits(is, others_2_bits,&eaf->others_2[i]); + rb_bits(is, 32,&eaf->others_1[i]); + rb_bits(is, others_2_bits,&eaf->others_2[i]); } @@ -496,7 +496,7 @@ fail: /* converts an EALAYER3 frame to a standard MPEG frame from pre-parsed info */ -static int ealayer3_rebuild_mpeg_frame(vgm_bitstream *is_0, ealayer3_frame_t *eaf_0, vgm_bitstream *is_1, ealayer3_frame_t *eaf_1, vgm_bitstream* os) { +static int ealayer3_rebuild_mpeg_frame(bitstream_t* is_0, ealayer3_frame_t* eaf_0, bitstream_t* is_1, ealayer3_frame_t* eaf_1, bitstream_t* os) { uint32_t c = 0; int i, j; int expected_bitrate_index, expected_frame_size; @@ -537,55 +537,55 @@ static int ealayer3_rebuild_mpeg_frame(vgm_bitstream *is_0, ealayer3_frame_t *ea #endif /* write MPEG1/2 frame header */ - w_bits(os, 11, 0x7FF); /* sync */ - w_bits(os, 2, eaf_0->version_index); - w_bits(os, 2, 0x01); /* layer III index */ - w_bits(os, 1, 1); /* "no CRC" flag */ - w_bits(os, 4, expected_bitrate_index); - w_bits(os, 2, eaf_0->sample_rate_index); - w_bits(os, 1, 0); /* padding */ - w_bits(os, 1, 0); /* private */ - w_bits(os, 2, eaf_0->channel_mode); - w_bits(os, 2, eaf_0->mode_extension); - w_bits(os, 1, 1); /* copyrighted */ - w_bits(os, 1, 1); /* original */ - w_bits(os, 2, 0); /* emphasis */ + wb_bits(os, 11, 0x7FF); /* sync */ + wb_bits(os, 2, eaf_0->version_index); + wb_bits(os, 2, 0x01); /* layer III index */ + wb_bits(os, 1, 1); /* "no CRC" flag */ + wb_bits(os, 4, expected_bitrate_index); + wb_bits(os, 2, eaf_0->sample_rate_index); + wb_bits(os, 1, 0); /* padding */ + wb_bits(os, 1, 0); /* private */ + wb_bits(os, 2, eaf_0->channel_mode); + wb_bits(os, 2, eaf_0->mode_extension); + wb_bits(os, 1, 1); /* copyrighted */ + wb_bits(os, 1, 1); /* original */ + wb_bits(os, 2, 0); /* emphasis */ if (eaf_0->mpeg1) { int private_bits = (eaf_0->channels==1 ? 5 : 3); /* write MPEG1 side info */ - w_bits(os, 9, 0); /* main data start (no bit reservoir) */ - w_bits(os, private_bits, 0); + wb_bits(os, 9, 0); /* main data start (no bit reservoir) */ + wb_bits(os, private_bits, 0); for (i = 0; i < eaf_1->channels; i++) { - w_bits(os, 4, eaf_1->scfsi[i]); /* saved in granule1 only */ + wb_bits(os, 4, eaf_1->scfsi[i]); /* saved in granule1 only */ } for (i = 0; i < eaf_0->channels; i++) { /* granule0 */ - w_bits(os, 12, eaf_0->main_data_size[i]); - w_bits(os, 32, eaf_0->others_1[i]); - w_bits(os, 47-32, eaf_0->others_2[i]); + wb_bits(os, 12, eaf_0->main_data_size[i]); + wb_bits(os, 32, eaf_0->others_1[i]); + wb_bits(os, 47-32, eaf_0->others_2[i]); } for (i = 0; i < eaf_1->channels; i++) { /* granule1 */ - w_bits(os, 12, eaf_1->main_data_size[i]); - w_bits(os, 32, eaf_1->others_1[i]); - w_bits(os, 47-32, eaf_1->others_2[i]); + wb_bits(os, 12, eaf_1->main_data_size[i]); + wb_bits(os, 32, eaf_1->others_1[i]); + wb_bits(os, 47-32, eaf_1->others_2[i]); } /* write MPEG1 main data */ is_0->b_off = eaf_0->data_offset_b; for (i = 0; i < eaf_0->channels; i++) { /* granule0 */ for (j = 0; j < eaf_0->main_data_size[i]; j++) { - r_bits(is_0, 1, &c); - w_bits(os, 1, c); + rb_bits(is_0, 1, &c); + wb_bits(os, 1, c); } } is_1->b_off = eaf_1->data_offset_b; for (i = 0; i < eaf_1->channels; i++) { /* granule1 */ for (j = 0; j < eaf_1->main_data_size[i]; j++) { - r_bits(is_1, 1, &c); - w_bits(os, 1, c); + rb_bits(is_1, 1, &c); + wb_bits(os, 1, c); } } } @@ -593,21 +593,21 @@ static int ealayer3_rebuild_mpeg_frame(vgm_bitstream *is_0, ealayer3_frame_t *ea int private_bits = (eaf_0->channels==1 ? 1 : 2); /* write MPEG2 side info */ - w_bits(os, 8, 0); /* main data start (no bit reservoir) */ - w_bits(os, private_bits, 0); + wb_bits(os, 8, 0); /* main data start (no bit reservoir) */ + wb_bits(os, private_bits, 0); for (i = 0; i < eaf_0->channels; i++) { - w_bits(os, 12, eaf_0->main_data_size[i]); - w_bits(os, 32, eaf_0->others_1[i]); - w_bits(os, 51-32, eaf_0->others_2[i]); + wb_bits(os, 12, eaf_0->main_data_size[i]); + wb_bits(os, 32, eaf_0->others_1[i]); + wb_bits(os, 51-32, eaf_0->others_2[i]); } /* write MPEG2 main data */ is_0->b_off = eaf_0->data_offset_b; for (i = 0; i < eaf_0->channels; i++) { for (j = 0; j < eaf_0->main_data_size[i]; j++) { - r_bits(is_0, 1, &c); - w_bits(os, 1, c); + rb_bits(is_0, 1, &c); + wb_bits(os, 1, c); } } } @@ -615,7 +615,7 @@ static int ealayer3_rebuild_mpeg_frame(vgm_bitstream *is_0, ealayer3_frame_t *ea /* align to closest 8b */ if (os->b_off % 8) { int align_bits = 8 - (os->b_off % 8); - w_bits(os, align_bits, 0); + wb_bits(os, align_bits, 0); } @@ -636,7 +636,7 @@ fail: return 0; } -static void ealayer3_copy_pcm_block(uint8_t* outbuf, off_t pcm_offset, int pcm_number, int channels_per_frame, int is_packed, STREAMFILE *sf) { +static void ealayer3_copy_pcm_block(uint8_t* outbuf, off_t pcm_offset, int pcm_number, int channels_per_frame, int is_packed, STREAMFILE* sf) { int i, ch; uint8_t pcm_block[1152 * 2 * 2]; /* assumed max: 1 MPEG frame samples * 16b * max channels */ size_t pcm_size = pcm_number * 2 * channels_per_frame; @@ -687,7 +687,7 @@ static void ealayer3_copy_pcm_block(uint8_t* outbuf, off_t pcm_offset, int pcm_n /* write PCM block directly to sample buffer and setup decode discard (EALayer3 seems to use this as a prefetch of sorts). * Seems to alter decoded sample buffer to handle encoder delay/padding in a twisted way. */ -static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, ealayer3_frame_t *eaf) { +static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream, ealayer3_frame_t* eaf) { mpeg_custom_stream *ms = data->streams[num_stream]; int channels_per_frame = ms->channels_per_frame; size_t bytes_filled; @@ -817,7 +817,7 @@ fail: * * EALayer3 v1 in SCHl uses external offsets and 1ch multichannel instead. */ -static int ealayer3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start) { +static int ealayer3_skip_data(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data, int num_stream, int at_start) { int ok, i; ealayer3_buffer_t ib = {0}; ealayer3_frame_t eaf; @@ -845,7 +845,7 @@ fail: return 0; } -static int ealayer3_is_empty_frame_v2p(STREAMFILE *sf, off_t offset) { +static int ealayer3_is_empty_frame_v2p(STREAMFILE* sf, off_t offset) { /* V2P frame header should contain a valid frame size (lower 12b) */ uint16_t v2_header = read_u16be(offset, sf); return (v2_header % 0xFFF) == 0 || v2_header == 0xFFFF; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c index 0a19a050e..cf421c63f 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c @@ -3,11 +3,7 @@ #include "../vgmstream.h" #ifdef VGM_USE_MPEG -#ifdef __MACOSX__ #include -#else -#include -#endif #include "mpeg_decoder.h" @@ -509,8 +505,7 @@ void free_mpeg(mpeg_codec_data *data) { } /* seeks stream to 0 */ -void reset_mpeg(VGMSTREAM *vgmstream) { - mpeg_codec_data *data = vgmstream->codec_data; +void reset_mpeg(mpeg_codec_data* data) { if (!data) return; flush_mpeg(data); @@ -597,6 +592,10 @@ void flush_mpeg(mpeg_codec_data * data) { data->buffer_used = 0; } +int mpeg_get_sample_rate(mpeg_codec_data* data) { + return data->sample_rate_per_frame; +} + long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data *data) { /* if not found just return 0 and expect to fail (if used for num_samples) */ if (!data->custom) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h index a8f117335..af8293c6f 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.h @@ -1,34 +1,87 @@ -#ifndef _MPEG_DECODER_H_ -#define _MPEG_DECODER_H_ - -#include "../vgmstream.h" -#include "../coding/coding.h" - -/* used by mpeg_decoder.c, but scattered in other .c files */ -#ifdef VGM_USE_MPEG -typedef struct { - int version; - int layer; - int bit_rate; - int sample_rate; - int frame_samples; - int frame_size; /* bytes */ - int channels; -} mpeg_frame_info; - -int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * info); - -int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); -int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); -int mpeg_custom_setup_init_awc(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); -int mpeg_custom_setup_init_eamp3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); - -int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); -int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); -int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); -int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); -int mpeg_custom_parse_frame_eamp3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); - -#endif/* VGM_USE_MPEG */ - -#endif/*_MPEG_DECODER_H_ */ +#ifndef _MPEG_DECODER_H_ +#define _MPEG_DECODER_H_ + +#include "../vgmstream.h" +#include "../coding/coding.h" + +/* used by mpeg_decoder.c, but scattered in other .c files */ +#ifdef VGM_USE_MPEG +typedef struct { + int version; + int layer; + int bit_rate; + int sample_rate; + int frame_samples; + int frame_size; /* bytes */ + int channels; +} mpeg_frame_info; + +/* represents a single MPEG stream */ +typedef struct { + /* per stream as sometimes mpg123 must be fed in passes if data is big enough (ex. EALayer3 multichannel) */ + uint8_t *buffer; /* raw data buffer */ + size_t buffer_size; + size_t bytes_in_buffer; + int buffer_full; /* raw buffer has been filled */ + int buffer_used; /* raw buffer has been fed to the decoder */ + mpg123_handle *m; /* MPEG decoder */ + + uint8_t *output_buffer; /* decoded samples from this stream (in bytes for mpg123) */ + size_t output_buffer_size; + size_t samples_filled; /* data in the buffer (in samples) */ + size_t samples_used; /* data extracted from the buffer */ + + size_t current_size_count; /* data read (if the parser needs to know) */ + size_t current_size_target; /* max data, until something happens */ + size_t decode_to_discard; /* discard from this stream only (for EALayer3 or AWC) */ + + int channels_per_frame; /* for rare cases that streams don't share this */ +} mpeg_custom_stream; + +struct mpeg_codec_data { + /* regular/single MPEG internals */ + uint8_t *buffer; /* raw data buffer */ + size_t buffer_size; + size_t bytes_in_buffer; + int buffer_full; /* raw buffer has been filled */ + int buffer_used; /* raw buffer has been fed to the decoder */ + mpg123_handle *m; /* MPEG decoder */ + struct mpg123_frameinfo mi; /* start info, so it's available even when resetting */ + + /* for internal use */ + int channels_per_frame; + int samples_per_frame; + /* for some calcs */ + int bitrate_per_frame; + int sample_rate_per_frame; + + /* custom MPEG internals */ + int custom; /* flag */ + mpeg_custom_t type; /* mpeg subtype */ + mpeg_custom_config config; /* config depending on the mode */ + + size_t default_buffer_size; + mpeg_custom_stream **streams; /* array of MPEG streams (ex. 2ch+2ch) */ + size_t streams_size; + + size_t skip_samples; /* base encoder delay */ + size_t samples_to_discard; /* for custom mpeg looping */ + +}; + +int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * info); + +int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); +int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); +int mpeg_custom_setup_init_awc(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); +int mpeg_custom_setup_init_eamp3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); + +int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); +int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); +int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); +int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); +int mpeg_custom_parse_frame_eamp3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); + +#endif/* VGM_USE_MPEG */ + +#endif/*_MPEG_DECODER_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/msadpcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/msadpcm_decoder.c index f76f2d0d3..3f1899db3 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/msadpcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/msadpcm_decoder.c @@ -31,8 +31,8 @@ void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t fir streamfile = ch1->streamfile; /* external interleave (variable size), stereo */ - bytes_per_frame = get_vgmstream_frame_size(vgmstream); - samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + bytes_per_frame = vgmstream->frame_size; + samples_per_frame = (vgmstream->frame_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2; frames_in = first_sample / samples_per_frame; first_sample = first_sample % samples_per_frame; @@ -104,8 +104,8 @@ void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample_t * outbuf, int channelsp off_t frame_offset; /* external interleave (variable size), mono */ - bytes_per_frame = get_vgmstream_frame_size(vgmstream); - samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + bytes_per_frame = vgmstream->frame_size; + samples_per_frame = (vgmstream->frame_size - 0x07)*2 + 2; frames_in = first_sample / samples_per_frame; first_sample = first_sample % samples_per_frame; @@ -167,8 +167,8 @@ void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample_t * outbuf, int channelspac off_t frame_offset; /* external interleave (variable size), mono */ - bytes_per_frame = get_vgmstream_frame_size(vgmstream); - samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + bytes_per_frame = vgmstream->frame_size; + samples_per_frame = (vgmstream->frame_size - 0x07)*2 + 2; frames_in = first_sample / samples_per_frame; first_sample = first_sample % samples_per_frame; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.c index 62fb87866..0e7beabf4 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.c @@ -1,4 +1,33 @@ -/* originally from nwatowav.cc 2007.7.28 version, which read: */ +/* Originally from nwatowav.cc (2007.7.28 version) by jagarl. + * - http://www.creator.club.ne.jp/~jagarl/ + * + * Converted to .c by hcs (redone as a lib without RIFF/main handling), some cleanup by bnnm. + * + * nwa format (abridged from the original) + * NWA Header + * data offset index + * data block<0> + * data block<1> + * ... + * data block + * + * - NWA header: 0x2c with nwa info (channels, compression level, etc), no magic number + * - data offset index: pointers to data blocks + * - data block: variable sized DPCM blocks to fixed size PCM (a,b,c compresses to (a),b-a,c-b), + * DPCM codes use variable bits. Usually for 16-bit PCM ends ups using 6-8 bits. + * - Block format: + * - mono: initial PCM (8 or 16-bit) then bitstream + * - stereo: initial PCM for left + right channel then bitstream + * Differential accuracy isn't high so initial PCM is used to correct data in each block (?) + * - bitstream: Roughly each code has an 'exponent' (2 bits) + 'mantissa' (variable bits). + * Depending on compression level + type it configures final shift value and matissa bits. + * There is a run length encoding mode in some cases (Tomoyo After voice files). + * Bitstream bytes follow little endian. + * (some examples here in the original, see decoder). + * + * Original copyright: + */ + /* * Copyright 2001-2007 jagarl / Kazunori Ueno * All Rights Reserved. @@ -33,319 +62,380 @@ #include #include "nwa_decoder.h" -/* can serve up 8 bits at a time */ -static int -getbits (STREAMFILE *file, off_t *offset, int *shift, int bits) -{ - int ret; - if (*shift > 8) - { - ++*offset; - *shift -= 8; + +//NWAInfo::UseRunLength +static int is_use_runlength(NWAData* nwa) { + if (nwa->channels == 2 && nwa->bps == 16 && nwa->complevel == 2) { + return 0; /* sw2 */ } - ret = read_16bitLE(*offset,file) >> *shift; - *shift += bits; - return ret & ((1 << bits) - 1); /* mask */ + + if (nwa->complevel == 5) { + if (nwa->channels == 2) + return 0; // BGM*.nwa in Little Busters! + return 1; // Tomoyo After (.nwk koe file) + } + + return 0; } -NWAData * -open_nwa (STREAMFILE * streamFile, const char *filename) -{ +NWAData* nwalib_open(STREAMFILE* sf) { + uint8_t header[0x2c] = {0}; int i; - NWAData * const nwa = malloc(sizeof(NWAData)); + NWAData* const nwa = malloc(sizeof(NWAData)); if (!nwa) goto fail; - nwa->channels = read_16bitLE(0x00,streamFile); - nwa->bps = read_16bitLE(0x02,streamFile); - nwa->freq = read_32bitLE(0x04,streamFile); - nwa->complevel = read_32bitLE(0x08,streamFile); - nwa->blocks = read_32bitLE(0x10,streamFile); - nwa->datasize = read_32bitLE(0x14,streamFile); - nwa->compdatasize = read_32bitLE(0x18,streamFile); - nwa->samplecount = read_32bitLE(0x1c,streamFile); - nwa->blocksize = read_32bitLE(0x20,streamFile); - nwa->restsize = read_32bitLE(0x24,streamFile); + //NWAData::ReadHeader + + read_streamfile(header, 0x00, sizeof(header), sf); + nwa->channels = get_s16le(header+0x00); + nwa->bps = get_s16le(header+0x02); + nwa->freq = get_s32le(header+0x04); + nwa->complevel = get_s32le(header+0x08); + nwa->dummy = get_s32le(header+0x0c); + nwa->blocks = get_s32le(header+0x10); + nwa->datasize = get_s32le(header+0x14); + nwa->compdatasize = get_s32le(header+0x18); + nwa->samplecount = get_s32le(header+0x1c); + nwa->blocksize = get_s32le(header+0x20); + nwa->restsize = get_s32le(header+0x24); + nwa->dummy2 = get_s32le(header+0x28); + nwa->offsets = NULL; - nwa->buffer = NULL; - nwa->buffer_readpos = NULL; - nwa->file = NULL; + nwa->outdata = NULL; + nwa->outdata_readpos = NULL; + nwa->tmpdata = NULL; + nwa->filesize = get_streamfile_size(sf); - /* PCM not handled here */ - if (nwa->complevel < 0 || nwa->complevel > 5) goto fail; - if (nwa->channels != 1 && nwa->channels != 2) goto fail; + if (nwa->blocks <= 0 || nwa->blocks > 1000000) + /* 1時間を超える曲ってのはないでしょ*/ //surely there won't be songs over 1 hour + goto fail; - if (nwa->bps != 8 && nwa->bps != 16) goto fail; + // NWAData::CheckHeader: - if (nwa->blocks <= 0) goto fail; + if (nwa->channels != 1 && nwa->channels != 2) + goto fail; - if (nwa->compdatasize == 0 - || get_streamfile_size(streamFile) != nwa->compdatasize) goto fail; + if (nwa->bps != 8 && nwa->bps != 16) + goto fail; - if (nwa->datasize != nwa->samplecount * (nwa->bps/8)) goto fail; + // (PCM not handled) - if (nwa->samplecount != - (nwa->blocks-1) * nwa->blocksize + nwa->restsize) goto fail; + if (nwa->complevel < 0 || nwa->complevel > 5) + goto fail; - nwa->offsets = malloc(sizeof(off_t)*nwa->blocks); + if (nwa->filesize != nwa->compdatasize) + goto fail; + + + if (nwa->datasize != nwa->samplecount * (nwa->bps / 8)) + goto fail; + + if (nwa->samplecount != (nwa->blocks - 1) * nwa->blocksize + nwa->restsize) + goto fail; + + /* offset index 読み込み */ //read offset index + nwa->offsets = malloc(sizeof(off_t) * nwa->blocks); if (!nwa->offsets) goto fail; - for (i = 0; i < nwa->blocks; i++) - { - int32_t o = read_32bitLE(0x2c+i*4,streamFile); + for (i = 0; i < nwa->blocks; i++) { + int32_t o = read_s32le(0x2c + i*4, sf); if (o < 0) goto fail; nwa->offsets[i] = o; } - if (nwa->offsets[nwa->blocks-1] >= nwa->compdatasize) goto fail; - - if (nwa->restsize > nwa->blocksize) nwa->buffer = - malloc(sizeof(sample)*nwa->restsize); - else nwa->buffer = - malloc(sizeof(sample)*nwa->blocksize); - if (nwa->buffer == NULL) goto fail; - - nwa->buffer_readpos = nwa->buffer; - - nwa->samples_in_buffer = 0; + if (nwa->offsets[nwa->blocks-1] >= nwa->compdatasize) + goto fail; + nwa->use_runlength = is_use_runlength(nwa); nwa->curblock = 0; - /* if we got this far, it's probably safe */ - nwa->file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!nwa->file) goto fail; + + //extra + if (nwa->restsize > nwa->blocksize) { + nwa->outdata = malloc(sizeof(int16_t) * nwa->restsize); + } + else { + nwa->outdata = malloc(sizeof(int16_t) * nwa->blocksize); + } + if (!nwa->outdata) + goto fail; + + /* これ以上の大きさはないだろう、、、 */ //probably not over this size + nwa->tmpdata = malloc(sizeof(uint8_t) * nwa->blocksize * (nwa->bps / 8) * 2); + if (!nwa->tmpdata) + goto fail; + + nwa->outdata_readpos = nwa->outdata; + nwa->samples_in_buffer = 0; return nwa; fail: - if (nwa) - { - close_nwa(nwa); - } - + nwalib_close(nwa); return NULL; } -void -close_nwa(NWAData * nwa) -{ +void nwalib_close(NWAData * nwa) { if (!nwa) return; - if (nwa->offsets) - free (nwa->offsets); - nwa->offsets = NULL; - if (nwa->buffer) - free (nwa->buffer); - nwa->buffer = NULL; - if (nwa->file) - close_streamfile (nwa->file); - nwa->file = NULL; + free(nwa->offsets); + free(nwa->outdata); + free(nwa->tmpdata); free(nwa); } -void -reset_nwa(NWAData *nwa) -{ +//NWAData::Rewind +void nwalib_reset(NWAData* nwa) { nwa->curblock = 0; - nwa->buffer_readpos = nwa->buffer; + nwa->outdata_readpos = nwa->outdata; nwa->samples_in_buffer = 0; } -static int use_runlength(NWAData *nwa) -{ - if (nwa->channels == 2 && nwa->bps == 16 && nwa->complevel == 2) - { - /* sw2 */ - return 0; +// can serve up 8 bits at a time +static int getbits(const uint8_t** p_data, int* shift, int bits) { + int ret; + if (*shift > 8) { + (*p_data)++; + *shift -= 8; } - if (nwa->complevel == 5) - { - if (nwa->channels == 2) return 0; /* BGM*.nwa in Little Busters! */ - return 1; /* Tomoyo After (.nwk koe file) */ + + ret = get_s16le(*p_data) >> *shift; + *shift += bits; + return ret & ((1 << bits) - 1); /* mask */ +} + +// NWADecode +static void decode_block(NWAData* nwa, const uint8_t* data, int outdatasize) { + sample d[2]; + int i; + int shift = 0; + + int dsize = outdatasize / (nwa->bps / 8); + int flip_flag = 0; /* stereo 用 */ //for stereo + int runlength = 0; + + /* 最初のデータを読み込む */ //read initial data + for (i = 0; i < nwa->channels; i++) { + if (nwa->bps == 8) { + d[i] = get_s8(data); + data += 1; + } + else { /* nwa->bps == 16 */ + d[i] = get_s16le(data); + data += 2; + } } + + for (i = 0; i < dsize; i++) { + if (runlength == 0) { /* コピーループ中でないならデータ読み込み */ //read data if not in the copy loop + int type = getbits(&data, &shift, 3); + + /* type により分岐:0, 1-6, 7 */ //fork depending on type + if (type == 7) { + /* 7 : 大きな差分 */ //big diff + /* RunLength() 有効時(CompLevel==5, 音声ファイル) では無効 */ //invalid when using RLE (comp=5, voice file) + if (getbits(&data, &shift, 1) == 1) { + d[flip_flag] = 0; /* 未使用 */ //unused + } + else { + int BITS, SHIFT; + if (nwa->complevel >= 3) { + BITS = 8; + SHIFT = 9; + } + else { + BITS = 8 - nwa->complevel; + SHIFT = 2 + 7 + nwa->complevel; + } + + { + const int MASK1 = (1 << (BITS - 1)); + const int MASK2 = (1 << (BITS - 1)) - 1; + int b = getbits(&data, &shift, BITS); + if (b & MASK1) + d[flip_flag] -= (b & MASK2) << SHIFT; + else + d[flip_flag] += (b & MASK2) << SHIFT; + } + } + } + else if (type != 0) { + /* 1-6 : 通常の差分 */ //normal diff + int BITS, SHIFT; + if (nwa->complevel >= 3) { + BITS = nwa->complevel + 3; + SHIFT = 1 + type; + } + else { + BITS = 5 - nwa->complevel; + SHIFT = 2 + type + nwa->complevel; + } + { + const int MASK1 = (1 << (BITS - 1)); + const int MASK2 = (1 << (BITS - 1)) - 1; + int b = getbits(&data, &shift, BITS); + if (b & MASK1) + d[flip_flag] -= (b & MASK2) << SHIFT; + else + d[flip_flag] += (b & MASK2) << SHIFT; + } + } + else { /* type == 0 */ + /* ランレングス圧縮なしの場合はなにもしない */ //does nothing in case of no RLE compression + if (nwa->use_runlength) { + /* ランレングス圧縮ありの場合 */ //in case of RLE compression + runlength = getbits(&data, &shift, 1); + if (runlength == 1) { + runlength = getbits(&data, &shift, 2); + if (runlength == 3) { + runlength = getbits(&data, &shift, 8); + } + } + } + } + } + else { + runlength--; + } + + if (nwa->bps == 8) { + nwa->outdata[i] = d[flip_flag] * 256; //extra (original outputs 8-bit) + } + else { + nwa->outdata[i] = d[flip_flag]; + } + + if (nwa->channels == 2) + flip_flag ^= 1; /* channel 切り替え */ //channel swap + } + + nwa->samples_in_buffer = dsize; +} + +//NWAData::Decode +int nwalib_decode(STREAMFILE* sf, NWAData* nwa) { + /* some wav/pcm handling skipped here */ + + /* 今回読み込む/デコードするデータの大きさを得る */ //get current read/decode data size + int curblocksize, curcompsize; + if (nwa->curblock != nwa->blocks - 1) { + curblocksize = nwa->blocksize * (nwa->bps / 8); + curcompsize = nwa->offsets[nwa->curblock + 1] - nwa->offsets[nwa->curblock]; + if (curblocksize >= nwa->blocksize * (nwa->bps / 8) * 2) { + return -1; // Fatal error + } + } + else { //last block + curblocksize = nwa->restsize * (nwa->bps / 8); + curcompsize = nwa->blocksize * (nwa->bps / 8) * 2; + } + // (in practice compsize is ~200-400 and blocksize ~0x800, but last block can be different) + + /* データ読み込み */ //data read (may read less on last block?) + read_streamfile(nwa->tmpdata, nwa->offsets[nwa->curblock], curcompsize, sf); + + nwa->samples_in_buffer = 0; + nwa->outdata_readpos = nwa->outdata; + + decode_block(nwa, nwa->tmpdata, curblocksize); + + nwa->curblock++; //todo check not over max blocks? + return 0; } -static void -nwa_decode_block(NWAData *nwa) -{ - /* 今回読み込む/デコードするデータの大きさを得る */ - int curblocksize; - if (nwa->curblock != nwa->blocks - 1) - { - curblocksize = nwa->blocksize * (nwa->bps / 8); - //curcompsize = nwa->offsets[nwa->curblock + 1] - nwa->offsets[nwa->curblock]; - } - else - { - curblocksize = nwa->restsize * (nwa->bps / 8); - //curcompsize = nwa->blocksize * (nwa->bps / 8) * 2; - } - - nwa->samples_in_buffer = 0; - nwa->buffer_readpos = nwa->buffer; - - { - sample d[2]; - int i; - int shift = 0; - off_t offset = nwa->offsets[nwa->curblock]; - int dsize = curblocksize / (nwa->bps / 8); - int flip_flag = 0; /* stereo 用 */ - int runlength = 0; - - /* read initial sample value */ - for (i=0;ichannels;i++) - { - if (nwa->bps == 8) { d[i] = read_8bit(offset,nwa->file); } - else /* bps == 16 */ - { - d[i] = read_16bitLE(offset,nwa->file); - offset += 2; - } - } - - for (i = 0; i < dsize; i++) - { - if (runlength == 0) - { /* コピーループ中でないならデータ読み込み */ - int type = getbits(nwa->file, &offset, &shift, 3); - /* type により分岐:0, 1-6, 7 */ - if (type == 7) - { - /* 7 : 大きな差分 */ - /* RunLength() 有効時(CompLevel==5, 音声ファイル) では無効 */ - if (getbits(nwa->file, &offset, &shift, 1) == 1) - { - d[flip_flag] = 0; /* 未使用 */ - } - else - { - int BITS, SHIFT; - if (nwa->complevel >= 3) - { - BITS = 8; - SHIFT = 9; - } - else - { - BITS = 8 - nwa->complevel; - SHIFT = 2 + 7 + nwa->complevel; - } - { - const int MASK1 = (1 << (BITS - 1)); - const int MASK2 = (1 << (BITS - 1)) - 1; - int b = getbits(nwa->file, &offset, &shift, BITS); - if (b & MASK1) - d[flip_flag] -= (b & MASK2) << SHIFT; - else - d[flip_flag] += (b & MASK2) << SHIFT; - } - } - } - else if (type != 0) - { - /* 1-6 : 通常の差分 */ - int BITS, SHIFT; - if (nwa->complevel >= 3) - { - BITS = nwa->complevel + 3; - SHIFT = 1 + type; - } - else - { - BITS = 5 - nwa->complevel; - SHIFT = 2 + type + nwa->complevel; - } - { - const int MASK1 = (1 << (BITS - 1)); - const int MASK2 = (1 << (BITS - 1)) - 1; - int b = getbits(nwa->file, &offset, &shift, BITS); - if (b & MASK1) - d[flip_flag] -= (b & MASK2) << SHIFT; - else - d[flip_flag] += (b & MASK2) << SHIFT; - } - } - else - { /* type == 0 */ - /* ランレングス圧縮なしの場合はなにもしない */ - if (use_runlength(nwa)) - { - /* ランレングス圧縮ありの場合 */ - runlength = getbits(nwa->file, &offset, &shift, 1); - if (runlength == 1) - { - runlength = getbits(nwa->file, &offset, &shift, 2); - if (runlength == 3) - { - runlength = getbits(nwa->file, &offset, &shift, 8); - } - } - } - } - } - else - { - runlength--; - } - if (nwa->bps == 8) - { - nwa->buffer[i] = d[flip_flag]*0x100; - } - else - { - nwa->buffer[i] = d[flip_flag]; - } - nwa->samples_in_buffer++; - if (nwa->channels == 2) - flip_flag ^= 1; /* channel 切り替え */ - } - } - - nwa->curblock++; - return; -} - -void -seek_nwa(NWAData *nwa, int32_t seekpos) -{ - int dest_block = seekpos/(nwa->blocksize/nwa->channels); - int32_t remainder = seekpos%(nwa->blocksize/nwa->channels); +//NWAFILE::Seek (not too similar) +void nwalib_seek(STREAMFILE* sf, NWAData* nwa, int32_t seekpos) { + int dest_block = seekpos / (nwa->blocksize / nwa->channels); + int32_t remainder = seekpos % (nwa->blocksize / nwa->channels); nwa->curblock = dest_block; - nwa_decode_block(nwa); + nwalib_decode(sf, nwa); - nwa->buffer_readpos = nwa->buffer + remainder*nwa->channels; + nwa->outdata_readpos = nwa->outdata + remainder * nwa->channels; nwa->samples_in_buffer -= remainder*nwa->channels; } -/* interface to vgmstream */ -void -decode_nwa(NWAData *nwa, sample *outbuf, - int32_t samples_to_do) -{ - while (samples_to_do > 0) - { - int32_t samples_to_read; +/* ****************************************************************** */ - if (nwa->samples_in_buffer > 0) - { - samples_to_read = nwa->samples_in_buffer/nwa->channels; +#include "coding.h" + + +struct nwa_codec_data { + STREAMFILE* sf; + NWAData* nwa; +}; + +/* interface to vgmstream */ +void decode_nwa(nwa_codec_data* data, sample_t* outbuf, int32_t samples_to_do) { + NWAData* nwa = data->nwa; + + while (samples_to_do > 0) { + if (nwa->samples_in_buffer > 0) { + int32_t samples_to_read = nwa->samples_in_buffer / nwa->channels; if (samples_to_read > samples_to_do) samples_to_read = samples_to_do; - memcpy(outbuf,nwa->buffer_readpos, - sizeof(sample)*samples_to_read*nwa->channels); + memcpy(outbuf, nwa->outdata_readpos, sizeof(sample_t) * samples_to_read * nwa->channels); - nwa->buffer_readpos += samples_to_read*nwa->channels; - nwa->samples_in_buffer -= samples_to_read*nwa->channels; - outbuf += samples_to_read*nwa->channels; + nwa->outdata_readpos += samples_to_read * nwa->channels; + nwa->samples_in_buffer -= samples_to_read * nwa->channels; + outbuf += samples_to_read * nwa->channels; samples_to_do -= samples_to_read; } - else - { - nwa_decode_block(nwa); + else { + int err = nwalib_decode(data->sf, nwa); + if (err < 0) { + VGM_LOG("NWA: decoding error\n"); + return; + } } } } + + +nwa_codec_data* init_nwa(STREAMFILE* sf) { + nwa_codec_data* data = NULL; + + data = malloc(sizeof(nwa_codec_data)); + if (!data) goto fail; + + data->nwa = nwalib_open(sf); + if (!data->nwa) goto fail; + + data->sf = reopen_streamfile(sf, 0); + if (!data->sf) goto fail; + + return data; + +fail: + free_nwa(data); + return NULL; +} + +void seek_nwa(nwa_codec_data* data, int32_t sample) { + if (!data) return; + + nwalib_seek(data->sf, data->nwa, sample); +} + +void reset_nwa(nwa_codec_data* data) { + if (!data) return; + + nwalib_reset(data->nwa); +} + +void free_nwa(nwa_codec_data* data) { + if (!data) return; + + close_streamfile(data->sf); + nwalib_close(data->nwa); + free(data); +} + +STREAMFILE* nwa_get_streamfile(nwa_codec_data* data) { + if (!data) return NULL; + + return data->sf; + } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.h b/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.h index 361736918..983a7c88d 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.h @@ -1,4 +1,4 @@ -/* originally from nwatowav.cc 2007.7.28 version, which read: */ +/* derived from nwatowav.cc 2007.7.28 version, which read: */ /* * Copyright 2001-2007 jagarl / Kazunori Ueno * All Rights Reserved. @@ -39,33 +39,39 @@ #include "streamfile.h" #endif -typedef struct NWAData_s -{ +typedef struct NWAData_s { int channels; - int bps; /* bits per sample */ - int freq; /* samples per second */ - int complevel; /* compression level */ - int blocks; /* block count */ - int datasize; /* all data size */ - int compdatasize; /* compressed data size */ - int samplecount; /* all samples */ - int blocksize; /* samples per block */ - int restsize; /* samples of the last block */ + int bps; /* bits per sample */ + int freq; /* samples per second */ + + int complevel; /* compression level */ + int dummy; /* ? : 0x00 */ + + int blocks; /* block count */ + int datasize; /* all data size */ + + int compdatasize; /* compressed data size */ + int samplecount; /* all samples */ + int blocksize; /* samples per block */ + int restsize; /* samples of the last block */ + int dummy2; /* ? : 0x89 */ int curblock; - off_t *offsets; + off_t* offsets; + int filesize; - STREAMFILE *file; + int use_runlength; //extra - /* temporarily store samples */ - sample *buffer; - sample *buffer_readpos; + uint8_t *tmpdata; + int16_t *outdata; + int16_t *outdata_readpos; int samples_in_buffer; } NWAData; -NWAData *open_nwa(STREAMFILE *streamFile, const char *filename); -void close_nwa(NWAData *nwa); -void reset_nwa(NWAData *nwa); -void seek_nwa(NWAData *nwa, int32_t seekpos); +NWAData* nwalib_open(STREAMFILE* sf); +void nwalib_close(NWAData* nwa); +int nwalib_decode(STREAMFILE* sf, NWAData* nwa); +void nwalib_seek(STREAMFILE* sf, NWAData* nwa, int32_t seekpos); +void nwalib_reset(NWAData* nwa); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c index 83566456d..f27e46f6c 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c @@ -265,8 +265,7 @@ void reset_ogg_vorbis(VGMSTREAM *vgmstream) { ov_pcm_seek(&data->ogg_vorbis_file, 0); } -void seek_ogg_vorbis(VGMSTREAM *vgmstream, int32_t num_sample) { - ogg_vorbis_codec_data *data = vgmstream->codec_data; +void seek_ogg_vorbis(ogg_vorbis_codec_data *data, int32_t num_sample) { if (!data) return; /* this seek crosslaps to avoid possible clicks, so seeking to 0 will diff --git a/Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c index 2151c476d..352806813 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/oki_decoder.c @@ -17,7 +17,7 @@ static const int stex_indexes[16] = { /* OKI table (also from IMA) */ }; -static void pcfx_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index, int16_t *out_sample, int mode) { +static void pcfx_expand_nibble(VGMSTREAMCHANNEL* stream, off_t byte_offset, int nibble_shift, int32_t* hist1, int32_t* step_index, int16_t* out_sample, int mode) { int code, step, delta; code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; @@ -57,7 +57,7 @@ static void pcfx_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int } } -static void oki16_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index, int16_t *out_sample) { +static void oki16_expand_nibble(VGMSTREAMCHANNEL* stream, off_t byte_offset, int nibble_shift, int32_t* hist1, int32_t* step_index, int16_t* out_sample) { int code, step, delta; code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; @@ -79,7 +79,15 @@ static void oki16_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, in *out_sample = *hist1; } -static void oki4s_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index, int16_t *out_sample) { +/* Possible variation for adp_konami (Viper hardware): + * delta = ((n&7) + 0.5) * stepsize / 4; clamps 2047,-2048; nigh nibble first + * + * Results are very similar, but can't verify actual decoding, and oki4s is used in + * Jubeat (also Konami) so it makes sense they would have reused it. + * Viper sound chip may be a YMZ280B though. + */ + +static void oki4s_expand_nibble(VGMSTREAMCHANNEL* stream, off_t byte_offset, int nibble_shift, int32_t* hist1, int32_t* step_index, int16_t* out_sample) { int code, step, delta; code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; @@ -116,7 +124,7 @@ static void oki4s_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, in * so it's needs GENH/TXTH. Sample rate can only be base_value divided by 1/2/3/4, where * base_value is approximately ~31468.5 (follows hardware clocks), mono or interleaved for stereo. */ -void decode_pcfx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode) { +void decode_pcfx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode) { int i, sample_count = 0; int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; @@ -137,7 +145,7 @@ void decode_pcfx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin /* OKI variation with 16-bit output (vs standard's 12-bit), found in FrontWing's PS2 games (Sweet Legacy, Hooligan). * Reverse engineered from the ELF with help from the folks at hcs. */ -void decode_oki16(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { +void decode_oki16(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int i, sample_count = 0; int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; @@ -169,7 +177,7 @@ void decode_oki16(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspaci /* OKI variation with 16-bit output (vs standard's 12-bit) and pre-adjusted tables (shifted by 4), found in Jubeat Clan (AC). * Reverse engineered from the DLLs. */ -void decode_oki4s(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { +void decode_oki4s(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int i, sample_count = 0; int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ptadpcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ptadpcm_decoder.c index 29d3d71da..9a5910f03 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ptadpcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ptadpcm_decoder.c @@ -66,6 +66,8 @@ void decode_ptadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspaci index = frame[0x04]; VGM_ASSERT_ONCE(index > 12, "PTADPCM: incorrect index at %x\n", (uint32_t)frame_offset); + if (index > 12) + index = 12; /* write header samples (needed) */ if (sample_count >= first_sample && samples_done < samples_to_do) { @@ -108,5 +110,5 @@ void decode_ptadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspaci size_t ptadpcm_bytes_to_samples(size_t bytes, int channels, size_t frame_size) { if (channels <= 0 || frame_size < 0x06) return 0; - return (bytes / channels / frame_size) * ((frame_size-0x05) * 2); + return (bytes / (channels * frame_size)) * (2 + (frame_size-0x05) * 2); } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/tgcadpcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/tgcadpcm_decoder.c index 896a2ae03..2362e14b7 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/tgcadpcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/tgcadpcm_decoder.c @@ -1,42 +1,47 @@ #include "coding.h" /* Decodes SunPlus' ADPCM codec used on the Tiger Game.com. - * Reverse engineered from the Game.com's BIOS. */ - -static uint16_t slopeTable[64] = + * Highly improved, optimised signed 16-bit version of the algorithm. */ +static const int16_t slope_table[8][16] = { - 0x0000, 0x0100, 0x0200, 0x0400, 0x0610, 0x0810, 0x0C18, 0x1020, - 0x0100, 0x0300, 0x0508, 0x0908, 0x0D18, 0x1118, 0x1920, 0x2128, - 0x0208, 0x0508, 0x0810, 0x0E10, 0x1420, 0x1A20, 0x2628, 0x3230, - 0x0310, 0x0710, 0x0B18, 0x1318, 0x1B28, 0x2328, 0x2930, 0x4338, - 0x0418, 0x0918, 0x0E20, 0x1820, 0x2230, 0x2C30, 0x4038, 0x5438, - 0x0520, 0x0B20, 0x1128, 0x1D28, 0x2938, 0x3538, 0x4D38, 0x6F38, - 0x0628, 0x0D28, 0x1430, 0x2230, 0x3038, 0x3E38, 0x5A38, 0x7638, - 0x0730, 0x0F30, 0x1738, 0x2738, 0x3738, 0x4738, 0x6738, 0x7D38 + { 0, 0, 256, -256, 512, -512, 1024, -1024, 1536, -1536, 2048, -2048, 3072, -3072, 4096, -4096 }, + { 256, -256, 768, -768, 1280, -1280, 2304, -2304, 3328, -3328, 4352, -4352, 6400, -6400, 8448, -8448 }, + { 512, -512, 1280, -1280, 2048, -2048, 3584, -3584, 5120, -5120, 6656, -6656, 9728, -9728, 12800, -12800 }, + { 768, -768, 1792, -1792, 2816, -2816, 4864, -4864, 6912, -6912, 8960, -8960, 10496, -10496, 17152, -17152 }, + { 1024, -1024, 2304, -2304, 3584, -3584, 6144, -6144, 8704, -8704, 11264, -11264, 16384, -16384, 21504, -21504 }, + { 1280, -1280, 2816, -2816, 4352, -4352, 7424, -7424, 10496, -10496, 13568, -13568, 19712, -19712, 28416, -28416 }, + { 1536, -1536, 3328, -3328, 5120, -5120, 8704, -8704, 12288, -12288, 15872, -15872, 23040, -23040, 30208, -30208 }, + { 1792, -1792, 3840, -3840, 5888, -5888, 9984, -9984, 14080, -14080, 18176, -18176, 26368, -26368, 32000, -32000 }, +}; + +static const uint8_t next_step[8][16] = +{ + { 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 4 }, + { 0, 0, 0, 0, 1, 1, 1, 1, 3, 3, 3, 3, 4, 4, 5, 5 }, + { 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 5, 5, 6, 6 }, + { 2, 2, 2, 2, 3, 3, 3, 3, 5, 5, 5, 5, 6, 6, 7, 7 }, + { 3, 3, 3, 3, 4, 4, 4, 4, 6, 6, 6, 6, 7, 7, 7, 7 }, + { 4, 4, 4, 4, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7 }, + { 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7 }, + { 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 } }; void decode_tgc(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int32_t first_sample, int32_t samples_to_do) { for (int i = first_sample, sample_count = 0; i < first_sample + samples_to_do; i++, sample_count++) { - uint8_t samp = ((uint8_t)read_8bit(i/2, stream->streamfile) >> + uint8_t nibble = ((uint8_t)read_8bit(stream->offset + i/2, stream->streamfile) >> (i & 1 ? 4 : 0)) & 0xf; - uint8_t slopeIndex = stream->adpcm_scale | (samp >> 1); + stream->adpcm_history1_32 += slope_table[stream->adpcm_step_index][nibble]; + stream->adpcm_step_index = next_step [stream->adpcm_step_index][nibble]; - stream->adpcm_step_index = slopeTable[slopeIndex] >> 8; - stream->adpcm_scale = slopeTable[slopeIndex] & 0xff; + if (stream->adpcm_history1_32 < -32768) + stream->adpcm_history1_32 = -32768; - stream->adpcm_history1_16 += (samp & 1) ? - -stream->adpcm_step_index: - stream->adpcm_step_index; + if (stream->adpcm_history1_32 > 32767) + stream->adpcm_history1_32 = 32767; - if (stream->adpcm_history1_16 < 0) - stream->adpcm_history1_16 = 0; - - if (stream->adpcm_history1_16 > 0xff) - stream->adpcm_history1_16 = 0xff; - - outbuf[sample_count] = stream->adpcm_history1_16 * 0x100 - 0x8000; + outbuf[sample_count] = (sample_t)stream->adpcm_history1_32; } -} \ No newline at end of file +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vadpcm_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/vadpcm_decoder.c index d2f59ad87..746c6be03 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vadpcm_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vadpcm_decoder.c @@ -119,7 +119,7 @@ void decode_vadpcm(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacin } /* update hist once all frame is actually copied */ - if (first_sample + sample_count == samples_per_frame) { + if (first_sample + sample_count / channelspacing == samples_per_frame) { stream->adpcm_history2_16 = hist[6]; stream->adpcm_history1_16 = hist[7]; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_bitreader.h b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_bitreader.h new file mode 100644 index 000000000..89ac08175 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_bitreader.h @@ -0,0 +1,103 @@ +#ifndef _VORBIS_BITREADER_H +#define _VORBIS_BITREADER_H + +/* Simple bitreader for Vorbis' bit format. + * Kept in .h since it's slightly faster (compiler can optimize statics better) */ + + +typedef struct { + uint8_t* buf; /* buffer to read/write */ + size_t bufsize; /* max size of the buffer */ + uint32_t b_off; /* current offset in bits inside the buffer */ +} bitstream_t; + +/* convenience util */ +static void init_bitstream(bitstream_t* b, uint8_t* buf, size_t bufsize) { + b->buf = buf; + b->bufsize = bufsize; + b->b_off = 0; +} + +/* same as (1 << bits) - 1, but that seems to trigger some nasty UB when bits = 32 + * (though in theory (1 << 32) = 0, 0 - 1 = UINT_MAX, but gives 0 compiling in some cases, but not always) */ +static const uint32_t MASK_TABLE[33] = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, + 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, 0x0001ffff, + 0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff, + 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff +}; + +/* Read bits (max 32) from buf and update the bit offset. Vorbis packs values in LSB order and byte by byte. + * (ex. from 2 bytes 00100111 00000001 we can could read 4b=0111 and 6b=010010, 6b=remainder (second value is split into the 2nd byte) */ +static int rv_bits(bitstream_t* ib, uint32_t bits, uint32_t* value) { + uint32_t shift, mask, pos, val; + + if (bits > 32 || ib->b_off + bits > ib->bufsize * 8) + goto fail; + + pos = ib->b_off / 8; /* byte offset */ + shift = ib->b_off % 8; /* bit sub-offset */ + mask = MASK_TABLE[bits]; /* to remove upper in highest byte */ + + val = ib->buf[pos+0] >> shift; + if (bits + shift > 8) { + val |= ib->buf[pos+1] << (8u - shift); + if (bits + shift > 16) { + val |= ib->buf[pos+2] << (16u - shift); + if (bits + shift > 24) { + val |= ib->buf[pos+3] << (24u - shift); + if (bits + shift > 32) { + val |= ib->buf[pos+4] << (32u - shift); /* upper bits are lost (shifting over 32) */ + } + } + } + } + + *value = (val & mask); + + ib->b_off += bits; + + return 1; +fail: + VGM_LOG_ONCE("BITREADER: read fail\n"); + *value = 0; + return 0; +} + +#ifndef BITSTREAM_READ_ONLY +/* Write bits (max 32) to buf and update the bit offset. Vorbis packs values in LSB order and byte by byte. + * (ex. writing 1101011010 from b_off 2 we get 01101011 00001101 (value split, and 11 in the first byte skipped)*/ +static int wv_bits(bitstream_t* ob, uint32_t bits, uint32_t value) { + uint32_t shift, mask, pos; + + if (bits > 32 || ob->b_off + bits > ob->bufsize*8) + goto fail; + + pos = ob->b_off / 8; /* byte offset */ + shift = ob->b_off % 8; /* bit sub-offset */ + mask = (1 << shift) - 1; /* to keep lower bits in lowest byte */ + + ob->buf[pos+0] = (value << shift) | (ob->buf[pos+0] & mask); + if (bits + shift > 8) { + ob->buf[pos+1] = value >> (8 - shift); + if (bits + shift > 16) { + ob->buf[pos+2] = value >> (16 - shift); + if (bits + shift > 24) { + ob->buf[pos+3] = value >> (24 - shift); + if (bits + shift > 32) { + /* upper bits are set to 0 (shifting unsigned) but shouldn't matter */ + ob->buf[pos+4] = value >> (32 - shift); + } + } + } + } + + ob->b_off += bits; + return 1; +fail: + VGM_LOG_ONCE("BITREADER: write fail\n"); + return 0; +} +#endif + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c index d04d199fd..4b887a461 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.c @@ -1,230 +1,228 @@ -#include -#include "coding.h" -#include "vorbis_custom_decoder.h" - -#ifdef VGM_USE_VORBIS -#include - -#define VORBIS_DEFAULT_BUFFER_SIZE 0x8000 /* should be at least the size of the setup header, ~0x2000 */ - -static void pcm_convert_float_to_16(int channels, sample_t * outbuf, int samples_to_do, float ** pcm); - -/** - * Inits a vorbis stream of some custom variety. - * - * Normally Vorbis packets are stored in .ogg, which is divided into OggS pages/packets, and the first packets contain necessary - * Vorbis setup. For custom vorbis the OggS layer is replaced/optimized, the setup can be modified or stored elsewhere - * (i.e.- in the .exe) and raw Vorbis packets may be modified as well, presumably to shave off some kb and/or obfuscate. - * We'll manually read/modify the data and decode it with libvorbis calls. - * - * Reference: https://www.xiph.org/vorbis/doc/libvorbis/overview.html - */ -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; - - /* init stuff */ - data = calloc(1,sizeof(vorbis_custom_codec_data)); - if (!data) goto fail; - - data->buffer_size = VORBIS_DEFAULT_BUFFER_SIZE; - data->buffer = calloc(sizeof(uint8_t), data->buffer_size); - if (!data->buffer) goto fail; - - /* keep around to decode too */ - data->type = type; - memcpy(&data->config, config, sizeof(vorbis_custom_config)); - - - /* init vorbis stream state, using 3 fake Ogg setup packets (info, comments, setup/codebooks) - * libvorbis expects parsed Ogg pages, but we'll fake them with our raw data instead */ - vorbis_info_init(&data->vi); - vorbis_comment_init(&data->vc); - - data->op.packet = data->buffer; - data->op.b_o_s = 1; /* fake headers start */ - - /* init header */ - switch(data->type) { - case VORBIS_FSB: ok = vorbis_custom_setup_init_fsb(streamFile, start_offset, data); break; - case VORBIS_WWISE: ok = vorbis_custom_setup_init_wwise(streamFile, start_offset, data); break; - case VORBIS_OGL: ok = vorbis_custom_setup_init_ogl(streamFile, start_offset, data); break; - case VORBIS_SK: ok = vorbis_custom_setup_init_sk(streamFile, start_offset, data); break; - case VORBIS_VID1: ok = vorbis_custom_setup_init_vid1(streamFile, start_offset, data); break; - default: goto fail; - } - if(!ok) goto fail; - - data->op.b_o_s = 0; /* end of fake headers */ - - /* init vorbis global and block state */ - if (vorbis_synthesis_init(&data->vd,&data->vi) != 0) goto fail; - if (vorbis_block_init(&data->vd,&data->vb) != 0) goto fail; - - - /* write output */ - config->data_start_offset = data->config.data_start_offset; - - - return data; - -fail: - VGM_LOG("VORBIS: init fail at around 0x%x\n", (uint32_t)start_offset); - free_vorbis_custom(data); - return NULL; -} - -/* Decodes Vorbis packets into a libvorbis sample buffer, and copies them to outbuf */ -void decode_vorbis_custom(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t samples_to_do, int channels) { - VGMSTREAMCHANNEL *stream = &vgmstream->ch[0]; - vorbis_custom_codec_data * data = vgmstream->codec_data; - size_t stream_size = get_streamfile_size(stream->streamfile); - //data->op.packet = data->buffer;/* implicit from init */ - int samples_done = 0; - - while (samples_done < samples_to_do) { - - /* extra EOF check for edge cases */ - if (stream->offset >= stream_size) { - memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels); - break; - } - - - if (data->samples_full) { /* read more samples */ - int samples_to_get; - float **pcm; - - /* get PCM samples from libvorbis buffers */ - samples_to_get = vorbis_synthesis_pcmout(&data->vd, &pcm); - if (!samples_to_get) { - data->samples_full = 0; /* request more if empty*/ - continue; - } - - 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 convert from Vorbis float pcm to 16bit pcm */ - if (samples_to_get > samples_to_do - samples_done) - samples_to_get = samples_to_do - samples_done; - pcm_convert_float_to_16(data->vi.channels, outbuf + samples_done * channels, samples_to_get, pcm); - samples_done += samples_to_get; - } - - /* mark consumed samples from the buffer - * (non-consumed samples are returned in next vorbis_synthesis_pcmout calls) */ - vorbis_synthesis_read(&data->vd, samples_to_get); - } - else { /* read more data */ - int ok, rc; - - /* not actually needed, but feels nicer */ - data->op.granulepos += samples_to_do; /* can be changed next if desired */ - data->op.packetno++; - - /* read/transform data into the ogg_packet buffer and advance offsets */ - switch(data->type) { - case VORBIS_FSB: ok = vorbis_custom_parse_packet_fsb(stream, data); break; - case VORBIS_WWISE: ok = vorbis_custom_parse_packet_wwise(stream, data); break; - case VORBIS_OGL: ok = vorbis_custom_parse_packet_ogl(stream, data); break; - case VORBIS_SK: ok = vorbis_custom_parse_packet_sk(stream, data); break; - case VORBIS_VID1: ok = vorbis_custom_parse_packet_vid1(stream, data); break; - default: goto decode_fail; - } - if(!ok) { - goto decode_fail; - } - - - /* parse the fake ogg packet into a logical vorbis block */ - rc = vorbis_synthesis(&data->vb,&data->op); - if (rc == OV_ENOTAUDIO) { - VGM_LOG("Vorbis: not an audio packet (size=0x%x) @ %x\n",(size_t)data->op.bytes,(uint32_t)stream->offset); - //VGM_LOGB(data->op.packet, (size_t)data->op.bytes,0); - continue; /* rarely happens, seems ok? */ - } else if (rc != 0) goto decode_fail; - - /* finally decode the logical block into samples */ - rc = vorbis_synthesis_blockin(&data->vd,&data->vb); - if (rc != 0) goto decode_fail; /* ? */ - - - data->samples_full = 1; - } - } - - return; - -decode_fail: - /* on error just put some 0 samples */ - VGM_LOG("VORBIS: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, (samples_to_do - samples_done)); - memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * channels * sizeof(sample)); -} - -/* converts from internal Vorbis format to standard PCM (mostly from Xiph's decoder_example.c) */ -static void pcm_convert_float_to_16(int channels, sample_t * outbuf, int samples_to_do, float ** pcm) { - int ch, s; - sample_t *ptr; - float *channel; - - /* convert float PCM (multichannel float array, with pcm[0]=ch0, pcm[1]=ch1, pcm[2]=ch0, etc) - * to 16 bit signed PCM ints (host order) and interleave + fix clipping */ - for (ch = 0; ch < channels; ch++) { - /* channels should be in standard order unlike Ogg Vorbis (at least in FSB) */ - ptr = outbuf + ch; - channel = pcm[ch]; - for (s = 0; s < samples_to_do; s++) { - int val = (int)floor(channel[s] * 32767.0f + 0.5f); - if (val > 32767) val = 32767; - else if (val < -32768) val = -32768; - - *ptr = val; - ptr += channels; - } - } -} - -/* ********************************************** */ - -void free_vorbis_custom(vorbis_custom_codec_data * data) { - if (!data) - return; - - /* internal decoder cleanup */ - vorbis_block_clear(&data->vb); - vorbis_dsp_clear(&data->vd); - vorbis_comment_clear(&data->vc); - vorbis_info_clear(&data->vi); - - free(data->buffer); - free(data); -} - -void reset_vorbis_custom(VGMSTREAM *vgmstream) { - vorbis_custom_codec_data *data = vgmstream->codec_data; - if (!data) return; - - /* Seeking is provided by the Ogg layer, so with custom vorbis we'd need seek tables instead. - * To avoid having to parse different formats we'll just discard until the expected sample */ - vorbis_synthesis_restart(&data->vd); - data->samples_to_discard = 0; -} - -void seek_vorbis_custom(VGMSTREAM *vgmstream, int32_t num_sample) { - vorbis_custom_codec_data *data = vgmstream->codec_data; - if (!data) return; - - /* Seeking is provided by the Ogg layer, so with custom vorbis we'd need seek tables instead. - * To avoid having to parse different formats we'll just discard until the expected sample */ - vorbis_synthesis_restart(&data->vd); - data->samples_to_discard = num_sample; - if (vgmstream->loop_ch) - vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; -} - -#endif +#include +#include "coding.h" +#include "vorbis_custom_decoder.h" + +#ifdef VGM_USE_VORBIS +#include + +#define VORBIS_DEFAULT_BUFFER_SIZE 0x8000 /* should be at least the size of the setup header, ~0x2000 */ + +static void pcm_convert_float_to_16(sample_t* outbuf, int samples_to_do, float** pcm, int channels); + +/** + * Inits a vorbis stream of some custom variety. + * + * Normally Vorbis packets are stored in .ogg, which is divided into OggS pages/packets, and the first packets contain necessary + * Vorbis setup. For custom vorbis the OggS layer is replaced/optimized, the setup can be modified or stored elsewhere + * (i.e.- in the .exe) and raw Vorbis packets may be modified as well, presumably to shave off some kb and/or obfuscate. + * We'll manually read/modify the data and decode it with libvorbis calls. + * + * Reference: https://www.xiph.org/vorbis/doc/libvorbis/overview.html + */ +vorbis_custom_codec_data* init_vorbis_custom(STREAMFILE* sf, off_t start_offset, vorbis_custom_t type, vorbis_custom_config* config) { + vorbis_custom_codec_data* data = NULL; + int ok; + + /* init stuff */ + data = calloc(1,sizeof(vorbis_custom_codec_data)); + if (!data) goto fail; + + data->buffer_size = VORBIS_DEFAULT_BUFFER_SIZE; + data->buffer = calloc(sizeof(uint8_t), data->buffer_size); + if (!data->buffer) goto fail; + + /* keep around to decode too */ + data->type = type; + memcpy(&data->config, config, sizeof(vorbis_custom_config)); + + + /* init vorbis stream state, using 3 fake Ogg setup packets (info, comments, setup/codebooks) + * libvorbis expects parsed Ogg pages, but we'll fake them with our raw data instead */ + vorbis_info_init(&data->vi); + vorbis_comment_init(&data->vc); + + data->op.packet = data->buffer; + data->op.b_o_s = 1; /* fake headers start */ + + /* init header */ + switch(data->type) { + case VORBIS_FSB: ok = vorbis_custom_setup_init_fsb(sf, start_offset, data); break; + case VORBIS_WWISE: ok = vorbis_custom_setup_init_wwise(sf, start_offset, data); break; + case VORBIS_OGL: ok = vorbis_custom_setup_init_ogl(sf, start_offset, data); break; + case VORBIS_SK: ok = vorbis_custom_setup_init_sk(sf, start_offset, data); break; + case VORBIS_VID1: ok = vorbis_custom_setup_init_vid1(sf, start_offset, data); break; + default: goto fail; + } + if(!ok) goto fail; + + data->op.b_o_s = 0; /* end of fake headers */ + + /* init vorbis global and block state */ + if (vorbis_synthesis_init(&data->vd,&data->vi) != 0) goto fail; + if (vorbis_block_init(&data->vd,&data->vb) != 0) goto fail; + + + /* write output */ + config->data_start_offset = data->config.data_start_offset; + + + return data; + +fail: + VGM_LOG("VORBIS: init fail at around 0x%x\n", (uint32_t)start_offset); + free_vorbis_custom(data); + return NULL; +} + +/* Decodes Vorbis packets into a libvorbis sample buffer, and copies them to outbuf */ +void decode_vorbis_custom(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) { + VGMSTREAMCHANNEL *stream = &vgmstream->ch[0]; + vorbis_custom_codec_data* data = vgmstream->codec_data; + size_t stream_size = get_streamfile_size(stream->streamfile); + //data->op.packet = data->buffer;/* implicit from init */ + int samples_done = 0; + + while (samples_done < samples_to_do) { + + /* extra EOF check for edge cases */ + if (stream->offset >= stream_size) { + memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels); + break; + } + + + if (data->samples_full) { /* read more samples */ + int samples_to_get; + float **pcm; + + /* get PCM samples from libvorbis buffers */ + samples_to_get = vorbis_synthesis_pcmout(&data->vd, &pcm); + if (!samples_to_get) { + data->samples_full = 0; /* request more if empty*/ + continue; + } + + 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 convert from Vorbis float pcm to 16bit pcm */ + if (samples_to_get > samples_to_do - samples_done) + samples_to_get = samples_to_do - samples_done; + pcm_convert_float_to_16(outbuf + samples_done * channels, samples_to_get, pcm, data->vi.channels); + samples_done += samples_to_get; + } + + /* mark consumed samples from the buffer + * (non-consumed samples are returned in next vorbis_synthesis_pcmout calls) */ + vorbis_synthesis_read(&data->vd, samples_to_get); + } + else { /* read more data */ + int ok, rc; + + /* not actually needed, but feels nicer */ + data->op.granulepos += samples_to_do; /* can be changed next if desired */ + data->op.packetno++; + + /* read/transform data into the ogg_packet buffer and advance offsets */ + switch(data->type) { + case VORBIS_FSB: ok = vorbis_custom_parse_packet_fsb(stream, data); break; + case VORBIS_WWISE: ok = vorbis_custom_parse_packet_wwise(stream, data); break; + case VORBIS_OGL: ok = vorbis_custom_parse_packet_ogl(stream, data); break; + case VORBIS_SK: ok = vorbis_custom_parse_packet_sk(stream, data); break; + case VORBIS_VID1: ok = vorbis_custom_parse_packet_vid1(stream, data); break; + default: goto decode_fail; + } + if(!ok) { + goto decode_fail; + } + + + /* parse the fake ogg packet into a logical vorbis block */ + rc = vorbis_synthesis(&data->vb,&data->op); + if (rc == OV_ENOTAUDIO) { + VGM_LOG("Vorbis: not an audio packet (size=0x%x) @ %x\n",(size_t)data->op.bytes,(uint32_t)stream->offset); + //VGM_LOGB(data->op.packet, (size_t)data->op.bytes,0); + continue; /* rarely happens, seems ok? */ + } else if (rc != 0) goto decode_fail; + + /* finally decode the logical block into samples */ + rc = vorbis_synthesis_blockin(&data->vd,&data->vb); + if (rc != 0) goto decode_fail; /* ? */ + + + data->samples_full = 1; + } + } + + return; + +decode_fail: + /* on error just put some 0 samples */ + VGM_LOG("VORBIS: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, (samples_to_do - samples_done)); + memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * channels * sizeof(sample)); +} + +/* converts from internal Vorbis format to standard PCM (mostly from Xiph's decoder_example.c) */ +static void pcm_convert_float_to_16(sample_t* outbuf, int samples_to_do, float** pcm, int channels) { + int ch, s; + sample_t* ptr; + float* channel; + + /* convert float PCM (multichannel float array, with pcm[0]=ch0, pcm[1]=ch1, pcm[2]=ch0, etc) + * to 16 bit signed PCM ints (host order) and interleave + fix clipping */ + for (ch = 0; ch < channels; ch++) { + /* channels should be in standard order unlike Ogg Vorbis (at least in FSB) */ + ptr = outbuf + ch; + channel = pcm[ch]; + for (s = 0; s < samples_to_do; s++) { + int val = (int)floor(channel[s] * 32767.0f + 0.5f); + if (val > 32767) val = 32767; + else if (val < -32768) val = -32768; + + *ptr = val; + ptr += channels; + } + } +} + +/* ********************************************** */ + +void free_vorbis_custom(vorbis_custom_codec_data* data) { + if (!data) + return; + + /* internal decoder cleanup */ + vorbis_block_clear(&data->vb); + vorbis_dsp_clear(&data->vd); + vorbis_comment_clear(&data->vc); + vorbis_info_clear(&data->vi); + + free(data->buffer); + free(data); +} + +void reset_vorbis_custom(VGMSTREAM* vgmstream) { + vorbis_custom_codec_data *data = vgmstream->codec_data; + if (!data) return; + + vorbis_synthesis_restart(&data->vd); + data->samples_to_discard = 0; +} + +void seek_vorbis_custom(VGMSTREAM* vgmstream, int32_t num_sample) { + vorbis_custom_codec_data *data = vgmstream->codec_data; + if (!data) return; + + /* Seeking is provided by the Ogg layer, so with custom vorbis we'd need seek tables instead. + * To avoid having to parse different formats we'll just discard until the expected sample */ + vorbis_synthesis_restart(&data->vd); + data->samples_to_discard = num_sample; + if (vgmstream->loop_ch) + vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.h b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.h index 0801d91c1..8080800d2 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_decoder.h @@ -1,22 +1,54 @@ -#ifndef _VORBIS_CUSTOM_DECODER_H_ -#define _VORBIS_CUSTOM_DECODER_H_ - -#include "../vgmstream.h" -#include "../coding/coding.h" - -/* used by vorbis_custom_decoder.c, but scattered in other .c files */ -#ifdef VGM_USE_VORBIS -int vorbis_custom_setup_init_fsb(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data); -int vorbis_custom_setup_init_wwise(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data); -int vorbis_custom_setup_init_ogl(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data); -int vorbis_custom_setup_init_sk(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data); -int vorbis_custom_setup_init_vid1(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data); - -int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data); -int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data); -int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data); -int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data); -int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data); -#endif/* VGM_USE_VORBIS */ - -#endif/*_VORBIS_CUSTOM_DECODER_H_ */ +#ifndef _VORBIS_CUSTOM_DECODER_H_ +#define _VORBIS_CUSTOM_DECODER_H_ + +#include "../vgmstream.h" +#include "../coding/coding.h" + +/* used by vorbis_custom_decoder.c, but scattered in other .c files */ +#ifdef VGM_USE_VORBIS + +/* custom Vorbis without Ogg layer */ +struct vorbis_custom_codec_data { + vorbis_info vi; /* stream settings */ + vorbis_comment vc; /* stream comments */ + vorbis_dsp_state vd; /* decoder global state */ + vorbis_block vb; /* decoder local state */ + ogg_packet op; /* fake packet for internal use */ + + uint8_t* buffer; /* internal raw data buffer */ + size_t buffer_size; + + size_t samples_to_discard; /* for looping purposes */ + int samples_full; /* flag, samples available in vorbis buffers */ + + vorbis_custom_t type; /* Vorbis subtype */ + vorbis_custom_config config; /* config depending on the mode */ + + /* Wwise Vorbis: saved data to reconstruct modified packets */ + uint8_t mode_blockflag[64+1]; /* max 6b+1; flags 'n stuff */ + int mode_bits; /* bits to store mode_number */ + uint8_t prev_blockflag; /* blockflag in the last decoded packet */ + /* Ogg-style Vorbis: packet within a page */ + int current_packet; + /* reference for page/blocks */ + off_t block_offset; + size_t block_size; + + int prev_block_samples; /* count for optimization */ +}; + + +int vorbis_custom_setup_init_fsb(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data); +int vorbis_custom_setup_init_wwise(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data); +int vorbis_custom_setup_init_ogl(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data); +int vorbis_custom_setup_init_sk(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data); +int vorbis_custom_setup_init_vid1(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data); + +int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data); +int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data); +int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data); +int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data); +int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data* data); +#endif/* VGM_USE_VORBIS */ + +#endif/*_VORBIS_CUSTOM_DECODER_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_fsb.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_fsb.c index 309283260..cb43c11f1 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_fsb.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_fsb.c @@ -1,269 +1,269 @@ -#include "vorbis_custom_decoder.h" - -#ifdef VGM_USE_VORBIS -#include - -#define FSB_VORBIS_USE_PRECOMPILED_FVS 1 /* if enabled vgmstream weights ~600kb more but doesn't need external .fvs packets */ -#if FSB_VORBIS_USE_PRECOMPILED_FVS -#include "vorbis_custom_data_fsb.h" -#endif - - -/* **************************************************************************** */ -/* DEFS */ -/* **************************************************************************** */ - -static int build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long); -static int build_header_comment(uint8_t * buf, size_t bufsize); -static int build_header_setup(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile); - -static int load_fvs_file_single(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile); -static int load_fvs_file_multi(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile); -static int load_fvs_array(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile); - - -/* **************************************************************************** */ -/* EXTERNAL API */ -/* **************************************************************************** */ - -/** - * FSB references an external setup packet by the setup_id, and packets have mini headers with the size. - * - * Format info from python-fsb5 (https://github.com/HearthSim/python-fsb5) and - * fsb-vorbis-extractor (https://github.com/tmiasko/fsb-vorbis-extractor). - */ -int vorbis_custom_setup_init_fsb(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) { - vorbis_custom_config cfg = data->config; - - data->op.bytes = build_header_identification(data->buffer, data->buffer_size, cfg.channels, cfg.sample_rate, 256, 2048); /* FSB default block sizes */ - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ - - data->op.bytes = build_header_comment(data->buffer, data->buffer_size); - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ - - data->op.bytes = build_header_setup(data->buffer, data->buffer_size, cfg.setup_id, streamFile); - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ - - return 1; - -fail: - return 0; -} - - -int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) { - size_t bytes; - - /* get next packet size from the FSB 16b header (doesn't count this 16b) */ - data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile); - stream->offset += 2; - if (data->op.bytes == 0 || data->op.bytes == 0xFFFF || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */ - - /* read raw block */ - bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile); - stream->offset += data->op.bytes; - if (bytes != data->op.bytes) goto fail; /* wrong packet? */ - - return 1; - -fail: - return 0; -} - -/* **************************************************************************** */ -/* INTERNAL HELPERS */ -/* **************************************************************************** */ - -static int build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long) { - int bytes = 0x1e; - uint8_t blocksizes, exp_blocksize_0, exp_blocksize_1; - - if (bytes > bufsize) return 0; - - /* guetto log2 for allowed blocksizes (2-exp), could be improved */ - switch(blocksize_long) { - case 64: exp_blocksize_0 = 6; break; - case 128: exp_blocksize_0 = 7; break; - case 256: exp_blocksize_0 = 8; break; - case 512: exp_blocksize_0 = 9; break; - case 1024: exp_blocksize_0 = 10; break; - case 2048: exp_blocksize_0 = 11; break; - case 4096: exp_blocksize_0 = 12; break; - case 8192: exp_blocksize_0 = 13; break; - default: return 0; - } - switch(blocksize_short) { - case 64: exp_blocksize_1 = 6; break; - case 128: exp_blocksize_1 = 7; break; - case 256: exp_blocksize_1 = 8; break; - case 512: exp_blocksize_1 = 9; break; - case 1024: exp_blocksize_1 = 10; break; - case 2048: exp_blocksize_1 = 11; break; - case 4096: exp_blocksize_1 = 12; break; - case 8192: exp_blocksize_1 = 13; break; - default: return 0; - } - blocksizes = (exp_blocksize_0 << 4) | (exp_blocksize_1); - - put_8bit (buf+0x00, 0x01); /* packet_type (id) */ - memcpy (buf+0x01, "vorbis", 6); /* id */ - put_32bitLE(buf+0x07, 0x00); /* vorbis_version (fixed) */ - put_8bit (buf+0x0b, channels); /* audio_channels */ - put_32bitLE(buf+0x0c, sample_rate); /* audio_sample_rate */ - put_32bitLE(buf+0x10, 0x00); /* bitrate_maximum (optional hint) */ - put_32bitLE(buf+0x14, 0x00); /* bitrate_nominal (optional hint) */ - put_32bitLE(buf+0x18, 0x00); /* bitrate_minimum (optional hint) */ - put_8bit (buf+0x1c, blocksizes); /* blocksize_0 + blocksize_1 nibbles */ - put_8bit (buf+0x1d, 0x01); /* framing_flag (fixed) */ - - return bytes; -} - -static int build_header_comment(uint8_t * buf, size_t bufsize) { - int bytes = 0x19; - - if (bytes > bufsize) return 0; - - put_8bit (buf+0x00, 0x03); /* packet_type (comments) */ - memcpy (buf+0x01, "vorbis", 6); /* id */ - put_32bitLE(buf+0x07, 0x09); /* vendor_length */ - memcpy (buf+0x0b, "vgmstream", 9); /* vendor_string */ - put_32bitLE(buf+0x14, 0x00); /* user_comment_list_length */ - put_8bit (buf+0x18, 0x01); /* framing_flag (fixed) */ - - return bytes; -} - -static int build_header_setup(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) { - int bytes; - - /* try to locate from the precompiled list */ - bytes = load_fvs_array(buf, bufsize, setup_id, streamFile); - if (bytes) - return bytes; - - /* try to load from external files */ - bytes = load_fvs_file_single(buf, bufsize, setup_id, streamFile); - if (bytes) - return bytes; - - bytes = load_fvs_file_multi(buf, bufsize, setup_id, streamFile); - if (bytes) - return bytes; - - /* not found */ - VGM_LOG("FSB Vorbis: setup_id %08x not found\n", setup_id); - return 0; -} - -static int load_fvs_file_single(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) { - STREAMFILE * streamFileSetup = NULL; - - { - char setupname[PATH_LIMIT]; - char pathname[PATH_LIMIT]; - char *path; - - /* read "(dir/).fvs_{setup_id}" */ - streamFile->get_name(streamFile,pathname,sizeof(pathname)); - path = strrchr(pathname,DIR_SEPARATOR); - if (path) - *(path+1) = '\0'; - else - pathname[0] = '\0'; - - snprintf(setupname,PATH_LIMIT,"%s.fvs_%08x", pathname, setup_id); - streamFileSetup = streamFile->open(streamFile,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); - } - - if (streamFileSetup) { - /* file found, get contents into the buffer */ - size_t bytes = streamFileSetup->get_size(streamFileSetup); - if (bytes > bufsize) goto fail; - - if (read_streamfile(buf, 0, bytes, streamFileSetup) != bytes) - goto fail; - - streamFileSetup->close(streamFileSetup); - return bytes; - } - -fail: - if (streamFileSetup) streamFileSetup->close(streamFileSetup); - return 0; -} - -static int load_fvs_file_multi(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) { - STREAMFILE * streamFileSetup = NULL; - - { - char setupname[PATH_LIMIT]; - char pathname[PATH_LIMIT]; - char *path; - - /* read "(dir/).fvs" */ - streamFile->get_name(streamFile,pathname,sizeof(pathname)); - path = strrchr(pathname,DIR_SEPARATOR); - if (path) - *(path+1) = '\0'; - else - pathname[0] = '\0'; - - snprintf(setupname,PATH_LIMIT,"%s.fvs", pathname); - streamFileSetup = streamFile->open(streamFile,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); - } - - if (streamFileSetup) { - /* file found: read mini-header (format by bnnm, feel free to change) and locate FVS */ - int entries, i; - uint32_t offset = 0, size = 0; - - if (read_32bitBE(0x0, streamFileSetup) != 0x56465653) goto fail; /* "VFVS" */ - entries = read_32bitLE(0x08, streamFileSetup); /* 0x04=v0, 0x0c-0x20: reserved */ - if (entries <= 0) goto fail; - - for (i=0; i < entries; i++) { /* entry = id, offset, size, reserved */ - if ((uint32_t)read_32bitLE(0x20 + i*0x10, streamFileSetup) == setup_id) { - offset = read_32bitLE(0x24 + i*0x10, streamFileSetup); - size = read_32bitLE(0x28 + i*0x10, streamFileSetup); - break; - } - } - if (!size || !offset || size > bufsize) goto fail; - - /* read into buf */ - if (read_streamfile(buf, offset, size, streamFileSetup) != size) - goto fail; - - streamFileSetup->close(streamFileSetup); - return size; - } - -fail: - if (streamFileSetup) streamFileSetup->close(streamFileSetup); - return 0; -} - -static int load_fvs_array(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) { -#if FSB_VORBIS_USE_PRECOMPILED_FVS - int i, list_length; - - list_length = sizeof(fvs_list) / sizeof(fvs_info); - for (i=0; i < list_length; i++) { - if (fvs_list[i].id == setup_id) { - if (fvs_list[i].size > bufsize) goto fail; - /* found: copy data as-is */ - memcpy(buf,fvs_list[i].setup, fvs_list[i].size); - return fvs_list[i].size; - } - } - -fail: -#endif - return 0; -} - -#endif +#include "vorbis_custom_decoder.h" + +#ifdef VGM_USE_VORBIS +#include + +#define FSB_VORBIS_USE_PRECOMPILED_FVS 1 /* if enabled vgmstream weights ~600kb more but doesn't need external .fvs packets */ +#if FSB_VORBIS_USE_PRECOMPILED_FVS +#include "vorbis_custom_data_fsb.h" +#endif + + +/* **************************************************************************** */ +/* DEFS */ +/* **************************************************************************** */ + +static int build_header_identification(uint8_t* buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long); +static int build_header_comment(uint8_t* buf, size_t bufsize); +static int build_header_setup(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf); + +static int load_fvs_file_single(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf); +static int load_fvs_file_multi(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf); +static int load_fvs_array(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf); + + +/* **************************************************************************** */ +/* EXTERNAL API */ +/* **************************************************************************** */ + +/** + * FSB references an external setup packet by the setup_id, and packets have mini headers with the size. + * + * Format info from python-fsb5 (https://github.com/HearthSim/python-fsb5) and + * fsb-vorbis-extractor (https://github.com/tmiasko/fsb-vorbis-extractor). + */ +int vorbis_custom_setup_init_fsb(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data) { + vorbis_custom_config cfg = data->config; + + data->op.bytes = build_header_identification(data->buffer, data->buffer_size, cfg.channels, cfg.sample_rate, 256, 2048); /* FSB default block sizes */ + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ + + data->op.bytes = build_header_comment(data->buffer, data->buffer_size); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ + + data->op.bytes = build_header_setup(data->buffer, data->buffer_size, cfg.setup_id, sf); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ + + return 1; + +fail: + return 0; +} + + +int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data) { + size_t bytes; + + /* get next packet size from the FSB 16b header (doesn't count this 16b) */ + data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile); + stream->offset += 2; + if (data->op.bytes == 0 || data->op.bytes == 0xFFFF || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */ + + /* read raw block */ + bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile); + stream->offset += data->op.bytes; + if (bytes != data->op.bytes) goto fail; /* wrong packet? */ + + return 1; + +fail: + return 0; +} + +/* **************************************************************************** */ +/* INTERNAL HELPERS */ +/* **************************************************************************** */ + +static int build_header_identification(uint8_t* buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long) { + int bytes = 0x1e; + uint8_t blocksizes, exp_blocksize_0, exp_blocksize_1; + + if (bytes > bufsize) return 0; + + /* guetto log2 for allowed blocksizes (2-exp), could be improved */ + switch(blocksize_long) { + case 64: exp_blocksize_0 = 6; break; + case 128: exp_blocksize_0 = 7; break; + case 256: exp_blocksize_0 = 8; break; + case 512: exp_blocksize_0 = 9; break; + case 1024: exp_blocksize_0 = 10; break; + case 2048: exp_blocksize_0 = 11; break; + case 4096: exp_blocksize_0 = 12; break; + case 8192: exp_blocksize_0 = 13; break; + default: return 0; + } + switch(blocksize_short) { + case 64: exp_blocksize_1 = 6; break; + case 128: exp_blocksize_1 = 7; break; + case 256: exp_blocksize_1 = 8; break; + case 512: exp_blocksize_1 = 9; break; + case 1024: exp_blocksize_1 = 10; break; + case 2048: exp_blocksize_1 = 11; break; + case 4096: exp_blocksize_1 = 12; break; + case 8192: exp_blocksize_1 = 13; break; + default: return 0; + } + blocksizes = (exp_blocksize_0 << 4) | (exp_blocksize_1); + + put_8bit (buf+0x00, 0x01); /* packet_type (id) */ + memcpy (buf+0x01, "vorbis", 6); /* id */ + put_32bitLE(buf+0x07, 0x00); /* vorbis_version (fixed) */ + put_8bit (buf+0x0b, channels); /* audio_channels */ + put_32bitLE(buf+0x0c, sample_rate); /* audio_sample_rate */ + put_32bitLE(buf+0x10, 0x00); /* bitrate_maximum (optional hint) */ + put_32bitLE(buf+0x14, 0x00); /* bitrate_nominal (optional hint) */ + put_32bitLE(buf+0x18, 0x00); /* bitrate_minimum (optional hint) */ + put_8bit (buf+0x1c, blocksizes); /* blocksize_0 + blocksize_1 nibbles */ + put_8bit (buf+0x1d, 0x01); /* framing_flag (fixed) */ + + return bytes; +} + +static int build_header_comment(uint8_t* buf, size_t bufsize) { + int bytes = 0x19; + + if (bytes > bufsize) return 0; + + put_8bit (buf+0x00, 0x03); /* packet_type (comments) */ + memcpy (buf+0x01, "vorbis", 6); /* id */ + put_32bitLE(buf+0x07, 0x09); /* vendor_length */ + memcpy (buf+0x0b, "vgmstream", 9); /* vendor_string */ + put_32bitLE(buf+0x14, 0x00); /* user_comment_list_length */ + put_8bit (buf+0x18, 0x01); /* framing_flag (fixed) */ + + return bytes; +} + +static int build_header_setup(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf) { + int bytes; + + /* try to locate from the precompiled list */ + bytes = load_fvs_array(buf, bufsize, setup_id, sf); + if (bytes) + return bytes; + + /* try to load from external files */ + bytes = load_fvs_file_single(buf, bufsize, setup_id, sf); + if (bytes) + return bytes; + + bytes = load_fvs_file_multi(buf, bufsize, setup_id, sf); + if (bytes) + return bytes; + + /* not found */ + VGM_LOG("FSB Vorbis: setup_id %08x not found\n", setup_id); + return 0; +} + +static int load_fvs_file_single(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf) { + STREAMFILE* sf_setup = NULL; + + { + char setupname[PATH_LIMIT]; + char pathname[PATH_LIMIT]; + char *path; + + /* read "(dir/).fvs_{setup_id}" */ + sf->get_name(sf,pathname,sizeof(pathname)); + path = strrchr(pathname,DIR_SEPARATOR); + if (path) + *(path+1) = '\0'; + else + pathname[0] = '\0'; + + snprintf(setupname,PATH_LIMIT,"%s.fvs_%08x", pathname, setup_id); + sf_setup = sf->open(sf,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); + } + + if (sf_setup) { + /* file found, get contents into the buffer */ + size_t bytes = sf_setup->get_size(sf_setup); + if (bytes > bufsize) goto fail; + + if (read_streamfile(buf, 0, bytes, sf_setup) != bytes) + goto fail; + + sf_setup->close(sf_setup); + return bytes; + } + +fail: + if (sf_setup) sf_setup->close(sf_setup); + return 0; +} + +static int load_fvs_file_multi(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf) { + STREAMFILE* sf_setup = NULL; + + { + char setupname[PATH_LIMIT]; + char pathname[PATH_LIMIT]; + char* path; + + /* read "(dir/).fvs" */ + sf->get_name(sf,pathname,sizeof(pathname)); + path = strrchr(pathname,DIR_SEPARATOR); + if (path) + *(path+1) = '\0'; + else + pathname[0] = '\0'; + + snprintf(setupname,PATH_LIMIT,"%s.fvs", pathname); + sf_setup = sf->open(sf,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); + } + + if (sf_setup) { + /* file found: read mini-header (format by bnnm, feel free to change) and locate FVS */ + int entries, i; + uint32_t offset = 0, size = 0; + + if (read_32bitBE(0x0, sf_setup) != 0x56465653) goto fail; /* "VFVS" */ + entries = read_32bitLE(0x08, sf_setup); /* 0x04=v0, 0x0c-0x20: reserved */ + if (entries <= 0) goto fail; + + for (i=0; i < entries; i++) { /* entry = id, offset, size, reserved */ + if ((uint32_t)read_32bitLE(0x20 + i*0x10, sf_setup) == setup_id) { + offset = read_32bitLE(0x24 + i*0x10, sf_setup); + size = read_32bitLE(0x28 + i*0x10, sf_setup); + break; + } + } + if (!size || !offset || size > bufsize) goto fail; + + /* read into buf */ + if (read_streamfile(buf, offset, size, sf_setup) != size) + goto fail; + + sf_setup->close(sf_setup); + return size; + } + +fail: + if (sf_setup) sf_setup->close(sf_setup); + return 0; +} + +static int load_fvs_array(uint8_t* buf, size_t bufsize, uint32_t setup_id, STREAMFILE* sf) { +#if FSB_VORBIS_USE_PRECOMPILED_FVS + int i, list_length; + + list_length = sizeof(fvs_list) / sizeof(fvs_info); + for (i=0; i < list_length; i++) { + if (fvs_list[i].id == setup_id) { + if (fvs_list[i].size > bufsize) goto fail; + /* found: copy data as-is */ + memcpy(buf,fvs_list[i].setup, fvs_list[i].size); + return fvs_list[i].size; + } + } + +fail: +#endif + return 0; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_ogl.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_ogl.c index 8e746362a..bafa949c3 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_ogl.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_ogl.c @@ -1,71 +1,71 @@ -#include "vorbis_custom_decoder.h" - -#ifdef VGM_USE_VORBIS -#include - - -/* **************************************************************************** */ -/* EXTERNAL API */ -/* **************************************************************************** */ - -/** - * OGL removes the Ogg layer and uses 16b packet headers, that have the size of the next packet, but - * the lower 2b need to be removed (usually 00 but 01 for the id packet, not sure about the meaning). - */ -int vorbis_custom_setup_init_ogl(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) { - off_t offset = start_offset; - size_t packet_size; - - /* read 3 packets with triad (id/comment/setup), each with an OGL header */ - - /* normal identificacion packet */ - packet_size = (uint16_t)read_16bitLE(offset, streamFile) >> 2; - if (packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, streamFile); - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ - offset += 2+packet_size; - - /* normal comment packet */ - packet_size = (uint16_t)read_16bitLE(offset, streamFile) >> 2; - if (packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, streamFile); - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ - offset += 2+packet_size; - - /* normal setup packet */ - packet_size = (uint16_t)read_16bitLE(offset, streamFile) >> 2; - if (packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, streamFile); - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ - offset += 2+packet_size; - - /* data starts after triad */ - data->config.data_start_offset = offset; - - return 1; - -fail: - return 0; -} - - -int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) { - size_t bytes; - - /* get next packet size from the OGL 16b header (upper 14b) */ - data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile) >> 2; - stream->offset += 2; - if (data->op.bytes == 0 || data->op.bytes == 0xFFFF || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */ - - /* read raw block */ - bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile); - stream->offset += data->op.bytes; - if (bytes != data->op.bytes) goto fail; /* wrong packet? */ - - return 1; - -fail: - return 0; -} - -#endif +#include "vorbis_custom_decoder.h" + +#ifdef VGM_USE_VORBIS +#include + + +/* **************************************************************************** */ +/* EXTERNAL API */ +/* **************************************************************************** */ + +/** + * OGL removes the Ogg layer and uses 16b packet headers, that have the size of the next packet, but + * the lower 2b need to be removed (usually 00 but 01 for the id packet, not sure about the meaning). + */ +int vorbis_custom_setup_init_ogl(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data) { + off_t offset = start_offset; + size_t packet_size; + + /* read 3 packets with triad (id/comment/setup), each with an OGL header */ + + /* normal identificacion packet */ + packet_size = (uint16_t)read_16bitLE(offset, sf) >> 2; + if (packet_size > data->buffer_size) goto fail; + data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, sf); + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ + offset += 2+packet_size; + + /* normal comment packet */ + packet_size = (uint16_t)read_16bitLE(offset, sf) >> 2; + if (packet_size > data->buffer_size) goto fail; + data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, sf); + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ + offset += 2+packet_size; + + /* normal setup packet */ + packet_size = (uint16_t)read_16bitLE(offset, sf) >> 2; + if (packet_size > data->buffer_size) goto fail; + data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, sf); + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ + offset += 2+packet_size; + + /* data starts after triad */ + data->config.data_start_offset = offset; + + return 1; + +fail: + return 0; +} + + +int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data) { + size_t bytes; + + /* get next packet size from the OGL 16b header (upper 14b) */ + data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile) >> 2; + stream->offset += 2; + if (data->op.bytes == 0 || data->op.bytes == 0xFFFF || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */ + + /* read raw block */ + bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile); + stream->offset += data->op.bytes; + if (bytes != data->op.bytes) goto fail; /* wrong packet? */ + + return 1; + +fail: + return 0; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_sk.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_sk.c index 408a7e200..9688eea8a 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_sk.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_sk.c @@ -1,187 +1,187 @@ -#include "vorbis_custom_decoder.h" - -#ifdef VGM_USE_VORBIS -#include - -/* **************************************************************************** */ -/* DEFS */ -/* **************************************************************************** */ - -static int get_page_info(STREAMFILE *streamFile, off_t page_offset, off_t *out_packet_offset, size_t *out_packet_size, int *out_page_packets, int target_packet); -static int build_header(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile, off_t packet_offset, size_t packet_size); - - -/* **************************************************************************** */ -/* EXTERNAL API */ -/* **************************************************************************** */ - -/** - * SK just replaces the id 0x4F676753 ("OggS") by 0x11534B10 (\11"SK"\10), and the word "vorbis" by "SK" - * in init packets (for obfuscation, surely). So essentially we are parsing regular Ogg here. - * - * A simpler way to implement this would be in ogg_vorbis_file with read callbacks (pretend this is proof of concept). - */ -int vorbis_custom_setup_init_sk(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) { - off_t offset = start_offset; - off_t id_offset = 0, comment_offset = 0, setup_offset = 0; - size_t id_size = 0, comment_size = 0, setup_size = 0; - int page_packets; - - /* rebuild header packets, they are standard except the "vorbis" keyword is replaced by "SK" */ - - /* first page has the id packet */ - if (!get_page_info(streamFile, offset, &id_offset, &id_size, &page_packets, 0)) goto fail; - if (page_packets != 1) goto fail; - offset = id_offset + id_size; - - /* second page has the comment and setup packets */ - if (!get_page_info(streamFile, offset, &comment_offset, &comment_size, &page_packets, 0)) goto fail; - if (page_packets != 2) goto fail; - if (!get_page_info(streamFile, offset, &setup_offset, &setup_size, &page_packets, 1)) goto fail; - if (page_packets != 2) goto fail; - offset = comment_offset + comment_size + setup_size; - - - /* init with all offsets found */ - data->op.bytes = build_header(data->buffer, data->buffer_size, streamFile, id_offset, id_size); - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ - - data->op.bytes = build_header(data->buffer, data->buffer_size, streamFile, comment_offset, comment_size); - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ - - data->op.bytes = build_header(data->buffer, data->buffer_size, streamFile, setup_offset, setup_size); - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ - - /* data starts after triad */ - data->config.data_start_offset = offset; - - return 1; - -fail: - return 0; -} - - -int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) { - off_t packet_offset = 0; - size_t packet_size = 0; - int page_packets; - int res; - - /* read OggS/SK page and get current packet */ - res = get_page_info(stream->streamfile, stream->offset, &packet_offset, &packet_size, &page_packets, data->current_packet); - data->current_packet++; - if (!res || packet_size > data->buffer_size) goto fail; - - /* read raw block */ - data->op.bytes = read_streamfile(data->buffer, packet_offset, packet_size, stream->streamfile); - if (data->op.bytes != packet_size) goto fail; /* wrong packet? */ - - /* go next page when processed all packets in page */ - if (data->current_packet >= page_packets) { - if (!get_page_info(stream->streamfile, stream->offset, &packet_offset, &packet_size, &page_packets, -1)) goto fail; - stream->offset = packet_offset + packet_size; - data->current_packet = 0; - } - - return 1; - -fail: - return 0; -} - -/* **************************************************************************** */ -/* INTERNAL HELPERS */ -/* **************************************************************************** */ - -/** - * Get packet info from an Ogg page, from segment/packet N (-1 = all segments) - * - * Page format: - * 0x00(4): capture pattern ("OggS") - * 0x01(1): stream structure version - * 0x05(1): header type flag - * 0x06(8): absolute granule position - * 0x0e(4): stream serial number - * 0x12(4): page sequence number - * 0x16(4): page checksum - * 0x1a(1): page segments (total bytes in segment table) - * 0x1b(n): segment table (N bytes, 1 packet is sum of sizes until != 0xFF) - * 0x--(n): data - * Reference: https://xiph.org/ogg/doc/framing.html - */ -static int get_page_info(STREAMFILE *streamFile, off_t page_offset, off_t *out_packet_offset, size_t *out_packet_size, int *out_page_packets, int target_packet) { - off_t table_offset, current_packet_offset, target_packet_offset = 0; - size_t total_packets_size = 0, current_packet_size = 0, target_packet_size = 0; - int page_packets = 0; - uint8_t segments; - int i; - - - if (read_32bitBE(page_offset+0x00, streamFile) != 0x11534B10) /* \11"SK"\10 */ - goto fail; /* not a valid page */ - /* No point on validating other stuff, but they look legal enough (CRC too it seems) */ - - segments = (uint8_t)read_8bit(page_offset+0x1a, streamFile); - - table_offset = page_offset + 0x1b; - current_packet_offset = page_offset + 0x1b + segments; /* first packet starts after segments */ - - /* process segments */ - for (i = 0; i < segments; i++) { - uint8_t segment_size = (uint8_t)read_8bit(table_offset, streamFile); - total_packets_size += segment_size; - current_packet_size += segment_size; - table_offset += 0x01; - - if (segment_size != 0xFF) { /* packet complete */ - page_packets++; - - if (target_packet+1 == page_packets) { - target_packet_offset = current_packet_offset; - target_packet_size = current_packet_size; - } - - /* keep reading to fill page_packets */ - current_packet_offset += current_packet_size; /* move to next packet */ - current_packet_size = 0; - } - } - - /* < 0 is accepted and returns first offset and all packets sizes */ - if (target_packet+1 > page_packets) goto fail; - if (target_packet < 0) { - target_packet_offset = page_offset + 0x1b + segments; /* first */ - target_packet_size = total_packets_size; - } - - if (out_packet_offset) *out_packet_offset = target_packet_offset; - if (out_packet_size) *out_packet_size = target_packet_size; - if (out_page_packets) *out_page_packets = page_packets; - - return 1; - -fail: - //VGM_LOG("SK Vorbis: failed to read page @ 0x%08lx\n", page_offset); - return 0; -} - -/* rebuild a "SK" header packet to a "vorbis" one */ -static int build_header(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile, off_t packet_offset, size_t packet_size) { - int bytes; - - if (0x07+packet_size-0x03 > bufsize) return 0; - - put_8bit (buf+0x00, read_8bit(packet_offset,streamFile)); /* packet_type */ - memcpy (buf+0x01, "vorbis", 6); /* id */ - bytes = read_streamfile(buf+0x07,packet_offset+0x03, packet_size-0x03,streamFile); /* copy rest (all except id+"SK") */ - if (packet_size-0x03 != bytes) - return 0; - - return 0x07+packet_size-0x03; -} - -#endif +#include "vorbis_custom_decoder.h" + +#ifdef VGM_USE_VORBIS +#include + +/* **************************************************************************** */ +/* DEFS */ +/* **************************************************************************** */ + +static int get_page_info(STREAMFILE* sf, off_t page_offset, off_t* p_packet_offset, size_t* p_packet_size, int* p_page_packets, int target_packet); +static int build_header(uint8_t* buf, size_t bufsize, STREAMFILE* sf, off_t packet_offset, size_t packet_size); + + +/* **************************************************************************** */ +/* EXTERNAL API */ +/* **************************************************************************** */ + +/** + * SK just replaces the id 0x4F676753 ("OggS") by 0x11534B10 (\11"SK"\10), and the word "vorbis" by "SK" + * in init packets (for obfuscation, surely). So essentially we are parsing regular Ogg here. + * + * A simpler way to implement this would be in ogg_vorbis_file with read callbacks (pretend this is proof of concept). + */ +int vorbis_custom_setup_init_sk(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data) { + off_t offset = start_offset; + off_t id_offset = 0, comment_offset = 0, setup_offset = 0; + size_t id_size = 0, comment_size = 0, setup_size = 0; + int page_packets; + + /* rebuild header packets, they are standard except the "vorbis" keyword is replaced by "SK" */ + + /* first page has the id packet */ + if (!get_page_info(sf, offset, &id_offset, &id_size, &page_packets, 0)) goto fail; + if (page_packets != 1) goto fail; + offset = id_offset + id_size; + + /* second page has the comment and setup packets */ + if (!get_page_info(sf, offset, &comment_offset, &comment_size, &page_packets, 0)) goto fail; + if (page_packets != 2) goto fail; + if (!get_page_info(sf, offset, &setup_offset, &setup_size, &page_packets, 1)) goto fail; + if (page_packets != 2) goto fail; + offset = comment_offset + comment_size + setup_size; + + + /* init with all offsets found */ + data->op.bytes = build_header(data->buffer, data->buffer_size, sf, id_offset, id_size); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ + + data->op.bytes = build_header(data->buffer, data->buffer_size, sf, comment_offset, comment_size); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ + + data->op.bytes = build_header(data->buffer, data->buffer_size, sf, setup_offset, setup_size); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ + + /* data starts after triad */ + data->config.data_start_offset = offset; + + return 1; + +fail: + return 0; +} + + +int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data) { + off_t packet_offset = 0; + size_t packet_size = 0; + int page_packets; + int res; + + /* read OggS/SK page and get current packet */ + res = get_page_info(stream->streamfile, stream->offset, &packet_offset, &packet_size, &page_packets, data->current_packet); + data->current_packet++; + if (!res || packet_size > data->buffer_size) goto fail; + + /* read raw block */ + data->op.bytes = read_streamfile(data->buffer, packet_offset, packet_size, stream->streamfile); + if (data->op.bytes != packet_size) goto fail; /* wrong packet? */ + + /* go next page when processed all packets in page */ + if (data->current_packet >= page_packets) { + if (!get_page_info(stream->streamfile, stream->offset, &packet_offset, &packet_size, &page_packets, -1)) goto fail; + stream->offset = packet_offset + packet_size; + data->current_packet = 0; + } + + return 1; + +fail: + return 0; +} + +/* **************************************************************************** */ +/* INTERNAL HELPERS */ +/* **************************************************************************** */ + +/** + * Get packet info from an Ogg page, from segment/packet N (-1 = all segments) + * + * Page format: + * 0x00(4): capture pattern ("OggS") + * 0x01(1): stream structure version + * 0x05(1): header type flag + * 0x06(8): absolute granule position + * 0x0e(4): stream serial number + * 0x12(4): page sequence number + * 0x16(4): page checksum + * 0x1a(1): page segments (total bytes in segment table) + * 0x1b(n): segment table (N bytes, 1 packet is sum of sizes until != 0xFF) + * 0x--(n): data + * Reference: https://xiph.org/ogg/doc/framing.html + */ +static int get_page_info(STREAMFILE* sf, off_t page_offset, off_t* p_packet_offset, size_t* p_packet_size, int* p_page_packets, int target_packet) { + off_t table_offset, current_packet_offset, target_packet_offset = 0; + size_t total_packets_size = 0, current_packet_size = 0, target_packet_size = 0; + int page_packets = 0; + uint8_t segments; + int i; + + + if (read_32bitBE(page_offset+0x00, sf) != 0x11534B10) /* \11"SK"\10 */ + goto fail; /* not a valid page */ + /* No point on validating other stuff, but they look legal enough (CRC too it seems) */ + + segments = (uint8_t)read_8bit(page_offset+0x1a, sf); + + table_offset = page_offset + 0x1b; + current_packet_offset = page_offset + 0x1b + segments; /* first packet starts after segments */ + + /* process segments */ + for (i = 0; i < segments; i++) { + uint8_t segment_size = (uint8_t)read_8bit(table_offset, sf); + total_packets_size += segment_size; + current_packet_size += segment_size; + table_offset += 0x01; + + if (segment_size != 0xFF) { /* packet complete */ + page_packets++; + + if (target_packet+1 == page_packets) { + target_packet_offset = current_packet_offset; + target_packet_size = current_packet_size; + } + + /* keep reading to fill page_packets */ + current_packet_offset += current_packet_size; /* move to next packet */ + current_packet_size = 0; + } + } + + /* < 0 is accepted and returns first offset and all packets sizes */ + if (target_packet+1 > page_packets) goto fail; + if (target_packet < 0) { + target_packet_offset = page_offset + 0x1b + segments; /* first */ + target_packet_size = total_packets_size; + } + + if (p_packet_offset) *p_packet_offset = target_packet_offset; + if (p_packet_size) *p_packet_size = target_packet_size; + if (p_page_packets) *p_page_packets = page_packets; + + return 1; + +fail: + //VGM_LOG("SK Vorbis: failed to read page @ 0x%08lx\n", page_offset); + return 0; +} + +/* rebuild a "SK" header packet to a "vorbis" one */ +static int build_header(uint8_t* buf, size_t bufsize, STREAMFILE* sf, off_t packet_offset, size_t packet_size) { + int bytes; + + if (0x07+packet_size-0x03 > bufsize) return 0; + + put_8bit (buf+0x00, read_8bit(packet_offset,sf)); /* packet_type */ + memcpy (buf+0x01, "vorbis", 6); /* id */ + bytes = read_streamfile(buf+0x07,packet_offset+0x03, packet_size-0x03,sf); /* copy rest (all except id+"SK") */ + if (packet_size-0x03 != bytes) + return 0; + + return 0x07+packet_size-0x03; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_vid1.c b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_vid1.c index ec869db33..e93d9e603 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_vid1.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_vid1.c @@ -1,5 +1,8 @@ #include "vorbis_custom_decoder.h" +#define BITSTREAM_READ_ONLY /* config */ +#include "vorbis_bitreader.h" + #ifdef VGM_USE_VORBIS #include @@ -8,8 +11,8 @@ /* DEFS */ /* **************************************************************************** */ -static int get_packet_header(STREAMFILE *streamFile, off_t *offset, size_t *size); -static int build_header_comment(uint8_t * buf, size_t bufsize); +static int get_packet_header(STREAMFILE* sf, off_t* offset, size_t* size); +static int build_header_comment(uint8_t* buf, size_t bufsize); /* **************************************************************************** */ @@ -21,16 +24,16 @@ static int build_header_comment(uint8_t * buf, size_t bufsize); * * Info from hcs's vid1_2ogg: https://github.com/hcs64/vgm_ripping/tree/master/demux/vid1_2ogg */ -int vorbis_custom_setup_init_vid1(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) { +int vorbis_custom_setup_init_vid1(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data) { off_t offset = start_offset; size_t packet_size = 0; /* read header packets (id/setup), each with an VID1 header */ /* normal identificacion packet */ - get_packet_header(streamFile, &offset, &packet_size); + get_packet_header(sf, &offset, &packet_size); if (packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset,packet_size, streamFile); + data->op.bytes = read_streamfile(data->buffer,offset,packet_size, sf); if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ offset += packet_size; @@ -40,9 +43,9 @@ int vorbis_custom_setup_init_vid1(STREAMFILE *streamFile, off_t start_offset, vo if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ /* normal setup packet */ - get_packet_header(streamFile, &offset, &packet_size); + get_packet_header(sf, &offset, &packet_size); if (packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset,packet_size, streamFile); + data->op.bytes = read_streamfile(data->buffer,offset,packet_size, sf); if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ offset += packet_size; @@ -53,7 +56,7 @@ fail: } -int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) { +int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data) { size_t bytes; @@ -100,7 +103,7 @@ fail: /* INTERNAL HELPERS */ /* **************************************************************************** */ -static int build_header_comment(uint8_t * buf, size_t bufsize) { +static int build_header_comment(uint8_t* buf, size_t bufsize) { int bytes = 0x19; if (bytes > bufsize) return 0; @@ -116,26 +119,24 @@ static int build_header_comment(uint8_t * buf, size_t bufsize) { } /* read header in Vorbis bitpacking format */ -static int get_packet_header(STREAMFILE *streamFile, off_t *offset, size_t *size) { +static int get_packet_header(STREAMFILE* sf, off_t* offset, size_t* size) { uint8_t ibuf[0x04]; /* header buffer */ size_t ibufsize = 0x04; /* header ~max */ - vgm_bitstream ib = {0}; + bitstream_t ib = {0}; uint32_t size_bits; - if (read_streamfile(ibuf,(*offset),ibufsize, streamFile) != ibufsize) + if (read_streamfile(ibuf,(*offset),ibufsize, sf) != ibufsize) goto fail; - ib.buf = ibuf; - ib.bufsize = ibufsize; - ib.b_off = 0; - ib.mode = BITSTREAM_VORBIS; + + init_bitstream(&ib, ibuf, ibufsize); /* read using Vorbis weird LSF */ - r_bits(&ib, 4,&size_bits); - r_bits(&ib, (size_bits+1),(uint32_t*)size); + rv_bits(&ib, 4,&size_bits); + rv_bits(&ib, (size_bits+1),(uint32_t*)size); /* special meaning, seen in silent frames */ - if (size_bits == 0 && *size == 0 && (uint8_t)read_8bit(*offset, streamFile)==0x80) { + if (size_bits == 0 && *size == 0 && (uint8_t)read_8bit(*offset, sf) == 0x80) { *size = 0x01; } 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 eff24352b..1eeb38aff 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/vorbis_custom_utils_wwise.c @@ -1,1222 +1,1263 @@ -#include "vorbis_custom_decoder.h" - -#ifdef VGM_USE_VORBIS -#include - -#define WWISE_VORBIS_USE_PRECOMPILED_WVC 1 /* if enabled vgmstream weights ~150kb more but doesn't need external .wvc packets */ -#if WWISE_VORBIS_USE_PRECOMPILED_WVC -#include "vorbis_custom_data_wwise.h" -#endif - - -/* **************************************************************************** */ -/* DEFS */ -/* **************************************************************************** */ - -static size_t build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long); -static size_t build_header_comment(uint8_t * buf, size_t bufsize); -static size_t get_packet_header(STREAMFILE *streamFile, off_t offset, wwise_header_t header_type, int * granulepos, size_t * packet_size, int big_endian); -static size_t rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian); -static size_t rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian, int channels); - -static int ww2ogg_generate_vorbis_packet(vgm_bitstream * ow, vgm_bitstream * iw, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian); -static int ww2ogg_generate_vorbis_setup(vgm_bitstream * ow, vgm_bitstream * iw, vorbis_custom_codec_data * data, int channels, size_t packet_size, STREAMFILE *streamFile); -static int ww2ogg_codebook_library_copy(vgm_bitstream * ow, vgm_bitstream * iw); -static int ww2ogg_codebook_library_rebuild(vgm_bitstream * ow, vgm_bitstream * iw, size_t cb_size, STREAMFILE *streamFile); -static int ww2ogg_codebook_library_rebuild_by_id(vgm_bitstream * ow, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE *streamFile); -static int ww2ogg_tremor_ilog(unsigned int v); -static unsigned int ww2ogg_tremor_book_maptype1_quantvals(unsigned int entries, unsigned int dimensions); - -static int load_wvc(uint8_t * ibuf, size_t ibufsize, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE *streamFile); -static int load_wvc_file(uint8_t * buf, size_t bufsize, uint32_t codebook_id, STREAMFILE *streamFile); -static int load_wvc_array(uint8_t * buf, size_t bufsize, uint32_t codebook_id, wwise_setup_t setup_type); - - -/* **************************************************************************** */ -/* EXTERNAL API */ -/* **************************************************************************** */ - -/** - * Wwise stores a reduced setup, and packets have mini headers with the size, and data packets - * may reduced as well. The format evolved over time so there are many variations. - * The Wwise implementation uses Tremor (fixed-point Vorbis) but shouldn't matter. - * - * Format reverse-engineered by hcs in ww2ogg (https://github.com/hcs64/ww2ogg). - */ -int vorbis_custom_setup_init_wwise(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) { - size_t header_size, packet_size; - vorbis_custom_config cfg = data->config; - - 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; - - /* normal identificacion packet */ - header_size = get_packet_header(streamFile, offset, cfg.header_type, (int*)&data->op.granulepos, &packet_size, cfg.big_endian); - if (!header_size || packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset+header_size,packet_size, streamFile); - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ - offset += header_size + packet_size; - - /* normal comment packet */ - header_size = get_packet_header(streamFile, offset, cfg.header_type, (int*)&data->op.granulepos, &packet_size, cfg.big_endian); - if (!header_size || packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset+header_size,packet_size, streamFile); - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ - offset += header_size + packet_size; - - /* normal setup packet */ - header_size = get_packet_header(streamFile, offset, cfg.header_type, (int*)&data->op.granulepos, &packet_size, cfg.big_endian); - if (!header_size || packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset+header_size,packet_size, streamFile); - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ - offset += header_size + packet_size; - } - else { - /* rebuild headers */ - - /* new identificacion packet */ - data->op.bytes = build_header_identification(data->buffer, data->buffer_size, cfg.channels, cfg.sample_rate, cfg.blocksize_0_exp, cfg.blocksize_1_exp); - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ - - /* new comment packet */ - data->op.bytes = build_header_comment(data->buffer, data->buffer_size); - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ - - /* rebuild setup packet */ - data->op.bytes = rebuild_setup(data->buffer, data->buffer_size, streamFile, start_offset, data, cfg.big_endian, cfg.channels); - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ - } - - return 1; - -fail: - return 0; -} - - -int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) { - size_t header_size, packet_size = 0; - - /* reconstruct a Wwise packet, if needed; final bytes may be bigger than packet_size so we get the header offsets here */ - header_size = get_packet_header(stream->streamfile, stream->offset, data->config.header_type, (int*)&data->op.granulepos, &packet_size, data->config.big_endian); - if (!header_size || packet_size > data->buffer_size) goto fail; - - data->op.bytes = rebuild_packet(data->buffer, data->buffer_size, stream->streamfile,stream->offset, data, data->config.big_endian); - stream->offset += header_size + packet_size; - if (!data->op.bytes || data->op.bytes >= 0xFFFF) goto fail; - - return 1; - -fail: - return 0; -} - -/* **************************************************************************** */ -/* INTERNAL HELPERS */ -/* **************************************************************************** */ - -/* loads info from a wwise packet header */ -static size_t get_packet_header(STREAMFILE *streamFile, off_t offset, wwise_header_t header_type, int * granulepos, size_t * packet_size, int big_endian) { - 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; - - /* packet size doesn't include header size */ - switch(header_type) { - case WWV_TYPE_8: /* size 4+4 */ - *packet_size = (uint32_t)read_32bit(offset, streamFile); - *granulepos = read_32bit(offset+4, streamFile); - return 8; - - case WWV_TYPE_6: /* size 4+2 */ - *packet_size = (uint16_t)read_16bit(offset, streamFile); - *granulepos = read_32bit(offset+2, streamFile); - return 6; - - 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; - break; - default: /* ? */ - return 0; - } -} - -/* Transforms a Wwise data packet into a real Vorbis one (depending on config) */ -static size_t rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian) { - vgm_bitstream ow, iw; - int rc, granulepos; - size_t header_size, packet_size; - - size_t ibufsize = 0x8000; /* arbitrary max size of a setup packet */ - uint8_t ibuf[0x8000]; /* Wwise setup packet buffer */ - if (obufsize < ibufsize) goto fail; /* arbitrary expected min */ - - header_size = get_packet_header(streamFile, offset, data->config.header_type, &granulepos, &packet_size, big_endian); - if (!header_size || packet_size > obufsize) goto fail; - - /* load Wwise data into internal buffer */ - if (read_streamfile(ibuf,offset+header_size,packet_size, streamFile)!=packet_size) - goto fail; - - /* prepare helper structs */ - ow.buf = obuf; - ow.bufsize = obufsize; - ow.b_off = 0; - ow.mode = BITSTREAM_VORBIS; - - iw.buf = ibuf; - iw.bufsize = ibufsize; - iw.b_off = 0; - iw.mode = BITSTREAM_VORBIS; - - rc = ww2ogg_generate_vorbis_packet(&ow,&iw, streamFile,offset, data, big_endian); - if (!rc) goto fail; - - if (ow.b_off % 8 != 0) { - //VGM_LOG("Wwise Vorbis: didn't write exactly audio packet: 0x%lx + %li bits\n", ow.b_off / 8, ow.b_off % 8); - goto fail; - } - - - return ow.b_off / 8; -fail: - return 0; -} - - -/* Transforms a Wwise setup packet into a real Vorbis one (depending on config). */ -static size_t rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian, int channels) { - vgm_bitstream ow, iw; - int rc, granulepos; - size_t header_size, packet_size; - - size_t ibufsize = 0x8000; /* arbitrary max size of a setup packet */ - uint8_t ibuf[0x8000]; /* Wwise setup packet buffer */ - if (obufsize < ibufsize) goto fail; /* arbitrary expected min */ - - /* read Wwise packet header */ - header_size = get_packet_header(streamFile, offset, data->config.header_type, &granulepos, &packet_size, big_endian); - if (!header_size || packet_size > ibufsize) goto fail; - - /* load Wwise setup into internal buffer */ - if (read_streamfile(ibuf,offset+header_size,packet_size, streamFile)!=packet_size) - goto fail; - - /* prepare helper structs */ - ow.buf = obuf; - ow.bufsize = obufsize; - ow.b_off = 0; - ow.mode = BITSTREAM_VORBIS; - - iw.buf = ibuf; - iw.bufsize = ibufsize; - iw.b_off = 0; - iw.mode = BITSTREAM_VORBIS; - - rc = ww2ogg_generate_vorbis_setup(&ow,&iw, data, channels, packet_size, streamFile); - if (!rc) goto fail; - - if (ow.b_off % 8 != 0) { - //VGM_LOG("Wwise Vorbis: didn't write exactly setup packet: 0x%lx + %li bits\n", ow.b_off / 8, ow.b_off % 8); - goto fail; - } - - - return ow.b_off / 8; -fail: - return 0; -} - -static size_t build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_0_exp, int blocksize_1_exp) { - size_t bytes = 0x1e; - uint8_t blocksizes; - - if (bytes > bufsize) return 0; - - blocksizes = (blocksize_0_exp << 4) | (blocksize_1_exp); - - put_8bit (buf+0x00, 0x01); /* packet_type (id) */ - memcpy (buf+0x01, "vorbis", 6); /* id */ - put_32bitLE(buf+0x07, 0x00); /* vorbis_version (fixed) */ - put_8bit (buf+0x0b, channels); /* audio_channels */ - put_32bitLE(buf+0x0c, sample_rate); /* audio_sample_rate */ - put_32bitLE(buf+0x10, 0x00); /* bitrate_maximum (optional hint) */ - put_32bitLE(buf+0x14, 0x00); /* bitrate_nominal (optional hint) */ - put_32bitLE(buf+0x18, 0x00); /* bitrate_minimum (optional hint) */ - put_8bit (buf+0x1c, blocksizes); /* blocksize_0 + blocksize_1 nibbles */ - put_8bit (buf+0x1d, 0x01); /* framing_flag (fixed) */ - - return bytes; -} - -static size_t build_header_comment(uint8_t * buf, size_t bufsize) { - size_t bytes = 0x19; - - if (bytes > bufsize) return 0; - - put_8bit (buf+0x00, 0x03); /* packet_type (comments) */ - memcpy (buf+0x01, "vorbis", 6); /* id */ - put_32bitLE(buf+0x07, 0x09); /* vendor_length */ - memcpy (buf+0x0b, "vgmstream", 9); /* vendor_string */ - put_32bitLE(buf+0x14, 0x00); /* user_comment_list_length */ - put_8bit (buf+0x18, 0x01); /* framing_flag (fixed) */ - - return bytes; -} - -/* **************************************************************************** */ -/* INTERNAL WW2OGG STUFF */ -/* **************************************************************************** */ -/* The following code was mostly and manually converted from hcs's ww2ogg. - * Could be simplified but roughly tries to preserve the structure in case fixes have to be backported. - * - * Some validations are ommited (ex. read/write), as incorrect data should be rejected by libvorbis. - * To avoid GCC complaining all values are init to 0, and some that do need it are init again, for clarity. - * Reads/writes unsigned ints as most are bit values less than 32 and with no sign meaning. - */ - -/* Copy packet as-is or rebuild first byte if mod_packets is used. - * (ref: https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-720004.3) */ -static int ww2ogg_generate_vorbis_packet(vgm_bitstream * ow, vgm_bitstream * iw, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian) { - int i,granule; - size_t header_size, packet_size, data_size; - - header_size = get_packet_header(streamFile,offset, data->config.header_type, &granule, &packet_size, big_endian); - if (!header_size || packet_size > iw->bufsize) goto fail; - - data_size = get_streamfile_size(streamFile);//todo get external data_size - - if (offset + header_size + packet_size > data_size) { - VGM_LOG("Wwise Vorbis: page header truncated\n"); - goto fail; - } - - /* this may happen in the first packet; maybe it's for the encoder delay but doesn't seem to affect libvorbis */ - //VGM_ASSERT(granule < 0, "Wwise Vorbis: negative granule %i @ 0x%lx\n", granule, offset); - - - 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; - - if (!data->mode_blockflag) { /* config error */ - VGM_LOG("Wwise Vorbis: didn't load mode_blockflag\n"); - goto fail; - } - - /* audio packet type */ - packet_type = 0; - w_bits(ow, 1, packet_type); - - /* collect this packet mode from the first byte */ - r_bits(iw, data->mode_bits,&mode_number); /* max 6b */ - w_bits(ow, data->mode_bits, mode_number); - r_bits(iw, 8-data->mode_bits,&remainder); - - /* adjust window info */ - if (data->mode_blockflag[mode_number]) { - /* long window: peek at next frame to find flags */ - off_t next_offset = offset + header_size + packet_size; - uint32_t next_blockflag = 0, prev_window_type = 0, next_window_type = 0; - - next_blockflag = 0; - /* check if more data / not eof */ - if (next_offset + header_size <= data_size) { - size_t next_header_size, next_packet_size; - int next_granule; - - next_header_size = get_packet_header(streamFile,next_offset, data->config.header_type, &next_granule, &next_packet_size, big_endian); - if (!next_header_size) goto fail; - - if (next_packet_size > 0) { - /* get next first byte to read next_mode_number */ - uint32_t next_mode_number; - uint8_t nbuf[1]; - vgm_bitstream nw; - - nw.buf = nbuf; - nw.bufsize = 1; - nw.b_off = 0; - nw.mode = BITSTREAM_VORBIS; - - - if (read_streamfile(nw.buf, next_offset + next_header_size, nw.bufsize, streamFile) != nw.bufsize) - goto fail; - - r_bits(&nw, data->mode_bits,&next_mode_number); /* max 6b */ - - next_blockflag = data->mode_blockflag[next_mode_number]; - } - } - - prev_window_type = data->prev_blockflag; - w_bits(ow, 1, prev_window_type); - - next_window_type = next_blockflag; - w_bits(ow, 1, next_window_type); - } - - data->prev_blockflag = data->mode_blockflag[mode_number]; /* save for next packet */ - - w_bits(ow, 8-data->mode_bits, remainder); /* this *isn't* byte aligned (ex. could be 10 bits written) */ - } - else { - /* normal packets: first byte unchanged */ - uint32_t c = 0; - - r_bits(iw, 8, &c); - w_bits(ow, 8, c); - } - - - /* remainder of packet (not byte-aligned when using mod_packets) */ - for (i = 1; i < packet_size; i++) { - uint32_t c = 0; - - r_bits(iw, 8, &c); - w_bits(ow, 8, c); - } - - /* remove trailing garbage bits */ - if (ow->b_off % 8 != 0) { - uint32_t padding = 0; - int padding_bits = 8 - (ow->b_off % 8); - - w_bits(ow, padding_bits, padding); - } - - - return 1; -fail: - return 0; -} - - -/* Rebuild a Wwise setup (simplified with removed stuff), recreating all six setup parts. - * (ref: https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-650004.2.4) */ -static int ww2ogg_generate_vorbis_setup(vgm_bitstream * ow, vgm_bitstream * iw, vorbis_custom_codec_data * data, int channels, size_t packet_size, STREAMFILE *streamFile) { - int i,j,k; - uint32_t codebook_count = 0, floor_count = 0, residue_count = 0; - uint32_t codebook_count_less1 = 0; - uint32_t time_count_less1 = 0, dummy_time_value = 0; - - - /* packet header */ - put_8bit(ow->buf+0x00, 0x05); /* packet_type (setup) */ - memcpy (ow->buf+0x01, "vorbis", 6); /* id */ - ow->b_off += (1+6) * 8; /* bit offset of output (Vorbis) setup, after fake type + id */ - - - /* Codebooks */ - r_bits(iw, 8,&codebook_count_less1); - w_bits(ow, 8, codebook_count_less1); - codebook_count = codebook_count_less1 + 1; - - 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 == 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; - } - } - else { - /* rebuild Wwise codebooks: external (referenced by id) in simplified format */ - for (i = 0; i < codebook_count; i++) { - int rc; - uint32_t codebook_id = 0; - - r_bits(iw, 10,&codebook_id); - - rc = ww2ogg_codebook_library_rebuild_by_id(ow, codebook_id, data->config.setup_type, streamFile); - if (!rc) goto fail; - } - } - - - /* Time domain transforms */ - time_count_less1 = 0; - w_bits(ow, 6, time_count_less1); - dummy_time_value = 0; - w_bits(ow, 16, dummy_time_value); - - - 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; - uint32_t setup_packet_size_bits = packet_size*8; - - while (total_bits_read < setup_packet_size_bits) { - r_bits(iw, 1,&bitly); - w_bits(ow, 1, bitly); - total_bits_read = iw->b_off; - } - } - else { - /* rest of setup is altered, reconstruct */ - uint32_t floor_count_less1 = 0, floor1_multiplier_less1 = 0, rangebits = 0; - uint32_t residue_count_less1 = 0; - uint32_t mapping_count_less1 = 0, mapping_count = 0; - uint32_t mode_count_less1 = 0, mode_count = 0; - - - /* Floors */ - r_bits(iw, 6,&floor_count_less1); - w_bits(ow, 6, floor_count_less1); - floor_count = floor_count_less1 + 1; - - for (i = 0; i < floor_count; i++) { - uint32_t floor_type = 0, floor1_partitions = 0; - uint32_t maximum_class = 0; - uint32_t floor1_partition_class_list[32]; /* max 5b */ - uint32_t floor1_class_dimensions_list[16+1]; /* max 4b+1 */ - - // Always floor type 1 - floor_type = 1; - w_bits(ow, 16, floor_type); - - r_bits(iw, 5,&floor1_partitions); - w_bits(ow, 5, floor1_partitions); - - memset(floor1_partition_class_list, 0, sizeof(uint32_t)*32); - - maximum_class = 0; - for (j = 0; j < floor1_partitions; j++) { - uint32_t floor1_partition_class = 0; - - r_bits(iw, 4,&floor1_partition_class); - w_bits(ow, 4, floor1_partition_class); - - floor1_partition_class_list[j] = floor1_partition_class; - - if (floor1_partition_class > maximum_class) - maximum_class = floor1_partition_class; - } - - memset(floor1_class_dimensions_list, 0, sizeof(uint32_t)*(16+1)); - - for (j = 0; j <= maximum_class; j++) { - uint32_t class_dimensions_less1 = 0, class_subclasses = 0; - - r_bits(iw, 3,&class_dimensions_less1); - w_bits(ow, 3, class_dimensions_less1); - - floor1_class_dimensions_list[j] = class_dimensions_less1 + 1; - - r_bits(iw, 2,&class_subclasses); - w_bits(ow, 2, class_subclasses); - - if (0 != class_subclasses) { - uint32_t masterbook = 0; - - r_bits(iw, 8,&masterbook); - w_bits(ow, 8, masterbook); - - if (masterbook >= codebook_count) { - VGM_LOG("Wwise Vorbis: invalid floor1 masterbook\n"); - goto fail; - } - } - - for (k = 0; k < (1U<= 0 && subclass_book >= codebook_count) { - VGM_LOG("Wwise Vorbis: invalid floor1 subclass book\n"); - goto fail; - } - } - } - - r_bits(iw, 2,&floor1_multiplier_less1); - w_bits(ow, 2, floor1_multiplier_less1); - - r_bits(iw, 4,&rangebits); - w_bits(ow, 4, rangebits); - - for (j = 0; j < floor1_partitions; j++) { - uint32_t current_class_number = 0; - - current_class_number = floor1_partition_class_list[j]; - for (k = 0; k < floor1_class_dimensions_list[current_class_number]; k++) { - uint32_t X = 0; /* max 4b (15) */ - - r_bits(iw, rangebits,&X); - w_bits(ow, rangebits, X); - } - } - } - - - /* Residues */ - r_bits(iw, 6,&residue_count_less1); - w_bits(ow, 6, residue_count_less1); - residue_count = residue_count_less1 + 1; - - for (i = 0; i < residue_count; i++) { - uint32_t residue_type = 0, residue_classifications = 0; - uint32_t residue_begin = 0, residue_end = 0, residue_partition_size_less1 = 0, residue_classifications_less1 = 0, residue_classbook = 0; - uint32_t residue_cascade[64+1]; /* 6b +1 */ - - r_bits(iw, 2,&residue_type); - w_bits(ow, 16, residue_type); /* 2b to 16b */ - - if (residue_type > 2) { - VGM_LOG("Wwise Vorbis: invalid residue type\n"); - goto fail; - } - - r_bits(iw, 24,&residue_begin); - w_bits(ow, 24, residue_begin); - r_bits(iw, 24,&residue_end); - w_bits(ow, 24, residue_end); - r_bits(iw, 24,&residue_partition_size_less1); - w_bits(ow, 24, residue_partition_size_less1); - r_bits(iw, 6,&residue_classifications_less1); - w_bits(ow, 6, residue_classifications_less1); - r_bits(iw, 8,&residue_classbook); - w_bits(ow, 8, residue_classbook); - residue_classifications = residue_classifications_less1 + 1; - - if (residue_classbook >= codebook_count) { - VGM_LOG("Wwise Vorbis: invalid residue classbook\n"); - goto fail; - } - - memset(residue_cascade, 0, sizeof(uint32_t)*(64+1)); - - for (j = 0; j < residue_classifications; j++) { - uint32_t high_bits = 0, low_bits = 0, bitflag = 0; - - high_bits = 0; - - r_bits(iw, 3,&low_bits); - w_bits(ow, 3, low_bits); - - r_bits(iw, 1,&bitflag); - w_bits(ow, 1, bitflag); - if (bitflag) { - r_bits(iw, 5,&high_bits); - w_bits(ow, 5, high_bits); - } - - residue_cascade[j] = high_bits * 8 + low_bits; - } - - for (j = 0; j < residue_classifications; j++) { - for (k = 0; k < 8; k++) { - if (residue_cascade[j] & (1 << k)) { - uint32_t residue_book = 0; - - r_bits(iw, 8,&residue_book); - w_bits(ow, 8, residue_book); - - if (residue_book >= codebook_count) { - VGM_LOG("Wwise Vorbis: invalid residue book\n"); - goto fail; - } - } - } - } - } - - - /* Mappings */ - r_bits(iw, 6,&mapping_count_less1); - w_bits(ow, 6, mapping_count_less1); - mapping_count = mapping_count_less1 + 1; - - for (i = 0; i < mapping_count; i++) { - uint32_t mapping_type = 0, submaps_flag = 0, submaps = 0, square_polar_flag = 0; - uint32_t mapping_reserved = 0; - - // always mapping type 0, the only one - mapping_type = 0; - w_bits(ow, 16, mapping_type); - - r_bits(iw, 1,&submaps_flag); - w_bits(ow, 1, submaps_flag); - - submaps = 1; - if (submaps_flag) { - uint32_t submaps_less1 = 0; - - r_bits(iw, 4,&submaps_less1); - w_bits(ow, 4, submaps_less1); - submaps = submaps_less1 + 1; - } - - r_bits(iw, 1,&square_polar_flag); - w_bits(ow, 1, square_polar_flag); - - if (square_polar_flag) { - uint32_t coupling_steps_less1 = 0, coupling_steps = 0; - - r_bits(iw, 8,&coupling_steps_less1); - w_bits(ow, 8, coupling_steps_less1); - coupling_steps = coupling_steps_less1 + 1; - - for (j = 0; j < coupling_steps; j++) { - uint32_t magnitude = 0, angle = 0; - int magnitude_bits = ww2ogg_tremor_ilog(channels-1); - int angle_bits = ww2ogg_tremor_ilog(channels-1); - - r_bits(iw, magnitude_bits,&magnitude); - w_bits(ow, magnitude_bits, magnitude); - r_bits(iw, angle_bits,&angle); - w_bits(ow, angle_bits, angle); - - if (angle == magnitude || magnitude >= channels || angle >= channels) { - VGM_LOG("Wwise Vorbis: invalid coupling (angle=%i, mag=%i, ch=%i)\n", angle, magnitude,channels); - goto fail; - } - } - } - - // a rare reserved field not removed by Ak! - r_bits(iw, 2,&mapping_reserved); - w_bits(ow, 2, mapping_reserved); - if (0 != mapping_reserved) { - VGM_LOG("Wwise Vorbis: mapping reserved field nonzero\n"); - goto fail; - } - - if (submaps > 1) { - for (j = 0; j < channels; j++) { - uint32_t mapping_mux = 0; - - r_bits(iw, 4,&mapping_mux); - w_bits(ow, 4, mapping_mux); - if (mapping_mux >= submaps) { - VGM_LOG("Wwise Vorbis: mapping_mux >= submaps\n"); - goto fail; - } - } - } - - for (j = 0; j < submaps; j++) { - uint32_t time_config = 0, floor_number = 0, residue_number = 0; - - // Another! Unused time domain transform configuration placeholder! - r_bits(iw, 8,&time_config); - w_bits(ow, 8, time_config); - - r_bits(iw, 8,&floor_number); - w_bits(ow, 8, floor_number); - if (floor_number >= floor_count) { - VGM_LOG("Wwise Vorbis: invalid floor mapping\n"); - goto fail; - } - - r_bits(iw, 8,&residue_number); - w_bits(ow, 8, residue_number); - if (residue_number >= residue_count) { - VGM_LOG("Wwise Vorbis: invalid residue mapping\n"); - goto fail; - } - } - } - - - /* Modes */ - r_bits(iw, 6,&mode_count_less1); - w_bits(ow, 6, mode_count_less1); - mode_count = mode_count_less1 + 1; - - memset(data->mode_blockflag, 0, sizeof(uint8_t)*(64+1)); /* up to max mode_count */ - data->mode_bits = ww2ogg_tremor_ilog(mode_count-1); /* for mod_packets */ - - for (i = 0; i < mode_count; i++) { - uint32_t block_flag = 0, windowtype = 0, transformtype = 0, mapping = 0; - - r_bits(iw, 1,&block_flag); - w_bits(ow, 1, block_flag); - - data->mode_blockflag[i] = (block_flag != 0); /* for mod_packets */ - - windowtype = 0; - transformtype = 0; - w_bits(ow, 16, windowtype); - w_bits(ow, 16, transformtype); - - r_bits(iw, 8,&mapping); - w_bits(ow, 8, mapping); - if (mapping >= mapping_count) { - VGM_LOG("Wwise Vorbis: invalid mode mapping\n"); - goto fail; - } - } - } - - - /* end flag */ - { - uint32_t framing = 0; - - framing = 1; - w_bits(ow, 1, framing); - } - - /* remove trailing garbage bits */ - if (ow->b_off % 8 != 0) { - uint32_t padding = 0; - int padding_bits = 8 - (ow->b_off % 8); - - w_bits(ow, padding_bits, padding); - } - - - return 1; -fail: - return 0; -} - - -/* copies Vorbis codebooks (untouched, but size uncertain) */ -static int ww2ogg_codebook_library_copy(vgm_bitstream * ow, vgm_bitstream * iw) { - int i; - uint32_t id = 0, dimensions = 0, entries = 0; - uint32_t ordered = 0, lookup_type = 0; - - r_bits(iw, 24,&id); - w_bits(ow, 24, id); - r_bits(iw, 16,&dimensions); - w_bits(ow, 16, dimensions); - r_bits(iw, 24,&entries); - w_bits(ow, 24, entries); - - if (0x564342 != id) { /* "VCB" */ - VGM_LOG("Wwise Vorbis: invalid codebook identifier\n"); - goto fail; - } - - /* codeword lengths */ - r_bits(iw, 1,&ordered); - w_bits(ow, 1, ordered); - if (ordered) { - uint32_t initial_length = 0, current_entry = 0; - - r_bits(iw, 5,&initial_length); - w_bits(ow, 5, initial_length); - - current_entry = 0; - while (current_entry < entries) { - uint32_t number = 0; - int number_bits = ww2ogg_tremor_ilog(entries-current_entry); - - r_bits(iw, number_bits,&number); - w_bits(ow, number_bits, number); - current_entry += number; - } - if (current_entry > entries) { - VGM_LOG("Wwise Vorbis: current_entry out of range\n"); - goto fail; - } - } - else { - uint32_t sparse = 0; - - r_bits(iw, 1,&sparse); - w_bits(ow, 1, sparse); - - for (i = 0; i < entries; i++) { - uint32_t present_bool = 0; - - present_bool = 1; - if (sparse) { - uint32_t present = 0; - - r_bits(iw, 1,&present); - w_bits(ow, 1, present); - - present_bool = (0 != present); - } - - if (present_bool) { - uint32_t codeword_length = 0; - - r_bits(iw, 5,&codeword_length); - w_bits(ow, 5, codeword_length); - } - } - } - - - /* lookup table */ - r_bits(iw, 4,&lookup_type); - w_bits(ow, 4, lookup_type); - - if (0 == lookup_type) { - //VGM_LOG("Wwise Vorbis: no lookup table\n"); - } - else if (1 == lookup_type) { - //VGM_LOG("Wwise Vorbis: lookup type 1\n"); - uint32_t quantvals = 0, min = 0, max = 0; - uint32_t value_length = 0, sequence_flag = 0; - - r_bits(iw, 32,&min); - w_bits(ow, 32, min); - r_bits(iw, 32,&max); - w_bits(ow, 32, max); - r_bits(iw, 4,&value_length); - w_bits(ow, 4, value_length); - r_bits(iw, 1,&sequence_flag); - w_bits(ow, 1, sequence_flag); - - quantvals = ww2ogg_tremor_book_maptype1_quantvals(entries, dimensions); - for (i = 0; i < quantvals; i++) { - uint32_t val = 0, val_bits = 0; - val_bits = value_length+1; - - r_bits(iw, val_bits,&val); - w_bits(ow, val_bits, val); - } - } - else if (2 == lookup_type) { - VGM_LOG("Wwise Vorbis: didn't expect lookup type 2\n"); - goto fail; - } - else { - VGM_LOG("Wwise Vorbis: invalid lookup type\n"); - goto fail; - } - - - return 1; -fail: - return 0; -} - - -/* rebuilds a Wwise codebook into a Vorbis codebook */ -static int ww2ogg_codebook_library_rebuild(vgm_bitstream * ow, vgm_bitstream * iw, size_t cb_size, STREAMFILE *streamFile) { - int i; - uint32_t id = 0, dimensions = 0, entries = 0; - uint32_t ordered = 0, lookup_type = 0; - - id = 0x564342; /* "VCB" */ - - w_bits(ow, 24, id); - r_bits(iw, 4,&dimensions); - w_bits(ow, 16, dimensions); /* 4 to 16 */ - r_bits(iw, 14,&entries); - w_bits(ow, 24, entries); /* 14 to 24*/ - - /* codeword lengths */ - r_bits(iw, 1,&ordered); - w_bits(ow, 1, ordered); - if (ordered) { - uint32_t initial_length = 0, current_entry = 0; - - r_bits(iw, 5,&initial_length); - w_bits(ow, 5, initial_length); - - current_entry = 0; - while (current_entry < entries) { - uint32_t number = 0; - int number_bits = ww2ogg_tremor_ilog(entries-current_entry); - - r_bits(iw, number_bits,&number); - w_bits(ow, number_bits, number); - current_entry += number; - } - if (current_entry > entries) { - VGM_LOG("Wwise Vorbis: current_entry out of range\n"); - goto fail; - } - } - else { - uint32_t codeword_length_length = 0, sparse = 0; - - r_bits(iw, 3,&codeword_length_length); - r_bits(iw, 1,&sparse); - w_bits(ow, 1, sparse); - - if (0 == codeword_length_length || 5 < codeword_length_length) { - VGM_LOG("Wwise Vorbis: nonsense codeword length\n"); - goto fail; - } - - for (i = 0; i < entries; i++) { - uint32_t present_bool = 0; - - present_bool = 1; - if (sparse) { - uint32_t present = 0; - - r_bits(iw, 1,&present); - w_bits(ow, 1, present); - - present_bool = (0 != present); - } - - if (present_bool) { - uint32_t codeword_length = 0; - - r_bits(iw, codeword_length_length,&codeword_length); - w_bits(ow, 5, codeword_length); /* max 7 (3b) to 5 */ - } - } - } - - - /* lookup table */ - r_bits(iw, 1,&lookup_type); - w_bits(ow, 4, lookup_type); /* 1 to 4 */ - - if (0 == lookup_type) { - //VGM_LOG("Wwise Vorbis: no lookup table\n"); - } - else if (1 == lookup_type) { - //VGM_LOG("Wwise Vorbis: lookup type 1\n"); - uint32_t quantvals = 0, min = 0, max = 0; - uint32_t value_length = 0, sequence_flag = 0; - - r_bits(iw, 32,&min); - w_bits(ow, 32, min); - r_bits(iw, 32,&max); - w_bits(ow, 32, max); - r_bits(iw, 4,&value_length); - w_bits(ow, 4, value_length); - r_bits(iw, 1,&sequence_flag); - w_bits(ow, 1, sequence_flag); - - quantvals = ww2ogg_tremor_book_maptype1_quantvals(entries, dimensions); - for (i = 0; i < quantvals; i++) { - uint32_t val = 0, val_bits = 0; - val_bits = value_length+1; - - r_bits(iw, val_bits,&val); - w_bits(ow, val_bits, val); - } - } - else if (2 == lookup_type) { - VGM_LOG("Wwise Vorbis: didn't expect lookup type 2\n"); - goto fail; - } - else { - VGM_LOG("Wwise Vorbis: invalid lookup type\n"); - goto fail; - } - - - /* check that we used exactly all bytes */ - /* note: if all bits are used in the last byte there will be one extra 0 byte */ - if ( 0 != cb_size && iw->b_off/8+1 != cb_size ) { - //VGM_LOG("Wwise Vorbis: codebook size mistach (expected 0x%x, wrote 0x%lx)\n", cb_size, iw->b_off/8+1); - goto fail; - } - - return 1; -fail: - return 0; -} - -/* rebuilds an external Wwise codebook referenced by id to a Vorbis codebook */ -static int ww2ogg_codebook_library_rebuild_by_id(vgm_bitstream * ow, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE *streamFile) { - size_t ibufsize = 0x8000; /* arbitrary max size of a codebook */ - uint8_t ibuf[0x8000]; /* Wwise codebook buffer */ - size_t cb_size; - vgm_bitstream iw; - - cb_size = load_wvc(ibuf,ibufsize, codebook_id, setup_type, streamFile); - if (cb_size == 0) goto fail; - - iw.buf = ibuf; - iw.bufsize = ibufsize; - iw.b_off = 0; - iw.mode = BITSTREAM_VORBIS; - - return ww2ogg_codebook_library_rebuild(ow, &iw, cb_size, streamFile); -fail: - return 0; -} - - -/* fixed-point ilog from Xiph's Tremor */ -static int ww2ogg_tremor_ilog(unsigned int v) { - int ret=0; - while(v){ - ret++; - v>>=1; - } - return(ret); -} -/* quantvals-something from Xiph's Tremor */ -static unsigned int ww2ogg_tremor_book_maptype1_quantvals(unsigned int entries, unsigned int dimensions) { - /* get us a starting hint, we'll polish it below */ - int bits=ww2ogg_tremor_ilog(entries); - int vals=entries>>((bits-1)*(dimensions-1)/dimensions); - - while(1){ - unsigned long acc=1; - unsigned long acc1=1; - unsigned int i; - for(i=0;ientries){ - return(vals); - }else{ - if(acc>entries){ - vals--; - }else{ - vals++; - } - } - } -} - - -/* **************************************************************************** */ -/* INTERNAL UTILS */ -/* **************************************************************************** */ - -/* loads an external Wwise Vorbis Codebooks file (wvc) referenced by ID and returns size */ -static int load_wvc(uint8_t * ibuf, size_t ibufsize, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE *streamFile) { - size_t bytes; - - /* try to locate from the precompiled list */ - bytes = load_wvc_array(ibuf, ibufsize, codebook_id, setup_type); - if (bytes) - return bytes; - - /* try to load from external file (ignoring type, just use file if found) */ - bytes = load_wvc_file(ibuf, ibufsize, codebook_id, streamFile); - if (bytes) - return bytes; - - /* not found */ - VGM_LOG("Wwise Vorbis: codebook_id %04x not found\n", codebook_id); - return 0; -} - -static int load_wvc_file(uint8_t * buf, size_t bufsize, uint32_t codebook_id, STREAMFILE *streamFile) { - STREAMFILE * streamFileWvc = NULL; - size_t wvc_size = 0; - - { - char setupname[PATH_LIMIT]; - char pathname[PATH_LIMIT]; - char *path; - - /* read "(dir/).wvc" */ - streamFile->get_name(streamFile,pathname,sizeof(pathname)); - path = strrchr(pathname,DIR_SEPARATOR); - if (path) - *(path+1) = '\0'; - else - pathname[0] = '\0'; - - snprintf(setupname,PATH_LIMIT,"%s.wvc", pathname); - streamFileWvc = streamFile->open(streamFile,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileWvc) goto fail; - - wvc_size = streamFileWvc->get_size(streamFileWvc); - } - - /* find codebook and copy to buffer */ - { - off_t table_start, codebook_offset; - size_t codebook_size; - int codebook_count; - - /* at the end of the WVC is an offset table, and we need to find codebook id (number) offset */ - table_start = read_32bitLE(wvc_size - 4, streamFileWvc); /* last offset */ - codebook_count = ((wvc_size - table_start) / 4) - 1; - if (table_start > wvc_size || codebook_id >= codebook_count) goto fail; - - codebook_offset = read_32bitLE(table_start + codebook_id*4, streamFileWvc); - codebook_size = read_32bitLE(table_start + codebook_id*4 + 4, streamFileWvc) - codebook_offset; - if (codebook_size > bufsize) goto fail; - - if (read_streamfile(buf, codebook_offset, codebook_size, streamFileWvc) != codebook_size) - goto fail; - streamFileWvc->close(streamFileWvc); - - return codebook_size; - } - - -fail: - if (streamFileWvc) streamFileWvc->close(streamFileWvc); - return 0; -} - -static int load_wvc_array(uint8_t * buf, size_t bufsize, uint32_t codebook_id, wwise_setup_t setup_type) { -#if WWISE_VORBIS_USE_PRECOMPILED_WVC - - /* get pointer to array */ - { - int i, list_length; - const wvc_info * wvc_list; - - switch (setup_type) { - case WWV_EXTERNAL_CODEBOOKS: - wvc_list = wvc_list_standard; - list_length = sizeof(wvc_list_standard) / sizeof(wvc_info); - break; - case WWV_AOTUV603_CODEBOOKS: - wvc_list = wvc_list_aotuv603; - list_length = sizeof(wvc_list_standard) / sizeof(wvc_info); - break; - default: - goto fail; - } - - for (i=0; i < list_length; i++) { - if (wvc_list[i].id == codebook_id) { - if (wvc_list[i].size > bufsize) goto fail; - /* found: copy data as-is */ - memcpy(buf,wvc_list[i].codebook, wvc_list[i].size); - return wvc_list[i].size; - } - } - } - - // this can be used if the lists contained a 1:1 dump of the codebook files -#if 0 - if (wvc == NULL) goto fail; - /* find codebook and copy to buffer */ - { - off_t table_start, codebook_offset; - size_t codebook_size; - int codebook_count; - - /* at the end of the WVC is an offset table, and we need to find codebook id (number) offset */ - table_start = get_32bitLE(wvc + wvc_size - 4); /* last offset */ - codebook_count = ((wvc_size - table_start) / 4) - 1; - if (codebook_id >= codebook_count) goto fail; - - codebook_offset = get_32bitLE(wvc + table_start + codebook_id*4); - codebook_size = get_32bitLE(wvc + table_start + codebook_id*4 + 4) - codebook_offset; - if (codebook_size > bufsize) goto fail; - - memcpy(buf, wvc+codebook_offset, codebook_size); - - return codebook_size; - } -#endif - -fail: -#endif - return 0; -} - -#endif +#include "vorbis_custom_decoder.h" +#include "vorbis_bitreader.h" + +#ifdef VGM_USE_VORBIS +#include + +#define WWISE_VORBIS_USE_PRECOMPILED_WVC 1 /* if enabled vgmstream weights ~150kb more but doesn't need external .wvc packets */ +#if WWISE_VORBIS_USE_PRECOMPILED_WVC +#include "vorbis_custom_data_wwise.h" +#endif + + +/* **************************************************************************** */ +/* DEFS */ +/* **************************************************************************** */ + +typedef struct { + size_t header_size; + size_t packet_size; + int granulepos; + + int has_next; + uint8_t inxt[0x01]; +} wpacket_t; + +static size_t build_header_identification(uint8_t* buf, size_t bufsize, vorbis_custom_config* cfg); +static size_t build_header_comment(uint8_t* buf, size_t bufsize); + +static int read_packet(wpacket_t* wp, uint8_t* ibuf, size_t ibufsize, STREAMFILE* sf, off_t offset, vorbis_custom_codec_data* data, int is_setup); +static size_t rebuild_packet(uint8_t* obuf, size_t obufsize, wpacket_t* wp, STREAMFILE* sf, off_t offset, vorbis_custom_codec_data* data); +static size_t rebuild_setup(uint8_t* obuf, size_t obufsize, wpacket_t* wp, STREAMFILE* sf, off_t offset, vorbis_custom_codec_data* data); + +static int ww2ogg_generate_vorbis_packet(bitstream_t* ow, bitstream_t* iw, wpacket_t* wp, vorbis_custom_codec_data* data); +static int ww2ogg_generate_vorbis_setup(bitstream_t* ow, bitstream_t* iw, vorbis_custom_codec_data* data, size_t packet_size, STREAMFILE* sf); + +static int load_wvc(uint8_t* ibuf, size_t ibufsize, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE* sf); +static int load_wvc_file(uint8_t* buf, size_t bufsize, uint32_t codebook_id, STREAMFILE* sf); +static int load_wvc_array(uint8_t* buf, size_t bufsize, uint32_t codebook_id, wwise_setup_t setup_type); + + +/* **************************************************************************** */ +/* EXTERNAL API */ +/* **************************************************************************** */ + +/** + * Wwise stores a reduced setup, and packets have mini headers with the size, and data packets + * may reduced as well. The format evolved over time so there are many variations. + * The Wwise implementation uses Tremor (fixed-point Vorbis) but shouldn't matter. + * + * Format reverse-engineered by hcs in ww2ogg (https://github.com/hcs64/ww2ogg). + */ +int vorbis_custom_setup_init_wwise(STREAMFILE* sf, off_t start_offset, vorbis_custom_codec_data* data) { + wpacket_t wp = {0}; + int ok; + + + if (data->config.setup_type == WWV_HEADER_TRIAD) { + /* read 3 Wwise packets with triad (id/comment/setup), each with a Wwise header */ + off_t offset = start_offset; + + /* normal identificacion packet */ + ok = read_packet(&wp, data->buffer, data->buffer_size, sf, offset, data, 1); + if (!ok) goto fail; + data->op.bytes = wp.packet_size; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; + offset += wp.header_size + wp.packet_size; + + /* normal comment packet */ + ok = read_packet(&wp, data->buffer, data->buffer_size, sf, offset, data, 1); + if (!ok) goto fail; + data->op.bytes = wp.packet_size; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; + offset += wp.header_size + wp.packet_size; + + /* normal setup packet */ + ok = read_packet(&wp, data->buffer, data->buffer_size, sf, offset, data, 1); + if (!ok) goto fail; + data->op.bytes = wp.packet_size; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; + offset += wp.header_size + wp.packet_size; + } + else { + /* rebuild headers */ + + /* new identificacion packet */ + data->op.bytes = build_header_identification(data->buffer, data->buffer_size, &data->config); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ + + /* new comment packet */ + data->op.bytes = build_header_comment(data->buffer, data->buffer_size); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ + + /* rebuild setup packet */ + data->op.bytes = rebuild_setup(data->buffer, data->buffer_size, &wp, sf, start_offset, data); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ + } + + return 1; + +fail: + return 0; +} + + +int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL* stream, vorbis_custom_codec_data* data) { + wpacket_t wp = {0}; + + /* reconstruct a Wwise packet, if needed; final bytes may be bigger than packet_size */ + data->op.bytes = rebuild_packet(data->buffer, data->buffer_size, &wp, stream->streamfile, stream->offset, data); + stream->offset += wp.header_size + wp.packet_size; + if (!data->op.bytes || data->op.bytes >= 0xFFFF) goto fail; + + data->op.granulepos = wp.granulepos; + + return 1; + +fail: + return 0; +} + +/* **************************************************************************** */ +/* INTERNAL HELPERS */ +/* **************************************************************************** */ + +static int read_packet(wpacket_t* wp, uint8_t* ibuf, size_t ibufsize, STREAMFILE* sf, off_t offset, vorbis_custom_codec_data* data, int is_setup) { + uint32_t (*get_u32)(const uint8_t*) = data->config.big_endian ? get_u32be : get_u32le; + uint16_t (*get_u16)(const uint8_t*) = data->config.big_endian ? get_u16be : get_u16le; + int32_t (*get_s32)(const uint8_t*) = data->config.big_endian ? get_s32be : get_s32le; + + /* read header info (packet size doesn't include header size) */ + switch(data->config.header_type) { + case WWV_TYPE_8: + wp->header_size = 0x08; + read_streamfile(ibuf, offset, wp->header_size, sf); + wp->packet_size = get_u32(ibuf + 0x00); + wp->granulepos = get_s32(ibuf + 0x04); + break; + + case WWV_TYPE_6: + wp->header_size = 0x06; + read_streamfile(ibuf, offset, wp->header_size, sf); + wp->packet_size = get_u16(ibuf + 0x00); + wp->granulepos = get_s32(ibuf + 0x02); + break; + + case WWV_TYPE_2: + wp->header_size = 0x02; + read_streamfile(ibuf, offset, wp->header_size, sf); + wp->packet_size = get_u16(ibuf + 0x00); + wp->granulepos = 0; /* granule is an arbitrary unit so we could use offset instead; libvorbis has no need for it */ + break; + + default: /* ? */ + wp->header_size = 0; + wp->packet_size = 0; + wp->granulepos = 0; + break; + } + + if (wp->header_size == 0 || wp->packet_size == 0) + goto fail; + + /* read packet data */ + { + size_t read_size = wp->packet_size; + size_t read; + + /* mod packets need next packet's first byte (6 bits) except at EOF, so read now too */ + if (!is_setup && data->config.packet_type == WWV_MODIFIED) { + read_size += wp->header_size + 0x01; + } + + if (!wp->header_size || read_size > ibufsize) + goto fail; + + read = read_streamfile(ibuf, offset + wp->header_size, read_size, sf); + if (read < wp->packet_size) { + VGM_LOG("Wwise Vorbis: truncated packet\n"); + goto fail; + } + + if (!is_setup && data->config.packet_type == WWV_MODIFIED && read == read_size) { + wp->has_next = 1; + wp->inxt[0] = ibuf[wp->packet_size + wp->header_size]; + } + else { + wp->has_next = 0; + } + } + + return 1; +fail: + return 0; +} + + +/* Transforms a Wwise data packet into a real Vorbis one (depending on config) */ +static size_t rebuild_packet(uint8_t* obuf, size_t obufsize, wpacket_t* wp, STREAMFILE* sf, off_t offset, vorbis_custom_codec_data* data) { + bitstream_t ow, iw; + int ok; + uint8_t ibuf[0x8000]; /* arbitrary max */ + size_t ibufsize = sizeof(ibuf); + + if (obufsize < ibufsize) /* arbitrary min */ + goto fail; + + ok = read_packet(wp, ibuf, ibufsize, sf, offset, data, 0); + if (!ok) goto fail; + + init_bitstream(&ow, obuf, obufsize); + init_bitstream(&iw, ibuf, ibufsize); + + ok = ww2ogg_generate_vorbis_packet(&ow, &iw, wp, data); + if (!ok) goto fail; + + if (ow.b_off % 8 != 0) { + //VGM_LOG("Wwise Vorbis: didn't write exactly audio packet: 0x%lx + %li bits\n", ow.b_off / 8, ow.b_off % 8); + goto fail; + } + + + return ow.b_off / 8; +fail: + return 0; +} + +/* Transforms a Wwise setup packet into a real Vorbis one (depending on config). */ +static size_t rebuild_setup(uint8_t* obuf, size_t obufsize, wpacket_t* wp, STREAMFILE* sf, off_t offset, vorbis_custom_codec_data* data) { + bitstream_t ow, iw; + int ok; + uint8_t ibuf[0x8000]; /* arbitrary max */ + size_t ibufsize = sizeof(ibuf); + + if (obufsize < ibufsize) /* arbitrary min */ + goto fail; + + ok = read_packet(wp, ibuf, ibufsize, sf, offset, data, 1); + if (!ok) goto fail; + + init_bitstream(&ow, obuf, obufsize); + init_bitstream(&iw, ibuf, ibufsize); + + ok = ww2ogg_generate_vorbis_setup(&ow,&iw, data, wp->packet_size, sf); + if (!ok) goto fail; + + if (ow.b_off % 8 != 0) { + //VGM_LOG("Wwise Vorbis: didn't write exactly setup packet: 0x%lx + %li bits\n", ow.b_off / 8, ow.b_off % 8); + goto fail; + } + + + return ow.b_off / 8; +fail: + return 0; +} + +static size_t build_header_identification(uint8_t* buf, size_t bufsize, vorbis_custom_config* cfg) { + size_t bytes = 0x1e; + uint8_t blocksizes; + + if (bytes > bufsize) return 0; + + blocksizes = (cfg->blocksize_0_exp << 4) | (cfg->blocksize_1_exp); + + put_8bit (buf+0x00, 0x01); /* packet_type (id) */ + memcpy (buf+0x01, "vorbis", 6); /* id */ + put_32bitLE(buf+0x07, 0x00); /* vorbis_version (fixed) */ + put_8bit (buf+0x0b, cfg->channels); /* audio_channels */ + put_32bitLE(buf+0x0c, cfg->sample_rate);/* audio_sample_rate */ + put_32bitLE(buf+0x10, 0x00); /* bitrate_maximum (optional hint) */ + put_32bitLE(buf+0x14, 0x00); /* bitrate_nominal (optional hint) */ + put_32bitLE(buf+0x18, 0x00); /* bitrate_minimum (optional hint) */ + put_8bit (buf+0x1c, blocksizes); /* blocksize_0 + blocksize_1 nibbles */ + put_8bit (buf+0x1d, 0x01); /* framing_flag (fixed) */ + + return bytes; +} + +static size_t build_header_comment(uint8_t* buf, size_t bufsize) { + size_t bytes = 0x19; + + if (bytes > bufsize) return 0; + + put_8bit (buf+0x00, 0x03); /* packet_type (comments) */ + memcpy (buf+0x01, "vorbis", 6); /* id */ + put_32bitLE(buf+0x07, 0x09); /* vendor_length */ + memcpy (buf+0x0b, "vgmstream", 9); /* vendor_string */ + put_32bitLE(buf+0x14, 0x00); /* user_comment_list_length */ + put_8bit (buf+0x18, 0x01); /* framing_flag (fixed) */ + + return bytes; +} + + +/* copy packet bytes, where input/output bufs may not be byte-aligned (so no memcpy) */ +static int copy_bytes(bitstream_t* ob, bitstream_t* ib, uint32_t bytes) { + int i; + +#if 0 + /* in theory this would be faster, but not clear results; maybe default is just optimized by compiler */ + for (i = 0; i < bytes / 4; i++) { + uint32_t c = 0; + + rv_bits(ib, 32, &c); + wv_bits(ob, 32, c); + } + for (i = 0; i < bytes % 4; i++) { + uint32_t c = 0; + + rv_bits(ib, 8, &c); + wv_bits(ob, 8, c); + } +#endif + +#if 0 + /* output bits are never(?) byte aligned but input always is, yet this doesn't seem any faster */ + if (ib->b_off % 8 == 0) { + int iw_pos = ib->b_off / 8; + + for (i = 0; i < bytes; i++, iw_pos++) { + uint32_t c = ib->buf[iw_pos]; + + //rv_bits(ib, 8, &c); + wv_bits(ob, 8, c); + } + + ib->b_off += bytes * 8; + return 1; + } +#endif + + for (i = 0; i < bytes; i++) { + uint32_t c = 0; + + rv_bits(ib, 8, &c); + wv_bits(ob, 8, c); + } + + return 1; +} + +/* **************************************************************************** */ +/* INTERNAL WW2OGG STUFF */ +/* **************************************************************************** */ +/* The following code was mostly and manually converted from hcs's ww2ogg (https://github.com/hcs64/ww2ogg). + * Could be simplified but roughly tries to preserve the structure for comparison. + * + * Some validations are ommited (ex. read/write), as incorrect data should be rejected by libvorbis. + * To avoid GCC complaining all values are init to 0, and some that do need it are init again, for clarity. + * Reads/writes unsigned ints as most are bit values less than 32 and with no sign meaning. + */ + +/* Copy packet as-is or rebuild to standard Vorbis packet if mod_packets is used. + * (ref: https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-720004.3) */ +static int ww2ogg_generate_vorbis_packet(bitstream_t* ow, bitstream_t* iw, wpacket_t* wp, vorbis_custom_codec_data* data) { + + /* this may happen in the first packet; maybe it's for the encoder delay but doesn't seem to affect libvorbis */ + //VGM_ASSERT(granule < 0, "Wwise Vorbis: negative granule %i @ 0x%lx\n", granule, offset); + + + 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; + + if (!data->mode_blockflag) { /* config error */ + VGM_LOG("Wwise Vorbis: didn't load mode_blockflag\n"); + goto fail; + } + + /* audio packet type */ + packet_type = 0; + wv_bits(ow, 1, packet_type); + + /* collect this packet mode from the first byte */ + rv_bits(iw, data->mode_bits,&mode_number); /* max 6b */ + wv_bits(ow, data->mode_bits, mode_number); + + rv_bits(iw, 8-data->mode_bits,&remainder); + + /* adjust window info */ + if (data->mode_blockflag[mode_number]) { + /* long window: peek at next frame to find flags */ + uint32_t next_blockflag = 0, prev_window_type = 0, next_window_type = 0; + + if (wp->has_next) { + /* get next first byte to read next_mode_number */ + uint32_t next_mode_number; + bitstream_t nw; + + init_bitstream(&nw, wp->inxt, sizeof(wp->inxt)); + + rv_bits(&nw, data->mode_bits,&next_mode_number); /* max 6b */ + + next_blockflag = data->mode_blockflag[next_mode_number]; + } + else { + /* EOF (probably doesn't matter) */ + next_blockflag = 0; + } + + prev_window_type = data->prev_blockflag; + wv_bits(ow, 1, prev_window_type); + + next_window_type = next_blockflag; + wv_bits(ow, 1, next_window_type); + } + + data->prev_blockflag = data->mode_blockflag[mode_number]; /* save for next packet */ + + wv_bits(ow, 8-data->mode_bits, remainder); + + /* rest of the packet (input/output bytes aren't byte aligned here, so no memcpy) */ + copy_bytes(ow, iw, wp->packet_size - 1); + + /* remove trailing garbage bits (probably unneeded) */ + if (ow->b_off % 8 != 0) { + uint32_t padding = 0; + int padding_bits = 8 - (ow->b_off % 8); + + wv_bits(ow, padding_bits, padding); + } + } + else { + /* normal packets */ + + /* can directly copy (much, much faster), but least common case vs the above... */ + memcpy(ow->buf + ow->b_off / 8, iw->buf + iw->b_off / 8, wp->packet_size); + ow->b_off += wp->packet_size * 8; + iw->b_off += wp->packet_size * 8; + } + + + return 1; +fail: + return 0; +} + +/*******************************************************************************/ + +/* fixed-point ilog from Xiph's Tremor */ +static int ww2ogg_tremor_ilog(unsigned int v) { + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +/* quantvals-something from Xiph's Tremor */ +static unsigned int ww2ogg_tremor_book_maptype1_quantvals(unsigned int entries, unsigned int dimensions) { + /* get us a starting hint, we'll polish it below */ + int bits=ww2ogg_tremor_ilog(entries); + int vals=entries>>((bits-1)*(dimensions-1)/dimensions); + + while(1){ + unsigned long acc=1; + unsigned long acc1=1; + unsigned int i; + for(i=0;ientries){ + return(vals); + }else{ + if(acc>entries){ + vals--; + }else{ + vals++; + } + } + } +} + + +/* copies Vorbis codebooks (untouched, but size uncertain) */ +static int ww2ogg_codebook_library_copy(bitstream_t* ow, bitstream_t* iw) { + int i; + uint32_t id = 0, dimensions = 0, entries = 0; + uint32_t ordered = 0, lookup_type = 0; + + rv_bits(iw, 24,&id); + wv_bits(ow, 24, id); + rv_bits(iw, 16,&dimensions); + wv_bits(ow, 16, dimensions); + rv_bits(iw, 24,&entries); + wv_bits(ow, 24, entries); + + if (0x564342 != id) { /* "VCB" */ + VGM_LOG("Wwise Vorbis: invalid codebook identifier\n"); + goto fail; + } + + /* codeword lengths */ + rv_bits(iw, 1,&ordered); + wv_bits(ow, 1, ordered); + if (ordered) { + uint32_t initial_length = 0, current_entry = 0; + + rv_bits(iw, 5,&initial_length); + wv_bits(ow, 5, initial_length); + + current_entry = 0; + while (current_entry < entries) { + uint32_t number = 0; + int numberv_bits = ww2ogg_tremor_ilog(entries-current_entry); + + rv_bits(iw, numberv_bits,&number); + wv_bits(ow, numberv_bits, number); + current_entry += number; + } + if (current_entry > entries) { + VGM_LOG("Wwise Vorbis: current_entry out of range\n"); + goto fail; + } + } + else { + uint32_t sparse = 0; + + rv_bits(iw, 1,&sparse); + wv_bits(ow, 1, sparse); + + for (i = 0; i < entries; i++) { + uint32_t present_bool = 0; + + present_bool = 1; + if (sparse) { + uint32_t present = 0; + + rv_bits(iw, 1,&present); + wv_bits(ow, 1, present); + + present_bool = (0 != present); + } + + if (present_bool) { + uint32_t codeword_length = 0; + + rv_bits(iw, 5,&codeword_length); + wv_bits(ow, 5, codeword_length); + } + } + } + + + /* lookup table */ + rv_bits(iw, 4,&lookup_type); + wv_bits(ow, 4, lookup_type); + + if (0 == lookup_type) { + //VGM_LOG("Wwise Vorbis: no lookup table\n"); + } + else if (1 == lookup_type) { + //VGM_LOG("Wwise Vorbis: lookup type 1\n"); + uint32_t quantvals = 0, min = 0, max = 0; + uint32_t value_length = 0, sequence_flag = 0; + + rv_bits(iw, 32,&min); + wv_bits(ow, 32, min); + rv_bits(iw, 32,&max); + wv_bits(ow, 32, max); + rv_bits(iw, 4,&value_length); + wv_bits(ow, 4, value_length); + rv_bits(iw, 1,&sequence_flag); + wv_bits(ow, 1, sequence_flag); + + quantvals = ww2ogg_tremor_book_maptype1_quantvals(entries, dimensions); + for (i = 0; i < quantvals; i++) { + uint32_t val = 0, val_bits = 0; + val_bits = value_length+1; + + rv_bits(iw, val_bits,&val); + wv_bits(ow, val_bits, val); + } + } + else if (2 == lookup_type) { + VGM_LOG("Wwise Vorbis: didn't expect lookup type 2\n"); + goto fail; + } + else { + VGM_LOG("Wwise Vorbis: invalid lookup type\n"); + goto fail; + } + + return 1; +fail: + return 0; +} + +/* rebuilds a Wwise codebook into a Vorbis codebook */ +static int ww2ogg_codebook_library_rebuild(bitstream_t* ow, bitstream_t* iw, size_t cb_size, STREAMFILE* sf) { + int i; + uint32_t id = 0, dimensions = 0, entries = 0; + uint32_t ordered = 0, lookup_type = 0; + + id = 0x564342; /* "VCB" */ + + wv_bits(ow, 24, id); + rv_bits(iw, 4,&dimensions); + wv_bits(ow, 16, dimensions); /* 4 to 16 */ + rv_bits(iw, 14,&entries); + wv_bits(ow, 24, entries); /* 14 to 24*/ + + /* codeword lengths */ + rv_bits(iw, 1,&ordered); + wv_bits(ow, 1, ordered); + if (ordered) { + uint32_t initial_length = 0, current_entry = 0; + + rv_bits(iw, 5,&initial_length); + wv_bits(ow, 5, initial_length); + + current_entry = 0; + while (current_entry < entries) { + uint32_t number = 0; + int numberv_bits = ww2ogg_tremor_ilog(entries-current_entry); + + rv_bits(iw, numberv_bits,&number); + wv_bits(ow, numberv_bits, number); + current_entry += number; + } + if (current_entry > entries) { + VGM_LOG("Wwise Vorbis: current_entry out of range\n"); + goto fail; + } + } + else { + uint32_t codeword_length_length = 0, sparse = 0; + + rv_bits(iw, 3,&codeword_length_length); + rv_bits(iw, 1,&sparse); + wv_bits(ow, 1, sparse); + + if (0 == codeword_length_length || 5 < codeword_length_length) { + VGM_LOG("Wwise Vorbis: nonsense codeword length\n"); + goto fail; + } + + for (i = 0; i < entries; i++) { + uint32_t present_bool = 0; + + present_bool = 1; + if (sparse) { + uint32_t present = 0; + + rv_bits(iw, 1,&present); + wv_bits(ow, 1, present); + + present_bool = (0 != present); + } + + if (present_bool) { + uint32_t codeword_length = 0; + + rv_bits(iw, codeword_length_length,&codeword_length); + wv_bits(ow, 5, codeword_length); /* max 7 (3b) to 5 */ + } + } + } + + + /* lookup table */ + rv_bits(iw, 1,&lookup_type); + wv_bits(ow, 4, lookup_type); /* 1 to 4 */ + + if (0 == lookup_type) { + //VGM_LOG("Wwise Vorbis: no lookup table\n"); + } + else if (1 == lookup_type) { + //VGM_LOG("Wwise Vorbis: lookup type 1\n"); + uint32_t quantvals = 0, min = 0, max = 0; + uint32_t value_length = 0, sequence_flag = 0; + + rv_bits(iw, 32,&min); + wv_bits(ow, 32, min); + rv_bits(iw, 32,&max); + wv_bits(ow, 32, max); + rv_bits(iw, 4,&value_length); + wv_bits(ow, 4, value_length); + rv_bits(iw, 1,&sequence_flag); + wv_bits(ow, 1, sequence_flag); + + quantvals = ww2ogg_tremor_book_maptype1_quantvals(entries, dimensions); + for (i = 0; i < quantvals; i++) { + uint32_t val = 0, val_bits = 0; + val_bits = value_length+1; + + rv_bits(iw, val_bits,&val); + wv_bits(ow, val_bits, val); + } + } + else if (2 == lookup_type) { + VGM_LOG("Wwise Vorbis: didn't expect lookup type 2\n"); + goto fail; + } + else { + VGM_LOG("Wwise Vorbis: invalid lookup type\n"); + goto fail; + } + + + /* check that we used exactly all bytes */ + /* note: if all bits are used in the last byte there will be one extra 0 byte */ + if ( 0 != cb_size && iw->b_off/8+1 != cb_size ) { + //VGM_LOG("Wwise Vorbis: codebook size mistach (expected 0x%x, wrote 0x%lx)\n", cb_size, iw->b_off/8+1); + goto fail; + } + + return 1; +fail: + return 0; +} + +/* rebuilds an external Wwise codebook referenced by id to a Vorbis codebook */ +static int ww2ogg_codebook_library_rebuild_by_id(bitstream_t* ow, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE* sf) { + size_t ibufsize = 0x8000; /* arbitrary max size of a codebook */ + uint8_t ibuf[0x8000]; /* Wwise codebook buffer */ + size_t cb_size; + bitstream_t iw; + + cb_size = load_wvc(ibuf,ibufsize, codebook_id, setup_type, sf); + if (cb_size == 0) goto fail; + + init_bitstream(&iw, ibuf, ibufsize); + + return ww2ogg_codebook_library_rebuild(ow, &iw, cb_size, sf); +fail: + return 0; +} + +/* Rebuild a Wwise setup (simplified with removed stuff), recreating all six setup parts. + * (ref: https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-650004.2.4) */ +static int ww2ogg_generate_vorbis_setup(bitstream_t* ow, bitstream_t* iw, vorbis_custom_codec_data* data, size_t packet_size, STREAMFILE* sf) { + int i, j, k; + int channels = data->config.channels; + uint32_t codebook_count = 0, floor_count = 0, residue_count = 0; + uint32_t codebook_count_less1 = 0; + uint32_t time_count_less1 = 0, dummy_time_value = 0; + + + /* packet header */ + put_8bit(ow->buf+0x00, 0x05); /* packet_type (setup) */ + memcpy (ow->buf+0x01, "vorbis", 6); /* id */ + ow->b_off += (1+6) * 8; /* bit offset of output (Vorbis) setup, after fake type + id */ + + + /* Codebooks */ + rv_bits(iw, 8,&codebook_count_less1); + wv_bits(ow, 8, codebook_count_less1); + codebook_count = codebook_count_less1 + 1; + + 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 == 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, sf)) + goto fail; + } + } + else { + /* rebuild Wwise codebooks: external (referenced by id) in simplified format */ + for (i = 0; i < codebook_count; i++) { + int rc; + uint32_t codebook_id = 0; + + rv_bits(iw, 10,&codebook_id); + + rc = ww2ogg_codebook_library_rebuild_by_id(ow, codebook_id, data->config.setup_type, sf); + if (!rc) goto fail; + } + } + + + /* Time domain transforms */ + time_count_less1 = 0; + wv_bits(ow, 6, time_count_less1); + dummy_time_value = 0; + wv_bits(ow, 16, dummy_time_value); + + + 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; + uint32_t setup_packet_size_bits = packet_size * 8; + + while (total_bits_read < setup_packet_size_bits) { + rv_bits(iw, 1,&bitly); + wv_bits(ow, 1, bitly); + total_bits_read = iw->b_off; + } + } + else { + /* rest of setup is altered, reconstruct */ + uint32_t floor_count_less1 = 0, floor1_multiplier_less1 = 0, rangebits = 0; + uint32_t residue_count_less1 = 0; + uint32_t mapping_count_less1 = 0, mapping_count = 0; + uint32_t mode_count_less1 = 0, mode_count = 0; + + + /* Floors */ + rv_bits(iw, 6,&floor_count_less1); + wv_bits(ow, 6, floor_count_less1); + floor_count = floor_count_less1 + 1; + + for (i = 0; i < floor_count; i++) { + uint32_t floor_type = 0, floor1_partitions = 0; + uint32_t maximum_class = 0; + uint32_t floor1_partition_class_list[32]; /* max 5b */ + uint32_t floor1_class_dimensions_list[16+1]; /* max 4b+1 */ + + // Always floor type 1 + floor_type = 1; + wv_bits(ow, 16, floor_type); + + rv_bits(iw, 5,&floor1_partitions); + wv_bits(ow, 5, floor1_partitions); + + memset(floor1_partition_class_list, 0, sizeof(uint32_t)*32); + + maximum_class = 0; + for (j = 0; j < floor1_partitions; j++) { + uint32_t floor1_partition_class = 0; + + rv_bits(iw, 4,&floor1_partition_class); + wv_bits(ow, 4, floor1_partition_class); + + floor1_partition_class_list[j] = floor1_partition_class; + + if (floor1_partition_class > maximum_class) + maximum_class = floor1_partition_class; + } + + memset(floor1_class_dimensions_list, 0, sizeof(uint32_t)*(16+1)); + + for (j = 0; j <= maximum_class; j++) { + uint32_t class_dimensions_less1 = 0, class_subclasses = 0; + + rv_bits(iw, 3,&class_dimensions_less1); + wv_bits(ow, 3, class_dimensions_less1); + + floor1_class_dimensions_list[j] = class_dimensions_less1 + 1; + + rv_bits(iw, 2,&class_subclasses); + wv_bits(ow, 2, class_subclasses); + + if (0 != class_subclasses) { + uint32_t masterbook = 0; + + rv_bits(iw, 8,&masterbook); + wv_bits(ow, 8, masterbook); + + if (masterbook >= codebook_count) { + VGM_LOG("Wwise Vorbis: invalid floor1 masterbook\n"); + goto fail; + } + } + + for (k = 0; k < (1U<= 0 && subclass_book >= codebook_count) { + VGM_LOG("Wwise Vorbis: invalid floor1 subclass book\n"); + goto fail; + } + } + } + + rv_bits(iw, 2,&floor1_multiplier_less1); + wv_bits(ow, 2, floor1_multiplier_less1); + + rv_bits(iw, 4,&rangebits); + wv_bits(ow, 4, rangebits); + + for (j = 0; j < floor1_partitions; j++) { + uint32_t current_class_number = 0; + + current_class_number = floor1_partition_class_list[j]; + for (k = 0; k < floor1_class_dimensions_list[current_class_number]; k++) { + uint32_t X = 0; /* max 4b (15) */ + + rv_bits(iw, rangebits,&X); + wv_bits(ow, rangebits, X); + } + } + } + + + /* Residues */ + rv_bits(iw, 6,&residue_count_less1); + wv_bits(ow, 6, residue_count_less1); + residue_count = residue_count_less1 + 1; + + for (i = 0; i < residue_count; i++) { + uint32_t residue_type = 0, residue_classifications = 0; + uint32_t residue_begin = 0, residue_end = 0, residue_partition_size_less1 = 0, residue_classifications_less1 = 0, residue_classbook = 0; + uint32_t residue_cascade[64+1]; /* 6b +1 */ + + rv_bits(iw, 2,&residue_type); + wv_bits(ow, 16, residue_type); /* 2b to 16b */ + + if (residue_type > 2) { + VGM_LOG("Wwise Vorbis: invalid residue type\n"); + goto fail; + } + + rv_bits(iw, 24,&residue_begin); + wv_bits(ow, 24, residue_begin); + rv_bits(iw, 24,&residue_end); + wv_bits(ow, 24, residue_end); + rv_bits(iw, 24,&residue_partition_size_less1); + wv_bits(ow, 24, residue_partition_size_less1); + rv_bits(iw, 6,&residue_classifications_less1); + wv_bits(ow, 6, residue_classifications_less1); + rv_bits(iw, 8,&residue_classbook); + wv_bits(ow, 8, residue_classbook); + residue_classifications = residue_classifications_less1 + 1; + + if (residue_classbook >= codebook_count) { + VGM_LOG("Wwise Vorbis: invalid residue classbook\n"); + goto fail; + } + + memset(residue_cascade, 0, sizeof(uint32_t)*(64+1)); + + for (j = 0; j < residue_classifications; j++) { + uint32_t high_bits = 0, lowv_bits = 0, bitflag = 0; + + high_bits = 0; + + rv_bits(iw, 3,&lowv_bits); + wv_bits(ow, 3, lowv_bits); + + rv_bits(iw, 1,&bitflag); + wv_bits(ow, 1, bitflag); + if (bitflag) { + rv_bits(iw, 5,&high_bits); + wv_bits(ow, 5, high_bits); + } + + residue_cascade[j] = high_bits * 8 + lowv_bits; + } + + for (j = 0; j < residue_classifications; j++) { + for (k = 0; k < 8; k++) { + if (residue_cascade[j] & (1 << k)) { + uint32_t residue_book = 0; + + rv_bits(iw, 8,&residue_book); + wv_bits(ow, 8, residue_book); + + if (residue_book >= codebook_count) { + VGM_LOG("Wwise Vorbis: invalid residue book\n"); + goto fail; + } + } + } + } + } + + + /* Mappings */ + rv_bits(iw, 6,&mapping_count_less1); + wv_bits(ow, 6, mapping_count_less1); + mapping_count = mapping_count_less1 + 1; + + for (i = 0; i < mapping_count; i++) { + uint32_t mapping_type = 0, submaps_flag = 0, submaps = 0, square_polar_flag = 0; + uint32_t mapping_reserved = 0; + + // always mapping type 0, the only one + mapping_type = 0; + wv_bits(ow, 16, mapping_type); + + rv_bits(iw, 1,&submaps_flag); + wv_bits(ow, 1, submaps_flag); + + submaps = 1; + if (submaps_flag) { + uint32_t submaps_less1 = 0; + + rv_bits(iw, 4,&submaps_less1); + wv_bits(ow, 4, submaps_less1); + submaps = submaps_less1 + 1; + } + + rv_bits(iw, 1,&square_polar_flag); + wv_bits(ow, 1, square_polar_flag); + + if (square_polar_flag) { + uint32_t coupling_steps_less1 = 0, coupling_steps = 0; + + rv_bits(iw, 8,&coupling_steps_less1); + wv_bits(ow, 8, coupling_steps_less1); + coupling_steps = coupling_steps_less1 + 1; + + for (j = 0; j < coupling_steps; j++) { + uint32_t magnitude = 0, angle = 0; + int magnitude_bits = ww2ogg_tremor_ilog(channels-1); + int angle_bits = ww2ogg_tremor_ilog(channels-1); + + rv_bits(iw, magnitude_bits,&magnitude); + wv_bits(ow, magnitude_bits, magnitude); + rv_bits(iw, angle_bits,&angle); + wv_bits(ow, angle_bits, angle); + + if (angle == magnitude || magnitude >= channels || angle >= channels) { + VGM_LOG("Wwise Vorbis: invalid coupling (angle=%i, mag=%i, ch=%i)\n", angle, magnitude,channels); + goto fail; + } + } + } + + // a rare reserved field not removed by Ak! + rv_bits(iw, 2,&mapping_reserved); + wv_bits(ow, 2, mapping_reserved); + if (0 != mapping_reserved) { + VGM_LOG("Wwise Vorbis: mapping reserved field nonzero\n"); + goto fail; + } + + if (submaps > 1) { + for (j = 0; j < channels; j++) { + uint32_t mapping_mux = 0; + + rv_bits(iw, 4,&mapping_mux); + wv_bits(ow, 4, mapping_mux); + if (mapping_mux >= submaps) { + VGM_LOG("Wwise Vorbis: mapping_mux >= submaps\n"); + goto fail; + } + } + } + + for (j = 0; j < submaps; j++) { + uint32_t time_config = 0, floor_number = 0, residue_number = 0; + + // Another! Unused time domain transform configuration placeholder! + rv_bits(iw, 8,&time_config); + wv_bits(ow, 8, time_config); + + rv_bits(iw, 8,&floor_number); + wv_bits(ow, 8, floor_number); + if (floor_number >= floor_count) { + VGM_LOG("Wwise Vorbis: invalid floor mapping\n"); + goto fail; + } + + rv_bits(iw, 8,&residue_number); + wv_bits(ow, 8, residue_number); + if (residue_number >= residue_count) { + VGM_LOG("Wwise Vorbis: invalid residue mapping\n"); + goto fail; + } + } + } + + + /* Modes */ + rv_bits(iw, 6,&mode_count_less1); + wv_bits(ow, 6, mode_count_less1); + mode_count = mode_count_less1 + 1; + + memset(data->mode_blockflag, 0, sizeof(uint8_t)*(64+1)); /* up to max mode_count */ + data->mode_bits = ww2ogg_tremor_ilog(mode_count-1); /* for mod_packets */ + + for (i = 0; i < mode_count; i++) { + uint32_t block_flag = 0, windowtype = 0, transformtype = 0, mapping = 0; + + rv_bits(iw, 1,&block_flag); + wv_bits(ow, 1, block_flag); + + data->mode_blockflag[i] = (block_flag != 0); /* for mod_packets */ + + windowtype = 0; + transformtype = 0; + wv_bits(ow, 16, windowtype); + wv_bits(ow, 16, transformtype); + + rv_bits(iw, 8,&mapping); + wv_bits(ow, 8, mapping); + if (mapping >= mapping_count) { + VGM_LOG("Wwise Vorbis: invalid mode mapping\n"); + goto fail; + } + } + } + + + /* end flag */ + { + uint32_t framing = 0; + + framing = 1; + wv_bits(ow, 1, framing); + } + + /* remove trailing garbage bits */ + if (ow->b_off % 8 != 0) { + uint32_t padding = 0; + int padding_bits = 8 - (ow->b_off % 8); + + wv_bits(ow, padding_bits, padding); + } + + + return 1; +fail: + return 0; +} + + +/* **************************************************************************** */ +/* INTERNAL UTILS */ +/* **************************************************************************** */ + +/* loads an external Wwise Vorbis Codebooks file (wvc) referenced by ID and returns size */ +static int load_wvc(uint8_t* ibuf, size_t ibufsize, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE* sf) { + size_t bytes; + + /* try to locate from the precompiled list */ + bytes = load_wvc_array(ibuf, ibufsize, codebook_id, setup_type); + if (bytes) + return bytes; + + /* try to load from external file (ignoring type, just use file if found) */ + bytes = load_wvc_file(ibuf, ibufsize, codebook_id, sf); + if (bytes) + return bytes; + + /* not found */ + VGM_LOG("Wwise Vorbis: codebook_id %04x not found\n", codebook_id); + return 0; +} + +static int load_wvc_file(uint8_t* buf, size_t bufsize, uint32_t codebook_id, STREAMFILE* sf) { + STREAMFILE* sfWvc = NULL; + size_t wvc_size = 0; + + { + char setupname[PATH_LIMIT]; + char pathname[PATH_LIMIT]; + char *path; + + /* read "(dir/).wvc" */ + sf->get_name(sf,pathname,sizeof(pathname)); + path = strrchr(pathname,DIR_SEPARATOR); + if (path) + *(path+1) = '\0'; + else + pathname[0] = '\0'; + + snprintf(setupname,PATH_LIMIT,"%s.wvc", pathname); + sfWvc = sf->open(sf,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); + if (!sfWvc) goto fail; + + wvc_size = sfWvc->get_size(sfWvc); + } + + /* find codebook and copy to buffer */ + { + off_t table_start, codebook_offset; + size_t codebook_size; + int codebook_count; + + /* at the end of the WVC is an offset table, and we need to find codebook id (number) offset */ + table_start = read_u32le(wvc_size - 4, sfWvc); /* last offset */ + codebook_count = ((wvc_size - table_start) / 4) - 1; + if (table_start > wvc_size || codebook_id >= codebook_count) goto fail; + + codebook_offset = read_u32le(table_start + codebook_id*4, sfWvc); + codebook_size = read_u32le(table_start + codebook_id*4 + 4, sfWvc) - codebook_offset; + if (codebook_size > bufsize) goto fail; + + if (read_streamfile(buf, codebook_offset, codebook_size, sfWvc) != codebook_size) + goto fail; + sfWvc->close(sfWvc); + + return codebook_size; + } + + +fail: + if (sfWvc) sfWvc->close(sfWvc); + return 0; +} + +static int load_wvc_array(uint8_t* buf, size_t bufsize, uint32_t codebook_id, wwise_setup_t setup_type) { +#if WWISE_VORBIS_USE_PRECOMPILED_WVC + + /* get pointer to array */ + { + int i, list_length; + const wvc_info * wvc_list; + + switch (setup_type) { + case WWV_EXTERNAL_CODEBOOKS: + wvc_list = wvc_list_standard; + list_length = sizeof(wvc_list_standard) / sizeof(wvc_info); + break; + case WWV_AOTUV603_CODEBOOKS: + wvc_list = wvc_list_aotuv603; + list_length = sizeof(wvc_list_standard) / sizeof(wvc_info); + break; + default: + goto fail; + } + + for (i=0; i < list_length; i++) { + if (wvc_list[i].id == codebook_id) { + if (wvc_list[i].size > bufsize) goto fail; + /* found: copy data as-is */ + memcpy(buf,wvc_list[i].codebook, wvc_list[i].size); + return wvc_list[i].size; + } + } + } + + // this can be used if the lists contained a 1:1 dump of the codebook files +#if 0 + if (wvc == NULL) goto fail; + /* find codebook and copy to buffer */ + { + off_t table_start, codebook_offset; + size_t codebook_size; + int codebook_count; + + /* at the end of the WVC is an offset table, and we need to find codebook id (number) offset */ + table_start = get_32bitLE(wvc + wvc_size - 4); /* last offset */ + codebook_count = ((wvc_size - table_start) / 4) - 1; + if (codebook_id >= codebook_count) goto fail; + + codebook_offset = get_32bitLE(wvc + table_start + codebook_id*4); + codebook_size = get_32bitLE(wvc + table_start + codebook_id*4 + 4) - codebook_offset; + if (codebook_size > bufsize) goto fail; + + memcpy(buf, wvc+codebook_offset, codebook_size); + + return codebook_size; + } +#endif + +fail: +#endif + return 0; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/wady_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/wady_decoder.c new file mode 100644 index 000000000..6f474f8c4 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/wady_decoder.c @@ -0,0 +1,48 @@ +#include "coding.h" + + +/* originally only positives are stored (pre-init by copying negatives) */ +static const int wady_table[64+64] = { + 0, 2, 4, 6, 8, 10, 12, 15, + 18, 21, 24, 28, 32, 36, 40, 44, + 49, 54, 59, 64, 70, 76, 82, 88, + 95, 102, 109, 116, 124, 132, 140, 148, + 160, 170, 180, 190, 200, 210, 220, 230, + 240, 255, 270, 285, 300, 320, 340, 360, + 380, 400, 425, 450, 475, 500, 525, 550, + 580, 610, 650, 700, 750, 800, 900, 1000, + -0, -2, -4, -6, -8, -10, -12, -15, + -18, -21, -24, -28, -32, -36, -40, -44, + -49, -54, -59, -64, -70, -76, -82, -88, + -95, -102,-109,-116,-124,-132,-140,-148, + -160,-170,-180,-190,-200,-210,-220,-230, + -240,-255,-270,-285,-300,-320,-340,-360, + -380,-400,-425,-450,-475,-500,-525,-550, + -580,-610,-650,-700,-750,-800,-900,-1000, +}; + +/* There is another decoding mode mainly for SFX. Uses headered frames/blocks (big), + * L-frame then R-frame, DPCM uses another table plus a RLE/LZ-like mode */ + +/* Marble engine WADY decoder, decompiled from the exe + * (some info from: https://github.com/morkt/GARbro/blob/master/ArcFormats/Marble/AudioWADY.cs) */ +void decode_wady(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + int i, sample_pos = 0; + off_t frame_offset = stream->offset; /* frame size is 1 */ + int32_t hist = stream->adpcm_history1_32; + int scale = stream->adpcm_scale; + + for (i = first_sample; i < first_sample + samples_to_do; i++) { + int8_t code = read_s8(frame_offset + i, stream->streamfile); + + if (code & 0x80) + hist = (code << 9); /* PCM */ + else + hist += scale * wady_table[code]; /* DPCM */ + + outbuf[sample_pos] = hist; /* no clamp */ + sample_pos += channelspacing; + } + + stream->adpcm_history1_32 = hist; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c index d0345c2e6..4ba8e91c1 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/yamaha_decoder.c @@ -19,10 +19,10 @@ static const int scale_delta[16] = { }; /* Yamaha ADPCM-B (aka DELTA-T) expand used in YM2608/YM2610/etc (cross referenced with various sources and .so) */ -static void yamaha_adpcmb_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t* hist1, int32_t* step_size, int16_t *out_sample) { +static void yamaha_adpcmb_expand_nibble(uint8_t byte, int shift, int32_t* hist1, int32_t* step_size, int16_t *out_sample) { int code, delta, sample; - code = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift) & 0xf; + code = (byte >> shift) & 0xf; delta = ((((code & 0x7) * 2) + 1) * (*step_size)) >> 3; /* like 'mul' IMA */ if (code & 8) delta = -delta; @@ -109,38 +109,47 @@ void decode_aica(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin /* tri-Ace Aska ADPCM, Yamaha ADPCM-B with headered frames (reversed from Android SO's .so) * implements table with if-else/switchs too but that's too goofy */ -void decode_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - int i, sample_count = 0, num_frame; +void decode_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, size_t frame_size) { + uint8_t frame[0x100] = {0}; /* known max is 0xC0 */ + off_t frame_offset; + int i, sample_count = 0, frames_in; int16_t out_sample; int32_t hist1 = stream->adpcm_history1_32; int step_size = stream->adpcm_step_index; /* external interleave */ - int block_samples = (0x40 - 0x04*channelspacing) * 2 / channelspacing; - num_frame = first_sample / block_samples; + int block_samples = (frame_size - 0x04*channelspacing) * 2 / channelspacing; + frames_in = first_sample / block_samples; first_sample = first_sample % block_samples; + if (frame_size > sizeof(frame)) { + VGM_LOG_ONCE("ASKA: unknown frame size %x\n", frame_size); + return; + } + + /* parse frame */ + frame_offset = stream->offset + frame_size * frames_in; + read_streamfile(frame, frame_offset, frame_size, stream->streamfile); /* ignore EOF errors */ + /* header (hist+step) */ if (first_sample == 0) { - off_t header_offset = stream->offset + 0x40*num_frame + 0x04*channel; - - hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); - step_size = read_16bitLE(header_offset+0x02,stream->streamfile); - /* in most files 1st frame has step 0 but it seems ok and accounted for */ + hist1 = get_s16le(frame + 0x04*channel + 0x00); + step_size = get_s16le(frame + 0x04*channel + 0x02); + /* in most files 1st frame has step 0 but it seems ok and needed for correct waveform */ //if (step_size < 0x7f) step_size = 0x7f; //else if (step_size > 0x6000) step_size = 0x6000; } - /* decode nibbles (layout: varies) */ + /* decode nibbles (layout: one nibble per channel, low-high order, ex 6ch=10325410 32541032 ...) */ for (i = first_sample; i < first_sample + samples_to_do; i++) { - off_t byte_offset = (channelspacing == 2) ? - (stream->offset + 0x40*num_frame + 0x04*channelspacing) + i : /* stereo: one nibble per channel */ - (stream->offset + 0x40*num_frame + 0x04*channelspacing) + i/2; /* mono: consecutive nibbles */ - int nibble_shift = (channelspacing == 2) ? - (!(channel&1) ? 0:4) : - (!(i&1) ? 0:4); /* even = low, odd = high */ + int pos = (channelspacing == 1) ? + (0x04*channelspacing) + i/2 : + (0x04*channelspacing) + (i * 4 * channelspacing + 4*channel) / 8; /* nibble position to closest byte */ + int shift = (channelspacing == 1) ? /* low first */ + (!(i&1) ? 0:4) : + (!(channel&1) ? 0:4); - yamaha_adpcmb_expand_nibble(stream, byte_offset, nibble_shift, &hist1, &step_size, &out_sample); + yamaha_adpcmb_expand_nibble(frame[pos], shift, &hist1, &step_size, &out_sample); outbuf[sample_count] = out_sample; sample_count += channelspacing; } @@ -150,24 +159,29 @@ void decode_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin } -/* NXAP ADPCM, Yamaha ADPCM-B with weird headered frames */ +/* NXAP ADPCM, Yamaha ADPCM-B with weird headered frames, partially rev'd from the ELF */ void decode_nxap(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i, sample_count = 0, num_frame; + uint8_t frame[0x40] = {0}; /* known max is 0xC0 */ + off_t frame_offset; + int i, sample_count = 0, frames_in; int32_t hist1 = stream->adpcm_history1_32; int step_size = stream->adpcm_step_index; int16_t out_sample; /* external interleave, mono */ - int block_samples = (0x40 - 0x4) * 2; - num_frame = first_sample / block_samples; + size_t frame_size = 0x40; + int block_samples = (frame_size - 0x4) * 2; + frames_in = first_sample / block_samples; first_sample = first_sample % block_samples; + /* parse frame */ + frame_offset = stream->offset + frame_size * frames_in; + read_streamfile(frame, frame_offset, frame_size, stream->streamfile); /* ignore EOF errors */ + /* header (hist+step) */ if (first_sample == 0) { - off_t header_offset = stream->offset + 0x40*num_frame; - - hist1 = read_s16le(header_offset+0x00,stream->streamfile); - step_size = read_u16le(header_offset+0x02,stream->streamfile) >> 1; /* remove lower bit, also note unsignedness */ + hist1 = get_s16le(frame + 0x00); + step_size = get_u16le(frame + 0x02) >> 1; /* remove lower bit, also note unsignedness */ if (step_size < 0x7f) step_size = 0x7f; else if (step_size > 0x6000) step_size = 0x6000; /* step's lower bit is hist1 sign (useless), and code doesn't seem to do anything useful with it? */ @@ -175,10 +189,10 @@ void decode_nxap(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin /* decode nibbles (layout: all nibbles from one channel) */ for (i = first_sample; i < first_sample + samples_to_do; i++) { - off_t byte_offset = (stream->offset + 0x40*num_frame + 0x04) + i/2; - int nibble_shift = (i&1?0:4); + int pos = 0x04 + i/2; + int shift = (i&1?0:4); - yamaha_adpcmb_expand_nibble(stream, byte_offset, nibble_shift, &hist1, &step_size, &out_sample); + yamaha_adpcmb_expand_nibble(frame[pos], shift, &hist1, &step_size, &out_sample); outbuf[sample_count] = out_sample; sample_count += channelspacing; } @@ -193,8 +207,8 @@ size_t yamaha_bytes_to_samples(size_t bytes, int channels) { return bytes * 2 / channels; } -size_t aska_bytes_to_samples(size_t bytes, int channels) { - int block_align = 0x40; +size_t aska_bytes_to_samples(size_t bytes, size_t frame_size, int channels) { + int block_align = frame_size; if (channels <= 0) return 0; return (bytes / block_align) * (block_align - 0x04*channels) * 2 / channels + ((bytes % block_align) ? ((bytes % block_align) - 0x04*channels) * 2 / channels : 0); diff --git a/Frameworks/vgmstream/vgmstream/src/decode.c b/Frameworks/vgmstream/vgmstream/src/decode.c new file mode 100644 index 000000000..427dd3828 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/decode.c @@ -0,0 +1,1487 @@ +#include "vgmstream.h" +#include "decode.h" +#include "layout/layout.h" +#include "coding/coding.h" +#include "mixing.h" +#include "plugins.h" + +/* custom codec handling, not exactly "decode" stuff but here to simplify adding new codecs */ + + +void free_codec(VGMSTREAM* vgmstream) { + +#ifdef VGM_USE_VORBIS + if (vgmstream->coding_type == coding_OGG_VORBIS) { + free_ogg_vorbis(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_VORBIS_custom) { + free_vorbis_custom(vgmstream->codec_data); + } +#endif + + if (vgmstream->coding_type == coding_CIRCUS_VQ) { + free_circus_vq(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_RELIC) { + free_relic(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_CRI_HCA) { + free_hca(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_UBI_ADPCM) { + free_ubi_adpcm(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_IMUSE) { + free_imuse(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_EA_MT) { + free_ea_mt(vgmstream->codec_data, vgmstream->channels); + } + +#ifdef VGM_USE_FFMPEG + if (vgmstream->coding_type == coding_FFmpeg) { + free_ffmpeg(vgmstream->codec_data); + } +#endif + +#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) + if (vgmstream->coding_type == coding_MP4_AAC) { + free_mp4_aac(vgmstream->codec_data); + } +#endif + +#ifdef VGM_USE_MPEG + if (vgmstream->coding_type == coding_MPEG_custom || + vgmstream->coding_type == coding_MPEG_ealayer3 || + vgmstream->coding_type == coding_MPEG_layer1 || + vgmstream->coding_type == coding_MPEG_layer2 || + vgmstream->coding_type == coding_MPEG_layer3) { + free_mpeg(vgmstream->codec_data); + } +#endif + +#ifdef VGM_USE_G7221 + if (vgmstream->coding_type == coding_G7221C) { + free_g7221(vgmstream->codec_data); + } +#endif + +#ifdef VGM_USE_G719 + if (vgmstream->coding_type == coding_G719) { + free_g719(vgmstream->codec_data, vgmstream->channels); + } +#endif + +#ifdef VGM_USE_MAIATRAC3PLUS + if (vgmstream->coding_type == coding_AT3plus) { + free_at3plus(vgmstream->codec_data); + } +#endif + +#ifdef VGM_USE_ATRAC9 + if (vgmstream->coding_type == coding_ATRAC9) { + free_atrac9(vgmstream->codec_data); + } +#endif + +#ifdef VGM_USE_CELT + if (vgmstream->coding_type == coding_CELT_FSB) { + free_celt_fsb(vgmstream->codec_data); + } +#endif + + if (vgmstream->coding_type == coding_ACM) { + free_acm(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_NWA) { + free_nwa(vgmstream->codec_data); + } +} + + +void seek_codec(VGMSTREAM* vgmstream) { + if (vgmstream->coding_type == coding_CIRCUS_VQ) { + seek_circus_vq(vgmstream->codec_data, vgmstream->loop_current_sample); + } + + if (vgmstream->coding_type == coding_RELIC) { + seek_relic(vgmstream->codec_data, vgmstream->loop_current_sample); + } + + if (vgmstream->coding_type == coding_CRI_HCA) { + loop_hca(vgmstream->codec_data, vgmstream->loop_current_sample); + } + + if (vgmstream->coding_type == coding_UBI_ADPCM) { + seek_ubi_adpcm(vgmstream->codec_data, vgmstream->loop_current_sample); + } + + if (vgmstream->coding_type == coding_IMUSE) { + seek_imuse(vgmstream->codec_data, vgmstream->loop_current_sample); + } + + if (vgmstream->coding_type == coding_EA_MT) { + seek_ea_mt(vgmstream, vgmstream->loop_current_sample); + } + +#ifdef VGM_USE_VORBIS + if (vgmstream->coding_type == coding_OGG_VORBIS) { + seek_ogg_vorbis(vgmstream->codec_data, vgmstream->loop_current_sample); + } + + if (vgmstream->coding_type == coding_VORBIS_custom) { + seek_vorbis_custom(vgmstream, vgmstream->loop_current_sample); + } +#endif + +#ifdef VGM_USE_FFMPEG + if (vgmstream->coding_type == coding_FFmpeg) { + seek_ffmpeg(vgmstream->codec_data, vgmstream->loop_current_sample); + } +#endif + +#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) + if (vgmstream->coding_type == coding_MP4_AAC) { + seek_mp4_aac(vgmstream, vgmstream->loop_sample); + } +#endif + +#ifdef VGM_USE_MAIATRAC3PLUS + if (vgmstream->coding_type == coding_AT3plus) { + seek_at3plus(vgmstream, vgmstream->loop_current_sample); + } +#endif + +#ifdef VGM_USE_ATRAC9 + if (vgmstream->coding_type == coding_ATRAC9) { + seek_atrac9(vgmstream, vgmstream->loop_current_sample); + } +#endif + +#ifdef VGM_USE_CELT + if (vgmstream->coding_type == coding_CELT_FSB) { + seek_celt_fsb(vgmstream, vgmstream->loop_current_sample); + } +#endif + +#ifdef VGM_USE_MPEG + if (vgmstream->coding_type == coding_MPEG_custom || + vgmstream->coding_type == coding_MPEG_ealayer3 || + vgmstream->coding_type == coding_MPEG_layer1 || + vgmstream->coding_type == coding_MPEG_layer2 || + vgmstream->coding_type == coding_MPEG_layer3) { + seek_mpeg(vgmstream, vgmstream->loop_current_sample); + } +#endif + + if (vgmstream->coding_type == coding_NWA) { + seek_nwa(vgmstream->codec_data, vgmstream->loop_current_sample); + } +} + + +void reset_codec(VGMSTREAM* vgmstream) { + +#ifdef VGM_USE_VORBIS + if (vgmstream->coding_type == coding_OGG_VORBIS) { + reset_ogg_vorbis(vgmstream); + } + + if (vgmstream->coding_type == coding_VORBIS_custom) { + reset_vorbis_custom(vgmstream); + } +#endif + + if (vgmstream->coding_type == coding_CIRCUS_VQ) { + reset_circus_vq(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_RELIC) { + reset_relic(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_CRI_HCA) { + reset_hca(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_UBI_ADPCM) { + reset_ubi_adpcm(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_IMUSE) { + reset_imuse(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_EA_MT) { + reset_ea_mt(vgmstream); + } + +#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) + if (vgmstream->coding_type == coding_MP4_AAC) { + reset_mp4_aac(vgmstream); + } +#endif + +#ifdef VGM_USE_MPEG + if (vgmstream->coding_type == coding_MPEG_custom || + vgmstream->coding_type == coding_MPEG_ealayer3 || + vgmstream->coding_type == coding_MPEG_layer1 || + vgmstream->coding_type == coding_MPEG_layer2 || + vgmstream->coding_type == coding_MPEG_layer3) { + reset_mpeg(vgmstream->codec_data); + } +#endif + +#ifdef VGM_USE_G7221 + if (vgmstream->coding_type == coding_G7221C) { + reset_g7221(vgmstream->codec_data); + } +#endif + +#ifdef VGM_USE_G719 + if (vgmstream->coding_type == coding_G719) { + reset_g719(vgmstream->codec_data, vgmstream->channels); + } +#endif + +#ifdef VGM_USE_MAIATRAC3PLUS + if (vgmstream->coding_type == coding_AT3plus) { + reset_at3plus(vgmstream); + } +#endif + +#ifdef VGM_USE_ATRAC9 + if (vgmstream->coding_type == coding_ATRAC9) { + reset_atrac9(vgmstream->codec_data); + } +#endif + +#ifdef VGM_USE_CELT + if (vgmstream->coding_type == coding_CELT_FSB) { + reset_celt_fsb(vgmstream->codec_data); + } +#endif + +#ifdef VGM_USE_FFMPEG + if (vgmstream->coding_type == coding_FFmpeg) { + reset_ffmpeg(vgmstream->codec_data); + } +#endif + + if (vgmstream->coding_type == coding_ACM) { + reset_acm(vgmstream->codec_data); + } + + if (vgmstream->coding_type == coding_NWA) { + reset_nwa(vgmstream->codec_data); + } +} + + +/* Get the number of samples of a single frame (smallest self-contained sample group, 1/N channels) */ +int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) { + /* Value returned here is the max (or less) that vgmstream will ask a decoder per + * "decode_x" call. Decoders with variable samples per frame or internal discard + * may return 0 here and handle arbitrary samples_to_do values internally + * (or some internal sample buffer max too). */ + + switch (vgmstream->coding_type) { + case coding_SILENCE: + return 0; + + case coding_CRI_ADX: + case coding_CRI_ADX_fixed: + case coding_CRI_ADX_exp: + case coding_CRI_ADX_enc_8: + case coding_CRI_ADX_enc_9: + return (vgmstream->interleave_block_size - 2) * 2; + + case coding_NGC_DSP: + case coding_NGC_DSP_subint: + return 14; + case coding_NGC_AFC: + case coding_VADPCM: + return 16; + case coding_NGC_DTK: + return 28; + case coding_G721: + return 1; + + case coding_PCM16LE: + case coding_PCM16BE: + case coding_PCM16_int: + case coding_PCM8: + case coding_PCM8_int: + case coding_PCM8_U: + case coding_PCM8_U_int: + case coding_PCM8_SB: + case coding_ULAW: + case coding_ULAW_int: + case coding_ALAW: + case coding_PCMFLOAT: + return 1; +#ifdef VGM_USE_VORBIS + case coding_OGG_VORBIS: + case coding_VORBIS_custom: +#endif +#ifdef VGM_USE_MPEG + case coding_MPEG_custom: + case coding_MPEG_ealayer3: + case coding_MPEG_layer1: + case coding_MPEG_layer2: + case coding_MPEG_layer3: +#endif + case coding_SDX2: + case coding_SDX2_int: + case coding_CBD2: + case coding_ACM: + case coding_DERF: + case coding_WADY: + case coding_NWA: + case coding_SASSC: + case coding_CIRCUS_ADPCM: + return 1; + + case coding_IMA: + case coding_DVI_IMA: + case coding_SNDS_IMA: + case coding_OTNS_IMA: + case coding_UBI_IMA: + case coding_OKI16: + case coding_OKI4S: + case coding_MTF_IMA: + return 1; + case coding_PCM4: + case coding_PCM4_U: + case coding_IMA_int: + case coding_DVI_IMA_int: + case coding_3DS_IMA: + case coding_WV6_IMA: + case coding_ALP_IMA: + case coding_FFTA2_IMA: + case coding_BLITZ_IMA: + case coding_PCFX: + return 2; + case coding_XBOX_IMA: + case coding_XBOX_IMA_mch: + case coding_XBOX_IMA_int: + case coding_FSB_IMA: + case coding_WWISE_IMA: + case coding_CD_IMA: + return 64; + case coding_APPLE_IMA4: + return 64; + case coding_MS_IMA: + case coding_REF_IMA: + return ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1; + case coding_RAD_IMA: + return (vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels; + case coding_NDS_IMA: + case coding_DAT4_IMA: + return (vgmstream->interleave_block_size - 0x04) * 2; + case coding_AWC_IMA: + return (0x800 - 0x04) * 2; + case coding_RAD_IMA_mono: + return 32; + case coding_H4M_IMA: + return 0; /* variable (block-controlled) */ + + case coding_XA: + return 28*8 / vgmstream->channels; /* 8 subframes per frame, mono/stereo */ + case coding_PSX: + case coding_PSX_badflags: + case coding_HEVAG: + return 28; + case coding_PSX_cfg: + case coding_PSX_pivotal: + return (vgmstream->interleave_block_size - 0x01) * 2; /* size 0x01 header */ + + case coding_EA_XA: + case coding_EA_XA_int: + case coding_EA_XA_V2: + case coding_MAXIS_XA: + return 28; + case coding_EA_XAS_V0: + return 32; + case coding_EA_XAS_V1: + return 128; + + case coding_MSADPCM: + return (vgmstream->frame_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2; + case coding_MSADPCM_int: + case coding_MSADPCM_ck: + return (vgmstream->frame_size - 0x07)*2 + 2; + case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */ + return vgmstream->ws_output_size; + case coding_AICA: + return 1; + case coding_AICA_int: + return 2; + case coding_ASKA: + return (vgmstream->frame_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels; + case coding_NXAP: + return (0x40-0x04) * 2; + case coding_NDS_PROCYON: + return 30; + case coding_L5_555: + return 32; + case coding_LSF: + return 54; + +#ifdef VGM_USE_G7221 + case coding_G7221C: + return 32000/50; /* Siren7: 16000/50 */ +#endif +#ifdef VGM_USE_G719 + case coding_G719: + return 48000/50; +#endif +#ifdef VGM_USE_FFMPEG + case coding_FFmpeg: + return 0; +#endif + case coding_MTAF: + return 128*2; + case coding_MTA2: + return 128*2; + case coding_MC3: + return 10; + case coding_FADPCM: + return 256; /* (0x8c - 0xc) * 2 */ + case coding_ASF: + return 32; /* (0x11 - 0x1) * 2 */ + case coding_DSA: + return 14; /* (0x08 - 0x1) * 2 */ + case coding_XMD: + return (vgmstream->interleave_block_size - 0x06)*2 + 2; + case coding_PTADPCM: + return (vgmstream->interleave_block_size - 0x05)*2 + 2; + case coding_UBI_ADPCM: + return 0; /* varies per mode */ + case coding_IMUSE: + return 0; /* varies per frame */ + case coding_EA_MT: + return 0; /* 432, but variable in looped files */ + case coding_CIRCUS_VQ: + return 0; + case coding_RELIC: + return 0; /* 512 */ + case coding_CRI_HCA: + return 0; /* 1024 - delay/padding (which can be bigger than 1024) */ +#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) + 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; +#endif +#ifdef VGM_USE_ATRAC9 + case coding_ATRAC9: + return 0; /* varies with config data, usually 256 or 1024 */ +#endif +#ifdef VGM_USE_CELT + case coding_CELT_FSB: + return 0; /* 512? */ +#endif + default: + return 0; + } +} + +/* Get the number of bytes of a single frame (smallest self-contained byte group, 1/N channels) */ +int get_vgmstream_frame_size(VGMSTREAM* vgmstream) { + switch (vgmstream->coding_type) { + case coding_SILENCE: + return 0; + + case coding_CRI_ADX: + case coding_CRI_ADX_fixed: + case coding_CRI_ADX_exp: + case coding_CRI_ADX_enc_8: + case coding_CRI_ADX_enc_9: + return vgmstream->interleave_block_size; + + case coding_NGC_DSP: + return 0x08; + case coding_NGC_DSP_subint: + return 0x08 * vgmstream->channels; + case coding_NGC_AFC: + case coding_VADPCM: + return 0x09; + case coding_NGC_DTK: + return 0x20; + case coding_G721: + return 0; + + case coding_PCM16LE: + case coding_PCM16BE: + case coding_PCM16_int: + return 0x02; + case coding_PCM8: + case coding_PCM8_int: + case coding_PCM8_U: + case coding_PCM8_U_int: + case coding_PCM8_SB: + case coding_ULAW: + case coding_ULAW_int: + case coding_ALAW: + return 0x01; + case coding_PCMFLOAT: + return 0x04; + + case coding_SDX2: + case coding_SDX2_int: + case coding_CBD2: + case coding_DERF: + case coding_WADY: + case coding_NWA: + case coding_SASSC: + case coding_CIRCUS_ADPCM: + return 0x01; + + case coding_PCM4: + case coding_PCM4_U: + case coding_IMA: + case coding_IMA_int: + case coding_DVI_IMA: + case coding_DVI_IMA_int: + case coding_3DS_IMA: + case coding_WV6_IMA: + case coding_ALP_IMA: + case coding_FFTA2_IMA: + case coding_BLITZ_IMA: + case coding_PCFX: + case coding_OKI16: + case coding_OKI4S: + case coding_MTF_IMA: + return 0x01; + case coding_MS_IMA: + case coding_RAD_IMA: + case coding_NDS_IMA: + case coding_DAT4_IMA: + case coding_REF_IMA: + return vgmstream->interleave_block_size; + case coding_AWC_IMA: + return 0x800; + case coding_RAD_IMA_mono: + return 0x14; + case coding_SNDS_IMA: + case coding_OTNS_IMA: + return 0; //todo: 0x01? + case coding_UBI_IMA: /* variable (PCM then IMA) */ + return 0; + case coding_XBOX_IMA: + //todo should be 0x48 when stereo, but blocked/interleave layout don't understand stereo codecs + return 0x24; //vgmstream->channels==1 ? 0x24 : 0x48; + case coding_XBOX_IMA_int: + case coding_WWISE_IMA: + case coding_CD_IMA: + return 0x24; + case coding_XBOX_IMA_mch: + case coding_FSB_IMA: + return 0x24 * vgmstream->channels; + case coding_APPLE_IMA4: + return 0x22; + case coding_H4M_IMA: + return 0x00; /* variable (block-controlled) */ + + case coding_XA: + return 0x80; + case coding_PSX: + case coding_PSX_badflags: + case coding_HEVAG: + return 0x10; + case coding_PSX_cfg: + case coding_PSX_pivotal: + return vgmstream->interleave_block_size; + + case coding_EA_XA: + return 0x1E; + case coding_EA_XA_int: + return 0x0F; + case coding_MAXIS_XA: + return 0x0F*vgmstream->channels; + case coding_EA_XA_V2: + return 0; /* variable (ADPCM frames of 0x0f or PCM frames of 0x3d) */ + case coding_EA_XAS_V0: + return 0xF+0x02+0x02; + case coding_EA_XAS_V1: + return 0x4c*vgmstream->channels; + + case coding_MSADPCM: + case coding_MSADPCM_int: + case coding_MSADPCM_ck: + return vgmstream->frame_size; + case coding_WS: + return vgmstream->current_block_size; + case coding_AICA: + case coding_AICA_int: + return 0x01; + case coding_ASKA: + return vgmstream->frame_size; + case coding_NXAP: + return 0x40; + case coding_NDS_PROCYON: + return 0x10; + case coding_L5_555: + return 0x12; + case coding_LSF: + return 0x1C; + +#ifdef VGM_USE_G7221 + case coding_G7221C: +#endif +#ifdef VGM_USE_G719 + case coding_G719: +#endif +#ifdef VGM_USE_MAIATRAC3PLUS + case coding_AT3plus: +#endif +#ifdef VGM_USE_FFMPEG + case coding_FFmpeg: +#endif + case coding_MTAF: + return vgmstream->interleave_block_size; + case coding_MTA2: + return 0x90; + case coding_MC3: + return 0x04; + case coding_FADPCM: + return 0x8c; + case coding_ASF: + return 0x11; + case coding_DSA: + return 0x08; + case coding_XMD: + return vgmstream->interleave_block_size; + case coding_PTADPCM: + return vgmstream->interleave_block_size; + case coding_UBI_ADPCM: + return 0; /* varies per mode? */ + case coding_IMUSE: + return 0; /* varies per frame */ + 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 +#ifdef VGM_USE_CELT + case coding_CELT_FSB: + return 0; /* varies, usually 0x80-100 */ +#endif + default: /* Vorbis, MPEG, ACM, etc */ + return 0; + } +} + +/* In NDS IMA the frame size is the block size, so the last one is short */ +int get_vgmstream_samples_per_shortframe(VGMSTREAM* vgmstream) { + switch (vgmstream->coding_type) { + case coding_NDS_IMA: + return (vgmstream->interleave_last_block_size-4)*2; + default: + return get_vgmstream_samples_per_frame(vgmstream); + } +} + +int get_vgmstream_shortframe_size(VGMSTREAM* vgmstream) { + switch (vgmstream->coding_type) { + case coding_NDS_IMA: + return vgmstream->interleave_last_block_size; + default: + return get_vgmstream_frame_size(vgmstream); + } +} + +/* Decode samples into the buffer. Assume that we have written samples_written into the + * buffer already, and we have samples_to_do consecutive samples ahead of us (won't call + * more than one frame if configured above to do so). + * Called by layouts since they handle samples written/to_do */ +void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_do, sample_t* buffer) { + int ch; + + buffer += samples_written * vgmstream->channels; /* passed externally to simplify I guess */ + + switch (vgmstream->coding_type) { + case coding_SILENCE: + memset(buffer, 0, samples_to_do * vgmstream->channels * sizeof(sample_t)); + break; + + case coding_CRI_ADX: + case coding_CRI_ADX_exp: + case coding_CRI_ADX_fixed: + case coding_CRI_ADX_enc_8: + case coding_CRI_ADX_enc_9: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_adx(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + vgmstream->interleave_block_size, vgmstream->coding_type); + } + break; + case coding_NGC_DSP: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ngc_dsp(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_NGC_DSP_subint: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ngc_dsp_subint(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, + vgmstream->interleave_block_size); + } + break; + + case coding_PCM16LE: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm16le(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_PCM16BE: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm16be(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_PCM16_int: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm16_int(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + vgmstream->codec_endian); + } + break; + case coding_PCM8: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_PCM8_int: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8_int(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_PCM8_U: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8_unsigned(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_PCM8_U_int: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8_unsigned_int(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_PCM8_SB: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8_sb(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_PCM4: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm4(vgmstream,&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_PCM4_U: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm4_unsigned(vgmstream, &vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + + case coding_ULAW: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ulaw(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_ULAW_int: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ulaw_int(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_ALAW: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_alaw(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_PCMFLOAT: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcmfloat(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + vgmstream->codec_endian); + } + break; + + case coding_NDS_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_nds_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_DAT4_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_dat4_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_XBOX_IMA: + case coding_XBOX_IMA_int: { + int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_XBOX_IMA); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_xbox_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, is_stereo); + } + break; + } + case coding_XBOX_IMA_mch: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_xbox_ima_mch(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_MS_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ms_ima(vgmstream,&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_RAD_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_rad_ima(vgmstream,&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_RAD_IMA_mono: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_rad_ima_mono(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_NGC_DTK: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ngc_dtk(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_G721: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_g721(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_NGC_AFC: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ngc_afc(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_VADPCM: { + int order = vgmstream->codec_config; + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_vadpcm(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, order); + } + break; + } + case coding_PSX: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_psx(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + 0, vgmstream->codec_config); + } + break; + case coding_PSX_badflags: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_psx(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + 1, vgmstream->codec_config); + } + break; + case coding_PSX_cfg: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_psx_configurable(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + vgmstream->interleave_block_size, vgmstream->codec_config); + } + break; + case coding_PSX_pivotal: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_psx_pivotal(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + vgmstream->interleave_block_size); + } + break; + case coding_HEVAG: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_hevag(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_XA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_xa(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_EA_XA: + case coding_EA_XA_int: { + int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_EA_XA); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_xa(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, is_stereo); + } + break; + } + case coding_EA_XA_V2: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_xa_v2(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_MAXIS_XA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_maxis_xa(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_EA_XAS_V0: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_xas_v0(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_EA_XAS_V1: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_xas_v1(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; +#ifdef VGM_USE_VORBIS + case coding_OGG_VORBIS: + decode_ogg_vorbis(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels); + break; + + case coding_VORBIS_custom: + decode_vorbis_custom(vgmstream, buffer, samples_to_do, vgmstream->channels); + break; +#endif + case coding_CIRCUS_VQ: + decode_circus_vq(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels); + break; + case coding_RELIC: + decode_relic(&vgmstream->ch[0], vgmstream->codec_data, buffer, samples_to_do); + break; + case coding_CRI_HCA: + decode_hca(vgmstream->codec_data, buffer, samples_to_do); + break; +#ifdef VGM_USE_FFMPEG + case coding_FFmpeg: + decode_ffmpeg(vgmstream, buffer, samples_to_do, vgmstream->channels); + break; +#endif +#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) + case coding_MP4_AAC: + decode_mp4_aac(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels); + break; +#endif + case coding_SDX2: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_sdx2(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_SDX2_int: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_sdx2_int(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_CBD2: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_cbd2(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_CBD2_int: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_cbd2_int(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_DERF: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_derf(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_WADY: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_wady(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_CIRCUS_ADPCM: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_circus_adpcm(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + + case coding_IMA: + case coding_IMA_int: + case coding_DVI_IMA: + case coding_DVI_IMA_int: { + int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_IMA) + || (vgmstream->channels > 1 && vgmstream->coding_type == coding_DVI_IMA); + int is_high_first = vgmstream->coding_type == coding_DVI_IMA + || vgmstream->coding_type == coding_DVI_IMA_int; + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_standard_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, + is_stereo, is_high_first); + } + break; + } + case coding_MTF_IMA: { + int is_stereo = (vgmstream->channels > 1); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_mtf_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, is_stereo); + } + break; + } + case coding_3DS_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_3ds_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_WV6_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_wv6_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_ALP_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_alp_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_FFTA2_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ffta2_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_BLITZ_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_blitz_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + + case coding_APPLE_IMA4: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_apple_ima4(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_SNDS_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_snds_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_OTNS_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_otns_ima(vgmstream, &vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_FSB_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_fsb_ima(vgmstream, &vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_WWISE_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_wwise_ima(vgmstream,&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_REF_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ref_ima(vgmstream,&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_AWC_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_awc_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_UBI_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ubi_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, + vgmstream->codec_config); + } + break; + case coding_H4M_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + uint16_t frame_format = (uint16_t)((vgmstream->codec_config >> 8) & 0xFFFF); + + decode_h4m_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, + frame_format); + } + break; + case coding_CD_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_cd_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + + case coding_WS: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ws(vgmstream, ch, buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + +#ifdef VGM_USE_MPEG + case coding_MPEG_custom: + case coding_MPEG_ealayer3: + case coding_MPEG_layer1: + case coding_MPEG_layer2: + case coding_MPEG_layer3: + decode_mpeg(vgmstream, buffer, samples_to_do, vgmstream->channels); + break; +#endif +#ifdef VGM_USE_G7221 + case coding_G7221C: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_g7221(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch); + } + break; +#endif +#ifdef VGM_USE_G719 + case coding_G719: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_g719(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch); + } + break; +#endif +#ifdef VGM_USE_MAIATRAC3PLUS + case coding_AT3plus: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_at3plus(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch); + } + break; +#endif +#ifdef VGM_USE_ATRAC9 + case coding_ATRAC9: + decode_atrac9(vgmstream, buffer, samples_to_do, vgmstream->channels); + break; +#endif +#ifdef VGM_USE_CELT + case coding_CELT_FSB: + decode_celt_fsb(vgmstream, buffer, samples_to_do, vgmstream->channels); + break; +#endif + case coding_ACM: + decode_acm(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels); + break; + case coding_NWA: + decode_nwa(vgmstream->codec_data, buffer, samples_to_do); + break; + case coding_MSADPCM: + case coding_MSADPCM_int: + if (vgmstream->channels == 1 || vgmstream->coding_type == coding_MSADPCM_int) { + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_msadpcm_mono(vgmstream,buffer+ch, + vgmstream->channels,vgmstream->samples_into_block, samples_to_do, ch); + } + } + else if (vgmstream->channels == 2) { + decode_msadpcm_stereo(vgmstream, buffer, vgmstream->samples_into_block,samples_to_do); + } + break; + case coding_MSADPCM_ck: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_msadpcm_ck(vgmstream, buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_AICA: + case coding_AICA_int: { + int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_AICA); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_aica(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, + is_stereo); + } + break; + } + case coding_ASKA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_aska(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, vgmstream->frame_size); + } + break; + case coding_NXAP: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_nxap(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_TGC: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_tgc(&vgmstream->ch[ch], buffer+ch, + vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_NDS_PROCYON: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_nds_procyon(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_L5_555: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_l5_555(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_SASSC: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_sassc(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_LSF: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_lsf(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_MTAF: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_mtaf(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_MTA2: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_mta2(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_MC3: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_mc3(vgmstream, &vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + case coding_FADPCM: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_fadpcm(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels,vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_ASF: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_asf(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_DSA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_dsa(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; + case coding_XMD: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_xmd(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + vgmstream->interleave_block_size); + } + break; + case coding_PTADPCM: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ptadpcm(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + vgmstream->interleave_block_size); + } + break; + case coding_PCFX: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcfx(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, + vgmstream->codec_config); + } + break; + case coding_OKI16: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_oki16(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + + case coding_OKI4S: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_oki4s(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + } + break; + + case coding_UBI_ADPCM: + decode_ubi_adpcm(vgmstream, buffer, samples_to_do); + break; + + case coding_IMUSE: + decode_imuse(vgmstream, buffer, samples_to_do); + break; + + case coding_EA_MT: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_mt(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch); + } + break; + default: + break; + } +} + +/* Calculate number of consecutive samples we can decode. Takes into account hitting + * a loop start or end, or going past a single frame. */ +int get_vgmstream_samples_to_do(int samples_this_block, int samples_per_frame, VGMSTREAM* vgmstream) { + int samples_to_do; + int samples_left_this_block; + + samples_left_this_block = samples_this_block - vgmstream->samples_into_block; + samples_to_do = samples_left_this_block; /* by default decodes all samples left */ + + /* fun loopy crap, why did I think this would be any simpler? */ + if (vgmstream->loop_flag) { + int samples_after_decode = vgmstream->current_sample + samples_left_this_block; + + /* are we going to hit the loop end during this block? */ + if (samples_after_decode > vgmstream->loop_end_sample) { + /* only do samples up to loop end */ + samples_to_do = vgmstream->loop_end_sample - vgmstream->current_sample; + } + + /* are we going to hit the loop start during this block? (first time only) */ + if (samples_after_decode > vgmstream->loop_start_sample && !vgmstream->hit_loop) { + /* only do samples up to loop start */ + samples_to_do = vgmstream->loop_start_sample - vgmstream->current_sample; + } + } + + /* if it's a framed encoding don't do more than one frame */ + if (samples_per_frame > 1 && (vgmstream->samples_into_block % samples_per_frame) + samples_to_do > samples_per_frame) + samples_to_do = samples_per_frame - (vgmstream->samples_into_block % samples_per_frame); + + return samples_to_do; +} + +/* Detect loop start and save values, or detect loop end and restore (loop back). + * Returns 1 if loop was done. */ +int vgmstream_do_loop(VGMSTREAM* vgmstream) { + /*if (!vgmstream->loop_flag) return 0;*/ + + /* is this the loop end? = new loop, continue from loop_start_sample */ + if (vgmstream->current_sample == vgmstream->loop_end_sample) { + + /* disable looping if target count reached and continue normally + * (only needed with the "play stream end after looping N times" option enabled) */ + vgmstream->loop_count++; + if (vgmstream->loop_target && vgmstream->loop_target == vgmstream->loop_count) { + vgmstream->loop_flag = 0; /* could be improved but works ok, will be restored on resets */ + return 0; + } + + /* against everything I hold sacred, preserve adpcm history before looping for certain types */ + if (vgmstream->meta_type == meta_DSP_STD || + vgmstream->meta_type == meta_DSP_RS03 || + vgmstream->meta_type == meta_DSP_CSTR || + vgmstream->coding_type == coding_PSX || + vgmstream->coding_type == coding_PSX_badflags) { + int ch; + for (ch = 0; ch < vgmstream->channels; ch++) { + vgmstream->loop_ch[ch].adpcm_history1_16 = vgmstream->ch[ch].adpcm_history1_16; + vgmstream->loop_ch[ch].adpcm_history2_16 = vgmstream->ch[ch].adpcm_history2_16; + vgmstream->loop_ch[ch].adpcm_history1_32 = vgmstream->ch[ch].adpcm_history1_32; + vgmstream->loop_ch[ch].adpcm_history2_32 = vgmstream->ch[ch].adpcm_history2_32; + } + } + + /* loop codecs */ + seek_codec(vgmstream); + + /* restore! */ + memcpy(vgmstream->ch, vgmstream->loop_ch, sizeof(VGMSTREAMCHANNEL) * vgmstream->channels); + vgmstream->current_sample = vgmstream->loop_current_sample; + vgmstream->samples_into_block = vgmstream->loop_samples_into_block; + vgmstream->current_block_size = vgmstream->loop_block_size; + vgmstream->current_block_samples = vgmstream->loop_block_samples; + vgmstream->current_block_offset = vgmstream->loop_block_offset; + vgmstream->next_block_offset = vgmstream->loop_next_block_offset; + //vgmstream->pstate = vgmstream->lstate; /* play state is applied over loops */ + + /* loop layouts (after restore, in case layout needs state manipulations) */ + switch(vgmstream->layout_type) { + case layout_segmented: + loop_layout_segmented(vgmstream, vgmstream->loop_current_sample); + break; + case layout_layered: + loop_layout_layered(vgmstream, vgmstream->loop_current_sample); + break; + default: + break; + } + + return 1; /* looped */ + } + + + /* is this the loop start? save if we haven't saved yet (right when first loop starts) */ + if (!vgmstream->hit_loop && vgmstream->current_sample == vgmstream->loop_start_sample) { + /* save! */ + memcpy(vgmstream->loop_ch, vgmstream->ch, sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); + vgmstream->loop_current_sample = vgmstream->current_sample; + vgmstream->loop_samples_into_block = vgmstream->samples_into_block; + vgmstream->loop_block_size = vgmstream->current_block_size; + vgmstream->loop_block_samples = vgmstream->current_block_samples; + vgmstream->loop_block_offset = vgmstream->current_block_offset; + vgmstream->loop_next_block_offset = vgmstream->next_block_offset; + //vgmstream->lstate = vgmstream->pstate; /* play state is applied over loops */ + + vgmstream->hit_loop = 1; /* info that loop is now ready to use */ + } + + return 0; /* not looped */ +} diff --git a/Frameworks/vgmstream/vgmstream/src/decode.h b/Frameworks/vgmstream/vgmstream/src/decode.h new file mode 100644 index 000000000..01adf3586 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/decode.h @@ -0,0 +1,32 @@ +#ifndef _DECODE_H +#define _DECODE_H + +#include "vgmstream.h" + +void free_codec(VGMSTREAM* vgmstream); +void seek_codec(VGMSTREAM* vgmstream); +void reset_codec(VGMSTREAM* vgmstream); + +/* Decode samples into the buffer. Assume that we have written samples_written into the + * buffer already, and we have samples_to_do consecutive samples ahead of us. */ +void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_do, sample_t* buffer); + +/* Detect loop start and save values, or detect loop end and restore (loop back). Returns 1 if loop was done. */ +int vgmstream_do_loop(VGMSTREAM* vgmstream); + +/* Calculate number of consecutive samples to do (taking into account stopping for loop start and end) */ +int get_vgmstream_samples_to_do(int samples_this_block, int samples_per_frame, VGMSTREAM* vgmstream); + + +/* Get the number of samples of a single frame (smallest self-contained sample group, 1/N channels) */ +int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream); + +/* Get the number of bytes of a single frame (smallest self-contained byte group, 1/N channels) */ +int get_vgmstream_frame_size(VGMSTREAM* vgmstream); + +/* In NDS IMA the frame size is the block size, but last one is shorter */ +int get_vgmstream_samples_per_shortframe(VGMSTREAM* vgmstream); +int get_vgmstream_shortframe_size(VGMSTREAM* vgmstream); + + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 8288c0032..66e6c4089 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -37,7 +37,6 @@ static const char* extension_list[] = { "abk", //"ac3", //common, FFmpeg/not parsed (AC3) "acb", - "ace", //fake extension for tri-Ace's .aac (renamed, to be removed) "acm", "ad", //txth/reserved [Xenosaga Freaks (PS2)] "adc", //txth/reserved [Tomb Raider The Last Revelation (DC), Tomb Raider Chronicles (DC)] @@ -67,6 +66,7 @@ static const char* extension_list[] = { "ams", //txth/reserved [Super Dragon Ball Z (PS2) ELF names] "amts", //fake extension/header id for .stm (renamed? to be removed?) "ao", + "ap", "apc", "as4", "asd", @@ -126,6 +126,7 @@ static const char* extension_list[] = { "ccc", "cd", "cfn", //fake extension for CAF (renamed, to be removed?) + "chk", "ckb", "ckd", "cks", @@ -138,7 +139,7 @@ static const char* extension_list[] = { "cxs", "da", - "dat", + //"dat", //common "data", "dax", "dbm", @@ -150,6 +151,7 @@ static const char* extension_list[] = { "diva", "dmsg", "ds2", //txth/reserved [Star Wars Bounty Hunter (GC)] + "dsb", "dsf", "dsp", "dspw", @@ -165,7 +167,6 @@ static const char* extension_list[] = { "enm", "eno", "ens", - "enth", "exa", "ezw", @@ -242,7 +243,6 @@ static const char* extension_list[] = { "kat", "kces", "kcey", //fake extension/header id for .pcm (renamed, to be removed) - "khv", //fake extension/header id for .vas (renamed, to be removed) "km9", "kovs", //fake extension/header id for .kvs "kns", @@ -261,6 +261,7 @@ static const char* extension_list[] = { "lasf", //fake extension for .asf (various) "lbin", //fake extension for .bin (various) "leg", + "lep", "lflac", //fake extension for .flac, FFmpeg/not parsed "lin", "lm0", @@ -277,6 +278,7 @@ static const char* extension_list[] = { "lmpc", //fake extension for .mpc, FFmpeg/not parsed "logg", //fake extension for .ogg "lopus", //fake extension for .opus + "lp", "lpcm", "lpk", "lps", @@ -343,6 +345,7 @@ static const char* extension_list[] = { "nop", "nps", "npsf", //fake extension/header id for .nps (in bigfiles) + "nsopus", "nub", "nub2", "nus3audio", @@ -395,6 +398,7 @@ static const char* extension_list[] = { "rsd", "rsf", "rsm", + "rsp", "rstm", //fake extension/header id for .rstm (in bigfiles) "rvws", "rwar", @@ -552,6 +556,7 @@ static const char* extension_list[] = { "wavebatch", "wavm", "wavx", //txth/reserved [LEGO Star Wars (Xbox)] + "way", "wb", "wb2", "wbd", @@ -559,6 +564,7 @@ static const char* extension_list[] = { "wem", "wii", "wip", //txth/reserved [Colin McRae DiRT (PC)] + "wlv", //txth/reserved [ToeJam & Earl III: Mission to Earth (DC)] "wma", //common "wmus", "wp2", @@ -586,6 +592,7 @@ static const char* extension_list[] = { "xmu", "xnb", "xsf", + "xse", "xsew", "xss", "xvag", @@ -609,6 +616,7 @@ static const char* extension_list[] = { "zsm", "zss", "zwdsp", + "zwv", "vgmstream" /* fake extension, catch-all for FFmpeg/txth/etc */ @@ -621,6 +629,7 @@ static const char* common_extension_list[] = { "aif", //common "aiff", //common "bin", //common + "dat", //common "flac", //common "m4a", //common "m4v", //common @@ -666,6 +675,8 @@ typedef struct { static const coding_info coding_info_list[] = { + {coding_SILENCE, "Silence"}, + {coding_PCM16LE, "Little Endian 16-bit PCM"}, {coding_PCM16BE, "Big Endian 16-bit PCM"}, {coding_PCM16_int, "16-bit PCM with 2 byte interleave (block)"}, @@ -737,6 +748,7 @@ static const coding_info coding_info_list[] = { {coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"}, {coding_UBI_IMA, "Ubisoft 4-bit IMA ADPCM"}, {coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"}, + {coding_CD_IMA, "Crystal Dynamics 4-bit IMA ADPCM"}, {coding_MSADPCM, "Microsoft 4-bit ADPCM"}, {coding_MSADPCM_int, "Microsoft 4-bit ADPCM (mono/interleave)"}, @@ -761,6 +773,7 @@ static const coding_info coding_info_list[] = { {coding_OKI16, "OKI 4-bit ADPCM (16-bit output)"}, {coding_OKI4S, "OKI 4-bit ADPCM (4-shift)"}, {coding_PTADPCM, "Platinum 4-bit ADPCM"}, + {coding_IMUSE, "LucasArts iMUSE VIMA ADPCM"}, {coding_SDX2, "Squareroot-delta-exact (SDX2) 8-bit DPCM"}, {coding_SDX2_int, "Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"}, @@ -768,11 +781,11 @@ static const coding_info coding_info_list[] = { {coding_CBD2_int, "Cuberoot-delta-exact (CBD2) 8-bit DPCM with 1 byte interleave"}, {coding_SASSC, "Activision / EXAKT SASSC 8-bit DPCM"}, {coding_DERF, "Xilam DERF 8-bit DPCM"}, - {coding_ACM, "InterPlay ACM"}, + {coding_WADY, "Marble WADY 8-bit DPCM"}, {coding_NWA, "VisualArt's NWA DPCM"}, + {coding_ACM, "InterPlay ACM"}, {coding_CIRCUS_ADPCM, "Circus 8-bit ADPCM"}, {coding_UBI_ADPCM, "Ubisoft 4/6-bit ADPCM"}, - {coding_IMUSE, "LucasArts iMUSE VIMA ADPCM"}, {coding_EA_MT, "Electronic Arts MicroTalk"}, {coding_CIRCUS_VQ, "Circus VQ"}, @@ -850,7 +863,7 @@ static const layout_info layout_info_list[] = { {layout_blocked_ea_sns, "blocked (EA SNS)"}, {layout_blocked_awc, "blocked (AWC)"}, {layout_blocked_vgs, "blocked (VGS)"}, - {layout_blocked_vawx, "blocked (VAWX)"}, + {layout_blocked_xwav, "blocked (XWAV)"}, {layout_blocked_xvag_subsong, "blocked (XVAG subsong)"}, {layout_blocked_ea_wve_au00, "blocked (EA WVE au00)"}, {layout_blocked_ea_wve_ad10, "blocked (EA WVE Ad10)"}, @@ -863,6 +876,7 @@ static const layout_info layout_info_list[] = { }; static const meta_info meta_info_list[] = { + {meta_SILENCE, "Silence"}, {meta_RSTM, "Nintendo RSTM header"}, {meta_STRM, "Nintendo STRM header"}, {meta_ADX_03, "CRI ADX header type 03"}, @@ -896,7 +910,7 @@ static const meta_info meta_info_list[] = { {meta_PS2_OMU, "Alter Echo OMU Header"}, {meta_DSP_STM, "Intelligent Systems STM header"}, {meta_PS2_EXST, "Sony EXST header"}, - {meta_PS2_SVAG, "Konami SVAG header"}, + {meta_SVAG_KCET, "Konami SVAG header"}, {meta_PS_HEADERLESS, "Headerless PS-ADPCM raw header"}, {meta_PS2_MIB_MIH, "Sony MultiStream MIH+MIB header"}, {meta_DSP_MPDSP, "Single DSP header stereo by .mpdsp extension"}, @@ -945,11 +959,11 @@ static const meta_info meta_info_list[] = { {meta_EA_1SNH, "Electronic Arts 1SNh header"}, {meta_EA_EACS, "Electronic Arts EACS header"}, {meta_SL3, "Atari Melbourne House SL3 header"}, - {meta_FSB1, "FMOD Sample Bank (FSB1) Header"}, - {meta_FSB2, "FMOD Sample Bank (FSB2) Header"}, - {meta_FSB3, "FMOD Sample Bank (FSB3) Header"}, - {meta_FSB4, "FMOD Sample Bank (FSB4) Header"}, - {meta_FSB5, "FMOD Sample Bank (FSB5) Header"}, + {meta_FSB1, "FMOD FSB1 header"}, + {meta_FSB2, "FMOD FSB2 header"}, + {meta_FSB3, "FMOD FSB3 header"}, + {meta_FSB4, "FMOD FSB4 header"}, + {meta_FSB5, "FMOD FSB5 header"}, {meta_RWX, "RWX Header"}, {meta_XWB, "Microsoft XWB header"}, {meta_PS2_XA30, "Reflections XA30 PS2 header"}, @@ -1036,7 +1050,7 @@ static const meta_info meta_info_list[] = { {meta_RIFF_WAVE_MWV, "RIFF WAVE header with .mwv flavoring"}, {meta_FFCC_STR, "Final Fantasy: Crystal Chronicles STR header"}, {meta_SAT_BAKA, "BAKA header from Crypt Killer"}, - {meta_NDS_SWAV, "SWAV Header"}, + {meta_SWAV, "Nintendo SWAV header"}, {meta_VSF, "Square-Enix VSF header"}, {meta_NDS_RRDS, "Ridger Racer DS Header"}, {meta_PS2_TK5, "Tekken 5 Stream Header"}, @@ -1062,7 +1076,7 @@ static const meta_info meta_info_list[] = { {meta_NGC_SCK_DSP, "The Scorpion King SCK Header"}, {meta_CAFF, "Apple Core Audio Format File header"}, {meta_PC_MXST, "Lego Island MxSt Header"}, - {meta_SAB, "Team17 SAB header"}, + {meta_SAB, "Sensaura SAB header"}, {meta_MAXIS_XA, "Maxis XAI/XAJ Header"}, {meta_EXAKT_SC, "assumed Activision / EXAKT SC by extension"}, {meta_WII_BNS, "Nintendo BNS header"}, @@ -1121,7 +1135,7 @@ static const meta_info meta_info_list[] = { {meta_PS2_IAB, "Runtime .IAB header"}, {meta_VS_STR, "Square .VS STR* header"}, {meta_LSF_N1NJ4N, ".lsf !n1nj4n header"}, - {meta_VAWX, "feelplus VAWX header"}, + {meta_XWAV, "feelplus XWAV header"}, {meta_RAW_SNDS, "PC .snds raw header"}, {meta_PS2_WMUS, "assumed The Warriors Sony ADPCM by .wmus extension"}, {meta_HYPERSCAN_KVAG, "Mattel Hyperscan KVAG"}, @@ -1152,7 +1166,7 @@ static const meta_info meta_info_list[] = { {meta_MCA, "Capcom MCA header"}, {meta_XB3D_ADX, "Xenoblade 3D ADX header"}, {meta_HCA, "CRI HCA header"}, - {meta_PS2_SVAG_SNK, "SNK SVAG header"}, + {meta_SVAG_SNK, "SNK SVAG header"}, {meta_PS2_VDS_VDM, "Procyon Studio VDS/VDM header"}, {meta_FFMPEG, "FFmpeg supported file format"}, {meta_X360_CXS, "tri-Crescendo CXS header"}, @@ -1166,9 +1180,7 @@ static const meta_info meta_info_list[] = { {meta_OGL, "Shin'en OGL header"}, {meta_MC3, "Paradigm MC3 header"}, {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_MOBILE, "tri-Ace AAC (Mobile) header"}, + {meta_TA_AAC, "tri-Ace AAC header"}, {meta_MTA2, "Konami MTA2 header"}, {meta_NGC_ULW, "Criterion ULW raw header"}, {meta_XA_XA30, "Reflections XA30 header"}, @@ -1219,10 +1231,9 @@ static const meta_info meta_info_list[] = { {meta_TXTP, "TXTP generic header"}, {meta_SMC_SMH, "Genki SMC+SMH header"}, {meta_PPST, "Parappa PPST header"}, - {meta_OPUS_PPP, "AT9 OPUS header"}, + {meta_SPS_N1, "Nippon Ichi .SPS header"}, {meta_UBI_BAO, "Ubisoft BAO header"}, {meta_DSP_SWITCH_AUDIO, "UE4 Switch Audio header"}, - {meta_TA_AAC_VITA, "tri-Ace AAC (Vita) header"}, {meta_SADF, "Procyon Studio SADF header"}, {meta_H4M, "Hudson HVQM4 header"}, {meta_ASF, "Argonaut ASF header"}, @@ -1300,9 +1311,14 @@ static const meta_info meta_info_list[] = { {meta_KTSR, "Koei Tecmo KTSR header"}, {meta_KAT, "Sega KAT header"}, {meta_PCM_SUCCESS, "Success PCM header"}, + {meta_ADP_KONAMI, "Konami ADP header"}, + {meta_SDRH, "feelplus SDRH header"}, + {meta_WADY, "Marble WADY header"}, + {meta_DSP_SQEX, "Square Enix DSP header"}, + {meta_DSP_WIIVOICE, "Koei Tecmo WiiVoice header"}, }; -void get_vgmstream_coding_description(VGMSTREAM *vgmstream, char *out, size_t out_size) { +void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { int i, list_length; const char *description; @@ -1313,7 +1329,8 @@ void get_vgmstream_coding_description(VGMSTREAM *vgmstream, char *out, size_t ou layered_layout_data* layout_data = vgmstream->layout_data; get_vgmstream_coding_description(layout_data->layers[0], out, out_size); return; - } else if (vgmstream->layout_type == layout_segmented) { + } + else if (vgmstream->layout_type == layout_segmented) { segmented_layout_data* layout_data = vgmstream->layout_data; get_vgmstream_coding_description(layout_data->segments[0], out, out_size); return; @@ -1342,7 +1359,8 @@ void get_vgmstream_coding_description(VGMSTREAM *vgmstream, char *out, size_t ou strncpy(out, description, out_size); } -const char * get_vgmstream_layout_name(layout_t layout_type) { + +static const char* get_layout_name(layout_t layout_type) { int i, list_length; list_length = sizeof(layout_info_list) / sizeof(layout_info); @@ -1353,40 +1371,103 @@ const char * get_vgmstream_layout_name(layout_t layout_type) { return NULL; } -void get_vgmstream_layout_description(VGMSTREAM *vgmstream, char *out, size_t out_size) { - char temp[256]; - VGMSTREAM* vgmstreamsub = NULL; - const char* description; - description = get_vgmstream_layout_name(vgmstream->layout_type); +static int has_sublayouts(VGMSTREAM** vgmstreams, int count) { + int i; + for (i = 0; i < count; i++) { + if (vgmstreams[i]->layout_type == layout_segmented || vgmstreams[i]->layout_type == layout_layered) + return 1; + } + return 0; +} + +/* Makes a mixed description, considering a segments/layers can contain segments/layers infinitely, like: + * + * "(L3[S2L2]S3)" "(S3[L2[S2S2]])" + * L3 S3 + * S2 L2 + * file S2 + * file file + * file file + * L2 file + * file file + * file file + * + * ("mixed" is added externally) + */ +static int get_layout_mixed_description(VGMSTREAM* vgmstream, char* dst, int dst_size) { + int i, count, done = 0; + VGMSTREAM** vgmstreams = NULL; + + if (vgmstream->layout_type == layout_layered) { + layered_layout_data* data = vgmstream->layout_data; + vgmstreams = data->layers; + count = data->layer_count; + done = snprintf(dst, dst_size, "L%i", count); + } + else if (vgmstream->layout_type == layout_segmented) { + segmented_layout_data* data = vgmstream->layout_data; + vgmstreams = data->segments; + count = data->segment_count; + done = snprintf(dst, dst_size, "S%i", count); + } + + if (!vgmstreams || done == 0 || done >= dst_size) + return 0; + + if (!has_sublayouts(vgmstreams, count)) + return done; + + if (done + 1 < dst_size) { + dst[done++] = '['; + } + + for (i = 0; i < count; i++) { + done += get_layout_mixed_description(vgmstreams[i], dst + done, dst_size - done); + } + + if (done + 1 < dst_size) { + dst[done++] = ']'; + } + + return done; +} + +void get_vgmstream_layout_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { + const char* description; + int mixed = 0; + + description = get_layout_name(vgmstream->layout_type); if (!description) description = "INCONCEIVABLE"; if (vgmstream->layout_type == layout_layered) { - vgmstreamsub = ((layered_layout_data*)vgmstream->layout_data)->layers[0]; - snprintf(temp, sizeof(temp), "%s (%i layers)", description, ((layered_layout_data*)vgmstream->layout_data)->layer_count); - } else if (vgmstream->layout_type == layout_segmented) { - snprintf(temp, sizeof(temp), "%s (%i segments)", description, ((segmented_layout_data*)vgmstream->layout_data)->segment_count); - vgmstreamsub = ((segmented_layout_data*)vgmstream->layout_data)->segments[0]; - } else { - snprintf(temp, sizeof(temp), "%s", description); + layered_layout_data* data = vgmstream->layout_data; + mixed = has_sublayouts(data->layers, data->layer_count); + if (!mixed) + snprintf(out, out_size, "%s (%i layers)", description, data->layer_count); + } + else if (vgmstream->layout_type == layout_segmented) { + segmented_layout_data* data = vgmstream->layout_data; + mixed = has_sublayouts(data->segments, data->segment_count); + if (!mixed) + snprintf(out, out_size, "%s (%i segments)", description, data->segment_count); + } + else { + snprintf(out, out_size, "%s", description); } - strncpy(out, temp, out_size); - /* layouts can contain layouts infinitely let's leave it at one level deep (most common) */ - /* TODO: improve this somehow */ - if (vgmstreamsub && vgmstreamsub->layout_type == layout_layered) { - description = get_vgmstream_layout_name(vgmstreamsub->layout_type); - snprintf(temp, sizeof(temp), " + %s (%i layers)", description, ((layered_layout_data*)vgmstreamsub->layout_data)->layer_count); - concatn(out_size, out, temp); - } else if (vgmstreamsub && vgmstreamsub->layout_type == layout_segmented) { - description = get_vgmstream_layout_name(vgmstreamsub->layout_type); - snprintf(temp, sizeof(temp), " + %s (%i segments)", description, ((segmented_layout_data*)vgmstream->layout_data)->segment_count); - concatn(out_size, out, temp); + if (mixed) { + char tmp[256] = {0}; + + get_layout_mixed_description(vgmstream, tmp, sizeof(tmp) - 1); + snprintf(out, out_size, "mixed (%s)", tmp); + return; } } -void get_vgmstream_meta_description(VGMSTREAM *vgmstream, char *out, size_t out_size) { + +void get_vgmstream_meta_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { int i, list_length; - const char *description; + const char* description; description = "THEY SHOULD HAVE SENT A POET"; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c index 323353994..c28c4fe06 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c @@ -1,11 +1,12 @@ #include "layout.h" #include "../vgmstream.h" +#include "../decode.h" /* Decodes samples for blocked streams. * Data is divided into headered blocks with a bunch of data. The layout calls external helper functions * when a block is decoded, and those must parse the new block and move offsets accordingly. */ -void render_vgmstream_blocked(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { +void render_vgmstream_blocked(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream) { int samples_written = 0; int frame_size, samples_per_frame, samples_this_block; @@ -52,7 +53,7 @@ void render_vgmstream_blocked(sample_t * buffer, int32_t sample_count, VGMSTREAM break; } - samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); + samples_to_do = get_vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); if (samples_to_do > sample_count - samples_written) samples_to_do = sample_count - samples_written; @@ -181,8 +182,8 @@ void block_update(off_t block_offset, VGMSTREAM * vgmstream) { case layout_blocked_vgs: block_update_vgs(block_offset,vgmstream); break; - case layout_blocked_vawx: - block_update_vawx(block_offset,vgmstream); + case layout_blocked_xwav: + block_update_xwav(block_offset,vgmstream); break; case layout_blocked_xvag_subsong: block_update_xvag_subsong(block_offset,vgmstream); diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ast.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ast.c index 3aab1668a..ac14fc3d8 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ast.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ast.c @@ -6,9 +6,10 @@ void block_update_ast(off_t block_offset, VGMSTREAM * vgmstream) { STREAMFILE* streamFile = vgmstream->ch[0].streamfile; int i; size_t block_data, header_size; + int32_t(*read_32bit)(off_t, STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; /* 0x00: "BLCK", rest: null */ - block_data = read_32bitBE(block_offset+0x04,streamFile); + block_data = read_32bit(block_offset+0x04,streamFile); header_size = 0x20; vgmstream->current_block_offset = block_offset; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c index fe3aa80db..8cded1499 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_schl.c @@ -12,6 +12,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { uint32_t flag_lang = (vgmstream->codec_config >> 16) & 0xFFFF; int flag_be = (vgmstream->codec_config & 0x02); int flag_adpcm = (vgmstream->codec_config & 0x01); + int flag_offsets = (vgmstream->codec_config & 0x04); /* EOF reads: signal we have nothing and let the layout fail */ @@ -90,105 +91,139 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { /* set new channel offsets and ADPCM history */ /* ADPCM hist could be considered part of the stream/decoder (some EAXA decoders call it "EAXA R1" when it has hist), and BNKs * (with no blocks) may also have them in the first offset, but also may not. To simplify we just read them here. */ - switch(vgmstream->coding_type) { - /* id, size, unk1, unk2, interleaved data */ - case coding_PSX: - for (i = 0; i < vgmstream->channels; i++) { - size_t interleave = (block_size-0x10) / vgmstream->channels; - vgmstream->ch[i].offset = block_offset + 0x10 + i*interleave; - } - /* 0x08/0x0c: unknown (doesn't look like hist or offsets, as 1ch files has them too) */ - - break; - - /* id, size, IMA hist, stereo/mono data */ - case coding_DVI_IMA: - for(i = 0; i < vgmstream->channels; i++) { - off_t header_offset = block_offset + 0xc + i*4; - vgmstream->ch[i].adpcm_history1_32 = read_16bitLE(header_offset+0x00, vgmstream->ch[i].streamfile); - vgmstream->ch[i].adpcm_step_index = read_16bitLE(header_offset+0x02, vgmstream->ch[i].streamfile); - vgmstream->ch[i].offset = block_offset + 0xc + (4*vgmstream->channels); - } - - break; - - /* id, size, samples */ - case coding_PCM16_int: - for (i = 0; i < vgmstream->channels; i++) { - vgmstream->ch[i].offset = block_offset + 0x0c + (i*0x02); - } - - break; - - /* 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; - size_t interleave; - - /* read ADPCM history from all channels before data (not actually read in sx.exe) */ - //vgmstream->ch[i].adpcm_history1_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x00,streamFile); - //vgmstream->ch[i].adpcm_history2_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x02,streamFile); - - /* the block can have padding so find the channel size from num_samples */ - interleave = is_interleaved ? (block_samples / 28 * 0x0f) : 0; - - /* 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; - - /* id, size, samples, offsets-per-channel, flag (0x01 = data start), data */ - case coding_EA_MT: - 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 + 0x01; - } - - /* flush decoder in every block change */ - flush_ea_mt(vgmstream); - break; - -#ifdef VGM_USE_MPEG - /* id, size, samples, offsets, unknown (null for MP2, some size/config for EALayer3; only if not >2ch) */ - case coding_MPEG_custom: - case coding_MPEG_layer1: - case coding_MPEG_layer2: - case coding_MPEG_layer3: - case coding_MPEG_ealayer3: - for (i = 0; i < vgmstream->channels; i++) { - off_t channel_start; - - /* EALayer3 6ch uses 1ch*6 with offsets, no flag in header [Medal of Honor 2010 (PC) movies] */ - if (vgmstream->channels > 2) { - channel_start = read_32bit(block_offset + 0x0C + 0x04*i,streamFile); - } else { - channel_start = read_32bit(block_offset + 0x0C,streamFile); - } - - vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start; - } - break; -#endif - /* id, size, samples, offsets-per-channel, interleaved data (w/ optional hist per channel) */ - default: - 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; - } - - /* read ADPCM history before each channel if needed (not actually read in sx.exe) */ - if (flag_adpcm) { + if (!flag_offsets) { /* v0 doesn't provide channel offsets, they need to be calculated */ + switch (vgmstream->coding_type) { + /* id, size, samples, data */ + case coding_PCM8_int: for (i = 0; i < vgmstream->channels; i++) { - //vgmstream->ch[i].adpcm_history1_32 = read_16bit(vgmstream->ch[i].offset+0x00,streamFile); - //vgmstream->ch[i].adpcm_history3_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile); - vgmstream->ch[i].offset += 4; + vgmstream->ch[i].offset = block_offset + 0x0c + (i*0x01); } - } - break; + break; + + /* id, size, samples, data */ + case coding_PCM16_int: + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + 0x0c + (i*0x02); + } + + break; + + /* id, size, samples, data */ + case coding_PCM8: + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + 0x0c + (block_samples*i*0x01); + } + + break; + + /* id, size, samples, data */ + case coding_PCM16LE: + case coding_PCM16BE: + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + 0x0c + (block_samples*i*0x02); + } + + break; + + /* id, size, unk1, unk2, interleaved data */ + case coding_PSX: + for (i = 0; i < vgmstream->channels; i++) { + size_t interleave = (block_size-0x10) / vgmstream->channels; + vgmstream->ch[i].offset = block_offset + 0x10 + i*interleave; + } + /* 0x08/0x0c: unknown (doesn't look like hist or offsets, as 1ch files has them too) */ + + break; + + /* id, size, samples, IMA hist, stereo/mono data */ + case coding_DVI_IMA: + for(i = 0; i < vgmstream->channels; i++) { + off_t header_offset = block_offset + 0xc + i*4; + vgmstream->ch[i].adpcm_history1_32 = read_16bitLE(header_offset+0x00, vgmstream->ch[i].streamfile); + vgmstream->ch[i].adpcm_step_index = read_16bitLE(header_offset+0x02, vgmstream->ch[i].streamfile); + vgmstream->ch[i].offset = block_offset + 0xc + (4*vgmstream->channels); + } + + break; + + /* id, size, samples, hists-per-channel, stereo/interleaved data */ + case coding_EA_XA: + case coding_EA_XA_int: + for (i = 0; i < vgmstream->channels; i++) { + int is_interleaved = vgmstream->coding_type == coding_EA_XA_int; + size_t interleave; + + /* read ADPCM history from all channels before data (not actually read in sx.exe) */ + //vgmstream->ch[i].adpcm_history1_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x00,streamFile); + //vgmstream->ch[i].adpcm_history2_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x02,streamFile); + + /* the block can have padding so find the channel size from num_samples */ + interleave = is_interleaved ? (block_samples / 28 * 0x0f) : 0; + + /* 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; + + case coding_EA_MT: /* not seen in v0 streams so far, may not exist */ + default: + VGM_LOG("EA SCHl: Unkonwn channel offsets in blocked layout\n"); + vgmstream->current_block_samples = -1; + break; + } + } else { + switch(vgmstream->coding_type) { + /* id, size, samples, offsets-per-channel, flag (0x01 = data start), data */ + case coding_EA_MT: + 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 + 0x01; + } + + /* flush decoder in every block change */ + flush_ea_mt(vgmstream); + break; + + #ifdef VGM_USE_MPEG + /* id, size, samples, offsets, unknown (null for MP2, some size/config for EALayer3; only if not >2ch) */ + case coding_MPEG_custom: + case coding_MPEG_layer1: + case coding_MPEG_layer2: + case coding_MPEG_layer3: + case coding_MPEG_ealayer3: + for (i = 0; i < vgmstream->channels; i++) { + off_t channel_start; + + /* EALayer3 6ch uses 1ch*6 with offsets, no flag in header [Medal of Honor 2010 (PC) movies] */ + if (vgmstream->channels > 2) { + channel_start = read_32bit(block_offset + 0x0C + 0x04*i,streamFile); + } else { + channel_start = read_32bit(block_offset + 0x0C,streamFile); + } + + vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start; + } + break; + #endif + /* id, size, samples, offsets-per-channel, interleaved data (w/ optional hist per channel) */ + default: + 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; + } + + /* read ADPCM history before each channel if needed (not actually read in sx.exe) */ + if (flag_adpcm) { + for (i = 0; i < vgmstream->channels; i++) { + //vgmstream->ch[i].adpcm_history1_32 = read_16bit(vgmstream->ch[i].offset+0x00,streamFile); + //vgmstream->ch[i].adpcm_history3_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile); + vgmstream->ch[i].offset += 4; + } + } + + break; + } } } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_vawx.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_xwav.c similarity index 88% rename from Frameworks/vgmstream/vgmstream/src/layout/blocked_vawx.c rename to Frameworks/vgmstream/vgmstream/src/layout/blocked_xwav.c index dd28dc7ef..ddd5dafb1 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_vawx.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_xwav.c @@ -2,7 +2,7 @@ #include "../vgmstream.h" /* pseudo-blocks that must skip last 0x20 every 0x8000 */ -void block_update_vawx(off_t block_offset, VGMSTREAM * vgmstream) { +void block_update_xwav(off_t block_offset, VGMSTREAM * vgmstream) { int i; size_t block_size; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/flat.c b/Frameworks/vgmstream/vgmstream/src/layout/flat.c index ae5b91e9e..ff3001b77 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/flat.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/flat.c @@ -1,10 +1,11 @@ #include "layout.h" #include "../vgmstream.h" +#include "../decode.h" /* Decodes samples for flat streams. * Data forms a single stream, and the decoder may internally skip chunks and move offsets as needed. */ -void render_vgmstream_flat(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { +void render_vgmstream_flat(sample_t* outbuf, int32_t sample_count, VGMSTREAM* vgmstream) { int samples_written = 0; int samples_per_frame, samples_this_block; @@ -20,21 +21,23 @@ void render_vgmstream_flat(sample_t * buffer, int32_t sample_count, VGMSTREAM * continue; } - samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); + samples_to_do = get_vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); if (samples_to_do > sample_count - samples_written) samples_to_do = sample_count - samples_written; - if (samples_to_do == 0) { - VGM_LOG("layout_flat: wrong samples_to_do 0 found\n"); /* could happen when calling render at EOF? */ - //VGM_LOG("layout_flat: tb=%i sib=%i, spf=%i\n", samples_this_block, vgmstream->samples_into_block, samples_per_frame); - memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample_t)); - break; + if (samples_to_do == 0) { /* when decoding more than num_samples */ + VGM_LOG_ONCE("FLAT: samples_to_do 0\n"); + goto decode_fail; } - decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); + decode_vgmstream(vgmstream, samples_written, samples_to_do, outbuf); samples_written += samples_to_do; vgmstream->current_sample += samples_to_do; vgmstream->samples_into_block += samples_to_do; } + + return; +decode_fail: + memset(outbuf + samples_written * vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample_t)); } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/interleave.c b/Frameworks/vgmstream/vgmstream/src/layout/interleave.c index 94715a3f8..0714d6d93 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/interleave.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/interleave.c @@ -1,5 +1,6 @@ #include "layout.h" #include "../vgmstream.h" +#include "../decode.h" /* Decodes samples for interleaved streams. @@ -82,7 +83,7 @@ void render_vgmstream_interleave(sample_t * buffer, int32_t sample_count, VGMSTR continue; } - samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); + samples_to_do = get_vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); if (samples_to_do > sample_count - samples_written) samples_to_do = sample_count - samples_written; @@ -144,6 +145,6 @@ void render_vgmstream_interleave(sample_t * buffer, int32_t sample_count, VGMSTR } return; fail: - VGM_LOG("layout_interleave: wrong values found\n"); + VGM_LOG_ONCE("layout_interleave: wrong values found\n"); memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample_t)); } diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layered.c b/Frameworks/vgmstream/vgmstream/src/layout/layered.c index 24b8f6d66..25f6e2122 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layered.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/layered.c @@ -1,34 +1,49 @@ #include "layout.h" #include "../vgmstream.h" +#include "../decode.h" #include "../mixing.h" +#include "../plugins.h" - -/* NOTE: if loop settings change the layered vgmstreams must be notified (preferably using vgmstream_force_loop) */ #define VGMSTREAM_MAX_LAYERS 255 #define VGMSTREAM_LAYER_SAMPLE_BUFFER 8192 /* Decodes samples for layered streams. - * Similar to interleave layout, but decodec samples are mixed from complete vgmstreams, each - * with custom codecs and different number of channels, creating a single super-vgmstream. + * Similar to flat layout, but decoded vgmstream are mixed into a final buffer, each vgmstream + * may have different codecs and number of channels, creating a single super-vgmstream. * Usually combined with custom streamfiles to handle data interleaved in weird ways. */ -void render_vgmstream_layered(sample_t * outbuf, int32_t sample_count, VGMSTREAM * vgmstream) { +void render_vgmstream_layered(sample_t* outbuf, int32_t sample_count, VGMSTREAM* vgmstream) { int samples_written = 0; - layered_layout_data *data = vgmstream->layout_data; + layered_layout_data* data = vgmstream->layout_data; + int samples_per_frame, samples_this_block; + samples_per_frame = VGMSTREAM_LAYER_SAMPLE_BUFFER; + samples_this_block = vgmstream->num_samples; /* do all samples if possible */ while (samples_written < sample_count) { - int samples_to_do = VGMSTREAM_LAYER_SAMPLE_BUFFER; - int layer, ch = 0; + int samples_to_do; + int layer, ch; + + if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { + /* handle looping (loop_layout has been called below) */ + continue; + } + + samples_to_do = get_vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); if (samples_to_do > sample_count - samples_written) samples_to_do = sample_count - samples_written; + if (samples_to_do <= 0) { /* when decoding more than num_samples */ + VGM_LOG_ONCE("LAYERED: samples_to_do 0\n"); + goto decode_fail; + } + + /* decode all layers */ + ch = 0; for (layer = 0; layer < data->layer_count; layer++) { int s, layer_ch, layer_channels; - /* each layer will handle its own looping/mixing internally */ - /* layers may have its own number of channels */ mixing_info(data->layers[layer], NULL, &layer_channels); @@ -49,12 +64,48 @@ void render_vgmstream_layered(sample_t * outbuf, int32_t sample_count, VGMSTREAM } } + samples_written += samples_to_do; - /* needed for info (ex. for mixing) */ - vgmstream->current_sample = data->layers[0]->current_sample; - vgmstream->loop_count = data->layers[0]->loop_count; - //vgmstream->samples_into_block = 0; /* handled in each layer */ + vgmstream->current_sample += samples_to_do; + vgmstream->samples_into_block += samples_to_do; } + + return; +decode_fail: + memset(outbuf + samples_written * data->output_channels, 0, (sample_count - samples_written) * data->output_channels * sizeof(sample_t)); +} + + +void loop_layout_layered(VGMSTREAM* vgmstream, int32_t loop_sample) { + int layer; + layered_layout_data* data = vgmstream->layout_data; + + + for (layer = 0; layer < data->layer_count; layer++) { + if (data->external_looping) { + /* looping is applied over resulting decode, as each layer is its own "solid" block with + * config and needs 'external' seeking */ + seek_vgmstream(data->layers[layer], loop_sample); + } + else { + /* looping is aplied as internal loops. normally each layer does it automatically, but + * just calls do_loop manually to behave a bit more controlled, and so that manual + * calls to do_loop work (used in seek_vgmstream) */ + if (data->layers[layer]->loop_flag) { /* mixing looping and non-looping layers is allowed */ + data->layers[layer]->current_sample = data->layers[layer]->loop_end_sample; /* forces do loop */ + vgmstream_do_loop(data->layers[layer]); /* guaranteed to work should loop_layout be called */ + } + else { + /* needed when mixing non-looping layers and installing loop externally */ + seek_vgmstream(data->layers[layer], loop_sample); + } + } + } + + /* could always call seek_vgmstream, but it's not optimized to loop non-config vgmstreams ATM */ + + vgmstream->current_sample = loop_sample; + vgmstream->samples_into_block = loop_sample; } @@ -88,12 +139,12 @@ int setup_layout_layered(layered_layout_data* data) { int layer_input_channels, layer_output_channels; if (data->layers[i] == NULL) { - VGM_LOG("layered: no vgmstream in %i\n", i); + VGM_LOG("LAYERED: no vgmstream in %i\n", i); goto fail; } if (data->layers[i]->num_samples <= 0) { - VGM_LOG("layered: no samples in %i\n", i); + VGM_LOG("LAYERED: no samples in %i\n", i); goto fail; } @@ -107,21 +158,25 @@ int setup_layout_layered(layered_layout_data* data) { if (i > 0) { /* a bit weird, but no matter */ if (data->layers[i]->sample_rate != data->layers[i-1]->sample_rate) { - VGM_LOG("layered: layer %i has different sample rate\n", i); + VGM_LOG("LAYERED: layer %i has different sample rate\n", i); } /* also weird */ if (data->layers[i]->coding_type != data->layers[i-1]->coding_type) { - VGM_LOG("layered: layer %i has different coding type\n", i); + VGM_LOG("LAYERED: layer %i has different coding type\n", i); } } - /* loops and other values could be mismatched but hopefully not */ + /* loops and other values could be mismatched, but should be handled on allocate */ + /* init mixing */ + mixing_setup(data->layers[i], VGMSTREAM_LAYER_SAMPLE_BUFFER); - setup_vgmstream(data->layers[i]); /* final setup in case the VGMSTREAM was created manually */ + /* allow config if set for fine-tuned parts (usually TXTP only) */ + data->layers[i]->config_enabled = data->layers[i]->config.config_set; - mixing_setup(data->layers[i], VGMSTREAM_LAYER_SAMPLE_BUFFER); /* init mixing */ + /* final setup in case the VGMSTREAM was created manually */ + setup_vgmstream(data->layers[i]); } if (max_output_channels > VGMSTREAM_MAX_CHANNELS || max_input_channels > VGMSTREAM_MAX_CHANNELS) @@ -168,29 +223,66 @@ void reset_layout_layered(layered_layout_data *data) { } /* helper for easier creation of layers */ -VGMSTREAM *allocate_layered_vgmstream(layered_layout_data* data) { - VGMSTREAM *vgmstream = NULL; - int i, channels, loop_flag; +VGMSTREAM* allocate_layered_vgmstream(layered_layout_data* data) { + VGMSTREAM* vgmstream = NULL; + int i, channels, loop_flag, sample_rate, external_looping; + int32_t num_samples, loop_start, loop_end; + int delta = 1024; /* get data */ channels = data->output_channels; + + num_samples = 0; loop_flag = 1; + loop_start = data->layers[0]->loop_start_sample; + loop_end = data->layers[0]->loop_end_sample; + external_looping = 0; + sample_rate = 0; for (i = 0; i < data->layer_count; i++) { - if (loop_flag && !data->layers[i]->loop_flag) + int32_t layer_samples = vgmstream_get_samples(data->layers[i]); + int layer_loop = data->layers[i]->loop_flag; + int32_t layer_loop_start = data->layers[i]->loop_start_sample; + int32_t layer_loop_end = data->layers[i]->loop_end_sample; + int layer_rate = data->layers[i]->sample_rate; + + /* internal has own config (and maybe looping), looping now must be done on layout level + * (instead of on each layer, that is faster) */ + if (data->layers[i]->config_enabled) { loop_flag = 0; + layer_loop = 0; + external_looping = 1; + } + + /* all layers should share loop pointsto consider looping enabled, + * but allow some leeway (ex. Dragalia Lost bgm+vocals ~12 samples) */ + if (!layer_loop + || !(loop_start >= layer_loop_start - delta && loop_start <= layer_loop_start + delta) + || !(loop_end >= layer_loop_end - delta && loop_start <= layer_loop_end + delta)) { + loop_flag = 0; + loop_start = 0; + loop_end = 0; + } + + if (num_samples < layer_samples) /* max */ + num_samples = layer_samples; + + if (sample_rate < layer_rate) + sample_rate = layer_rate; } + data->external_looping = external_looping; + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; - vgmstream->meta_type = data->layers[0]->meta_type; - vgmstream->sample_rate = data->layers[0]->sample_rate; - vgmstream->num_samples = data->layers[0]->num_samples; - vgmstream->loop_start_sample = data->layers[0]->loop_start_sample; - vgmstream->loop_end_sample = data->layers[0]->loop_end_sample; - vgmstream->coding_type = data->layers[0]->coding_type; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + vgmstream->meta_type = data->layers[0]->meta_type; /* info */ + vgmstream->coding_type = data->layers[0]->coding_type; /* info */ vgmstream->layout_type = layout_layered; vgmstream->layout_data = data; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layout.h b/Frameworks/vgmstream/vgmstream/src/layout/layout.h index 488f3b191..db4beef07 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layout.h +++ b/Frameworks/vgmstream/vgmstream/src/layout/layout.h @@ -5,67 +5,69 @@ #include "../vgmstream.h" /* blocked layouts */ -void render_vgmstream_blocked(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream); -void block_update(off_t block_offset, VGMSTREAM * vgmstream); +void render_vgmstream_blocked(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); +void block_update(off_t block_offset, VGMSTREAM* vgmstream); -void block_update_ast(off_t block_ofset, VGMSTREAM * vgmstream); -void block_update_mxch(off_t block_ofset, VGMSTREAM * vgmstream); -void block_update_halpst(off_t block_ofset, VGMSTREAM * vgmstream); -void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_caf(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_wsi(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_str_snds(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_ws_aud(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_matx(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_dec(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_vs(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_mul(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_gsb(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_xvas(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_thp(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_filp(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_ivaud(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_ea_swvr(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_adm(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_bdsp(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_tra(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_ps2_iab(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_vs_str(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_rws(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_hwas(off_t block_offset, VGMSTREAM * vgmstream); -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); -void block_update_ea_wve_au00(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_ea_wve_ad10(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_sthd(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_xa_aiff(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_vs_square(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_ast(off_t block_ofset, VGMSTREAM* vgmstream); +void block_update_mxch(off_t block_ofset, VGMSTREAM* vgmstream); +void block_update_halpst(off_t block_ofset, VGMSTREAM* vgmstream); +void block_update_xa(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_ea_schl(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_ea_1snh(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_caf(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_wsi(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_str_snds(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_ws_aud(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_matx(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_dec(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_vs(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_mul(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_gsb(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_xvas(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_thp(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_filp(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_ivaud(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_ea_swvr(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_adm(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_bdsp(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_tra(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_ps2_iab(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_vs_str(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_rws(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_hwas(off_t block_offset, VGMSTREAM* vgmstream); +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_xwav(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_xvag_subsong(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_ea_wve_au00(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_ea_wve_ad10(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_sthd(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_h4m(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_xa_aiff(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_vs_square(off_t block_offset, VGMSTREAM* vgmstream); void block_update_vid1(off_t block_offset, VGMSTREAM* vgmstream); void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream); /* other layouts */ -void render_vgmstream_interleave(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +void render_vgmstream_interleave(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); -void render_vgmstream_flat(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +void render_vgmstream_flat(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); -void render_vgmstream_segmented(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +void render_vgmstream_segmented(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); segmented_layout_data* init_layout_segmented(int segment_count); int setup_layout_segmented(segmented_layout_data* data); -void free_layout_segmented(segmented_layout_data *data); -void reset_layout_segmented(segmented_layout_data *data); +void free_layout_segmented(segmented_layout_data* data); +void reset_layout_segmented(segmented_layout_data* data); +void loop_layout_segmented(VGMSTREAM* vgmstream, int32_t seek_sample); VGMSTREAM *allocate_segmented_vgmstream(segmented_layout_data* data, int loop_flag, int loop_start_segment, int loop_end_segment); -void render_vgmstream_layered(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +void render_vgmstream_layered(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); layered_layout_data* init_layout_layered(int layer_count); int setup_layout_layered(layered_layout_data* data); -void free_layout_layered(layered_layout_data *data); -void reset_layout_layered(layered_layout_data *data); +void free_layout_layered(layered_layout_data* data); +void reset_layout_layered(layered_layout_data* data); +void loop_layout_layered(VGMSTREAM* vgmstream, int32_t seek_sample); VGMSTREAM *allocate_layered_vgmstream(layered_layout_data* data); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/layout/segmented.c b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c index 7965dc9b1..fe0422a8c 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/segmented.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/segmented.c @@ -1,6 +1,8 @@ #include "layout.h" #include "../vgmstream.h" +#include "../decode.h" #include "../mixing.h" +#include "../plugins.h" #define VGMSTREAM_MAX_SEGMENTS 1024 #define VGMSTREAM_SEGMENT_SAMPLE_BUFFER 8192 @@ -9,88 +11,67 @@ /* Decodes samples for segmented streams. * Chains together sequential vgmstreams, for data divided into separate sections or files * (like one part for intro and other for loop segments, which may even use different codecs). */ -void render_vgmstream_segmented(sample_t * outbuf, int32_t sample_count, VGMSTREAM * vgmstream) { - int samples_written = 0, loop_samples_skip = 0; - segmented_layout_data *data = vgmstream->layout_data; +void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREAM* vgmstream) { + int samples_written = 0, samples_this_block; + segmented_layout_data* data = vgmstream->layout_data; int use_internal_buffer = 0; - - /* normally uses outbuf directly (faster) but could need internal buffer if downmixing */ + /* normally uses outbuf directly (faster?) but could need internal buffer if downmixing */ if (vgmstream->channels != data->input_channels) { use_internal_buffer = 1; } + if (data->current_segment >= data->segment_count) { + VGM_LOG_ONCE("SEGMENT: wrong current segment\n"); + goto decode_fail; + } + + samples_this_block = vgmstream_get_samples(data->segments[data->current_segment]); while (samples_written < sample_count) { int samples_to_do; - int samples_this_segment = data->segments[data->current_segment]->num_samples; if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { - int segment, loop_segment, total_samples; + /* handle looping (loop_layout has been called below, changes segments/state) */ + samples_this_block = vgmstream_get_samples(data->segments[data->current_segment]); + continue; + } - /* handle looping by finding loop segment and loop_start inside that segment */ - loop_segment = 0; - total_samples = 0; - while (total_samples < vgmstream->num_samples) { - int32_t segment_samples = data->segments[loop_segment]->num_samples; + /* detect segment change and restart (after loop, but before decode, to allow looping to kick in) */ + if (vgmstream->samples_into_block >= samples_this_block) { + data->current_segment++; - if (vgmstream->loop_sample >= total_samples && vgmstream->loop_sample < total_samples + segment_samples) { - loop_samples_skip = vgmstream->loop_sample - total_samples; - break; /* loop_start falls within loop_segment's samples */ - } - total_samples += segment_samples; - loop_segment++; + if (data->current_segment >= data->segment_count) { /* when decoding more than num_samples */ + VGM_LOG_ONCE("SEGMENTED: reached last segment\n"); + goto decode_fail; } - if (loop_segment == data->segment_count) { - VGM_LOG("segmented_layout: can't find loop segment\n"); - loop_segment = 0; - } - - data->current_segment = loop_segment; - - /* loops can span multiple segments */ - for (segment = loop_segment; segment < data->segment_count; segment++) { - reset_vgmstream(data->segments[segment]); - } + /* in case of looping spanning multiple segments */ + reset_vgmstream(data->segments[data->current_segment]); + samples_this_block = vgmstream_get_samples(data->segments[data->current_segment]); vgmstream->samples_into_block = 0; continue; } - samples_to_do = vgmstream_samples_to_do(samples_this_segment, sample_count, vgmstream); + + samples_to_do = get_vgmstream_samples_to_do(samples_this_block, sample_count, vgmstream); if (samples_to_do > sample_count - samples_written) samples_to_do = sample_count - samples_written; if (samples_to_do > VGMSTREAM_SEGMENT_SAMPLE_BUFFER /*&& use_internal_buffer*/) /* always for fade/etc mixes */ samples_to_do = VGMSTREAM_SEGMENT_SAMPLE_BUFFER; - /* segment looping: discard until actual start */ - if (loop_samples_skip > 0) { - if (samples_to_do > loop_samples_skip) - samples_to_do = loop_samples_skip; - } - - /* detect segment change and restart */ - if (samples_to_do == 0) { - data->current_segment++; - reset_vgmstream(data->segments[data->current_segment]); - vgmstream->samples_into_block = 0; - continue; + if (samples_to_do < 0) { /* 0 is ok? */ + VGM_LOG_ONCE("SEGMENTED: wrong samples_to_do %i found\n", samples_to_do); + goto decode_fail; } render_vgmstream( use_internal_buffer ? - data->buffer : - &outbuf[samples_written * data->output_channels], + data->buffer : &outbuf[samples_written * data->output_channels], samples_to_do, data->segments[data->current_segment]); - if (loop_samples_skip > 0) { - loop_samples_skip -= samples_to_do; - vgmstream->samples_into_block += samples_to_do; - continue; - } - if (use_internal_buffer) { int s; for (s = 0; s < samples_to_do * data->output_channels; s++) { @@ -102,11 +83,42 @@ void render_vgmstream_segmented(sample_t * outbuf, int32_t sample_count, VGMSTRE vgmstream->current_sample += samples_to_do; vgmstream->samples_into_block += samples_to_do; } + + return; +decode_fail: + memset(outbuf + samples_written * data->output_channels, 0, (sample_count - samples_written) * data->output_channels * sizeof(sample_t)); +} + +void loop_layout_segmented(VGMSTREAM* vgmstream, int32_t loop_sample) { + int segment, total_samples; + segmented_layout_data* data = vgmstream->layout_data; + + segment = 0; + total_samples = 0; + while (total_samples < vgmstream->num_samples) { + int32_t segment_samples = vgmstream_get_samples(data->segments[segment]); + + /* find if loop falls within segment's samples */ + if (loop_sample >= total_samples && loop_sample < total_samples + segment_samples) { + int32_t loop_relative = loop_sample - total_samples; + + seek_vgmstream(data->segments[segment], loop_relative); + data->current_segment = segment; + vgmstream->samples_into_block = loop_relative; + break; + } + total_samples += segment_samples; + segment++; + } + + if (segment == data->segment_count) { + VGM_LOG("SEGMENTED: can't find loop segment\n"); + } } segmented_layout_data* init_layout_segmented(int segment_count) { - segmented_layout_data *data = NULL; + segmented_layout_data* data = NULL; if (segment_count <= 0 || segment_count > VGMSTREAM_MAX_SEGMENTS) goto fail; @@ -135,22 +147,27 @@ int setup_layout_segmented(segmented_layout_data* data) { for (i = 0; i < data->segment_count; i++) { int segment_input_channels, segment_output_channels; + /* allow config if set for fine-tuned parts (usually TXTP only) */ + data->segments[i]->config_enabled = data->segments[i]->config.config_set; + if (data->segments[i] == NULL) { - VGM_LOG("segmented: no vgmstream in segment %i\n", i); + VGM_LOG("SEGMENTED: no vgmstream in segment %i\n", i); goto fail; } - if (data->segments[i]->num_samples <= 0) { - VGM_LOG("segmented: no samples in segment %i\n", i); + VGM_LOG("SEGMENTED: no samples in segment %i\n", i); goto fail; } - /* disable so that looping is controlled by render_vgmstream_segmented */ if (data->segments[i]->loop_flag != 0) { - VGM_LOG("segmented: segment %i is looped\n", i); - data->segments[i]->loop_flag = 0; + VGM_LOG("SEGMENTED: segment %i is looped\n", i); + + /* config allows internal loops */ + if (!data->segments[i]->config_enabled) { + data->segments[i]->loop_flag = 0; + } } /* different segments may have different input channels, though output should be @@ -166,13 +183,13 @@ int setup_layout_segmented(segmented_layout_data* data) { mixing_info(data->segments[i-1], NULL, &prev_output_channels); if (segment_output_channels != prev_output_channels) { - VGM_LOG("segmented: segment %i has wrong channels %i vs prev channels %i\n", i, segment_output_channels, prev_output_channels); + VGM_LOG("SEGMENTED: segment %i has wrong channels %i vs prev channels %i\n", i, segment_output_channels, prev_output_channels); goto fail; } /* a bit weird, but no matter */ if (data->segments[i]->sample_rate != data->segments[i-1]->sample_rate) { - VGM_LOG("segmented: segment %i has different sample rate\n", i); + VGM_LOG("SEGMENTED: segment %i has different sample rate\n", i); } /* perfectly acceptable */ @@ -180,10 +197,11 @@ int setup_layout_segmented(segmented_layout_data* data) { // goto fail; } + /* init mixing */ + mixing_setup(data->segments[i], VGMSTREAM_SEGMENT_SAMPLE_BUFFER); - setup_vgmstream(data->segments[i]); /* final setup in case the VGMSTREAM was created manually */ - - mixing_setup(data->segments[i], VGMSTREAM_SEGMENT_SAMPLE_BUFFER); /* init mixing */ + /* final setup in case the VGMSTREAM was created manually */ + setup_vgmstream(data->segments[i]); } if (max_output_channels > VGMSTREAM_MAX_CHANNELS || max_input_channels > VGMSTREAM_MAX_CHANNELS) @@ -202,7 +220,7 @@ fail: return 0; /* caller is expected to free */ } -void free_layout_segmented(segmented_layout_data *data) { +void free_layout_segmented(segmented_layout_data* data) { int i, j; if (!data) @@ -228,7 +246,7 @@ void free_layout_segmented(segmented_layout_data *data) { free(data); } -void reset_layout_segmented(segmented_layout_data *data) { +void reset_layout_segmented(segmented_layout_data* data) { int i; if (!data) @@ -241,21 +259,27 @@ void reset_layout_segmented(segmented_layout_data *data) { } /* helper for easier creation of segments */ -VGMSTREAM *allocate_segmented_vgmstream(segmented_layout_data* data, int loop_flag, int loop_start_segment, int loop_end_segment) { - VGMSTREAM *vgmstream = NULL; +VGMSTREAM* allocate_segmented_vgmstream(segmented_layout_data* data, int loop_flag, int loop_start_segment, int loop_end_segment) { + VGMSTREAM* vgmstream = NULL; int channel_layout; - int i, num_samples, loop_start, loop_end; + int i, sample_rate; + int32_t num_samples, loop_start, loop_end; /* save data */ channel_layout = data->segments[0]->channel_layout; num_samples = 0; loop_start = 0; loop_end = 0; + sample_rate = 0; for (i = 0; i < data->segment_count; i++) { + /* needs get_samples since element may use play settings */ + int32_t segment_samples = vgmstream_get_samples(data->segments[i]); + int segment_rate = data->segments[i]->sample_rate; + if (loop_flag && i == loop_start_segment) loop_start = num_samples; - num_samples += data->segments[i]->num_samples; + num_samples += segment_samples; if (loop_flag && i == loop_end_segment) loop_end = num_samples; @@ -263,6 +287,9 @@ VGMSTREAM *allocate_segmented_vgmstream(segmented_layout_data* data, int loop_fl /* inherit first segment's layout but only if all segments' layout match */ if (channel_layout != 0 && channel_layout != data->segments[i]->channel_layout) channel_layout = 0; + + if (sample_rate < segment_rate) + sample_rate = segment_rate; } /* respect loop_flag even when no loop_end found as it's possible file loops are set outside */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/acb.c b/Frameworks/vgmstream/vgmstream/src/meta/acb.c index e143ce8f6..e5fbf35b0 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/acb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/acb.c @@ -15,11 +15,11 @@ VGMSTREAM* init_vgmstream_acb(STREAMFILE* sf) { /* checks */ if (!check_extensions(sf, "acb")) goto fail; - if (read_32bitBE(0x00,sf) != 0x40555446) /* "@UTF" */ + if (read_u32be(0x00,sf) != 0x40555446) /* "@UTF" */ goto fail; /* .acb is a cue sheet that uses @UTF (CRI's generic table format) to store row/columns - * with complex info (cues, sequences, spatial info, etc). it can store a memory .awb + * with complex info (cues, sequences, spatial info, etc). It can store a memory .awb * (our target here), or reference external/streamed .awb (loaded elsewhere) * we only want .awb with actual waves but may use .acb to get names */ { @@ -34,8 +34,6 @@ VGMSTREAM* init_vgmstream_acb(STREAMFILE* sf) { if (rows != 1 || strcmp(name, "Header") != 0) goto fail; - //todo acb+cpk is also possible - if (!utf_query_data(utf, 0, "AwbFile", &offset, &size)) goto fail; @@ -52,10 +50,16 @@ VGMSTREAM* init_vgmstream_acb(STREAMFILE* sf) { temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, "awb"); if (!temp_sf) goto fail; - vgmstream = init_vgmstream_awb_memory(temp_sf, sf); - if (!vgmstream) goto fail; + if (read_u32be(0x00, temp_sf) == 0x43504B20) { /* "CPK " */ + vgmstream = init_vgmstream_cpk_memory(temp_sf, sf); /* older */ + if (!vgmstream) goto fail; + } + else { + vgmstream = init_vgmstream_awb_memory(temp_sf, sf); /* newer */ + if (!vgmstream) goto fail; + } - /* name-loading for this for memory .awb will be called from init_vgmstream_awb_memory */ + /* name-loading for this for memory .awb will be called from init_vgmstream_awb/cpk_memory */ utf_close(utf); close_streamfile(temp_sf); @@ -75,6 +79,7 @@ fail: //TODO: could pre-load all sections first, but needs cache for multiple subsongs (+semaphs, if multiple read the same thing) #define ACB_TABLE_BUFFER_CUENAME 0x8000 #define ACB_TABLE_BUFFER_CUE 0x40000 +#define ACB_TABLE_BUFFER_BLOCKSEQUENCE 0x8000 #define ACB_TABLE_BUFFER_BLOCK 0x8000 #define ACB_TABLE_BUFFER_SEQUENCE 0x40000 #define ACB_TABLE_BUFFER_TRACK 0x10000 @@ -106,6 +111,7 @@ typedef struct { utf_context *CueNameTable; utf_context *CueTable; + utf_context *BlockSequenceTable; utf_context *BlockTable; utf_context *SequenceTable; utf_context *TrackTable; @@ -115,6 +121,7 @@ typedef struct { STREAMFILE* CueNameSf; STREAMFILE* CueSf; + STREAMFILE* BlockSequenceSf; STREAMFILE* BlockSf; STREAMFILE* SequenceSf; STREAMFILE* TrackSf; @@ -158,7 +165,6 @@ static int open_utf_subtable(acb_header* acb, STREAMFILE* *TableSf, utf_context* if (!*Table) goto fail; //;VGM_LOG("ACB: loaded table %s\n", TableName); - //;VGM_LOG("ACB: sf=%x\n", (uint32_t)*TableSf); return 1; fail: return 0; @@ -179,7 +185,7 @@ static void acb_cpy(char* dst, int dst_max, const char* src) { strcpy(dst, src); } -static void add_acb_name(acb_header* acb, int8_t Waveform_Streaming) { +static void add_acb_name(acb_header* acb, int8_t Streaming) { /* ignore name repeats */ if (acb->awbname_count) { @@ -198,7 +204,7 @@ static void add_acb_name(acb_header* acb, int8_t Waveform_Streaming) { else { acb_cpy(acb->name, sizeof(acb->name), acb->cuename_name); } - if (Waveform_Streaming == 2 && acb->is_memory) { + if (Streaming == 2 && acb->is_memory) { acb_cat(acb->name, sizeof(acb->name), " [pre]"); } @@ -211,35 +217,38 @@ static void add_acb_name(acb_header* acb, int8_t Waveform_Streaming) { } +/*******************************************************************************/ +/* OBJECT HANDLERS */ + static int load_acb_waveform(acb_header* acb, int16_t Index) { - uint16_t Waveform_Id; - uint8_t Waveform_Streaming; + uint16_t Id; + uint8_t Streaming; /* read Waveform[Index] */ if (!open_utf_subtable(acb, &acb->WaveformSf, &acb->WaveformTable, "WaveformTable", NULL, ACB_TABLE_BUFFER_WAVEFORM)) goto fail; - if (!utf_query_u16(acb->WaveformTable, Index, "Id", &Waveform_Id)) { /* older versions use Id */ + if (!utf_query_u16(acb->WaveformTable, Index, "Id", &Id)) { /* older versions use Id */ if (acb->is_memory) { - if (!utf_query_u16(acb->WaveformTable, Index, "MemoryAwbId", &Waveform_Id)) + if (!utf_query_u16(acb->WaveformTable, Index, "MemoryAwbId", &Id)) goto fail; } else { - if (!utf_query_u16(acb->WaveformTable, Index, "StreamAwbId", &Waveform_Id)) + if (!utf_query_u16(acb->WaveformTable, Index, "StreamAwbId", &Id)) goto fail; } } - if (!utf_query_u8(acb->WaveformTable, Index, "Streaming", &Waveform_Streaming)) + if (!utf_query_u8(acb->WaveformTable, Index, "Streaming", &Streaming)) goto fail; - //;VGM_LOG("ACB: Waveform[%i]: Id=%i, Streaming=%i\n", Index, Waveform_Id, Waveform_Streaming); + //;VGM_LOG("ACB: Waveform[%i]: Id=%i, Streaming=%i\n", Index, Id, Streaming); /* not found but valid */ - if (Waveform_Id != acb->target_waveid) + if (Id != acb->target_waveid) return 1; /* must match our target's (0=memory, 1=streaming, 2=memory (prefetch)+stream) */ - if ((acb->is_memory && Waveform_Streaming == 1) || (!acb->is_memory && Waveform_Streaming == 0)) + if ((acb->is_memory && Streaming == 1) || (!acb->is_memory && Streaming == 0)) return 1; /* aaand finally get name (phew) */ - add_acb_name(acb, Waveform_Streaming); + add_acb_name(acb, Streaming); return 1; fail: @@ -251,27 +260,30 @@ static int load_acb_sequence(acb_header* acb, int16_t Index); static int load_acb_synth(acb_header* acb, int16_t Index) { int i, count; - uint8_t Synth_Type; - uint32_t Synth_ReferenceItems_offset; - uint32_t Synth_ReferenceItems_size; + uint8_t Type; + uint32_t ReferenceItems_offset, ReferenceItems_size; /* read Synth[Index] */ if (!open_utf_subtable(acb, &acb->SynthSf, &acb->SynthTable, "SynthTable", NULL, ACB_TABLE_BUFFER_SYNTH)) goto fail; - if (!utf_query_u8(acb->SynthTable, Index, "Type", &Synth_Type)) + if (!utf_query_u8(acb->SynthTable, Index, "Type", &Type)) goto fail; - if (!utf_query_data(acb->SynthTable, Index, "ReferenceItems", &Synth_ReferenceItems_offset, &Synth_ReferenceItems_size)) + if (!utf_query_data(acb->SynthTable, Index, "ReferenceItems", &ReferenceItems_offset, &ReferenceItems_size)) goto fail; - //;VGM_LOG("ACB: Synth[%i]: Type=%x, ReferenceItems={%x,%x}\n", Index, Synth_Type, Synth_ReferenceItems_offset, Synth_ReferenceItems_size); + //;VGM_LOG("ACB: Synth[%i]: Type=%x, ReferenceItems={%x,%x}\n", Index, Type, ReferenceItems_offset, ReferenceItems_size); acb->synth_depth++; - if (acb->synth_depth > 2) { + /* sometimes 2 (ex. Yakuza 6) or even 3 (Street Fighter vs Tekken) */ + if (acb->synth_depth > 3) { VGM_LOG("ACB: Synth depth too high\n"); - goto fail; /* max Synth > Synth > Waveform (ex. Yakuza 6) */ + goto fail; } + //todo .CommandIndex > CommandTable + //todo .TrackValues > TrackTable? + /* Cue.ReferenceType 2 uses Synth.Type, while 3 always sets it to 0 and uses Sequence.Type instead * Both look the same and probably affect which item in the ReferenceItems list is picked: * - 0: polyphonic (1 item) @@ -287,35 +299,37 @@ static int load_acb_synth(acb_header* acb, int16_t Index) { * Since we want to find all possible Waveforms that could match our id, we ignore Type and just parse all ReferenceItems. */ - count = Synth_ReferenceItems_size / 0x04; + count = ReferenceItems_size / 0x04; for (i = 0; i < count; i++) { - uint16_t Synth_ReferenceItem_type = read_u16be(Synth_ReferenceItems_offset + i*0x04 + 0x00, acb->SynthSf); - uint16_t Synth_ReferenceItem_index = read_u16be(Synth_ReferenceItems_offset + i*0x04 + 0x02, acb->SynthSf); - //;VGM_LOG("ACB: Synth.ReferenceItem: type=%x, index=%x\n", Synth_ReferenceItem_type, Synth_ReferenceItem_index); + uint16_t item_type = read_u16be(ReferenceItems_offset + i*0x04 + 0x00, acb->SynthSf); + uint16_t item_index = read_u16be(ReferenceItems_offset + i*0x04 + 0x02, acb->SynthSf); + //;VGM_LOG("ACB: Synth.ReferenceItem: type=%x, index=%x\n", item_type, item_index); - switch(Synth_ReferenceItem_type) { + switch(item_type) { case 0x00: /* no reference */ count = 0; break; case 0x01: /* Waveform (most common) */ - if (!load_acb_waveform(acb, Synth_ReferenceItem_index)) + if (!load_acb_waveform(acb, item_index)) goto fail; break; case 0x02: /* Synth, possibly random (rare, found in Sonic Lost World with ReferenceType 2) */ - if (!load_acb_synth(acb, Synth_ReferenceItem_index)) + if (!load_acb_synth(acb, item_index)) goto fail; break; case 0x03: /* Sequence of Synths w/ % in Synth.TrackValues (rare, found in Sonic Lost World with ReferenceType 2) */ - if (!load_acb_sequence(acb, Synth_ReferenceItem_index)) + if (!load_acb_sequence(acb, item_index)) goto fail; break; + /* others: same as cue's ReferenceType? */ + case 0x06: /* this seems to point to Synth but results don't make sense (rare, from Sonic Lost World) */ default: /* undefined/crashes AtomViewer */ - VGM_LOG("ACB: unknown Synth.ReferenceItem type %x at %x + %x\n", Synth_ReferenceItem_type, Synth_ReferenceItems_offset, Synth_ReferenceItems_size); + VGM_LOG("ACB: unknown Synth.ReferenceItem type %x at %x + %x\n", item_type, ReferenceItems_offset, ReferenceItems_size); count = 0; /* force end without failing */ break; } @@ -328,98 +342,133 @@ fail: return 0; } + +static int load_acb_command_tlvs(acb_header* acb, STREAMFILE* sf, uint32_t Command_offset, uint32_t Command_size) { + uint32_t offset = Command_offset; + uint32_t max_offset = Command_offset + Command_size; + uint16_t tlv_code, tlv_type, tlv_index; + uint8_t tlv_size; + + //todo read full offsets + + /* read a (name)Command multiple TLV data */ + while (offset < max_offset) { + tlv_code = read_u16be(offset + 0x00, sf); + tlv_size = read_u8 (offset + 0x02, sf); + offset += 0x03; + + /* There are around 160 codes (some unused), with things like set volume, pan, stop, mute, and so on. + * Multiple commands are linked and only "note on" seems to point so other objects, so maybe others + * apply to current object (since there is "note off" without reference. */ + switch(tlv_code) { + case 2000: /* noteOn */ + case 2003: /* noteOnWithNo plus 16b (null?) [rare, ex. PES 2014] */ + if (tlv_size < 0x04) { + VGM_LOG("ACB: TLV with unknown size\n"); + break; + } + + tlv_type = read_u16be(offset + 0x00, sf); /* ReferenceItem */ + tlv_index = read_u16be(offset + 0x02, sf); + //;VGM_LOG("ACB: TLV at %x: type %x, index=%x\n", offset, tlv_type, tlv_index); + + /* same as Synth's ReferenceItem type? */ + switch(tlv_type) { + case 0x02: /* Synth (common) */ + if (!load_acb_synth(acb, tlv_index)) + goto fail; + break; + + case 0x03: /* Sequence (common, ex. Yakuza 6, Yakuza Kiwami 2) */ + if (!load_acb_sequence(acb, tlv_index)) + goto fail; + break; + + default: + VGM_LOG("ACB: unknown TLV type %x at %x + %x\n", tlv_type, offset, tlv_size); + max_offset = 0; /* force end without failing */ + break; + } + break; + + case 2004: /* noteOnWithDuration */ + /* same as the above plus extra field */ + //;VGM_LOG("ACB: TLV at %x: usable code %i?\n", offset-0x03, tlv_code); + break; + + case 33: /* mute */ + case 124: /* stopAtLoopEnd */ + case 1000: /* noteOff */ + case 1251: /* sequenceCallbackWithId */ + case 1252: /* sequenceCallbackWithString */ + case 1253: /* sequenceCallbackWithIdAndString */ + case 2002: /* setSynthOrWaveform */ + case 4051: /* transitionTrack */ + case 7102: /* muteTrackAction */ + case 7100: /* startAction */ + case 7101: /* stopAction */ + /* may be needed? */ + //;VGM_LOG("ACB: TLV at %x: check code %i?\n", offset-0x03, tlv_code); + break; + + case 0: /* no-op */ + case 998: /* sequenceStartRandom (plays following note ons in random?)*/ + case 999: /* sequenceStart (plays following note ons in sequence?) */ + default: /* ignore others */ + break; + } + + offset += tlv_size; + } + + return 1; +fail: + return 0; +} + static int load_acb_track_event_command(acb_header* acb, int16_t Index) { - uint16_t Track_EventIndex; - uint32_t Track_Command_offset; - uint32_t Track_Command_size; + uint16_t EventIndex; + uint32_t Command_offset, Command_size; /* read Track[Index] */ if (!open_utf_subtable(acb, &acb->TrackSf, &acb->TrackTable, "TrackTable", NULL, ACB_TABLE_BUFFER_TRACK )) goto fail; - if (!utf_query_u16(acb->TrackTable, Index, "EventIndex", &Track_EventIndex)) + if (!utf_query_u16(acb->TrackTable, Index, "EventIndex", &EventIndex)) goto fail; - //;VGM_LOG("ACB: Track[%i]: EventIndex=%i\n", Index, Track_EventIndex); + //;VGM_LOG("ACB: Track[%i]: EventIndex=%i\n", Index, EventIndex); + + //todo CommandIndex? + + /* happens with some odd track without anything useful */ + if (EventIndex == 65535) + return 1; /* next link varies with version, check by table existence */ if (acb->has_CommandTable) { /* <=v1.27 */ /* read Command[EventIndex] */ if (!open_utf_subtable(acb, &acb->TrackCommandSf, &acb->TrackCommandTable, "CommandTable", NULL, ACB_TABLE_BUFFER_TRACKCOMMAND)) goto fail; - if (!utf_query_data(acb->TrackCommandTable, Track_EventIndex, "Command", &Track_Command_offset, &Track_Command_size)) + if (!utf_query_data(acb->TrackCommandTable, EventIndex, "Command", &Command_offset, &Command_size)) goto fail; - //;VGM_LOG("ACB: Command[%i]: Command={%x,%x}\n", Track_EventIndex, Track_Command_offset,Track_Command_size); + //;VGM_LOG("ACB: Command[%i]: Command={%x,%x}\n", EventIndex, Command_offset,Command_size); } else if (acb->has_TrackEventTable) { /* >=v1.28 */ /* read TrackEvent[EventIndex] */ if (!open_utf_subtable(acb, &acb->TrackCommandSf, &acb->TrackCommandTable, "TrackEventTable", NULL, ACB_TABLE_BUFFER_TRACKCOMMAND)) goto fail; - if (!utf_query_data(acb->TrackCommandTable, Track_EventIndex, "Command", &Track_Command_offset, &Track_Command_size)) + if (!utf_query_data(acb->TrackCommandTable, EventIndex, "Command", &Command_offset, &Command_size)) goto fail; - //;VGM_LOG("ACB: TrackEvent[%i]: Command={%x,%x}\n", Track_EventIndex, Track_Command_offset,Track_Command_size); + //;VGM_LOG("ACB: TrackEvent[%i]: Command={%x,%x}\n", EventIndex, Command_offset,Command_size); } else { VGM_LOG("ACB: unknown command table\n"); goto fail; } - /* read Command (some kind of multiple TLVs, this seems ok) */ - { - uint32_t offset = Track_Command_offset; - uint32_t max_offset = Track_Command_offset + Track_Command_size; - uint16_t tlv_code, tlv_type, tlv_index; - uint8_t tlv_size; - - - while (offset < max_offset) { - tlv_code = read_u16be(offset + 0x00, acb->TrackCommandSf); - tlv_size = read_u8 (offset + 0x02, acb->TrackCommandSf); - offset += 0x03; - - if (tlv_code == 0x07D0) { - if (tlv_size < 0x04) { - VGM_LOG("ACB: TLV with unknown size\n"); - break; - } - - tlv_type = read_u16be(offset + 0x00, acb->TrackCommandSf); - tlv_index = read_u16be(offset + 0x02, acb->TrackCommandSf); - //;VGM_LOG("ACB: TLV at %x: type %x, index=%x\n", offset, tlv_type, tlv_index); - - /* probably same as Synth_ReferenceItem_type */ - switch(tlv_type) { - - case 0x02: /* Synth (common) */ - if (!load_acb_synth(acb, tlv_index)) - goto fail; - break; - - case 0x03: /* Sequence of Synths (common, ex. Yakuza 6, Yakuza Kiwami 2) */ - if (!load_acb_sequence(acb, tlv_index)) - goto fail; - break; - - /* possible values? (from debug): - * - sequence - * - track - * - synth - * - trackEvent - * - seqParameterPallet, - * - trackParameterPallet, - * - synthParameterPallet, - */ - default: - VGM_LOG("ACB: unknown TLV type %x at %x + %x\n", tlv_type, offset, tlv_size); - max_offset = 0; /* force end without failing */ - break; - } - } - - /* 0x07D1 comes suspiciously often paired with 0x07D0 too */ - - offset += tlv_size; - } - } + /* read Command's TLVs */ + if (!load_acb_command_tlvs(acb, acb->TrackCommandSf, Command_offset, Command_size)) + goto fail; return 1; fail: @@ -428,19 +477,20 @@ fail: static int load_acb_sequence(acb_header* acb, int16_t Index) { int i; - uint16_t Sequence_NumTracks; - uint32_t Sequence_TrackIndex_offset; - uint32_t Sequence_TrackIndex_size; + uint16_t NumTracks; + uint32_t TrackIndex_offset, TrackIndex_size; /* read Sequence[Index] */ if (!open_utf_subtable(acb, &acb->SequenceSf, &acb->SequenceTable, "SequenceTable", NULL, ACB_TABLE_BUFFER_SEQUENCE)) goto fail; - if (!utf_query_u16(acb->SequenceTable, Index, "NumTracks", &Sequence_NumTracks)) + if (!utf_query_u16(acb->SequenceTable, Index, "NumTracks", &NumTracks)) goto fail; - if (!utf_query_data(acb->SequenceTable, Index, "TrackIndex", &Sequence_TrackIndex_offset, &Sequence_TrackIndex_size)) + if (!utf_query_data(acb->SequenceTable, Index, "TrackIndex", &TrackIndex_offset, &TrackIndex_size)) goto fail; - //;VGM_LOG("ACB: Sequence[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, Sequence_NumTracks, Sequence_TrackIndex_offset,Sequence_TrackIndex_size); + //;VGM_LOG("ACB: Sequence[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, NumTracks, TrackIndex_offset,TrackIndex_size); + + //todo .CommandIndex > SequenceCommand? acb->sequence_depth++; @@ -449,16 +499,16 @@ static int load_acb_sequence(acb_header* acb, int16_t Index) { goto fail; /* max Sequence > Sequence > Sequence > Synth > Waveform (ex. Yakuza 6) */ } - if (Sequence_NumTracks * 0x02 > Sequence_TrackIndex_size) { /* padding may exist */ + if (NumTracks * 0x02 > TrackIndex_size) { /* padding may exist */ VGM_LOG("ACB: wrong Sequence.TrackIndex size\n"); goto fail; } /* read Tracks inside Sequence */ - for (i = 0; i < Sequence_NumTracks; i++) { - int16_t Sequence_TrackIndex_index = read_s16be(Sequence_TrackIndex_offset + i*0x02, acb->SequenceSf); + for (i = 0; i < NumTracks; i++) { + int16_t TrackIndex_index = read_s16be(TrackIndex_offset + i*0x02, acb->SequenceSf); - if (!load_acb_track_event_command(acb, Sequence_TrackIndex_index)) + if (!load_acb_track_event_command(acb, TrackIndex_index)) goto fail; } @@ -471,75 +521,129 @@ fail: static int load_acb_block(acb_header* acb, int16_t Index) { int i; - uint16_t Block_NumTracks; - uint32_t Block_TrackIndex_offset; - uint32_t Block_TrackIndex_size; + uint16_t NumTracks; + uint32_t TrackIndex_offset, TrackIndex_size; /* read Block[Index] */ if (!open_utf_subtable(acb, &acb->BlockSf, &acb->BlockTable, "BlockTable", NULL, ACB_TABLE_BUFFER_BLOCK)) goto fail; - if (!utf_query_u16(acb->BlockTable, Index, "NumTracks", &Block_NumTracks)) + if (!utf_query_u16(acb->BlockTable, Index, "NumTracks", &NumTracks)) goto fail; - if (!utf_query_data(acb->BlockTable, Index, "TrackIndex", &Block_TrackIndex_offset, &Block_TrackIndex_size)) + if (!utf_query_data(acb->BlockTable, Index, "TrackIndex", &TrackIndex_offset, &TrackIndex_size)) goto fail; - //;VGM_LOG("ACB: Block[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, Block_NumTracks, Block_TrackIndex_offset,Block_TrackIndex_size); + //;VGM_LOG("ACB: Block[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, NumTracks, TrackIndex_offset,TrackIndex_size); - if (Block_NumTracks * 0x02 > Block_TrackIndex_size) { /* padding may exist */ + if (NumTracks * 0x02 > TrackIndex_size) { /* padding may exist */ VGM_LOG("ACB: wrong Block.TrackIndex size\n"); goto fail; } - /* read Tracks inside Block */ - for (i = 0; i < Block_NumTracks; i++) { - int16_t Block_TrackIndex_index = read_s16be(Block_TrackIndex_offset + i*0x02, acb->BlockSf); + //todo .ActionTrackStartIndex/NumActionTracks > ? - if (!load_acb_track_event_command(acb, Block_TrackIndex_index)) + /* read Tracks inside Block */ + for (i = 0; i < NumTracks; i++) { + int16_t TrackIndex_index = read_s16be(TrackIndex_offset + i*0x02, acb->BlockSf); + + if (!load_acb_track_event_command(acb, TrackIndex_index)) goto fail; } return 1; fail: return 0; +} +static int load_acb_blocksequence(acb_header* acb, int16_t Index) { + int i; + + uint16_t NumTracks; + uint32_t TrackIndex_offset, TrackIndex_size; + uint16_t NumBlocks; + uint32_t BlockIndex_offset, BlockIndex_size; + + + /* read BlockSequence[Index] */ + if (!open_utf_subtable(acb, &acb->BlockSequenceSf, &acb->BlockSequenceTable, "BlockSequenceTable", NULL, ACB_TABLE_BUFFER_BLOCKSEQUENCE)) + goto fail; + + if (!utf_query_u16(acb->BlockSequenceTable, Index, "NumTracks", &NumTracks)) + goto fail; + if (!utf_query_data(acb->BlockSequenceTable, Index, "TrackIndex", &TrackIndex_offset, &TrackIndex_size)) + goto fail; + if (!utf_query_u16(acb->BlockSequenceTable, Index, "NumBlocks", &NumBlocks)) + goto fail; + if (!utf_query_data(acb->BlockSequenceTable, Index, "BlockIndex", &BlockIndex_offset, &BlockIndex_size)) + goto fail; + //;VGM_LOG("ACB: BlockSequence[%i]: NumTracks=%i, TrackIndex={%x, %x}, NumBlocks=%i, BlockIndex={%x, %x}\n", Index, NumTracks, TrackIndex_offset,TrackIndex_size, NumBlocks, BlockIndex_offset,BlockIndex_size); + + + if (NumTracks * 0x02 > TrackIndex_size) { /* padding may exist */ + VGM_LOG("ACB: wrong BlockSequence.TrackIndex size\n"); + goto fail; + } + + /* read Tracks inside BlockSequence */ + for (i = 0; i < NumTracks; i++) { + int16_t TrackIndex_index = read_s16be(TrackIndex_offset + i*0x02, acb->BlockSequenceSf); + + if (!load_acb_track_event_command(acb, TrackIndex_index)) + goto fail; + } + + if (NumBlocks * 0x02 > BlockIndex_size) { + VGM_LOG("ACB: wrong BlockSequence.BlockIndex size\n"); + goto fail; + } + + /* read Blocks inside BlockSequence */ + for (i = 0; i < NumBlocks; i++) { + int16_t BlockIndex_index = read_s16be(BlockIndex_offset + i*0x02, acb->BlockSequenceSf); + + if (!load_acb_block(acb, BlockIndex_index)) + goto fail; + } + + return 1; +fail: + return 0; } static int load_acb_cue(acb_header* acb, int16_t Index) { - uint8_t Cue_ReferenceType; - uint16_t Cue_ReferenceIndex; + uint8_t ReferenceType; + uint16_t ReferenceIndex; /* read Cue[Index] */ if (!open_utf_subtable(acb, &acb->CueSf, &acb->CueTable, "CueTable", NULL, ACB_TABLE_BUFFER_CUE)) goto fail; - if (!utf_query_u8(acb->CueTable, Index, "ReferenceType", &Cue_ReferenceType)) + if (!utf_query_u8(acb->CueTable, Index, "ReferenceType", &ReferenceType)) goto fail; - if (!utf_query_u16(acb->CueTable, Index, "ReferenceIndex", &Cue_ReferenceIndex)) + if (!utf_query_u16(acb->CueTable, Index, "ReferenceIndex", &ReferenceIndex)) goto fail; - //;VGM_LOG("ACB: Cue[%i]: ReferenceType=%i, ReferenceIndex=%i\n", Index, Cue_ReferenceType, Cue_ReferenceIndex); + //;VGM_LOG("ACB: Cue[%i]: ReferenceType=%i, ReferenceIndex=%i\n", Index, ReferenceType, ReferenceIndex); /* usually older games use older references but not necessarily */ - switch(Cue_ReferenceType) { + switch(ReferenceType) { case 0x01: /* Cue > Waveform (ex. PES 2015) */ - if (!load_acb_waveform(acb, Cue_ReferenceIndex)) + if (!load_acb_waveform(acb, ReferenceIndex)) goto fail; break; case 0x02: /* Cue > Synth > Waveform (ex. Ukiyo no Roushi) */ - if (!load_acb_synth(acb, Cue_ReferenceIndex)) + if (!load_acb_synth(acb, ReferenceIndex)) goto fail; break; case 0x03: /* Cue > Sequence > Track > Command > Synth > Waveform (ex. Valkyrie Profile anatomia, Yakuza Kiwami 2) */ - if (!load_acb_sequence(acb, Cue_ReferenceIndex)) + if (!load_acb_sequence(acb, ReferenceIndex)) goto fail; break; - //todo "blockSequence"? - case 0x08: /* Cue > Block > Track > Command > Synth > Waveform (ex. Sonic Lost World, rare) */ - if (!load_acb_block(acb, Cue_ReferenceIndex)) + case 0x08: /* Cue > BlockSequence > Track / Block > Track > Command > Synth > Waveform (ex. Sonic Lost World, Kandagawa Jet Girls, rare) */ + if (!load_acb_blocksequence(acb, ReferenceIndex)) goto fail; break; @@ -552,7 +656,7 @@ static int load_acb_cue(acb_header* acb, int16_t Index) { case 0x0a: /* "eventCue_UnUse" */ case 0x0b: /* "soundGenerator" */ default: - VGM_LOG("ACB: unknown Cue.ReferenceType=%x, Cue.ReferenceIndex=%x\n", Cue_ReferenceType, Cue_ReferenceIndex); + VGM_LOG("ACB: unknown Cue.ReferenceType=%x, Cue.ReferenceIndex=%x\n", ReferenceType, ReferenceIndex); break; /* ignore and continue */ } @@ -560,29 +664,28 @@ static int load_acb_cue(acb_header* acb, int16_t Index) { return 1; fail: return 0; - } static int load_acb_cuename(acb_header* acb, int16_t Index) { - uint16_t CueName_CueIndex; - const char* CueName_CueName; + uint16_t CueIndex; + const char* CueName; /* read CueName[Index] */ if (!open_utf_subtable(acb, &acb->CueNameSf, &acb->CueNameTable, "CueNameTable", NULL, ACB_TABLE_BUFFER_CUENAME)) goto fail; - if (!utf_query_u16(acb->CueNameTable, Index, "CueIndex", &CueName_CueIndex)) + if (!utf_query_u16(acb->CueNameTable, Index, "CueIndex", &CueIndex)) goto fail; - if (!utf_query_string(acb->CueNameTable, Index, "CueName", &CueName_CueName)) + if (!utf_query_string(acb->CueNameTable, Index, "CueName", &CueName)) goto fail; - //;VGM_LOG("ACB: CueName[%i]: CueIndex=%i, CueName=%s\n", Index, CueName_CueIndex, CueName_CueName); + //;VGM_LOG("ACB: CueName[%i]: CueIndex=%i, CueName=%s\n", Index, CueIndex, CueName); /* save as will be needed if references waveform */ acb->cuename_index = Index; - acb->cuename_name = CueName_CueName; + acb->cuename_name = CueName; - if (!load_acb_cue(acb, CueName_CueIndex)) + if (!load_acb_cue(acb, CueIndex)) goto fail; return 1; @@ -615,10 +718,10 @@ void load_acb_wave_name(STREAMFILE* sf, VGMSTREAM* vgmstream, int waveid, int is * - CueName > Cue > Sequence > Track > TrackEvent > Command > Synth > Synth > Waveform (type 3, >=v1.28) * - CueName > Cue > Sequence > Track > TrackEvent > Command > Sequence > (...) > Synth > Waveform (type 3, >=v1.28) * - CueName > Cue > Block > Track > Command > Synth > Synth > Waveform (type 8) - * - others should be possible but haven't been observed + * - others should be possible * Atom Craft may only target certain .acb versions so some links are later removed - * Not all cues to point to though Waveforms, as some are just config events/commands. - * .acb link to .awb by name (loaded manually), though they have a checksum/hash to validate. + * Not all cues to point to Waveforms, some are just config events/commands. + * .acb link to .awb by name (loaded manually), though they have a checksum/hash/header to validate. */ //;VGM_LOG("ACB: find waveid=%i\n", waveid); @@ -654,6 +757,8 @@ fail: utf_close(acb.CueNameTable); utf_close(acb.CueTable); + utf_close(acb.BlockSequenceTable); + utf_close(acb.BlockTable); utf_close(acb.SequenceTable); utf_close(acb.TrackTable); utf_close(acb.TrackCommandTable); @@ -662,6 +767,8 @@ fail: close_streamfile(acb.CueNameSf); close_streamfile(acb.CueSf); + close_streamfile(acb.BlockSequenceSf); + close_streamfile(acb.BlockSf); close_streamfile(acb.SequenceSf); close_streamfile(acb.TrackSf); close_streamfile(acb.TrackCommandSf); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adp_konami.c b/Frameworks/vgmstream/vgmstream/src/meta/adp_konami.c new file mode 100644 index 000000000..8d458eb5c --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/adp_konami.c @@ -0,0 +1,51 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* ADP - from Konami Viper arcade games [ParaParaParadise 2ndMIX (AC)] */ +VGMSTREAM* init_vgmstream_adp_konami(STREAMFILE* sf) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channels; + size_t data_size, file_size; + + + /* checks */ + if (!check_extensions(sf, "adp")) + goto fail; + if (read_u32be(0x00,sf) != 0x41445002) /* "ADP\2" */ + goto fail; + + start_offset = 0x10; + channels = 2; /* probably @0x03 */ + loop_flag = 0; + + data_size = read_u32be(0x04,sf); + file_size = get_streamfile_size(sf); + if (!(data_size + start_offset - 0x04 <= file_size && + data_size + start_offset + 0x04 >= file_size)) /* 1 byte padding in some files */ + goto fail; + + if (read_u32be(0x08,sf) != 0 || read_u32be(0x0c,sf) != 0) /* maybe reserved for loop points */ + goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_ADP_KONAMI; + vgmstream->sample_rate = 44100; + + vgmstream->num_samples = oki_bytes_to_samples(data_size, channels); + + vgmstream->coding_type = coding_OKI4S; + vgmstream->layout_type = layout_none; + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx.c b/Frameworks/vgmstream/vgmstream/src/meta/adx.c index 5b90087c5..35d721b52 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx.c @@ -79,12 +79,14 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { coding_type = coding_CRI_ADX_enc_8; version = 0x0400; } + VGM_ASSERT(version != 0x0400, "ADX: keystring not found\n"); } else if (version == 0x0409) { if (find_adx_key(streamFile, 9, &xor_start, &xor_mult, &xor_add)) { coding_type = coding_CRI_ADX_enc_9; version = 0x0400; } + VGM_ASSERT(version != 0x0400, "ADX: keycode not found\n"); } /* version + extra data */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h index ea27d5426..158b978ca 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h @@ -196,6 +196,9 @@ static const adxkey_info adxkey8_list[] = { /* 428: Fuusasareta Shibuya de (PS3) */ {0x52ff,0x649f,0x448f, "hj1kviaqqdzUacryoacwmscfvwtlfkVbbbqpqmzqnbile2euljywazejgyxxvqlf",0}, + /* Mirai Nikki: 13-ninme no Nikki Shoyuusha Re-Write (PSP) */ + {0x58a3,0x66f5,0x599f, "FDRW17th",0}, + }; static const adxkey_info adxkey9_list[] = { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/akb.c b/Frameworks/vgmstream/vgmstream/src/meta/akb.c index 422a5676f..15a12ea8f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/akb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/akb.c @@ -128,12 +128,8 @@ VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile) { /* Alt decoding without libvorbis (minor number of beginning samples difference). * Otherwise same output with (inaudible) +-1 lower byte differences due to rounding. */ case 0x05: { /* Ogg Vorbis [Final Fantasy VI (iOS), Dragon Quest II-VI (iOS)] */ - ffmpeg_codec_data *ffmpeg_data; - - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size); - if ( !ffmpeg_data ) goto fail; - - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_offset(streamFile, start_offset,stream_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; /* These oggs have loop info in the comments, too */ @@ -149,12 +145,8 @@ VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile) { #ifdef VGM_USE_FFMPEG case 0x06: { /* M4A with AAC [The World Ends with You (iPad)] */ /* init_vgmstream_akb_mp4 above has priority, but this works fine too */ - ffmpeg_codec_data *ffmpeg_data; - - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size-start_offset); - if ( !ffmpeg_data ) goto fail; - - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_offset(streamFile, start_offset,stream_size-start_offset); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ast.c b/Frameworks/vgmstream/vgmstream/src/meta/ast.c index ede135947..5bd2f94ad 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ast.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ast.c @@ -7,37 +7,51 @@ VGMSTREAM * init_vgmstream_ast(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; int loop_flag, channel_count, codec; + int big_endian; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; /* checks */ if (!check_extensions(streamFile, "ast")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x5354524D) /* "STRM" */ + + if (((uint32_t)read_32bitBE(0x00, streamFile) == 0x5354524D) && /* "STRM" */ + ((uint32_t)read_32bitBE(0x40, streamFile) == 0x424C434B)) { /* "BLCK" */ + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + big_endian = 1; + } else if (((uint32_t)read_32bitBE(0x00, streamFile) == 0x4D525453) && /* "MRTS" */ // Super Mario Galaxy (Super Mario 3D All-Stars (Switch)) + ((uint32_t)read_32bitBE(0x40, streamFile) == 0x4B434C42)) { /* "KCLB" */ + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + big_endian = 0; + } else { goto fail; - if (read_16bitBE(0x0a,streamFile) != 0x10) /* ? */ + } + + if (read_16bit(0x0a,streamFile) != 0x10) /* ? */ goto fail; - if (read_32bitBE(0x04,streamFile)+0x40 != get_streamfile_size(streamFile)) - goto fail; - codec = read_16bitBE(0x08,streamFile); - channel_count = read_16bitBE(0x0c,streamFile); - loop_flag = read_16bitBE(0x0e,streamFile); - //max_block = read_32bitBE(0x20,streamFile); - - start_offset = 0x40; - if (read_32bitBE(start_offset,streamFile) != 0x424C434B) /* "BLCK" */ + if (read_32bit(0x04,streamFile)+0x40 != get_streamfile_size(streamFile)) goto fail; + codec = read_16bitBE(0x08,streamFile); // always big-endian? + channel_count = read_16bit(0x0c,streamFile); + loop_flag = read_16bit(0x0e,streamFile); + //max_block = read_32bit(0x20,streamFile); + start_offset = 0x40; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_AST; - vgmstream->sample_rate = read_32bitBE(0x10,streamFile); - vgmstream->num_samples = read_32bitBE(0x14,streamFile); - vgmstream->loop_start_sample = read_32bitBE(0x18,streamFile); - vgmstream->loop_end_sample = read_32bitBE(0x1c,streamFile); + vgmstream->sample_rate = read_32bit(0x10,streamFile); + vgmstream->num_samples = read_32bit(0x14,streamFile); + vgmstream->loop_start_sample = read_32bit(0x18,streamFile); + vgmstream->loop_end_sample = read_32bit(0x1c,streamFile); + vgmstream->codec_endian = big_endian; vgmstream->layout_type = layout_blocked_ast; switch (codec) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awb.c b/Frameworks/vgmstream/vgmstream/src/meta/awb.c index ede63671b..929d6f014 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/awb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/awb.c @@ -1,235 +1,240 @@ -#include "meta.h" -#include "../coding/coding.h" - -typedef enum { ADX, HCA, VAG, RIFF, CWAV, DSP } awb_type; - -static void load_awb_name(STREAMFILE *streamFile, STREAMFILE *acbFile, VGMSTREAM *vgmstream, int waveid); - -/* AFS2/AWB (Atom Wave Bank) - CRI container of streaming audio, often together with a .acb cue sheet */ -VGMSTREAM * init_vgmstream_awb(STREAMFILE *streamFile) { - return init_vgmstream_awb_memory(streamFile, NULL); -} - -VGMSTREAM * init_vgmstream_awb_memory(STREAMFILE *streamFile, STREAMFILE *acbFile) { - VGMSTREAM *vgmstream = NULL; - STREAMFILE *temp_streamFile = NULL; - off_t offset, subfile_offset, subfile_next; - size_t subfile_size; - int total_subsongs, target_subsong = streamFile->stream_index; - //uint32_t flags; - uint8_t offset_size; - uint16_t alignment, subkey; - awb_type type; - char *extension = NULL; - int waveid; - - - /* checks - * .awb: standard - * .afs2: sometimes [Okami HD (PS4)] */ - if (!check_extensions(streamFile, "awb,afs2")) - goto fail; - if (read_u32be(0x00,streamFile) != 0x41465332) /* "AFS2" */ - goto fail; - - //flags = read_32bitLE(0x08,streamFile); - /* 0x04(1): version? 0x01=common, 0x02=2018+ (no apparent differences) */ - offset_size = read_u8(0x05,streamFile); - /* 0x06(2): always 0x0002? */ - total_subsongs = read_s32le(0x08,streamFile); - alignment = read_u16le(0x0c,streamFile); - subkey = read_u16le(0x0e,streamFile); - - if (target_subsong == 0) target_subsong = 1; - if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail; - - offset = 0x10; - - /* id(?) table: read target */ - { - off_t waveid_offset = offset + (target_subsong-1) * 0x02; - - waveid = read_u16le(waveid_offset,streamFile); - - offset += total_subsongs * 0x02; - } - - /* offset table: find target */ - { - off_t file_size = get_streamfile_size(streamFile); - - /* last sub-offset is always file end, so table entries = total_subsongs+1 */ - offset += (target_subsong-1) * offset_size; - - switch(offset_size) { - case 0x04: /* common */ - subfile_offset = read_u32le(offset+0x00,streamFile); - subfile_next = read_u32le(offset+0x04,streamFile); - break; - case 0x02: /* mostly sfx in .acb */ - subfile_offset = read_u16le(offset+0x00,streamFile); - subfile_next = read_u16le(offset+0x02,streamFile); - break; - default: - VGM_LOG("AWB: unknown offset size\n"); - goto fail; - } - - /* offset are absolute but sometimes misaligned (specially first that just points to offset table end) */ - subfile_offset += (subfile_offset % alignment) ? - alignment - (subfile_offset % alignment) : 0; - subfile_next += (subfile_next % alignment) && subfile_next < file_size ? - alignment - (subfile_next % alignment) : 0; - subfile_size = subfile_next - subfile_offset; - } - - //;VGM_LOG("AWB: subfile offset=%lx + %x\n", subfile_offset, subfile_size); - - /* autodetect as there isn't anything, plus can mix types - * (waveid<>codec info is usually in the companion .acb) */ - if (read_u16be(subfile_offset, streamFile) == 0x8000) { /* ADX id (type 0) */ - type = ADX; - extension = "adx"; - } - else if ((read_u32be(subfile_offset,streamFile) & 0x7f7f7f7f) == 0x48434100) { /* "HCA\0" (type 2=HCA, 6=HCA-MX) */ - type = HCA; - extension = "hca"; - } - else if (read_u32be(subfile_offset,streamFile) == 0x56414770) { /* "VAGp" (type 7=VAG, 10=HEVAG) */ - type = VAG; - extension = "vag"; - } - else if (read_u32be(subfile_offset,streamFile) == 0x52494646) { /* "RIFF" (type 8=ATRAC3, 11=ATRAC9) */ - type = RIFF; - extension = "wav"; - } - else if (read_u32be(subfile_offset,streamFile) == 0x43574156) { /* "CWAV" (type 9) */ - type = CWAV; - extension = "bcwav"; - } - else if (read_u32be(subfile_offset + 0x08,streamFile) >= 8000 && - read_u32be(subfile_offset + 0x08,streamFile) <= 48000 && - read_u16be(subfile_offset + 0x0e,streamFile) == 0 && - read_u32be(subfile_offset + 0x18,streamFile) == 2 && - read_u32be(subfile_offset + 0x50,streamFile) == 0) { /* probably should call some check function (type 13) */ - type = DSP; - extension = "dsp"; - } - else { - VGM_LOG("AWB: unknown codec\n"); - goto fail; - } - - - temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, extension); - if (!temp_streamFile) goto fail; - - switch(type) { - case HCA: /* most common */ - vgmstream = init_vgmstream_hca_subkey(temp_streamFile, subkey); - if (!vgmstream) goto fail; - break; - case ADX: /* Okami HD (PS4) */ - vgmstream = init_vgmstream_adx(temp_streamFile); - if (!vgmstream) goto fail; - break; - case VAG: /* Ukiyo no Roushi (Vita) */ - vgmstream = init_vgmstream_vag(temp_streamFile); - if (!vgmstream) goto fail; - break; - case RIFF: /* Ukiyo no Roushi (Vita) */ - vgmstream = init_vgmstream_riff(temp_streamFile); - if (!vgmstream) goto fail; - break; - case CWAV: /* Sonic: Lost World (3DS) */ - vgmstream = init_vgmstream_rwsd(temp_streamFile); - if (!vgmstream) goto fail; - break; - case DSP: /* Sonic: Lost World (WiiU) */ - vgmstream = init_vgmstream_ngc_dsp_std(temp_streamFile); - if (!vgmstream) goto fail; - break; - default: - goto fail; - } - - vgmstream->num_streams = total_subsongs; - - /* try to load cue names */ - load_awb_name(streamFile, acbFile, vgmstream, waveid); - - close_streamfile(temp_streamFile); - return vgmstream; - -fail: - close_streamfile(temp_streamFile); - close_vgmstream(vgmstream); - return NULL; -} - - -static void load_awb_name(STREAMFILE *streamFile, STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid) { - int is_memory = (acbFile != NULL); - - /* .acb is passed when loading memory .awb inside .acb */ - if (!is_memory) { - /* load companion .acb using known pairs */ //todo improve, see xsb code - char filename[PATH_LIMIT]; - int len_name, len_cmp; - - - /* try (name).awb + (name).awb */ - acbFile = open_streamfile_by_ext(streamFile, "acb"); - - /* try (name)_streamfiles.awb + (name).acb */ - if (!acbFile) { - char *cmp = "_streamfiles"; - get_streamfile_basename(streamFile, filename, sizeof(filename)); - len_name = strlen(filename); - len_cmp = strlen(cmp); - - if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) { - filename[len_name - len_cmp] = '\0'; - strcat(filename, ".acb"); - acbFile = open_streamfile_by_filename(streamFile, filename); - } - } - - /* try (name)_STR.awb + (name).acb */ - if (!acbFile) { - char *cmp = "_STR"; - get_streamfile_basename(streamFile, filename, sizeof(filename)); - len_name = strlen(filename); - len_cmp = strlen(cmp); - - if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) { - filename[len_name - len_cmp] = '\0'; - strcat(filename, ".acb"); - acbFile = open_streamfile_by_filename(streamFile, filename); - } - } - - /* try (name)_(name)_R001.awb + (name).acb [Sengoku Basara Battle Party (Mobile)] */ - if (!acbFile) { - char *cmp = "_R001"; - get_streamfile_basename(streamFile, filename, sizeof(filename)); - len_name = strlen(filename); - len_cmp = strlen(cmp); - - if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) { - filename[(len_name - len_cmp) / 2] = '\0'; - strcat(filename, ".acb"); - VGM_LOG("%s\n", filename); - acbFile = open_streamfile_by_filename(streamFile, filename); - } - } - - /* probably loaded */ - load_acb_wave_name(acbFile, vgmstream, waveid, is_memory); - - close_streamfile(acbFile); - } - else { - load_acb_wave_name(acbFile, vgmstream, waveid, is_memory); - } -} +#include "meta.h" +#include "../coding/coding.h" + +typedef enum { ADX, HCA, VAG, RIFF, CWAV, DSP } awb_type; + +static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid); + +/* AFS2/AWB (Atom Wave Bank) - CRI container of streaming audio, often together with a .acb cue sheet */ +VGMSTREAM* init_vgmstream_awb(STREAMFILE* sf) { + return init_vgmstream_awb_memory(sf, NULL); +} + +VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + off_t offset, subfile_offset, subfile_next; + size_t subfile_size; + int total_subsongs, target_subsong = sf->stream_index; + //uint32_t flags; + uint8_t offset_size; + uint16_t alignment, subkey; + awb_type type; + const char* extension = NULL; + int waveid; + + + /* checks + * .awb: standard + * .afs2: sometimes [Okami HD (PS4)] */ + if (!check_extensions(sf, "awb,afs2")) + goto fail; + if (read_u32be(0x00,sf) != 0x41465332) /* "AFS2" */ + goto fail; + + //flags = read_32bitLE(0x08,sf); + /* 0x04(1): version? 0x01=common, 0x02=2018+ (no apparent differences) */ + offset_size = read_u8(0x05,sf); + /* 0x06(2): always 0x0002? */ + total_subsongs = read_s32le(0x08,sf); + alignment = read_u16le(0x0c,sf); + subkey = read_u16le(0x0e,sf); + + if (target_subsong == 0) target_subsong = 1; + if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail; + + offset = 0x10; + + /* id(?) table: read target */ + { + off_t waveid_offset = offset + (target_subsong-1) * 0x02; + + waveid = read_u16le(waveid_offset,sf); + + offset += total_subsongs * 0x02; + } + + /* offset table: find target */ + { + off_t file_size = get_streamfile_size(sf); + + /* last sub-offset is always file end, so table entries = total_subsongs+1 */ + offset += (target_subsong-1) * offset_size; + + switch(offset_size) { + case 0x04: /* common */ + subfile_offset = read_u32le(offset+0x00,sf); + subfile_next = read_u32le(offset+0x04,sf); + break; + case 0x02: /* mostly sfx in .acb */ + subfile_offset = read_u16le(offset+0x00,sf); + subfile_next = read_u16le(offset+0x02,sf); + break; + default: + VGM_LOG("AWB: unknown offset size\n"); + goto fail; + } + + /* offset are absolute but sometimes misaligned (specially first that just points to offset table end) */ + subfile_offset += (subfile_offset % alignment) ? + alignment - (subfile_offset % alignment) : 0; + subfile_next += (subfile_next % alignment) && subfile_next < file_size ? + alignment - (subfile_next % alignment) : 0; + subfile_size = subfile_next - subfile_offset; + } + + //;VGM_LOG("AWB: subfile offset=%lx + %x\n", subfile_offset, subfile_size); + + /* autodetect as there isn't anything, plus can mix types + * (waveid<>codec info is usually in the companion .acb) */ + if (read_u16be(subfile_offset, sf) == 0x8000) { /* ADX id (type 0) */ + type = ADX; + extension = "adx"; + } + else if ((read_u32be(subfile_offset,sf) & 0x7f7f7f7f) == 0x48434100) { /* "HCA\0" (type 2=HCA, 6=HCA-MX) */ + type = HCA; + extension = "hca"; + } + else if (read_u32be(subfile_offset,sf) == 0x56414770) { /* "VAGp" (type 7=VAG, 10=HEVAG) */ + type = VAG; + extension = "vag"; + } + else if (read_u32be(subfile_offset,sf) == 0x52494646) { /* "RIFF" (type 8=ATRAC3, 11=ATRAC9) */ + type = RIFF; + extension = "wav"; + subfile_size = read_u32le(subfile_offset + 0x04,sf) + 0x08; /* rough size, use RIFF's */ + } + else if (read_u32be(subfile_offset,sf) == 0x43574156) { /* "CWAV" (type 9) */ + type = CWAV; + extension = "bcwav"; + } + else if (read_u32be(subfile_offset + 0x08,sf) >= 8000 && + read_u32be(subfile_offset + 0x08,sf) <= 48000 && + read_u16be(subfile_offset + 0x0e,sf) == 0 && + read_u32be(subfile_offset + 0x18,sf) == 2 && + read_u32be(subfile_offset + 0x50,sf) == 0) { /* probably should call some check function (type 13) */ + type = DSP; + extension = "dsp"; + } + else { + VGM_LOG("AWB: unknown codec\n"); + goto fail; + } + + + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, extension); + if (!temp_sf) goto fail; + + switch(type) { + case HCA: /* most common */ + vgmstream = init_vgmstream_hca_subkey(temp_sf, subkey); + if (!vgmstream) goto fail; + break; + case ADX: /* Okami HD (PS4) */ + vgmstream = init_vgmstream_adx(temp_sf); + if (!vgmstream) goto fail; + break; + case VAG: /* Ukiyo no Roushi (Vita) */ + vgmstream = init_vgmstream_vag(temp_sf); + if (!vgmstream) goto fail; + break; + case RIFF: /* Ukiyo no Roushi (Vita) */ + vgmstream = init_vgmstream_riff(temp_sf); + if (!vgmstream) goto fail; + break; + case CWAV: /* Sonic: Lost World (3DS) */ + vgmstream = init_vgmstream_rwsd(temp_sf); + if (!vgmstream) goto fail; + break; + case DSP: /* Sonic: Lost World (WiiU) */ + vgmstream = init_vgmstream_ngc_dsp_std(temp_sf); + if (!vgmstream) goto fail; + break; + default: + goto fail; + } + + vgmstream->num_streams = total_subsongs; + + /* try to load cue names */ + load_awb_name(sf, sf_acb, vgmstream, waveid); + + close_streamfile(temp_sf); + return vgmstream; + +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} + + +static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid) { + int is_memory = (sf_acb != NULL); + + /* .acb is passed when loading memory .awb inside .acb */ + if (!is_memory) { + /* load companion .acb using known pairs */ //todo improve, see xsb code + char filename[PATH_LIMIT]; + int len_name, len_cmp; + + /* try parsing TXTM if present */ + sf_acb = read_filemap_file(sf, 0); + + /* try (name).awb + (name).awb */ + if (!sf_acb) { + sf_acb = open_streamfile_by_ext(sf, "acb"); + } + + /* try (name)_streamfiles.awb + (name).acb */ + if (!sf_acb) { + char *cmp = "_streamfiles"; + get_streamfile_basename(sf, filename, sizeof(filename)); + len_name = strlen(filename); + len_cmp = strlen(cmp); + + if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) { + filename[len_name - len_cmp] = '\0'; + strcat(filename, ".acb"); + sf_acb = open_streamfile_by_filename(sf, filename); + } + } + + /* try (name)_STR.awb + (name).acb */ + if (!sf_acb) { + char *cmp = "_STR"; + get_streamfile_basename(sf, filename, sizeof(filename)); + len_name = strlen(filename); + len_cmp = strlen(cmp); + + if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) { + filename[len_name - len_cmp] = '\0'; + strcat(filename, ".acb"); + sf_acb = open_streamfile_by_filename(sf, filename); + } + } + + /* try (name)_(name)_R001.awb + (name).acb [Sengoku Basara Battle Party (Mobile)] */ + if (!sf_acb) { + char *cmp = "_R001"; + get_streamfile_basename(sf, filename, sizeof(filename)); + len_name = strlen(filename); + len_cmp = strlen(cmp); + + if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) { + filename[(len_name - len_cmp) / 2] = '\0'; + strcat(filename, ".acb"); + VGM_LOG("%s\n", filename); + sf_acb = open_streamfile_by_filename(sf, filename); + } + } + + /* probably loaded */ + load_acb_wave_name(sf_acb, vgmstream, waveid, is_memory); + + close_streamfile(sf_acb); + } + else { + load_acb_wave_name(sf_acb, vgmstream, waveid, is_memory); + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c b/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c index 7b5606c39..a31044b2d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c @@ -8,9 +8,10 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) { STREAMFILE* temp_sf = NULL; off_t subfile_offset, base_offset = 0; size_t subfile_size; - uint32_t subfile_id, header_id; - int big_endian, version, is_dummy = 0; + uint32_t subfile_id; + int big_endian, version, is_riff = 0, is_dummy = 0, is_wmid = 0; uint32_t (*read_u32)(off_t,STREAMFILE*); + float (*read_f32)(off_t,STREAMFILE*); int total_subsongs, target_subsong = sf->stream_index; @@ -24,19 +25,21 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) { goto fail; big_endian = guess_endianness32bit(base_offset + 0x04, sf); read_u32 = big_endian ? read_u32be : read_u32le; + read_f32 = big_endian ? read_f32be : read_f32le; /* Wwise banks have event/track/sequence/etc info in the HIRC chunk, as well * as other chunks, and may have a DATA/DIDX index to memory .wem in DATA. * We support the internal .wem mainly for quick tests, as the HIRC is * complex and better handled with TXTP (some info from Nicknine's script). - * unlike RIFF, first chunk follows chunk rules */ + * Use this to explore HIRC and covert to .txtp: https://github.com/bnnm/wwiser */ version = read_u32(base_offset + 0x08, sf); if (version == 0 || version == 1) { /* early games */ version = read_u32(base_offset + 0x10, sf); } + /* first chunk also follows standard chunk sizes unlike RIFF */ if (version <= 26) { off_t data_offset, data_start, offset; if (!find_chunk(sf, 0x44415441, base_offset, 0, &data_offset, NULL, big_endian, 0)) /* "DATA" */ @@ -48,7 +51,7 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) { * 08: entries size * 0c: padding size after entries * 10: data size - * 14: size? + * 14: size? or null * 18: data start * 1c: data size * per entry: @@ -90,27 +93,38 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) { subfile_offset = read_u32(offset + 0x04, sf) + data_offset; subfile_size = read_u32(offset + 0x08, sf); } + + //;VGM_LOG("BKHD: %lx, %x\n", subfile_offset, subfile_size); - /* some indexes don't have that, but for now leave a dummy song for easier HIRC mapping */ + /* detect format */ if (subfile_offset <= 0 || subfile_size <= 0) { - //;VGM_LOG("BKHD: dummy entry"); - temp_sf = setup_subfile_streamfile(sf, 0x00, 0x10, "raw"); - if (!temp_sf) goto fail; - - //todo make some better silent entry - vgmstream = init_vgmstream_raw_pcm(temp_sf); - if (!vgmstream) goto fail; - + /* some indexes don't have data */ is_dummy = 1; } + else if (read_u32be(subfile_offset + 0x00, sf) == 0x52494646 || /* "RIFF" */ + read_u32be(subfile_offset + 0x00, sf) == 0x52494658) { /* "RIFX" */ + is_riff = 1; + } + else if (read_f32(subfile_offset + 0x02, sf) >= 30.0 && + read_f32(subfile_offset + 0x02, sf) <= 250.0) { + /* ignore Wwise's custom .wmid (similar to a regular midi but with simplified + * chunks and custom fields: 0x00=MThd's division, 0x02: bpm (new), etc) */ + is_wmid = 1; + } + /* default is sfx */ + + + if (is_dummy || is_wmid) { + /* for now leave a dummy song for easier .bnk index-to-subsong mapping */ + vgmstream = init_vgmstream_silence(0, 0, 0); + if (!vgmstream) goto fail; + } else { - //;VGM_LOG("BKHD: %lx, %x\n", subfile_offset, subfile_size); /* could pass .wem but few files need memory .wem detection */ temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, NULL); if (!temp_sf) goto fail; - header_id = read_u32be(0x00, temp_sf); - if (header_id == 0x52494646 || header_id == 0x52494658) { /* "RIFF" / "RIFX" */ + if (is_riff) { vgmstream = init_vgmstream_wwise(temp_sf); if (!vgmstream) goto fail; } @@ -122,11 +136,23 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) { vgmstream->num_streams = total_subsongs; - - if (is_dummy) - snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%s", "dummy"); - else if (subfile_id != 0xFFFFFFFF) - snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u", subfile_id); + + { + const char* info = NULL; + if (is_dummy) + info = "dummy"; + else if (is_wmid) + info = "wmid"; + + /* old Wwise shows index or (more often) -1, unify to index*/ + if (subfile_id == 0xFFFFFFFF) + subfile_id = target_subsong - 1; + + if (info) + snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u/%s", subfile_id, info); + else + snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u", subfile_id); + } close_streamfile(temp_sf); return vgmstream; @@ -138,7 +164,7 @@ fail: } -/* BKHD mini format, probably from a FX generator plugin [Borderlands 2 (X360)] */ +/* BKHD mini format, for FX plugins [Borderlands 2 (X360), Warhammer 40000 (PC)] */ VGMSTREAM* init_vgmstream_bkhd_fx(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; off_t start_offset, data_size; @@ -147,29 +173,48 @@ VGMSTREAM* init_vgmstream_bkhd_fx(STREAMFILE* sf) { /* checks */ - if (!check_extensions(sf,"wem,bnk")) /* assumed */ + /* .wem: used when (rarely) external */ + if (!check_extensions(sf,"wem,bnk")) goto fail; big_endian = guess_endianness32bit(0x00, sf); read_u32 = big_endian ? read_u32be : read_u32le; - if (read_u32(0x00, sf) != 0x0400) /* codec? */ - goto fail; - if (read_u32(0x04, sf) != 0x0800) /* codec? */ - goto fail; - sample_rate = read_u32(0x08, sf); - channels = read_u32(0x0c, sf); - /* 0x10: some id or small size? */ - /* 0x14/18: some float? */ - entries = read_u32(0x1c, sf); - /* 0x20 data size / 0x10 */ - /* 0x24 usually 4, sometimes higher values? */ - /* 0x30: unknown table of 16b that goes up and down */ + /* Not an actual stream but typically convolution reverb models and other FX plugin helpers. + * Useless but to avoid "subsong not playing" complaints. */ + + if (read_u32(0x00, sf) == 0x0400 && + read_u32(0x04, sf) == 0x0800) { + sample_rate = read_u32(0x08, sf); + channels = read_u32(0x0c, sf) & 0xFF; /* 0x31 at 0x0d in PC, field is 32b vs X360 */ + /* 0x10: some id or small size? (related to entries?) */ + /* 0x14/18: some float? */ + entries = read_u32(0x1c, sf); + /* 0x20 data size / 0x10 */ + /* 0x24 usually 4, sometimes higher values? */ + /* 0x30: unknown table of 16b that goes up and down, or is fixed */ + + start_offset = 0x30 + align_size_to_block(entries * 0x02, 0x10); + data_size = get_streamfile_size(sf) - start_offset; + } + else if (read_u32be(0x04, sf) == 0x00004844 && /* floats actually? */ + read_u32be(0x08, sf) == 0x0000FA45 && + read_u32be(0x1c, sf) == 0x80000000) { + /* seen in Crucible banks */ + sample_rate = 48000; /* meh */ + channels = 1; + + start_offset = 0; + data_size = get_streamfile_size(sf); + big_endian = 0; + } + else { + goto fail; + } - start_offset = 0x30 + align_size_to_block(entries * 0x02, 0x10); - data_size = get_streamfile_size(sf) - start_offset; loop_flag = 0; - /* output sounds a bit funny, maybe not an actual stream but parts using the table */ + + /* data seems divided in chunks of 0x2000 */ /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channels, loop_flag); @@ -185,7 +230,7 @@ VGMSTREAM* init_vgmstream_bkhd_fx(STREAMFILE* sf) { vgmstream->num_samples = pcm_bytes_to_samples(data_size, channels, 32); - if (!vgmstream_open_stream(vgmstream,sf,start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/brstm.c b/Frameworks/vgmstream/vgmstream/src/meta/brstm.c index 960c21b37..80f811957 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/brstm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/brstm.c @@ -106,26 +106,39 @@ VGMSTREAM * init_vgmstream_brstm(STREAMFILE *streamFile) { if (vgmstream->coding_type == coding_NGC_DSP) { off_t coef_offset; - off_t coef_offset1; - off_t coef_offset2; + off_t head_part3_offset; + off_t adpcm_header_offset; int i,j; - int coef_spacing = 0x38; + int coef_spacing; if (atlus_shrunken_head) { coef_offset = 0x50; coef_spacing = 0x30; + + for (j = 0; j < vgmstream->channels; j++) { + for (i = 0; i < 16; i++) { + vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(head_offset + coef_offset + j * coef_spacing + i * 2,streamFile); + } + } } else { - coef_offset1=read_32bitBE(head_offset+0x1c,streamFile); - coef_offset2=read_32bitBE(head_offset+0x10+coef_offset1,streamFile); - coef_offset=coef_offset2+0x10; - } + head_part3_offset = read_32bitBE(head_offset + 0x1c, streamFile); - for (j=0;jchannels;j++) { - for (i=0;i<16;i++) { - vgmstream->ch[j].adpcm_coef[i]=read_16bitBE(head_offset+coef_offset+j*coef_spacing+i*2,streamFile); + for (j = 0; j < vgmstream->channels; j++) { + adpcm_header_offset = head_offset + 0x08 + + head_part3_offset + 0x04 /* skip over HEAD part 3 */ + + j * 0x08 /* skip to channel's ADPCM offset table */ + + 0x04; /* ADPCM header offset field */ + + coef_offset = head_offset + 0x08 + + read_32bitBE(adpcm_header_offset, streamFile) + + 0x08; /* coeffs field */ + + for (i = 0; i < 16; i++) { + vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_offset + i * 2, streamFile); + } } } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bsf.c b/Frameworks/vgmstream/vgmstream/src/meta/bsf.c new file mode 100644 index 000000000..7e2cc22da --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/bsf.c @@ -0,0 +1,97 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* .bsf - from Kuju games [Reign of Fire ((PS2/GC/Xbox)] */ +VGMSTREAM* init_vgmstream_bsf(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + uint32_t subfile_type; + off_t subfile_name; + off_t subfile_offset; + size_t subfile_size; + int total_subsongs, target_subsong = sf->stream_index; + + + /* checks */ + if (!check_extensions(sf,"bsf")) + goto fail; + if (read_u32le(0x00,sf) != 0x42534648) /* "BSFH" (notice chunks are LE even on GC) */ + goto fail; + + total_subsongs = read_u32le(0x08,sf); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + /* dumb container of other formats */ + { + int i; + off_t offset = 0x08 + read_u32le(0x04,sf); /* 0x04: chunk size */ + + subfile_type = 0; + for (i = 0; i < total_subsongs; i++) { + /* subsong header "xxxH" */ + //uint32_t head_type = read_u32le(offset + 0x00,sf); + uint32_t head_size = read_u32le(offset + 0x04,sf); + /* 0x08: name + * 0x28: audio config? */ + /* subsong data "xxxD" */ + uint32_t data_type = read_u32le(offset + 0x08 + head_size + 0x00,sf); + uint32_t data_size = read_u32le(offset + 0x08 + head_size + 0x04,sf); + + if (i + 1 == target_subsong) { + subfile_name = offset + 0x08; + subfile_type = data_type; + subfile_size = data_size; + subfile_offset = offset + 0x08 + head_size + 0x08; + break; + } + + offset += 0x08 + head_size + 0x08 + data_size; + } + + if (subfile_type == 0) + goto fail; + } + + + switch(subfile_type) { + case 0x44535044: /* "DSPD" */ + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "dsp"); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_ngc_dsp_std(temp_sf); + if (!vgmstream) goto fail; + break; + + case 0x56414744: /* "VAGD" */ + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "vag"); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_vag(temp_sf); + if (!vgmstream) goto fail; + break; + + case 0x57415644: /* "WAVD" */ + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "wav"); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_riff(temp_sf); + if (!vgmstream) goto fail; + break; + + default: + goto fail; + } + + vgmstream->num_streams = total_subsongs; + read_string(vgmstream->stream_name, 0x20, subfile_name, sf); + + close_streamfile(temp_sf); + return vgmstream; + +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/cpk.c b/Frameworks/vgmstream/vgmstream/src/meta/cpk.c new file mode 100644 index 000000000..22dcccd6b --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/cpk.c @@ -0,0 +1,248 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "cri_utf.h" + + +typedef enum { HCA, CWAV, } cpk_type_t; + +static void load_cpk_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid); + +/* CPK - CRI container as audio bank [Metal Gear Solid: Snake Eater 3D (3DS), Street Fighter X Tekken (X360), Ace Combat Infinity (PS3)] */ +VGMSTREAM* init_vgmstream_cpk(STREAMFILE* sf) { + return init_vgmstream_cpk_memory(sf, NULL); +} + +VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + off_t subfile_offset = 0; + size_t subfile_size = 0; + utf_context* utf = NULL; + int total_subsongs, target_subsong = sf->stream_index; + int subfile_id = 0; + cpk_type_t type; + const char* extension = NULL; + uint32_t* sizes = NULL; + + + /* checks */ + if (!check_extensions(sf, "awb")) + goto fail; + if (read_u32be(0x00,sf) != 0x43504B20) /* "CPK " */ + goto fail; + if (read_u32be(0x10,sf) != 0x40555446) /* "@UTF" */ + goto fail; + /* 04: 0xFF? */ + /* 08: 0x02A0? */ + /* 0c: null? */ + + /* CPK .cpk is CRI's generic file container, but here we only support CPK .awb used as + * early audio bank, that like standard AFS2 .awb comes with .acb */ + { + int rows, i; + const char* name; + const char* Tvers; + uint32_t table_offset = 0, offset; + uint32_t Files = 0, FilesL = 0, FilesH = 0; + uint64_t ContentOffset = 0, ItocOffset = 0; + uint16_t Align = 0; + uint32_t DataL_offset = 0, DataL_size = 0, DataH_offset = 0, DataH_size = 0; + + /* base header */ + table_offset = 0x10; + utf = utf_open(sf, table_offset, &rows, &name); + if (!utf || strcmp(name, "CpkHeader") != 0 || rows != 1) + goto fail; + + if (!utf_query_string(utf, 0, "Tvers", &Tvers) || + !utf_query_u32(utf, 0, "Files", &Files) || + !utf_query_u64(utf, 0, "ContentOffset", &ContentOffset) || /* absolute */ + !utf_query_u64(utf, 0, "ItocOffset", &ItocOffset) || /* Toc seems used for regular files */ + !utf_query_u16(utf, 0, "Align", &Align)) + goto fail; + + utf_close(utf); + utf = NULL; + + if (strncmp(Tvers, "awb", 3) != 0) /* starts with "awb" + ".(version)" (SFvTK, MGS3D) or " for (version)" (ACI) */ + goto fail; + if (Files <= 0) + goto fail; + + + /* Itoc header (regular .CPK tend to use Toc or Etoc header) */ + table_offset = 0x10 + ItocOffset; + utf = utf_open(sf, table_offset, &rows, &name); + if (!utf) goto fail; + + if (rows != 1 || strcmp(name, "CpkItocInfo") != 0) + goto fail; + + if (!utf_query_u32(utf, 0, "FilesL", &FilesL) || + !utf_query_u32(utf, 0, "FilesH", &FilesH) || + !utf_query_data(utf, 0, "DataL", &DataL_offset, &DataL_size) || /* absolute */ + !utf_query_data(utf, 0, "DataH", &DataH_offset, &DataH_size)) /* absolute */ + goto fail; + + utf_close(utf); + utf = NULL; + + + /* For maximum annoyance there are 2 tables (small+big files) that only list sizes, + * and files can be mixed (small small big small big). + * Must pre-read all entries to find actual offset plus subsongs number. */ + if (FilesL + FilesH != Files) + goto fail; + + total_subsongs = Files; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail; + + sizes = calloc(Files, sizeof(uint32_t)); + if (!sizes) goto fail; + + /* DataL header */ + table_offset = DataL_offset; + utf = utf_open(sf, table_offset, &rows, &name); + if (!utf || strcmp(name, "CpkItocL") != 0 || rows != FilesL) + goto fail; + + for (i = 0; i < rows; i++) { + uint16_t ID = 0; + uint16_t FileSize, ExtractSize; + + if (!utf_query_u16(utf, i, "ID", &ID) || + !utf_query_u16(utf, i, "FileSize", &FileSize) || + !utf_query_u16(utf, i, "ExtractSize", &ExtractSize)) + goto fail; + + if (ID >= Files || FileSize != ExtractSize || sizes[ID]) + goto fail; + + sizes[ID] = FileSize; + } + + utf_close(utf); + utf = NULL; + + /* DataR header */ + table_offset = DataH_offset; + utf = utf_open(sf, table_offset, &rows, &name); + if (!utf || strcmp(name, "CpkItocH") != 0 || rows != FilesH) + goto fail; + + for (i = 0; i < rows; i++) { + uint16_t ID = 0; + uint32_t FileSize, ExtractSize; + + if (!utf_query_u16(utf, i, "ID", &ID) || + !utf_query_u32(utf, i, "FileSize", &FileSize) || + !utf_query_u32(utf, i, "ExtractSize", &ExtractSize)) + goto fail; + + if (ID >= Files || FileSize != ExtractSize || sizes[ID]) + goto fail; + + sizes[ID] = FileSize; + } + + utf_close(utf); + utf = NULL; + + + /* find actual offset */ + offset = ContentOffset; + for (i = 0; i < Files; i++) { + uint32_t size = sizes[i]; + if (i + 1 == target_subsong) { + subfile_id = i; + subfile_offset = offset; + subfile_size = size; + break; + } + + offset += size; + if (Align && (offset % Align)) + offset += Align - (offset % Align); + } + + free(sizes); + sizes = NULL; + } + + if (!subfile_offset) + goto fail; + + //;VGM_LOG("CPK: subfile offset=%lx + %x, id=%i\n", subfile_offset, subfile_size, subfile_id); + + + if ((read_u32be(subfile_offset,sf) & 0x7f7f7f7f) == 0x48434100) { /* "HCA\0" */ + type = HCA; + extension = "hca"; + } + else if (read_u32be(subfile_offset,sf) == 0x43574156) { /* "CWAV" */ + type = CWAV; + extension = "bcwav"; + } + else { + goto fail; + } + + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, extension); + if (!temp_sf) goto fail; + + switch(type) { + case HCA: + vgmstream = init_vgmstream_hca(temp_sf); + if (!vgmstream) goto fail; + break; + case CWAV: /* Metal Gear Solid: Snake Eater 3D (3DS) */ + vgmstream = init_vgmstream_rwsd(temp_sf); + if (!vgmstream) goto fail; + break; + default: + goto fail; + } + + vgmstream->num_streams = total_subsongs; + + /* try to load cue names */ + load_cpk_name(sf, sf_acb, vgmstream, subfile_id); + + close_streamfile(temp_sf); + return vgmstream; + +fail: + free(sizes); + utf_close(utf); + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} + +static void load_cpk_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid) { + int is_memory = (sf_acb != NULL); + + /* .acb is passed when loading memory .awb inside .acb */ + if (!is_memory) { + /* try parsing TXTM if present */ + sf_acb = read_filemap_file(sf, 0); + + /* try (name).awb + (name).awb */ + if (!sf_acb) + sf_acb = open_streamfile_by_ext(sf, "acb"); + + /* (name)_streamfiles.awb + (name).acb also exist */ + + if (!sf_acb) + return; + + /* companion .acb probably loaded */ + load_acb_wave_name(sf_acb, vgmstream, waveid, is_memory); + + close_streamfile(sf_acb); + } + else { + load_acb_wave_name(sf_acb, vgmstream, waveid, is_memory); + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.c b/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.c index f1dddb2d1..94851fed8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.c @@ -369,15 +369,30 @@ static int utf_query_value(utf_context* utf, int row, const char* column, void* return 1; } +int utf_query_s8(utf_context* utf, int row, const char* column, int8_t* value) { + return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT8); +} int utf_query_u8(utf_context* utf, int row, const char* column, uint8_t* value) { return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_UINT8); } +int utf_query_s16(utf_context* utf, int row, const char* column, int16_t* value) { + return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT16); +} int utf_query_u16(utf_context* utf, int row, const char* column, uint16_t* value) { return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_UINT16); } +int utf_query_s32(utf_context* utf, int row, const char* column, int32_t* value) { + return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT32); +} int utf_query_u32(utf_context* utf, int row, const char* column, uint32_t* value) { return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_UINT32); } +int utf_query_s64(utf_context* utf, int row, const char* column, int64_t* value) { + return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_SINT64); +} +int utf_query_u64(utf_context* utf, int row, const char* column, uint64_t* value) { + return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_UINT64); +} int utf_query_string(utf_context* utf, int row, const char* column, const char** value) { return utf_query_value(utf, row, column, (void*)value, COLUMN_TYPE_STRING); } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.h b/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.h index e8d2a6d20..6e7ec59db 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/cri_utf.h @@ -24,9 +24,14 @@ typedef struct utf_context utf_context; utf_context* utf_open(STREAMFILE* sf, uint32_t table_offset, int* p_rows, const char** p_row_name); void utf_close(utf_context* utf); /* query calls */ +int utf_query_s8(utf_context* utf, int row, const char* column, int8_t* value); int utf_query_u8(utf_context* utf, int row, const char* column, uint8_t* value); +int utf_query_s16(utf_context* utf, int row, const char* column, int16_t* value); int utf_query_u16(utf_context* utf, int row, const char* column, uint16_t* value); +int utf_query_s32(utf_context* utf, int row, const char* column, int32_t* value); int utf_query_u32(utf_context* utf, int row, const char* column, uint32_t* value); +int utf_query_s64(utf_context* utf, int row, const char* column, int64_t* value); +int utf_query_u64(utf_context* utf, int row, const char* column, uint64_t* value); int utf_query_string(utf_context* utf, int row, const char* column, const char** value); int utf_query_data(utf_context* utf, int row, const char* column, uint32_t* offset, uint32_t* size); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/deblock_streamfile.c b/Frameworks/vgmstream/vgmstream/src/meta/deblock_streamfile.c index 5b8ec2aee..681b4d35d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/deblock_streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/deblock_streamfile.c @@ -23,6 +23,7 @@ static size_t deblock_io_read(STREAMFILE* sf, uint8_t* dest, off_t offset, size_ data->block_size = 0; data->data_size = 0; data->skip_size = 0; + data->chunk_size = 0; data->step_count = data->cfg.step_start; //data->read_count = data->cfg.read_count; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/deblock_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/deblock_streamfile.h index ba6cc81d0..aec9e4178 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/deblock_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/deblock_streamfile.h @@ -49,6 +49,7 @@ struct deblock_io_data { off_t block_size; /* current block (added to physical offset) */ off_t skip_size; /* data to skip from block start to reach data (like a header) */ off_t data_size; /* usable data in a block (added to logical offset) */ + off_t chunk_size; /* current super-block size (for complex blocks, handled manually) */ int step_count; /* number of blocks to step over */ //int read_count; /* number of blocks to read */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/dsb.c b/Frameworks/vgmstream/vgmstream/src/meta/dsb.c new file mode 100644 index 000000000..24f0cc276 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/dsb.c @@ -0,0 +1,54 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .dsb - from Namco games [Taiko no Tatsujin DS: Dororon! Yokai Daikessen!! (DS)] */ +VGMSTREAM* init_vgmstream_dsb(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + off_t subfile_offset; + size_t subfile_size; + + + /* checks */ + if (!check_extensions(sf,"dsb")) + goto fail; + if (read_u32be(0x00,sf) != 0x44535342) /* "DSSB" */ + goto fail; + if (read_u32be(0x40,sf) != 0x44535354) /* "DSST" */ + goto fail; + + /* - DDSB: + * 0x04: chunk size + * 0x08: file name + * 0x14: sample rate + * 0x18: v01? + * 0x1c: file size + * 0x20: DSST offset + * + * - DDST: + * 0x44: chunk size + * 0x48: file name + * 0x58: small signed number? + * 0x5c: data size (with padding) + * 0x60: small signed number? + * 0x64: ? + * rest: null + */ + + subfile_offset = 0x80; + subfile_size = read_u32be(0x80 + 0x04, sf) + 0x08; /* files are padded so use BNSF */ + + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "bnsf"); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_bnsf(temp_sf); + if (!vgmstream) goto fail; + + close_streamfile(temp_sf); + return vgmstream; + +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c index 647496925..1abeca016 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c @@ -25,17 +25,17 @@ typedef struct { int codec_config; int is_bank; int total_subsongs; -} ea_header; +} eacs_header; -static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset); -static VGMSTREAM * init_vgmstream_main(STREAMFILE *streamFile, ea_header* ea); +static int parse_header(STREAMFILE* streamFile, eacs_header* ea, off_t begin_offset); +static VGMSTREAM * init_vgmstream_main(STREAMFILE *streamFile, eacs_header* ea); -static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE *streamFile, ea_header *ea, int find_loop); -static int get_ea_1snh_ima_version(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea); +static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE *streamFile, eacs_header *ea, int find_loop); +static int get_ea_1snh_ima_version(STREAMFILE* streamFile, off_t start_offset, const eacs_header* ea); /* EA 1SNh - from early EA games, stream (~1996, ex. Need for Speed) */ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) { - ea_header ea = { 0 }; + eacs_header ea = { 0 }; off_t offset, eacs_offset; VGMSTREAM *vgmstream = NULL; @@ -93,7 +93,7 @@ fail: /* EA EACS - from early EA games, bank (~1996, ex. Need for Speed) */ VGMSTREAM * init_vgmstream_ea_eacs(STREAMFILE *streamFile) { - ea_header ea = {0}; + eacs_header ea = {0}; off_t eacs_offset; @@ -157,7 +157,7 @@ fail: } -static VGMSTREAM * init_vgmstream_main(STREAMFILE *streamFile, ea_header* ea) { +static VGMSTREAM * init_vgmstream_main(STREAMFILE *streamFile, eacs_header* ea) { VGMSTREAM * vgmstream = NULL; @@ -210,7 +210,7 @@ fail: return NULL; } -static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) { +static int parse_header(STREAMFILE* streamFile, eacs_header* ea, off_t offset) { /* audio header endianness doesn't always match block headers, use sample rate to detect */ int32_t (*read_32bit)(off_t,STREAMFILE*); @@ -278,7 +278,7 @@ 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_num_samples(VGMSTREAM *vgmstream, STREAMFILE *streamFile, ea_header *ea, int find_loop) { +static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE *streamFile, eacs_header *ea, int find_loop) { int32_t num_samples = 0, block_id; size_t file_size; int32_t(*read_32bit)(off_t, STREAMFILE *) = ea->big_endian ? read_32bitBE : read_32bitLE; @@ -323,7 +323,7 @@ static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE *streamFile } /* 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) { +static int get_ea_1snh_ima_version(STREAMFILE* streamFile, off_t start_offset, const eacs_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; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c index 5dbccfe03..cd7ac3907 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c @@ -4,7 +4,7 @@ #include "../coding/coding.h" #include "ea_eaac_streamfile.h" -/* EAAudioCore formats, EA's current audio middleware */ +/* EAAudioCore (aka SND10) formats, EA's current audio middleware */ #define EAAC_VERSION_V0 0x00 /* SNR/SNS */ #define EAAC_VERSION_V1 0x01 /* SPS */ @@ -105,12 +105,12 @@ fail: /* EA ABK - ABK header seems to be same as in the old games but the sound table is different and it contains SNR/SNS sounds instead */ VGMSTREAM * init_vgmstream_ea_abk_eaac(STREAMFILE* sf) { int is_dupe, total_sounds = 0, target_stream = sf->stream_index; - off_t bnk_offset, header_table_offset, base_offset, unk_struct_offset, table_offset, snd_entry_offset, ast_offset; - off_t num_entries_off, base_offset_off, entries_off, sound_table_offset_off; - uint32_t i, j, k, num_sounds, total_sound_tables; - uint16_t num_tables, bnk_index, bnk_target_index; - uint8_t num_entries, extra_entries; - off_t sound_table_offsets[0x2000]; + off_t bnk_offset, modules_table, module_data, player_offset, samples_table, entry_offset, ast_offset; + off_t cfg_num_players_off, cfg_module_data_off, cfg_module_entry_size, cfg_samples_table_off; + uint32_t i, j, k, num_sounds, num_sample_tables; + uint16_t num_modules, bnk_index, bnk_target_index; + uint8_t num_players; + off_t sample_tables[0x400]; VGMSTREAM *vgmstream; int32_t(*read_32bit)(off_t, STREAMFILE*); int16_t(*read_16bit)(off_t, STREAMFILE*); @@ -135,10 +135,10 @@ VGMSTREAM * init_vgmstream_ea_abk_eaac(STREAMFILE* sf) { if (target_stream < 0) goto fail; - num_tables = read_16bit(0x0A, sf); - header_table_offset = read_32bit(0x1C, sf); + num_modules = read_16bit(0x0A, sf); + modules_table = read_32bit(0x1C, sf); bnk_offset = read_32bit(0x20, sf); - total_sound_tables = 0; + num_sample_tables = 0; bnk_target_index = 0xFFFF; ast_offset = 0; @@ -146,36 +146,35 @@ VGMSTREAM * init_vgmstream_ea_abk_eaac(STREAMFILE* sf) { goto fail; /* set up some common values */ - if (header_table_offset == 0x5C) { + if (modules_table == 0x5C) { /* the usual variant */ - num_entries_off = 0x24; - base_offset_off = 0x2C; - entries_off = 0x3C; - sound_table_offset_off = 0x04; - } else if (header_table_offset == 0x78) { + cfg_num_players_off = 0x24; + cfg_module_data_off = 0x2C; + cfg_module_entry_size = 0x3C; + cfg_samples_table_off = 0x04; + } else if (modules_table == 0x78) { /* FIFA 08 has a bunch of extra zeroes all over the place, don't know what's up with that */ - num_entries_off = 0x40; - base_offset_off = 0x54; - entries_off = 0x68; - sound_table_offset_off = 0x0C; + cfg_num_players_off = 0x40; + cfg_module_data_off = 0x54; + cfg_module_entry_size = 0x68; + cfg_samples_table_off = 0x0C; } else { goto fail; } - for (i = 0; i < num_tables; i++) { - num_entries = read_8bit(header_table_offset + num_entries_off, sf); - extra_entries = read_8bit(header_table_offset + num_entries_off + 0x03, sf); - base_offset = read_32bit(header_table_offset + base_offset_off, sf); - if (num_entries == 0xff) goto fail; /* EOF read */ + for (i = 0; i < num_modules; i++) { + num_players = read_8bit(modules_table + cfg_num_players_off, sf); + module_data = read_32bit(modules_table + cfg_module_data_off, sf); + if (num_players == 0xff) goto fail; /* EOF read */ - for (j = 0; j < num_entries; j++) { - unk_struct_offset = read_32bit(header_table_offset + entries_off + 0x04 * j, sf); - table_offset = read_32bit(base_offset + unk_struct_offset + sound_table_offset_off, sf); + for (j = 0; j < num_players; j++) { + player_offset = read_32bit(modules_table + cfg_module_entry_size + 0x04 * j, sf); + samples_table = read_32bit(module_data + player_offset + cfg_samples_table_off, sf); - /* For some reason, there are duplicate entries pointing at the same sound tables */ + /* multiple players may point at the same sound table */ is_dupe = 0; - for (k = 0; k < total_sound_tables; k++) { - if (table_offset == sound_table_offsets[k]) { + for (k = 0; k < num_sample_tables; k++) { + if (samples_table == sample_tables[k]) { is_dupe = 1; break; } @@ -184,17 +183,17 @@ VGMSTREAM * init_vgmstream_ea_abk_eaac(STREAMFILE* sf) { if (is_dupe) continue; - sound_table_offsets[total_sound_tables++] = table_offset; - num_sounds = read_32bit(table_offset, sf); + sample_tables[num_sample_tables++] = samples_table; + num_sounds = read_32bit(samples_table, sf); if (num_sounds == 0xffffffff) goto fail; /* EOF read */ for (k = 0; k < num_sounds; k++) { /* 0x00: sound index */ - /* 0x02: ??? */ - /* 0x04: ??? */ + /* 0x02: priority */ + /* 0x03: azimuth */ /* 0x08: streamed data offset */ - snd_entry_offset = table_offset + 0x04 + 0x0C * k; - bnk_index = read_16bit(snd_entry_offset + 0x00, sf); + entry_offset = samples_table + 0x04 + 0x0C * k; + bnk_index = read_16bit(entry_offset + 0x00, sf); /* some of these are dummies */ if (bnk_index == 0xFFFF) @@ -203,12 +202,14 @@ VGMSTREAM * init_vgmstream_ea_abk_eaac(STREAMFILE* sf) { total_sounds++; if (target_stream == total_sounds) { bnk_target_index = bnk_index; - ast_offset = read_32bit(snd_entry_offset + 0x08, sf); + ast_offset = read_32bit(entry_offset + 0x08, sf); } } } - header_table_offset += entries_off + num_entries * 0x04 + extra_entries * 0x04; + /* skip class controllers */ + num_players += read_8bit(modules_table + cfg_num_players_off + 0x03, sf); + modules_table += cfg_module_entry_size + num_players * 0x04; } if (bnk_target_index == 0xFFFF || ast_offset == 0) @@ -234,7 +235,9 @@ static VGMSTREAM * parse_s10a_header(STREAMFILE* sf, off_t offset, uint16_t targ /* header is always big endian */ /* 0x00: header magic */ - /* 0x04: zero */ + /* 0x04: version */ + /* 0x05: padding */ + /* 0x06: serial number */ /* 0x08: number of files */ /* 0x0C: offsets table */ if (read_32bitBE(offset + 0x00, sf) != 0x53313041) /* "S10A" */ @@ -380,13 +383,14 @@ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE* sf) { int32_t(*read_32bit)(off_t, STREAMFILE*); /* 0x00: ID */ - /* 0x02: userdata size */ + /* 0x02: parameters (userdata size, ...) */ /* 0x03: number of files */ /* 0x04: sub-ID (used for different police voices in NFS games) */ - /* 0x08: alt number of files? */ - /* 0x09: zero */ - /* 0x0A: related to size? */ - /* 0x0C: zero */ + /* 0x08: sample repeat (alt number of files?) */ + /* 0x09: block size (always zero?) */ + /* 0x0A: number of blocks (related to size?) */ + /* 0x0C: number of sub-banks (always zero?) */ + /* 0x0E: padding */ /* 0x10: table start */ if (!check_extensions(sf, "hdr")) @@ -508,9 +512,9 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) {"world.mpf", "World_Stream.mus"}, {"FreSkate.mpf", "track.mus,ram.mus"}, /* Skate It */ {"nsf_sing.mpf", "track_main.mus"}, /* Need for Speed: Nitro */ - {"nsf_wii.mpf", "Track.mus"}, /* Need for Speed: Nitro */ + {"nsf_wii.mpf", "Track.mus"}, {"ssx_fe.mpf", "stream_1.mus,stream_2.mus"}, /* SSX 2012 */ - {"ssxdd.mpf", "main_trk.mus," /* SSX 2012 */ + {"ssxdd.mpf", "main_trk.mus," "trick_alaska0.mus," "trick_rockies0.mus," "trick_pata0.mus," @@ -522,16 +526,20 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) "trick_alps0.mus," "trick_lhotse0.mus"} }; - STREAMFILE *musFile = NULL; + STREAMFILE *sf_mus = NULL; char file_name[PATH_LIMIT]; int pair_count = (sizeof(mapfile_pairs) / sizeof(mapfile_pairs[0])); int i, j; size_t file_len, map_len; + /* try parsing TXTM if present */ + sf_mus = read_filemap_file(sf, track); + if (sf_mus) return sf_mus; + /* if loading the first track, try opening MUS with the same name first (most common scenario) */ if (track == 0) { - musFile = open_streamfile_by_ext(sf, "mus"); - if (musFile) return musFile; + sf_mus = open_streamfile_by_ext(sf, "mus"); + if (sf_mus) return sf_mus; } get_streamfile_filename(sf, file_name, PATH_LIMIT); @@ -575,8 +583,8 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) strncpy(file_name, pch, PATH_LIMIT - 1); } - musFile = open_streamfile_by_filename(sf, file_name); - if (musFile) return musFile; + sf_mus = open_streamfile_by_filename(sf, file_name); + if (sf_mus) return sf_mus; get_streamfile_filename(sf, file_name, PATH_LIMIT); /* reset for next loop */ } @@ -587,8 +595,8 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) char *mod_name = strchr(file_name, '+'); if (mod_name) { mod_name[0] = '\0'; - musFile = open_streamfile_by_filename(sf, file_name); - if (musFile) return musFile; + sf_mus = open_streamfile_by_filename(sf, file_name); + if (sf_mus) return sf_mus; } } @@ -598,7 +606,7 @@ static STREAMFILE *open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) /* EA MPF/MUS combo - used in older 7th gen games for storing interactive music */ VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) { - uint32_t num_tracks, track_start, track_hash, mus_sounds, mus_stream = 0; + uint32_t num_tracks, track_start, track_hash = 0, mus_sounds, mus_stream = 0; uint8_t version, sub_version; off_t tracks_table, samples_table, eof_offset, table_offset, entry_offset, snr_offset, sns_offset; int32_t(*read_32bit)(off_t, STREAMFILE*); @@ -639,6 +647,9 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) { entry_offset = read_32bit(tracks_table + i * 0x04, sf) * 0x04; track_start = read_32bit(entry_offset + 0x00, sf); + if (track_start == 0 && i != 0) + continue; /* empty track */ + if (track_start <= target_stream - 1) { track_hash = read_32bitBE(entry_offset + 0x08, sf); is_ram = (track_hash == 0xF1F1F1F1); @@ -649,8 +660,6 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus_eaac(STREAMFILE* sf) { goto fail; track_hash = read_32bitBE(entry_offset + 0x14, sf); - if (track_hash == 0xF1F1F1F1) - continue; /* empty track */ } else { if (read_32bitBE(entry_offset + 0x0c, sf) == 0x00) goto fail; @@ -728,31 +737,35 @@ fail: return NULL; } -/* EA TMX - used for engine sounds in NFS games (2007-present) */ +/* EA TMX - used for engine sounds in NFS games (2007-2011) */ VGMSTREAM * init_vgmstream_ea_tmx(STREAMFILE* sf) { - uint32_t num_sounds, sound_type; - off_t table_offset, data_offset, entry_offset, sound_offset; + uint32_t num_sounds, sound_type, table_offset, data_offset, entry_offset, sound_offset; VGMSTREAM *vgmstream = NULL; int target_stream = sf->stream_index; + uint32_t(*read_u32)(off_t, STREAMFILE *); if (!check_extensions(sf, "tmx")) goto fail; - /* always little endian */ - if (read_32bitLE(0x0c, sf) != 0x30303031) /* "0001" */ + if (read_u32be(0x0c, sf) == 0x30303031) { /* "0001" */ + read_u32 = read_u32be; + } else if (read_u32le(0x0c, sf) == 0x30303031) { /* "1000" */ + read_u32 = read_u32le; + } else { goto fail; + } - num_sounds = read_32bitLE(0x20, sf); - table_offset = read_32bitLE(0x58, sf); - data_offset = read_32bitLE(0x5c, sf); + num_sounds = read_u32(0x20, sf); + table_offset = read_u32(0x58, sf); + data_offset = read_u32(0x5c, sf); if (target_stream == 0) target_stream = 1; if (target_stream < 0 || num_sounds == 0 || target_stream > num_sounds) goto fail; entry_offset = table_offset + (target_stream - 1) * 0x24; - sound_type = read_32bitLE(entry_offset + 0x00, sf); - sound_offset = read_32bitLE(entry_offset + 0x08, sf) + data_offset; + sound_type = read_u32(entry_offset + 0x00, sf); + sound_offset = read_u32(entry_offset + 0x08, sf) + data_offset; switch (sound_type) { case 0x47494E20: /* "GIN " */ @@ -1017,7 +1030,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM eaac.channel_config = (header1 >> 18) & 0x3F; /* 6 bits */ eaac.sample_rate = (header1 >> 0) & 0x03FFFF; /* 18 bits */ eaac.type = (header2 >> 30) & 0x03; /* 2 bits */ - eaac.loop_flag = (header2 >> 29) & 0x01; /* 1 bits */ + eaac.loop_flag = (header2 >> 29) & 0x01; /* 1 bit */ eaac.num_samples = (header2 >> 0) & 0x1FFFFFFF; /* 29 bits */ /* rest is optional, depends on used flags and codec (handled below) */ @@ -1027,7 +1040,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM /* EA 6ch channel mapping is L C R BL BR LFE, but may use stereo layers for dynamic music * instead, so we can't re-map automatically (use TXTP) */ - /* V0: SNR+SNS, V1: SPR+SPS (no apparent differences, other than block flags) */ + /* V0: SNR+SNS, V1: SPH+SPS (no apparent differences, other than block flags) */ if (eaac.version != EAAC_VERSION_V0 && eaac.version != EAAC_VERSION_V1) { VGM_LOG("EA EAAC: unknown version\n"); goto fail; @@ -1045,6 +1058,12 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM goto fail; } + if (eaac.version == EAAC_VERSION_V1 && eaac.type != EAAC_TYPE_STREAM) { + /* should never happen */ + VGM_LOG("EA EAAC: bad stream type for version %x\n", eaac.version); + goto fail; + } + /* Non-streamed sounds are stored as a single block (may not set block end flags) */ eaac.streamed = (eaac.type != EAAC_TYPE_RAM); @@ -1081,12 +1100,13 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM } break; case EAAC_TYPE_GIGASAMPLE: /* rarely seen [Def Jam Icon (X360)] */ - if (eaac.loop_flag) { - VGM_LOG("EAAC: Looped gigasample found.\n"); - goto fail; - } header_size += 0x04; - eaac.prefetch_samples = read_32bitBE(header_offset + 0x08, sf_head); + eaac.prefetch_samples = read_32bitBE(header_offset + eaac.loop_flag ? 0x0c : 0x08, sf_head); + + if (eaac.loop_flag && eaac.loop_start >= eaac.prefetch_samples) { + header_size += 0x04; + eaac.loop_offset = read_32bitBE(header_offset + 0x10, sf_head); + } break; } @@ -1114,7 +1134,20 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM /* SNR+SNS are separate so offsets are relative to the data start * (first .SNS block, or extra data before the .SNS block in case of .SNU) * SPS have headers+data together so offsets are relative to the file start [ex. FIFA 18 (PC)] */ - if (eaac.version == EAAC_VERSION_V1) { + if (eaac.version == EAAC_VERSION_V0) { + if (eaac.prefetch_samples != 0) { + if (eaac.loop_start == 0) { + /* loop from the beginning */ + eaac.loop_offset = 0x00; + } else if (eaac.loop_start < eaac.prefetch_samples) { + /* loop from the second RAM block */ + eaac.loop_offset = read_32bitBE(eaac.prefetch_offset, sf_head) & 0x00FFFFFF; + } else { + /* loop from offset within SNS */ + eaac.loop_offset += read_32bitBE(eaac.prefetch_offset, sf_head) & 0x00FFFFFF; + } + } + } else { eaac.loop_offset -= header_block_size; } } else if (eaac.loop_start > 0) { @@ -1340,13 +1373,15 @@ static size_t calculate_eaac_size(STREAMFILE *sf, eaac_header *ea, uint32_t num_ stream_size += block_size; block_offset += block_size; - if (is_ram) { - /* RAM data only consists of one block (two for looped sounds) */ - if (ea->loop_start > 0 && !looped) looped = 1; - else break; - } else if (ea->version == EAAC_VERSION_V0 && block_id == EAAC_BLOCKID0_END) { - if (ea->loop_offset > 0 && !looped) looped = 1; - else break; + if (ea->version == EAAC_VERSION_V0) { + if (is_ram) { + /* RAM data only consists of one block (two for looped sounds) */ + if (ea->loop_start > 0 && ea->loop_start < num_samples && !looped) looped = 1; + else break; + } else if (block_id == EAAC_BLOCKID0_END) { + if (ea->loop_offset > 0 && ea->loop_start >= ea->prefetch_samples && !looped) looped = 1; + else break; + } } } @@ -1425,13 +1460,7 @@ static STREAMFILE *setup_eaac_streamfile(eaac_header *ea, STREAMFILE* sf_head, S break; } } else { - if (ea->type == EAAC_TYPE_GIGASAMPLE) { - /* not seen so far, need samples */ - VGM_LOG("EAAC: Found SPS gigasample\n"); - goto fail; - } - - data_size = calculate_eaac_size(sf_head, ea, ea->num_samples, ea->stream_offset, ea->type == EAAC_TYPE_RAM); + data_size = calculate_eaac_size(sf_head, ea, ea->num_samples, ea->stream_offset, 0); if (data_size == 0) goto fail; new_sf = open_wrap_streamfile(sf_head); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c index 96a0081ee..75fb332b8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c @@ -5,51 +5,63 @@ /* header version */ #define EA_VERSION_NONE -1 -#define EA_VERSION_V0 0x00 /* ~early PC (when codec1 was used) */ -#define EA_VERSION_V1 0x01 /* ~PC */ -#define EA_VERSION_V2 0x02 /* ~PS1 */ -#define EA_VERSION_V3 0x03 /* ~PS2 */ +#define EA_VERSION_V0 0x00 /* ~early PC (when codec1 was used) */ +#define EA_VERSION_V1 0x01 /* ~PC */ +#define EA_VERSION_V2 0x02 /* ~PS1 */ +#define EA_VERSION_V3 0x03 /* ~PS2 */ /* platform constants (unassigned values seem internal only) */ -#define EA_PLATFORM_GENERIC -1 /* typically Wii/X360/PS3/videos */ #define EA_PLATFORM_PC 0x00 #define EA_PLATFORM_PSX 0x01 #define EA_PLATFORM_N64 0x02 #define EA_PLATFORM_MAC 0x03 #define EA_PLATFORM_SAT 0x04 #define EA_PLATFORM_PS2 0x05 -#define EA_PLATFORM_GC_WII 0x06 +#define EA_PLATFORM_GC 0x06 /* also used on Wii */ #define EA_PLATFORM_XBOX 0x07 +#define EA_PLATFORM_GENERIC 0x08 /* typically Wii/X360/PS3/videos */ #define EA_PLATFORM_X360 0x09 #define EA_PLATFORM_PSP 0x0A -#define EA_PLATFORM_PS3 0x0E /* very rare [Need for Speed: Carbon (PS3)] */ +#define EA_PLATFORM_PS3 0x0E /* very rare [Need for Speed: Carbon (PS3)] */ +#define EA_PLATFORM_WII 0x10 #define EA_PLATFORM_3DS 0x14 /* codec constants (undefined are probably reserved, ie.- sx.exe encodes PCM24/DVI but no platform decodes them) */ /* CODEC1 values were used early, then they migrated to CODEC2 values */ #define EA_CODEC1_NONE -1 #define EA_CODEC1_PCM 0x00 -#define EA_CODEC1_VAG 0x01 // unsure +#define EA_CODEC1_VAG 0x01 /* unsure */ #define EA_CODEC1_EAXA 0x07 #define EA_CODEC1_MT10 0x09 #define EA_CODEC1_N64 0x64 /* unknown but probably before MT10 */ #define EA_CODEC2_NONE -1 +#define EA_CODEC2_S16LE_INT 0x00 +#define EA_CODEC2_S16BE_INT 0x01 +#define EA_CODEC2_S8_INT 0x02 +#define EA_CODEC2_EAXA_INT 0x03 #define EA_CODEC2_MT10 0x04 #define EA_CODEC2_VAG 0x05 +#define EA_CODEC2_N64 0x06 #define EA_CODEC2_S16BE 0x07 #define EA_CODEC2_S16LE 0x08 #define EA_CODEC2_S8 0x09 #define EA_CODEC2_EAXA 0x0A +//#define EA_CODEC2_U8_INT 0x0B /* not used */ +//#define EA_CODEC2_CDXA 0x0C /* not used */ +//#define EA_CODEC2_IMA 0x0D /* not used */ +//#define EA_CODEC2_LAYER1 0x0E /* not used */ #define EA_CODEC2_LAYER2 0x0F -#define EA_CODEC2_LAYER3 0x10 +#define EA_CODEC2_LAYER3 0x10 /* not seen so far but may be used somewhere */ #define EA_CODEC2_GCADPCM 0x12 +//#define EA_CODEC2_S24LE_INT 0x13 /* not used */ #define EA_CODEC2_XBOXADPCM 0x14 +//#define EA_CODEC2_S24BE_INT 0x15 /* not used */ #define EA_CODEC2_MT5 0x16 #define EA_CODEC2_EALAYER3 0x17 +//#define EA_CODEC2_ATRAC3 0x1A /* not seen so far */ #define EA_CODEC2_ATRAC3PLUS 0x1B -#define EA_CODEC2_N64 0x64 /* unknown but probably before MT10 */ /* Block headers, SCxy - where x is block ID and y is endianness flag (always 'l'?) */ #define EA_BLOCKID_HEADER 0x5343486C /* "SCHl" */ @@ -105,6 +117,7 @@ typedef struct { int big_endian; int loop_flag; int codec_config; + int use_pcm_blocks; size_t stream_size; } ea_header; @@ -125,6 +138,7 @@ VGMSTREAM* init_vgmstream_ea_schl(STREAMFILE* sf) { * .asf: ~early (audio stream file?) [ex. Need for Speed II (PC)] * .lasf: fake for plugins * .str: ~early [ex. FIFA 98 (PS1), FIFA 2002 (PS1)] + * .chk: ~early [ex. NBA Live 98 (PS1)] * .eam: ~mid? * .exa: ~mid [ex. 007 - From Russia with Love] * .sng: ~late (FIFA games) @@ -138,7 +152,7 @@ VGMSTREAM* init_vgmstream_ea_schl(STREAMFILE* sf) { * .gsf: 007 - Everything or Nothing (GC) * .mus: map/mpf+mus only? * (extensionless): SSX (PS2) (inside .big) */ - if (!check_extensions(sf,"asf,lasf,str,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,mus,")) + if (!check_extensions(sf,"asf,lasf,str,chk,eam,exa,sng,aud,sx,xa,strm,stm,hab,xsf,gsf,mus,")) goto fail; /* check header */ @@ -287,11 +301,11 @@ fail: /* streamed assets are stored externally in AST file (mostly seen in earlier 6th-gen games) */ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE* sf) { int bnk_target_stream, is_dupe, total_sounds = 0, target_stream = sf->stream_index; - off_t bnk_offset, header_table_offset, base_offset, value_offset, table_offset, entry_offset, target_entry_offset, schl_offset, schl_loop_offset; - uint32_t i, j, k, num_sounds, total_sound_tables; - uint16_t num_tables; - uint8_t sound_type, num_entries; - off_t sound_table_offsets[0x2000]; + off_t bnk_offset, modules_table, module_data, player_offset, samples_table, entry_offset, target_entry_offset, schl_offset, schl_loop_offset; + uint32_t i, j, k, num_sounds, num_sample_tables; + uint16_t num_modules; + uint8_t sound_type, num_players; + off_t sample_tables[0x400]; STREAMFILE * astData = NULL; VGMSTREAM * vgmstream = NULL; segmented_layout_data *data_s = NULL; @@ -318,11 +332,11 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE* sf) { if (target_stream < 0) goto fail; - num_tables = read_16bit(0x0A, sf); - header_table_offset = read_32bit(0x1C, sf); + num_modules = read_16bit(0x0A, sf); + modules_table = read_32bit(0x1C, sf); bnk_offset = read_32bit(0x20, sf); target_entry_offset = 0; - total_sound_tables = 0; + num_sample_tables = 0; /* check to avoid clashing with the newer ABK format */ if (bnk_offset && @@ -330,19 +344,19 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE* sf) { read_32bitBE(bnk_offset, sf) != EA_BNK_HEADER_BE) goto fail; - for (i = 0; i < num_tables; i++) { - num_entries = read_8bit(header_table_offset + 0x24, sf); - base_offset = read_32bit(header_table_offset + 0x2C, sf); - if (num_entries == 0xff) goto fail; /* EOF read */ + for (i = 0; i < num_modules; i++) { + num_players = read_8bit(modules_table + 0x24, sf); + module_data = read_32bit(modules_table + 0x2C, sf); + if (num_players == 0xff) goto fail; /* EOF read */ - for (j = 0; j < num_entries; j++) { - value_offset = read_32bit(header_table_offset + 0x3C + 0x04 * j, sf); - table_offset = read_32bit(base_offset + value_offset + 0x04, sf); + for (j = 0; j < num_players; j++) { + player_offset = read_32bit(modules_table + 0x3C + 0x04 * j, sf); + samples_table = read_32bit(module_data + player_offset + 0x04, sf); - /* For some reason, there are duplicate entries pointing at the same sound tables */ + /* multiple players may point at the same sound table */ is_dupe = 0; - for (k = 0; k < total_sound_tables; k++) { - if (table_offset == sound_table_offsets[k]) { + for (k = 0; k < num_sample_tables; k++) { + if (samples_table == sample_tables[k]) { is_dupe = 1; break; } @@ -351,12 +365,12 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE* sf) { if (is_dupe) continue; - sound_table_offsets[total_sound_tables++] = table_offset; - num_sounds = read_32bit(table_offset, sf); + sample_tables[num_sample_tables++] = samples_table; + num_sounds = read_32bit(samples_table, sf); if (num_sounds == 0xffffffff) goto fail; /* EOF read */ for (k = 0; k < num_sounds; k++) { - entry_offset = table_offset + 0x04 + 0x0C * k; + entry_offset = samples_table + 0x04 + 0x0C * k; sound_type = read_8bit(entry_offset + 0x00, sf); /* some of these are dummies pointing at sound 0 in BNK */ @@ -369,16 +383,17 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE* sf) { } } - /* there can be another set of values, don't know what they mean */ - num_entries += read_8bit(header_table_offset + 0x27, sf); - header_table_offset += 0x3C + num_entries * 0x04; + /* skip class controllers */ + num_players += read_8bit(modules_table + 0x27, sf); + modules_table += 0x3C + num_players * 0x04; } if (target_entry_offset == 0) goto fail; /* 0x00: type (0x00 - normal, 0x01 - streamed, 0x02 - streamed looped) */ - /* 0x01: ??? */ + /* 0x01: priority */ + /* 0x02: padding */ /* 0x04: index for normal sounds, offset for streamed sounds */ /* 0x08: loop offset for streamed sounds */ sound_type = read_8bit(target_entry_offset + 0x00, sf); @@ -439,18 +454,9 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE* sf) { goto fail; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(data_s->segments[0]->channels, 1); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = data_s->segments[0]->sample_rate; - vgmstream->num_samples = data_s->segments[0]->num_samples + data_s->segments[1]->num_samples; - vgmstream->loop_start_sample = data_s->segments[0]->num_samples; - vgmstream->loop_end_sample = vgmstream->num_samples; - - vgmstream->meta_type = meta_EA_SCHL; - vgmstream->coding_type = data_s->segments[0]->coding_type; - vgmstream->layout_type = layout_segmented; - vgmstream->layout_data = data_s; + vgmstream = allocate_segmented_vgmstream(data_s, 1, 1, 1); + if (!vgmstream) + goto fail; break; default: @@ -562,10 +568,11 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat_v2(STREAMFILE* sf) { /* 0x02: parameters (userdata size, ...) */ /* 0x03: number of files */ /* 0x04: sub-ID (used for different police voices in NFS games) */ - /* 0x08: alt number of files? */ - /* 0x09: offset mult */ - /* 0x0a: DAT size divided by offset mult */ - /* 0x0c: zero */ + /* 0x08: sample repeat (alt number of files?) */ + /* 0x09: block size (offset multiplier) */ + /* 0x0A: number of blocks (DAT size divided by block size) */ + /* 0x0C: number of sub-banks (always zero?) */ + /* 0x0E: padding */ /* 0x10: table start */ /* no nice way to validate these so we do what we can */ @@ -627,13 +634,8 @@ static STREAMFILE* open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) {"mus_ctrl.mpf", "mus_str.mus"}, /* GoldenEye - Rogue Agent (others) */ {"AKA_Mus.mpf", "Track.mus"}, /* Boogie */ {"SSX4FE.mpf", "TrackFE.mus"}, /* SSX On Tour */ - {"SSX4Path.mpf", "Track.mus"}, /* SSX On Tour */ + {"SSX4Path.mpf", "Track.mus"}, {"SSX4.mpf", "moments0.mus,main.mus,load_loop0.mus"}, /* SSX Blur */ - {"willow.mpf", "willow.mus,willow_o.mus"}, /* Harry Potter and the Chamber of Secrets */ - {"exterior.mpf", "exterior.mus,ext_o.mus"}, /* Harry Potter and the Chamber of Secrets */ - {"Peak1Amb.mpf", "Peak1_Strm.mus,Peak1_Ovr0.mus"}, /* SSX 3 */ - {"Peak2Amb.mpf", "Peak2_Strm.mus,Peak2_Ovr0.mus"}, - {"Peak3Amb.mpf", "Peak3_Strm.mus,Peak3_Ovr0.mus"}, {"*.mpf", "*_main.mus"}, /* 007 - Everything or Nothing */ /* TODO: need better wildcard support * NSF2: @@ -656,6 +658,10 @@ static STREAMFILE* open_mapfile_pair(STREAMFILE* sf, int track, int num_tracks) int i, j; size_t file_len, map_len; + /* try parsing TXTM if present */ + sf_mus = read_filemap_file(sf, track); + if (sf_mus) return sf_mus; + /* if loading the first track, try opening MUS with the same name first (most common scenario) */ if (track == 0) { sf_mus = open_streamfile_by_ext(sf, "mus"); @@ -782,6 +788,7 @@ VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE* sf) { goto fail; vgmstream->num_streams = num_sounds; + get_streamfile_filename(sf_mus, vgmstream->stream_name, STREAM_NAME_SIZE); close_streamfile(sf_mus); return vgmstream; @@ -794,12 +801,12 @@ fail: VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; STREAMFILE* sf_mus = NULL; - off_t tracks_table, samples_table, section_offset, entry_offset, eof_offset, off_mult, sound_offset; - uint32_t track_start, track_hash = 0; + segmented_layout_data *data_s = NULL; + uint32_t track_start, track_end = 0, track_hash = 0, tracks_table, samples_table = 0, section_offset, entry_offset = 0, eof_offset = 0, off_mult, sound_offset; uint16_t num_nodes; - uint8_t version, sub_version, num_tracks, num_sections, num_events, num_routers, num_vars, subentry_num; - int32_t(*read_32bit)(off_t, STREAMFILE*); - int16_t(*read_16bit)(off_t, STREAMFILE*); + uint8_t version, sub_version, num_tracks, num_sections, num_events, num_routers, num_vars, subentry_num = 0; + uint32_t(*read_u32)(off_t, STREAMFILE*); + uint16_t(*read_u16)(off_t, STREAMFILE*); int i; int target_stream = sf->stream_index, total_streams, big_endian, is_bnk = 0; @@ -809,29 +816,29 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { /* detect endianness */ if (read_32bitBE(0x00, sf) == 0x50464478) { /* "PFDx" */ - read_32bit = read_32bitBE; - read_16bit = read_16bitBE; + read_u32 = read_u32be; + read_u16 = read_u16be; big_endian = 1; } else if (read_32bitLE(0x00, sf) == 0x50464478) { /* "xDFP" */ - read_32bit = read_32bitLE; - read_16bit = read_16bitLE; + read_u32 = read_u32le; + read_u16 = read_u16le; big_endian = 0; } else { goto fail; } - version = read_8bit(0x04, sf); - sub_version = read_8bit(0x05, sf); + version = read_u8(0x04, sf); + sub_version = read_u8(0x05, sf); if (version < 3 || version > 5) goto fail; if (version == 5 && sub_version > 2) goto fail; /* newer version using SNR/SNS */ - num_tracks = read_8bit(0x0d, sf); - num_sections = read_8bit(0x0e, sf); - num_events = read_8bit(0x0f, sf); - num_routers = read_8bit(0x10, sf); - num_vars = read_8bit(0x11, sf); - num_nodes = read_16bit(0x12, sf); + num_tracks = read_u8(0x0d, sf); + num_sections = read_u8(0x0e, sf); + num_events = read_u8(0x0f, sf); + num_routers = read_u8(0x10, sf); + num_vars = read_u8(0x11, sf); + num_nodes = read_u16(0x12, sf); /* HACK: number of sub-entries for nodes and events is stored in bitstreams that are different in LE and BE */ /* I can't figure it out, so let's just use a workaround for now */ @@ -841,109 +848,123 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { if (version == 3) /* SSX Tricky (v3.1), Harry Potter and the Chamber of Secrets (v3.4) */ { /* we need to go through all the sections to get to the samples table */ + if (sub_version != 1 && sub_version != 2 && sub_version != 4) + goto fail; + /* get the last entry offset */ section_offset = 0x24; - entry_offset = (uint16_t)read_16bit(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04; - if (sub_version == 1) { - subentry_num = read_8bit(entry_offset + 0x0b, sf); + entry_offset = read_u16(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04; + if (sub_version == 1 || sub_version == 2) { + subentry_num = read_u8(entry_offset + 0x0b, sf); } else if (sub_version == 4) { if (big_endian) { - subentry_num = (read_32bitBE(entry_offset + 0x04, sf) >> 19) & 0xFF; + subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 19) & 0xFF; } else { - subentry_num = (read_32bitBE(entry_offset + 0x04, sf) >> 16) & 0xFF; + subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 16) & 0xFF; } - } else { - goto fail; } section_offset = entry_offset + 0x0c + subentry_num * 0x04; section_offset += align_size_to_block(num_events * num_tracks * num_sections, 0x04); section_offset += num_routers * 0x04; section_offset += num_vars * 0x04; - tracks_table = read_32bit(section_offset, sf) * 0x04; - samples_table = tracks_table + (num_tracks + 1) * 0x04; + + tracks_table = read_u32(section_offset, sf) * 0x04; + if (sub_version == 1 || sub_version == 2) + samples_table = tracks_table + num_tracks * 0x04; + else if (sub_version == 4) + samples_table = tracks_table + (num_tracks + 1) * 0x04; + if (sub_version == 1 || sub_version == 2) + eof_offset = get_streamfile_size(sf); + else if (sub_version == 4) + eof_offset = read_u32(tracks_table + num_tracks * 0x04, sf) * 0x04; + total_streams = (eof_offset - samples_table) / 0x08; + off_mult = 0x04; + + track_start = total_streams; for (i = num_tracks - 1; i >= 0; i--) { - track_start = read_32bit(tracks_table + i * 0x04, sf) * 0x04; + track_end = track_start; + track_start = read_u32(tracks_table + i * 0x04, sf) * 0x04; track_start = (track_start - samples_table) / 0x08; if (track_start <= target_stream - 1) break; } - - eof_offset = read_32bit(tracks_table + num_tracks * 0x04, sf) * 0x04; - total_streams = (eof_offset - samples_table) / 0x08; - off_mult = 0x04; } else if (version == 4) { /* Need for Speed: Underground 2, SSX 3, Harry Potter and the Prisoner of Azkaban */ /* we need to go through all the sections to get to the samples table */ /* get the last entry offset */ section_offset = 0x20; - entry_offset = (uint16_t)read_16bit(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04; + entry_offset = read_u16(section_offset + (num_nodes - 1) * 0x02, sf) * 0x04; if (big_endian) { - subentry_num = (read_32bitBE(entry_offset + 0x04, sf) >> 15) & 0xFF; + subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 15) & 0xFF; } else { - subentry_num = (read_32bitBE(entry_offset + 0x04, sf) >> 20) & 0xFF; + subentry_num = (read_u32be(entry_offset + 0x04, sf) >> 20) & 0xFF; } section_offset = entry_offset + 0x10 + subentry_num * 0x04; /* get the last entry offset */ - entry_offset = (uint16_t)read_16bit(section_offset + (num_events - 1) * 0x02, sf) * 0x04; + entry_offset = read_u16(section_offset + (num_events - 1) * 0x02, sf) * 0x04; if (big_endian) { - subentry_num = (read_32bitBE(entry_offset + 0x0c, sf) >> 10) & 0xFF; + subentry_num = (read_u32be(entry_offset + 0x0c, sf) >> 10) & 0xFF; } else { - subentry_num = (read_32bitBE(entry_offset + 0x0c, sf) >> 8) & 0xFF; + subentry_num = (read_u32be(entry_offset + 0x0c, sf) >> 8) & 0xFF; } section_offset = entry_offset + 0x10 + subentry_num * 0x10; /* TODO: verify this */ - section_offset = read_32bit(section_offset, sf) * 0x04; + section_offset = read_u32(section_offset, sf) * 0x04; section_offset += num_routers * 0x04; section_offset += num_vars * 0x04; + tracks_table = section_offset; samples_table = tracks_table + (num_tracks + 1) * 0x04; + eof_offset = read_u32(tracks_table + num_tracks * 0x04, sf) * 0x04; + total_streams = (eof_offset - samples_table) / 0x08; + off_mult = 0x80; + + track_start = total_streams; for (i = num_tracks - 1; i >= 0; i--) { - track_start = read_32bit(tracks_table + i * 0x04, sf) * 0x04; + track_end = track_start; + track_start = read_u32(tracks_table + i * 0x04, sf) * 0x04; track_start = (track_start - samples_table) / 0x08; if (track_start <= target_stream - 1) break; } - - eof_offset = read_32bit(tracks_table + num_tracks * 0x04, sf) * 0x04; - total_streams = (eof_offset - samples_table) / 0x08; - off_mult = 0x80; } else if (version == 5) { /* Need for Speed: Most Wanted, Need for Speed: Carbon */ - tracks_table = read_32bit(0x2c, sf); - samples_table = read_32bit(0x34, sf); + tracks_table = read_u32(0x2c, sf); + samples_table = read_u32(0x34, sf); + eof_offset = read_u32(0x38, sf); + total_streams = (eof_offset - samples_table) / 0x08; + off_mult = 0x80; + + track_start = total_streams; for (i = num_tracks - 1; i >= 0; i--) { - entry_offset = read_32bit(tracks_table + i * 0x04, sf) * 0x04; - track_start = read_32bit(entry_offset + 0x00, sf); + track_end = track_start; + entry_offset = read_u32(tracks_table + i * 0x04, sf) * 0x04; + track_start = read_u32(entry_offset + 0x00, sf); + + if (track_start == 0 && i != 0) + continue; /* empty track */ if (track_start <= target_stream - 1) { - track_hash = read_32bitBE(entry_offset + 0x08, sf); + track_hash = read_u32be(entry_offset + 0x08, sf); is_bnk = (track_hash == 0xF1F1F1F1); /* checks to distinguish it from SNR/SNS version */ if (is_bnk) { - if (read_32bitBE(entry_offset + 0x0c, sf) == 0x00) + if (read_u32(entry_offset + 0x0c, sf) == 0x00) goto fail; - - track_hash = read_32bitBE(entry_offset + 0x14, sf); - if (track_hash == 0xF1F1F1F1) - continue; /* empty track */ } else { - if (read_32bitBE(entry_offset + 0x0c, sf) != 0x00) + if (read_u32(entry_offset + 0x0c, sf) != 0x00) goto fail; } break; } } - - eof_offset = read_32bit(0x38, sf); - total_streams = (eof_offset - samples_table) / 0x08; - off_mult = 0x80; } else { goto fail; } @@ -956,22 +977,83 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { if (!sf_mus) goto fail; - if (version == 5) { - if (read_32bitBE(0x00, sf_mus) != track_hash) - goto fail; - } else { - is_bnk = (read_32bitBE(0x00, sf_mus) == (big_endian ? EA_BNK_HEADER_BE : EA_BNK_HEADER_LE)); + if (version < 5) { + is_bnk = (read_u32be(0x00, sf_mus) == (big_endian ? EA_BNK_HEADER_BE : EA_BNK_HEADER_LE)); } /* 0x00 - offset/BNK index, 0x04 - duration (in milliseconds) */ + sound_offset = read_u32(samples_table + (target_stream - 1) * 0x08 + 0x00, sf); + if (is_bnk) { - /* TODO: Harry Potter COS appears to reference only the first segments of multi-segment BNK sounds? */ - sound_offset = read_32bit(samples_table + (target_stream - 1) * 0x08 + 0x00, sf); - vgmstream = parse_bnk_header(sf_mus, version < 5 ? 0x00 : 0x100, sound_offset, 1); + /* for some reason, RAM segments are almost always split into multiple sounds (usually 4) */ + off_t bnk_offset = version < 5 ? 0x00 : 0x100; + uint32_t bnk_sound_index = (sound_offset & 0x0000FFFF); + uint32_t bnk_index = (sound_offset & 0xFFFF0000) >> 16; + uint32_t next_entry; + uint32_t bnk_total_sounds = read_u16(bnk_offset + 0x06, sf_mus); + int bnk_segments; + STREAMFILE *sf_bnk = sf_mus; + + if (version == 5 && bnk_index != 0) { + /* HACK: open proper .mus now since open_mapfile_pair doesn't let us adjust the name */ + char filename[PATH_LIMIT], basename[PATH_LIMIT], ext[32]; + int basename_len; + + get_streamfile_basename(sf_mus, basename, PATH_LIMIT); + basename_len = strlen(basename); + get_streamfile_ext(sf_mus, ext, sizeof(ext)); + + /* strip off 0 at the end */ + basename[basename_len - 1] = '\0'; + + /* append bank index to the name */ + snprintf(filename, PATH_LIMIT, "%s%d.%s", basename, bnk_index, ext); + + sf_bnk = open_streamfile_by_filename(sf_mus, filename); + if (!sf_bnk) goto fail; + bnk_total_sounds = read_u16(bnk_offset + 0x06, sf_bnk); + close_streamfile(sf_mus); + sf_mus = sf_bnk; + } + + if (version == 5) { + track_hash = read_u32be(entry_offset + 0x14 + 0x10 * bnk_index, sf); + if (read_u32be(0x00, sf_mus) != track_hash) + goto fail; + } + + /* play until the next entry in MPF track or the end of BNK */ + if (target_stream < track_end) { + next_entry = read_u32(samples_table + (target_stream - 0) * 0x08 + 0x00, sf); + if (((next_entry & 0xFFFF0000) >> 16) == bnk_index) { + bnk_segments = (next_entry & 0x0000FFFF) - bnk_sound_index; + } else { + bnk_segments = bnk_total_sounds - bnk_sound_index; + } + } else { + bnk_segments = bnk_total_sounds - bnk_sound_index; + } + + /* init layout */ + data_s = init_layout_segmented(bnk_segments); + if (!data_s) goto fail; + + for (i = 0; i < bnk_segments; i++) { + data_s->segments[i] = parse_bnk_header(sf_mus, bnk_offset, bnk_sound_index + i, 1); + if (!data_s->segments[i]) goto fail; + } + + /* setup segmented VGMSTREAMs */ + if (!setup_layout_segmented(data_s)) goto fail; + + vgmstream = allocate_segmented_vgmstream(data_s, 0, 0, 0); if (!vgmstream) goto fail; } else { - sound_offset = read_32bit(samples_table + (target_stream - 1) * 0x08 + 0x00, sf) * off_mult; + if (version == 5 && read_u32be(0x00, sf_mus) != track_hash) + goto fail; + + sound_offset *= off_mult;; if (read_32bitBE(sound_offset, sf_mus) != EA_BLOCKID_HEADER) goto fail; @@ -987,6 +1069,8 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE* sf) { fail: close_streamfile(sf_mus); + free_layout_segmented(data_s); + return NULL; } @@ -1142,79 +1226,53 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* vgmstream->codec_config = ea->codec_config; vgmstream->meta_type = is_bnk ? meta_EA_BNK : meta_EA_SCHL; - - if (is_bnk) { - vgmstream->layout_type = layout_none; - - /* BNKs usually have absolute offsets for all channels ("full" interleave) except in some versions */ - if (ea->channels > 1 && ea->codec1 == EA_CODEC1_PCM) { - int interleave = (ea->num_samples * (ea->bps == 8 ? 0x01 : 0x02)); /* full interleave */ - for (i = 0; i < ea->channels; i++) { - ea->offsets[i] = ea->offsets[0] + interleave*i; - } - } - else if (ea->channels > 1 && ea->codec1 == EA_CODEC1_VAG) { - int interleave = (ea->num_samples / 28 * 16); /* full interleave */ - for (i = 0; i < ea->channels; i++) { - ea->offsets[i] = ea->offsets[0] + interleave*i; - } - } - else if (ea->channels > 1 && ea->codec2 == EA_CODEC2_GCADPCM && ea->offsets[0] == ea->offsets[1]) { - /* pcstream+gcadpcm with sx.exe v2, not in flag_value, probably a bug (even with this parts of the wave are off) */ - int interleave = (ea->num_samples / 14 * 8); /* full interleave */ - for (i = 0; i < ea->channels; i++) { - ea->offsets[i] = ea->offsets[0] + interleave*i; - } - } - else if (ea->channels > 1 && ea->codec2 == EA_CODEC2_N64 && ea->offsets[1] == 0) { - uint32_t interleave = ea->flag_value; - for (i = 0; i < ea->channels; i++) { - ea->offsets[i] = ea->offsets[0] + interleave * i; - } - } - } - else { - vgmstream->layout_type = layout_blocked_ea_schl; - } + vgmstream->layout_type = is_bnk ? layout_none : layout_blocked_ea_schl; /* EA usually implements their codecs in all platforms (PS2/WII do EAXA/MT/EALAYER3) and * favors them over platform's natives (ex. EAXA vs VAG/DSP). * Unneeded codecs are removed over time (ex. LAYER3 when EALAYER3 was introduced). */ switch (ea->codec2) { - case EA_CODEC2_EAXA: /* EA-XA, CDXA ADPCM variant */ - if (ea->version == EA_VERSION_V0) { - if (ea->platform != EA_PLATFORM_SAT && ea->channels > 1) - vgmstream->coding_type = coding_EA_XA; /* original version, stereo stream */ - else - vgmstream->coding_type = coding_EA_XA_int; /* interleaved mono streams */ - } - else { /* later revision with PCM blocks and slighty modified decoding */ + case EA_CODEC2_EAXA_INT: /* EA-XA (stereo) */ + vgmstream->coding_type = coding_EA_XA; + break; + + case EA_CODEC2_EAXA: /* EA-XA (split mono) */ + if (!ea->use_pcm_blocks) { + /* original version */ + vgmstream->coding_type = coding_EA_XA_int; + } else { + /* later revision with PCM blocks and slighty modified decoding */ vgmstream->coding_type = coding_EA_XA_V2; } break; - case EA_CODEC2_S8: /* PCM8 */ + case EA_CODEC2_S8_INT: /* PCM8 (interleaved) */ + vgmstream->coding_type = coding_PCM8_int; + break; + + case EA_CODEC2_S16LE_INT: /* PCM16LE (interleaved) */ + case EA_CODEC2_S16BE_INT: /* PCM16BE (interleaved) */ + vgmstream->coding_type = coding_PCM16_int; + break; + + case EA_CODEC2_S8: /* PCM8 (split) */ vgmstream->coding_type = coding_PCM8; break; - case EA_CODEC2_S16BE: /* PCM16BE */ - vgmstream->coding_type = coding_PCM16BE; + case EA_CODEC2_S16LE: /* PCM16LE (split) */ + vgmstream->coding_type = coding_PCM16LE; break; - case EA_CODEC2_S16LE: /* PCM16LE */ - if (ea->version > EA_VERSION_V0) { - vgmstream->coding_type = coding_PCM16LE; - } else { /* Need for Speed III: Hot Pursuit (PC) */ - vgmstream->coding_type = coding_PCM16_int; - } + case EA_CODEC2_S16BE: /* PCM16BE (split) */ + vgmstream->coding_type = coding_PCM16BE; break; case EA_CODEC2_VAG: /* PS-ADPCM */ vgmstream->coding_type = coding_PSX; break; - case EA_CODEC2_XBOXADPCM: /* XBOX IMA (interleaved mono) */ + case EA_CODEC2_XBOXADPCM: /* XBOX IMA (split mono) */ vgmstream->coding_type = coding_XBOX_IMA_int; break; @@ -1235,7 +1293,6 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* case EA_CODEC2_N64: /* VADPCM */ vgmstream->coding_type = coding_VADPCM; - vgmstream->layout_type = layout_none; for (ch = 0; ch < ea->channels; ch++) { int order = read_u32be(ea->coefs[ch] + 0x00, sf); @@ -1254,7 +1311,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* 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(sf, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EA, &cfg); + vgmstream->codec_data = init_mpeg_custom(sf, mpeg_start_offset, &vgmstream->coding_type, ea->channels, MPEG_EA, &cfg); if (!vgmstream->codec_data) goto fail; break; } @@ -1267,23 +1324,14 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* 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(sf, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EAL31, &cfg); + vgmstream->codec_data = init_mpeg_custom(sf, mpeg_start_offset, &vgmstream->coding_type, ea->channels, MPEG_EAL31, &cfg); if (!vgmstream->codec_data) goto fail; break; } #endif case EA_CODEC2_MT10: /* MicroTalk (10:1 compression) */ - case EA_CODEC2_MT5: { /* MicroTalk (5:1 compression) */ - int use_pcm_blocks = 0; - - if (ea->version == EA_VERSION_V3 || (ea->version == EA_VERSION_V2 && - (ea->platform == EA_PLATFORM_PC || - ea->platform == EA_PLATFORM_MAC || - ea->platform == EA_PLATFORM_GENERIC))) { - use_pcm_blocks = 1; - } - + case EA_CODEC2_MT5: /* MicroTalk (5:1 compression) */ /* make relative loops absolute for the decoder */ if (ea->loop_flag) { for (i = 0; i < ea->channels; i++) { @@ -1292,13 +1340,12 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* } vgmstream->coding_type = coding_EA_MT; - vgmstream->codec_data = init_ea_mt_loops(vgmstream->channels, use_pcm_blocks, ea->loop_start, ea->loops); + vgmstream->codec_data = init_ea_mt_loops(ea->channels, ea->use_pcm_blocks, ea->loop_start, ea->loops); if (!vgmstream->codec_data) goto fail; break; - } #ifdef VGM_USE_FFMPEG - case EA_CODEC2_ATRAC3PLUS: { + case EA_CODEC2_ATRAC3PLUS: { /* ATRAC3+ */ /* regular ATRAC3plus chunked in SCxx blocks, including RIFF header [Medal of Honor Heroes 2 (PSP)] */ if (!is_bnk) { STREAMFILE* temp_sf = NULL; @@ -1337,31 +1384,76 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* if (is_bnk) { - /* setup channel offsets */ - if (vgmstream->coding_type == coding_EA_XA) { - /* shared (stereo/mono codec) */ - for (i = 0; i < vgmstream->channels; i++) { - vgmstream->ch[i].offset = ea->offsets[0]; + /* BNKs usually have absolute offsets for all channels ("full" interleave) except in some versions */ + if (!(ea->codec_config & 0x04)) { + switch (vgmstream->coding_type) { + case coding_EA_XA: + /* shared (stereo version) */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0]; + } + break; + case coding_EA_XA_int: { + int interleave = ea->num_samples / 28 * 0x0f; /* full interleave */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0] * interleave*i; + } + break; + } + case coding_PCM8_int: + case coding_PCM16_int: { + int interleave = ea->bps==8 ? 0x01 : 0x02; + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0] + interleave*i; + } + break; + } + case coding_PCM8: + case coding_PCM16LE: + case coding_PCM16BE: { + int interleave = ea->num_samples * (ea->bps==8 ? 0x01 : 0x02); /* full interleave */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0] + interleave*i; + } + break; + } + case coding_PSX: { + int interleave = ea->num_samples / 28 * 0x10; /* full interleave */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0] + interleave*i; + } + break; + } + case coding_VADPCM: { + uint32_t interleave = ea->flag_value; + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0] + interleave*i; + } + break; + } + case coding_EA_MT: { + uint32_t interleave = ea->flag_value; + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0] + interleave*i; + } + break; + } + default: + VGM_LOG("EA SCHl: Unknown channel offsets for codec 0x%02x in version %d\n", ea->codec1, ea->version); + goto fail; } - } - //else if (vgmstream->layout_type == layout_interleave) { /* interleaved */ - // for (i = 0; i < vgmstream->channels; i++) { - // vgmstream->ch[i].offset = ea->offsets[0] + vgmstream->interleave_block_size*i; - // } - //} - else if (vgmstream->coding_type == coding_PCM16_int && ea->version == EA_VERSION_V0) { - /* Need for Speed II (PC) bad offsets */ - for (i = 0; i < vgmstream->channels; i++) { - vgmstream->ch[i].offset = ea->offsets[0] + 0x02*i; + } else if (vgmstream->coding_type == coding_NGC_DSP && vgmstream->channels > 1 && ea->offsets[0] == ea->offsets[1]) { + /* pcstream+gcadpcm with sx.exe v2, not in flag_value, probably a bug (even with this parts of the wave are off) */ + int interleave = (ea->num_samples / 14 * 8); /* full interleave */ + for (i = 0; i < ea->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0] + interleave*i; } - } - else if (vgmstream->coding_type == coding_PCM8 && ea->platform == EA_PLATFORM_PS2 && ea->version == EA_VERSION_V3) { - /* SSX3 (PS2) weird 0x10 mini header (codec/loop start/loop end/samples) */ + } else if (ea->platform == EA_PLATFORM_PS2 && (ea->flag_value & 0x100)) { + /* weird 0x10 mini header when played on IOP (codec/loop start/loop end/samples) [SSX 3 (PS2)] */ for (i = 0; i < vgmstream->channels; i++) { - vgmstream->ch[i].offset = ea->offsets[0] + 0x10; + vgmstream->ch[i].offset = ea->offsets[i] + 0x10; } - } - else { + } else { /* absolute */ for (i = 0; i < vgmstream->channels; i++) { vgmstream->ch[i].offset = ea->offsets[i]; @@ -1369,8 +1461,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE* sf, ea_header* } /* TODO: Figure out how to get stream size for BNK sounds */ - } - else { + } else { update_ea_stream_size_and_samples(sf, start_offset, vgmstream, standalone); } @@ -1632,9 +1723,10 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs if (ea->platform == EA_PLATFORM_N64 || ea->platform == EA_PLATFORM_MAC || ea->platform == EA_PLATFORM_SAT - || ea->platform == EA_PLATFORM_GC_WII + || ea->platform == EA_PLATFORM_GC || ea->platform == EA_PLATFORM_X360 || ea->platform == EA_PLATFORM_PS3 + || ea->platform == EA_PLATFORM_WII || ea->platform == EA_PLATFORM_GENERIC) { ea->big_endian = 1; } @@ -1648,16 +1740,17 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs if (ea->version == EA_VERSION_NONE) { switch(ea->platform) { case EA_PLATFORM_PC: ea->version = EA_VERSION_V0; break; - case EA_PLATFORM_PSX: ea->version = EA_VERSION_V0; break; // assumed + case EA_PLATFORM_PSX: ea->version = EA_VERSION_V0; break; case EA_PLATFORM_N64: ea->version = EA_VERSION_V0; break; case EA_PLATFORM_MAC: ea->version = EA_VERSION_V0; break; case EA_PLATFORM_SAT: ea->version = EA_VERSION_V0; break; case EA_PLATFORM_PS2: ea->version = EA_VERSION_V1; break; - case EA_PLATFORM_GC_WII: ea->version = EA_VERSION_V2; break; + case EA_PLATFORM_GC: ea->version = EA_VERSION_V2; break; case EA_PLATFORM_XBOX: ea->version = EA_VERSION_V2; break; case EA_PLATFORM_X360: ea->version = EA_VERSION_V3; break; case EA_PLATFORM_PSP: ea->version = EA_VERSION_V3; break; case EA_PLATFORM_PS3: ea->version = EA_VERSION_V3; break; + case EA_PLATFORM_WII: ea->version = EA_VERSION_V3; break; case EA_PLATFORM_3DS: ea->version = EA_VERSION_V3; break; case EA_PLATFORM_GENERIC: ea->version = EA_VERSION_V2; break; default: @@ -1670,9 +1763,9 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs if (ea->codec1 == EA_CODEC1_NONE && ea->version == EA_VERSION_V0) { switch(ea->platform) { case EA_PLATFORM_PC: ea->codec1 = EA_CODEC1_PCM; break; - case EA_PLATFORM_PSX: ea->codec1 = EA_CODEC1_VAG; break; // assumed + case EA_PLATFORM_PSX: ea->codec1 = EA_CODEC1_VAG; break; case EA_PLATFORM_N64: ea->codec1 = EA_CODEC1_N64; break; - case EA_PLATFORM_MAC: ea->codec1 = EA_CODEC1_PCM; break; // assumed + case EA_PLATFORM_MAC: ea->codec1 = EA_CODEC1_PCM; break; case EA_PLATFORM_SAT: ea->codec1 = EA_CODEC1_PCM; break; default: VGM_LOG("EA SCHl: unknown default codec1 for platform 0x%02x\n", ea->platform); @@ -1684,10 +1777,18 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs if (ea->codec1 != EA_CODEC1_NONE && ea->codec2 == EA_CODEC2_NONE) { switch (ea->codec1) { case EA_CODEC1_PCM: - ea->codec2 = ea->bps==8 ? EA_CODEC2_S8 : (ea->big_endian ? EA_CODEC2_S16BE : EA_CODEC2_S16LE); + if (ea->platform == EA_PLATFORM_PC) + ea->codec2 = ea->bps==8 ? EA_CODEC2_S8_INT : (ea->big_endian ? EA_CODEC2_S16BE_INT : EA_CODEC2_S16LE_INT); + else + ea->codec2 = ea->bps==8 ? EA_CODEC2_S8 : (ea->big_endian ? EA_CODEC2_S16BE : EA_CODEC2_S16LE); break; case EA_CODEC1_VAG: ea->codec2 = EA_CODEC2_VAG; break; - case EA_CODEC1_EAXA: ea->codec2 = EA_CODEC2_EAXA; break; + case EA_CODEC1_EAXA: + if (ea->platform == EA_PLATFORM_PC || ea->platform == EA_PLATFORM_MAC) + ea->codec2 = EA_CODEC2_EAXA_INT; + else + ea->codec2 = EA_CODEC2_EAXA; + break; case EA_CODEC1_MT10: ea->codec2 = EA_CODEC2_MT10; break; case EA_CODEC1_N64: ea->codec2 = EA_CODEC2_N64; break; default: @@ -1704,11 +1805,12 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs case EA_PLATFORM_PSX: ea->codec2 = EA_CODEC2_VAG; break; case EA_PLATFORM_MAC: ea->codec2 = EA_CODEC2_EAXA; break; case EA_PLATFORM_PS2: ea->codec2 = EA_CODEC2_VAG; break; - case EA_PLATFORM_GC_WII: ea->codec2 = EA_CODEC2_S16BE; break; + case EA_PLATFORM_GC: ea->codec2 = EA_CODEC2_S16BE; break; case EA_PLATFORM_XBOX: ea->codec2 = EA_CODEC2_S16LE; break; case EA_PLATFORM_X360: ea->codec2 = EA_CODEC2_EAXA; break; case EA_PLATFORM_PSP: ea->codec2 = EA_CODEC2_EAXA; break; case EA_PLATFORM_PS3: ea->codec2 = EA_CODEC2_EAXA; break; + //case EA_PLATFORM_WII: ea->codec2 = EA_CODEC2_EAXA; break; /* not set? */ case EA_PLATFORM_3DS: ea->codec2 = EA_CODEC2_GCADPCM; break; default: VGM_LOG("EA SCHl: unknown default codec2 for platform 0x%02x\n", ea->platform); @@ -1726,11 +1828,12 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs case EA_PLATFORM_MAC: ea->sample_rate = 22050; break; case EA_PLATFORM_SAT: ea->sample_rate = 22050; break; case EA_PLATFORM_PS2: ea->sample_rate = 22050; break; - case EA_PLATFORM_GC_WII: ea->sample_rate = 24000; break; + case EA_PLATFORM_GC: ea->sample_rate = 24000; break; case EA_PLATFORM_XBOX: ea->sample_rate = 24000; break; case EA_PLATFORM_X360: ea->sample_rate = 44100; break; case EA_PLATFORM_PSP: ea->sample_rate = 22050; break; case EA_PLATFORM_PS3: ea->sample_rate = 44100; break; + case EA_PLATFORM_WII: ea->sample_rate = 32000; break; case EA_PLATFORM_3DS: ea->sample_rate = 32000; break; default: VGM_LOG("EA SCHl: unknown default sample rate for platform 0x%02x\n", ea->platform); @@ -1738,6 +1841,12 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs } } + /* EA-XA and MicroTalk got updated revisions with PCM blocks in sx v2.30 */ + ea->use_pcm_blocks = (ea->version == EA_VERSION_V3 || (ea->version == EA_VERSION_V2 && + (ea->platform == EA_PLATFORM_PC || + ea->platform == EA_PLATFORM_MAC || + ea->platform == EA_PLATFORM_GENERIC))); + /* some codecs have ADPCM hist at the start of every block in streams (but not BNKs) */ if (!is_bnk) { if (ea->codec2 == EA_CODEC2_GCADPCM) { @@ -1745,20 +1854,18 @@ static int parse_variable_header(STREAMFILE* sf, ea_header* ea, off_t begin_offs ea->codec_config |= 0x01; } else if (ea->codec2 == EA_CODEC2_EAXA) { - /* EA-XA has ADPCM hist in earlier versions */ - /* V0, V1: always */ - /* V2: consoles only */ - /* V3: never */ - if (ea->version <= EA_VERSION_V1) { + /* EA-XA has ADPCM hist in the original version */ + if (!ea->use_pcm_blocks) ea->codec_config |= 0x01; - } - else if (ea->version == EA_VERSION_V2) { - if (ea->platform == EA_PLATFORM_PS2 || ea->platform == EA_PLATFORM_GC_WII || ea->platform == EA_PLATFORM_XBOX) - ea->codec_config |= 0x01; - } } } + if (ea->version > EA_VERSION_V0) { + /* v0 needs channel offsets to be manually calculated + * v1+ always has split channels and provides channel offsets */ + ea->codec_config |= 0x04; + } + return offset; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c index eb5f48737..33f7b18a4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c @@ -16,9 +16,9 @@ typedef struct { int big_endian; int loop_flag; -} ea_header; +} ea_fixed_header; -static int parse_fixed_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset); +static int parse_fixed_header(STREAMFILE* streamFile, ea_fixed_header* ea, off_t begin_offset); /* EA SCHl with fixed header - from EA games (~1997? ex. NHL 97 PC) */ @@ -26,7 +26,7 @@ VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; size_t header_size; - ea_header ea = {0}; + ea_fixed_header ea = {0}; /* checks */ @@ -87,7 +87,7 @@ fail: } -static int parse_fixed_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset) { +static int parse_fixed_header(STREAMFILE* streamFile, ea_fixed_header* ea, off_t begin_offset) { off_t offset = begin_offset; if (read_32bitBE(offset+0x00, streamFile) != 0x5041546C && /* "PATl" */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c index a0b60e7fb..3d2d48a04 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c @@ -22,13 +22,13 @@ VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) { if (get_streamfile_size(sf) <= 0x1000) goto fail; + if (target_subsong == 0) target_subsong = 1; /* init ffmpeg */ - data = init_ffmpeg_offset(sf, 0, get_streamfile_size(sf)); + data = init_ffmpeg_header_offset_subsong(sf, NULL, 0, 0, get_streamfile_size(sf), target_subsong); if (!data) return NULL; - total_subsongs = data->streamCount; - if (target_subsong == 0) target_subsong = 1; + total_subsongs = data->streamCount; /* uncommon, ex. wmv [Lost Odyssey (X360)] */ if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* try to get .pos data */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c index 4f33a252a..1ae3d71a6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c @@ -36,7 +36,7 @@ typedef struct { static layered_layout_data* build_layered_fsb5_celt(STREAMFILE* sf, fsb5_header* fsb5); static layered_layout_data* build_layered_fsb5_atrac9(STREAMFILE* sf, fsb5_header* fsb5, off_t configs_offset, size_t configs_size); -/* FSB5 - FMOD Studio multiplatform format */ +/* FSB5 - Firelight's FMOD Studio SoundBank format */ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; fsb5_header fsb5 = {0}; @@ -207,10 +207,11 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { * (xN entries) */ break; - case 0x0d: /* unknown 32b (config? usually 0x3fnnnn00 BE and sometimes 0x3dnnnn00 BE) */ - /* found in some XMA2/Vorbis/FADPCM */ - VGM_LOG("FSB5: stream %i flag %x with value %08x\n", i, extraflag_type, read_32bitLE(extraflag_offset+0x04,sf)); + case 0x0d: /* peak volume float (optional setting when making fsb) */ break; + case 0x0f: /* OPUS data size not counting frames headers */ + break; + case 0x0e: /* number of layered Vorbis channels [Invisible, Inc. (Switch)] */ default: VGM_LOG("FSB5: stream %i unknown flag 0x%x at %x + 0x04 (size 0x%x)\n", i, extraflag_type, (uint32_t)extraflag_offset, extraflag_size); break; @@ -219,13 +220,19 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { extraflag_offset += 0x04 + extraflag_size; stream_header_size += 0x04 + extraflag_size; - } while (extraflag_end != 0x00); + } + while (extraflag_end != 0x00); } /* target found */ if (i + 1 == target_subsong) { fsb5.stream_offset = fsb5.base_header_size + fsb5.sample_header_size + fsb5.name_table_size + data_offset; + /* catch bad rips (like incorrectly split +1.5GB .fsb with wrong header+data) */ + if (fsb5.stream_offset > get_streamfile_size(sf)) + goto fail; + + /* get stream size from next stream offset or full size if there is only one */ if (i + 1 == fsb5.total_subsongs) { fsb5.stream_size = fsb5.sample_data_size - data_offset; @@ -472,6 +479,20 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { vgmstream->interleave_block_size = 0x8c; break; +#if 0 //disabled until some game is found, can be created in the GUI tool +#ifdef VGM_USE_FFMPEG + case 0x11: { /* FMOD_SOUND_FORMAT_OPUS */ + int skip = 312; //fsb_opus_get_encoder_delay(fsb5.stream_offset, sf); /* returns 120 but this seems correct */ + //vgmstream->num_samples -= skip; + + vgmstream->codec_data = init_ffmpeg_fsb_opus(sf, fsb5.stream_offset, fsb5.stream_size, vgmstream->channels, skip, vgmstream->sample_rate); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } +#endif +#endif default: VGM_LOG("FSB5: unknown codec %x found\n", fsb5.codec); goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5_fev.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5_fev.c index 3919f82bb..4014716c9 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5_fev.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5_fev.c @@ -1,70 +1,149 @@ #include "meta.h" #include "../coding/coding.h" + +static int get_subsongs(STREAMFILE* sf, off_t fsb5_offset, size_t fsb5_size); + /* FEV+FSB5 container [Just Cause 3 (PC), Shantae: Half-Genie Hero (Switch)] */ VGMSTREAM* init_vgmstream_fsb5_fev_bank(STREAMFILE* sf) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE *temp_sf = NULL; - off_t subfile_offset, chunk_offset, first_offset = 0x0c; - size_t subfile_size, chunk_size; + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + off_t subfile_offset, chunk_offset, bank_offset, offset; + size_t subfile_size, bank_size; + uint32_t version = 0; + int fsb5_pos, fsb5_subsong; + int total_subsongs, target_subsong = sf->stream_index; /* checks */ if (!check_extensions(sf, "bank")) goto fail; - if (read_32bitBE(0x00,sf) != 0x52494646) /* "RIFF" */ + if (read_u32be(0x00,sf) != 0x52494646) /* "RIFF" */ goto fail; - if (read_32bitBE(0x08,sf) != 0x46455620) /* "FEV " */ + if (read_u32be(0x08,sf) != 0x46455620) /* "FEV " */ goto fail; + version = read_u32le(0x14,sf); /* newer FEV have some kind of sub-version at 0x18 */ /* .fev is an event format referencing various external .fsb, but FMOD can bake .fev and .fsb to * form a .bank, which is the format we support here (regular .fev is complex and not very interesting). * Format is RIFF with FMT (main), LIST (config) and SND (FSB5 data), we want the FSB5 offset inside LIST */ - if (!find_chunk_le(sf, 0x4C495354,first_offset,0, &chunk_offset,NULL)) /* "LIST" */ + + if (!find_chunk_le(sf, 0x4C495354, 0x0c, 0, &chunk_offset,NULL)) /* "LIST" */ goto fail; + if (read_u32be(chunk_offset+0x00,sf) != 0x50524F4A || /* "PROJ" */ + read_u32be(chunk_offset+0x04,sf) != 0x424E4B49) /* "BNKI" */ + goto fail; /* event .fev has "OBCT" instead of "BNKI" (which can also be empty) */ - if (read_32bitBE(chunk_offset+0x00,sf) != 0x50524F4A || /* "PROJ" */ - read_32bitBE(chunk_offset+0x04,sf) != 0x424E4B49) /* "BNKI" */ - goto fail; /* event .fev has "OBCT" instead of "BNKI" */ - /* inside BNKI is a bunch of LIST each with event subchunks and finally fsb offset */ - first_offset = chunk_offset + 0x04; - if (!find_chunk_le(sf, 0x534E4448,first_offset,0, &chunk_offset,&chunk_size)) /* "SNDH" */ - goto fail; + /* inside BNKI is a bunch of LISTs each with event subchunks and somewhere the FSB5 offsets */ + bank_offset = 0; + offset = chunk_offset + 0x04; + while (bank_offset == 0 && offset < get_streamfile_size(sf)) { + uint32_t chunk_type = read_u32be(offset + 0x00,sf); + uint32_t chunk_size = read_u32le(offset + 0x04,sf); + offset += 0x08; - /* 0x00: unknown (version? ex LE: 0x00080003, 0x00080005) */ - { - int banks; - - /* multiple banks is possible but rare (only seen an extra "Silence" FSB5 in Guacamelee 2 (Switch), - * which on PC is a regular subsong in the only FSB5) */ - banks = (chunk_size - 0x04) / 0x08; - VGM_ASSERT(banks > 1, "FSB5FEV: multiple banks found\n"); - - /* Could try to set stream index based on FSB subsong ranges, also fixing num_streams and stream_index - * kinda involved and hard to test so for now just ignore it and use first offset */ - - if (banks > 2) + if (chunk_type == 0xFFFFFFFF || chunk_size == 0xFFFFFFFF) goto fail; - if (banks == 2) { - off_t temp_offset = read_32bitLE(chunk_offset + 0x04 + 0x08*1 + 0x00,sf); - //size_t temp_size = read_32bitLE(chunk_offset + 0x04 + 0x08*1 + 0x04,sf); - int bank_subsongs = read_32bitLE(temp_offset + 0x08,sf); - if (bank_subsongs != 1) goto fail; + switch(chunk_type) { + case 0x4C495354: /* "LIST" with "SNDH" (usually v0x28 but also in Fall Guys's BNK_Music_RoundReveal) */ + if (read_u32be(offset + 0x04, sf) == 0x534E4448) { + bank_offset = offset + 0x0c; + bank_size = read_s32le(offset + 0x08,sf); + } + break; + + case 0x534E4448: /* "SNDH" */ + bank_offset = offset; + bank_size = chunk_size; + break; + + default: + break; + } + + offset += chunk_size; + } + + if (bank_offset == 0) + goto fail; + + { + /* known bank versions: + * 0x28: Transistor (iOS) [~2015] + * 0x50: Runic Rampage (PC), Forza 7 (PC), Shantae: Half Genie Hero (Switch) [~2017] + * 0x58: Mana Spark (PC), Shantae and the Seven Sirens (PC) [~2018] + * 0x63: Banner Saga 3 (PC) [~2018] + * 0x64: Guacamelee! Super Turbo Championship Edition (Switch) [~2018] + * 0x65: Carrion (Switch) [~2020] + * 0x7D: Fall Guys (PC) [~2020] + * 0x84: SCP Unity (PC) [~2020] + * 0x86: Hades (Switch) [~2020] */ + size_t entry_size = version <= 0x28 ? 0x04 : 0x08; + int i, banks; + + /* 0x00: unknown (chunk version? ex LE: 0x00080003, 0x00080005) */ + banks = (bank_size - 0x04) / entry_size; + + /* multiple banks is possible but rare [Hades (Switch), Guacamelee 2 (Switch)], + * must map bank (global) subsong to FSB (internal) subsong */ + + if (target_subsong == 0) target_subsong = 1; + + fsb5_pos = 0; + fsb5_subsong = -1; + total_subsongs = 0; + for (i = 0; i < banks; i++) { + //TODO: fsb5_size fails for v0x28< + encrypted, but only used with multibanks = unlikely + off_t fsb5_offset = read_u32le(bank_offset + 0x04 + entry_size*i + 0x00,sf); + size_t fsb5_size = read_u32le(bank_offset+0x08 + entry_size*i,sf); + int fsb5_subsongs = get_subsongs(sf, fsb5_offset, fsb5_size); + if (!fsb5_subsongs) + goto fail; + + /* target in range */ + if (target_subsong >= total_subsongs + 1 && target_subsong < total_subsongs + 1 + fsb5_subsongs) { + fsb5_pos = i; + fsb5_subsong = target_subsong - total_subsongs; + } + + total_subsongs += fsb5_subsongs; + } + if (fsb5_subsong < 0) + goto fail; + + if (version <= 0x28) { + subfile_offset = read_u32le(bank_offset+0x04 + entry_size*fsb5_pos,sf); + subfile_size = /* meh */ + read_u32le(subfile_offset + 0x0C,sf) + + read_u32le(subfile_offset + 0x10,sf) + + read_u32le(subfile_offset + 0x14,sf) + + 0x3C; + } + else { + subfile_offset = read_u32le(bank_offset+0x04 + entry_size*fsb5_pos,sf); + subfile_size = read_u32le(bank_offset+0x08 + entry_size*fsb5_pos,sf); } } - subfile_offset = read_32bitLE(chunk_offset+0x04,sf); - subfile_size = read_32bitLE(chunk_offset+0x08,sf); + //;VGM_LOG("FSB5 FEV: offset=%lx, size=%x\n", subfile_offset,subfile_size); temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, "fsb"); if (!temp_sf) goto fail; - vgmstream = init_vgmstream_fsb5(temp_sf); - close_streamfile(temp_sf); + temp_sf->stream_index = fsb5_subsong; /* relative subsong, in case of multiple FSBs */ + vgmstream = (read_u32be(0x00, temp_sf) == 0x46534235) ? /* "FSB5" (better flag?)*/ + init_vgmstream_fsb5(temp_sf) : + init_vgmstream_fsb_encrypted(temp_sf); + if (!vgmstream) goto fail; + + vgmstream->stream_index = sf->stream_index; //target_subsong; /* 0-index matters */ + vgmstream->num_streams = total_subsongs; + + close_streamfile(temp_sf); return vgmstream; fail: @@ -72,3 +151,29 @@ fail: close_vgmstream(vgmstream); return NULL; } + +static int get_subsongs(STREAMFILE* sf, off_t fsb5_offset, size_t fsb5_size) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + int subsongs = 0; + + + /* standard */ + if (read_u32be(fsb5_offset, sf) == 0x46534235) { /* FSB5 */ + return read_s32le(fsb5_offset + 0x08,sf); + } + + /* encrypted, meh */ + temp_sf = setup_subfile_streamfile(sf, fsb5_offset, fsb5_size, "fsb"); + if (!temp_sf) goto end; + + vgmstream = init_vgmstream_fsb_encrypted(temp_sf); + if (!vgmstream) goto end; + + subsongs = vgmstream->num_streams; + +end: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return subsongs; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c index 289287b87..10d30b033 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted.c @@ -66,8 +66,7 @@ VGMSTREAM* init_vgmstream_fsb_encrypted(STREAMFILE* sf) { vgmstream = init_vgmstream_fsb(temp_sf); } - if (vgmstream) - dump_streamfile(temp_sf, 0); + //;if (vgmstream) dump_streamfile(temp_sf, 0); close_streamfile(temp_sf); if (vgmstream) break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted_streamfile.h index eae35d464..578f1df04 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_encrypted_streamfile.h @@ -13,22 +13,23 @@ typedef struct { /* Encrypted FSB info from guessfsb and fsbext */ static size_t fsb_decryption_read(STREAMFILE* sf, 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 + //00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F + 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0, //00 + 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8, //10 + 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4, //20 + 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC, //30 + 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2, //40 + 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA, //50 + 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6, //60 + 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE, //70 + 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1, //80 + 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9, //90 + 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5, //A0 + 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD, //B0 + 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3, //C0 + 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB, //D0 + 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7, //E0 + 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF //F0 }; size_t bytes_read; int i; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h index cb44125cc..16640d55f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h @@ -1,149 +1,160 @@ -#ifndef _FSB_KEYS_H_ -#define _FSB_KEYS_H_ - -typedef struct { - int is_fsb5; /* FSB5 or FSB4/3*/ - int is_alt; /* alt XOR mode (seemingly not tied to FSB version or anything) */ - size_t fsbkey_size; - const uint8_t *fsbkey; -} fsbkey_info; - -/** - * List of known keys, found in aluigi's site (http://aluigi.altervista.org), forums, guessfsb.exe or manually. - */ - -/* 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 }; - -/* Supreme Commander 2 */ //"B2A7BB00" -static const uint8_t key_sc2[] = { 0x42,0x32,0x41,0x37,0x42,0x42,0x30,0x30 }; - -/* Cookie Run: Ovenbreak */ //"ghfxhslrghfxhslr" -static const uint8_t key_cro[] = { 0x67,0x68,0x66,0x78,0x68,0x73,0x6C,0x72,0x67,0x68,0x66,0x78,0x68,0x73,0x6C,0x72 }; - -/* Monster Jam (PS2) */ //"truck/impact/carbody" -static const uint8_t key_mtj[] = { 0x74,0x72,0x75,0x63,0x6B,0x2F,0x69,0x6D,0x70,0x61,0x63,0x74,0x2F,0x63,0x61,0x72,0x62,0x6F,0x64,0x79 }; - -/* Guitar Hero 5 (X360) */ -static const uint8_t key_gh5[] = { 0xFC,0xF9,0xE4,0xB3,0xF5,0x57,0x5C,0xA5,0xAC,0x13,0xEC,0x4A,0x43,0x19,0x58,0xEB,0x4E,0xF3,0x84,0x0B,0x8B,0x78,0xFA,0xFD,0xBB,0x18,0x46,0x7E,0x31,0xFB,0xD0 }; - -/* Sekiro: Shadows Die Twice (PC) */ //"G0KTrWjS9syqF7vVD6RaVXlFD91gMgkC" -static const uint8_t key_sek[] = { 0x47,0x30,0x4B,0x54,0x72,0x57,0x6A,0x53,0x39,0x73,0x79,0x71,0x46,0x37,0x76,0x56,0x44,0x36,0x52,0x61,0x56,0x58,0x6C,0x46,0x44,0x39,0x31,0x67,0x4D,0x67,0x6B,0x43 }; - -// 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 - { 0,0, sizeof(key_sc2),key_sc2 },//untested - { 0,1, sizeof(key_sc2),key_sc2 },//untested - { 1,0, sizeof(key_sc2),key_sc2 },//untested - { 1,1, sizeof(key_sc2),key_sc2 },//untested - { 1,0, sizeof(key_cro),key_cro }, - { 0,1, sizeof(key_mtj),key_mtj },// FSB3 - { 0,1, sizeof(key_gh5),key_gh5 },// FSB4 - { 1,0, sizeof(key_sek),key_sek },// FSB5 - -}; -static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]); - - -#endif /* _FSB_KEYS_H_ */ +#ifndef _FSB_KEYS_H_ +#define _FSB_KEYS_H_ + +/** + * List of known keys, found in aluigi's site (http://aluigi.altervista.org), forums, guessfsb.exe or manually. + */ + +/* 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 }; + +/* Supreme Commander 2 */ //"B2A7BB00" +static const uint8_t key_sc2[] = { 0x42,0x32,0x41,0x37,0x42,0x42,0x30,0x30 }; + +/* Cookie Run: Ovenbreak */ //"ghfxhslrghfxhslr" +static const uint8_t key_cro[] = { 0x67,0x68,0x66,0x78,0x68,0x73,0x6C,0x72,0x67,0x68,0x66,0x78,0x68,0x73,0x6C,0x72 }; + +/* Monster Jam (PS2) */ //"truck/impact/carbody" +static const uint8_t key_mtj[] = { 0x74,0x72,0x75,0x63,0x6B,0x2F,0x69,0x6D,0x70,0x61,0x63,0x74,0x2F,0x63,0x61,0x72,0x62,0x6F,0x64,0x79 }; + +/* Guitar Hero 5 (X360) */ +static const uint8_t key_gh5[] = { 0xFC,0xF9,0xE4,0xB3,0xF5,0x57,0x5C,0xA5,0xAC,0x13,0xEC,0x4A,0x43,0x19,0x58,0xEB,0x4E,0xF3,0x84,0x0B,0x8B,0x78,0xFA,0xFD,0xBB,0x18,0x46,0x7E,0x31,0xFB,0xD0 }; + +/* Sekiro: Shadows Die Twice (PC) */ //"G0KTrWjS9syqF7vVD6RaVXlFD91gMgkC" +static const uint8_t key_sek[] = { 0x47,0x30,0x4B,0x54,0x72,0x57,0x6A,0x53,0x39,0x73,0x79,0x71,0x46,0x37,0x76,0x56,0x44,0x36,0x52,0x61,0x56,0x58,0x6C,0x46,0x44,0x39,0x31,0x67,0x4D,0x67,0x6B,0x43 }; + +/* SCP: Unity (PC) */ //"BasicEncryptionKey" +static const uint8_t key_scp[] = { 0x42,0x61,0x73,0x69,0x63,0x45,0x6E,0x63,0x72,0x79,0x70,0x74,0x69,0x6F,0x6E,0x4B,0x65,0x79 }; + +/* Guitar Hero: Metallica (X360) */ +static const uint8_t key_ghm[] = { 0x8C,0xFA,0xF3,0x14,0xB1,0x53,0xDA,0xAB,0x2B,0x82,0x6B,0xD5,0x55,0x16,0xCF,0x01,0x90,0x20,0x28,0x14,0xB1,0x53,0xD8 }; + +/* Worms Rumble Beta (PC) */ //"FXnTffGJ9LS855Gc" +static const uint8_t key_wrb[] = { 0x46,0x58,0x6E,0x54,0x66,0x66,0x47,0x4A,0x39,0x4C,0x53,0x38,0x35,0x35,0x47,0x63 }; + +// Unknown: +// - Battle: Los Angeles +// - Guitar Hero: Warriors of Rock, DJ hero FSB +// - Longmenkezhan +// - Gas Guzzlers: Combat Carnage (PC?) "C5FA83EA64B34EC2BFE" hex or text? [FSB5] + +typedef struct { + int is_fsb5; /* FSB5 or FSB4/3*/ + int is_alt; /* alt XOR mode (seemingly not tied to FSB version or anything) */ + size_t fsbkey_size; + const uint8_t *fsbkey; +} fsbkey_info; + +static const fsbkey_info fsbkey_list[] = { + { 0,0, sizeof(key_dj2),key_dj2 }, + { 0,0, sizeof(key_dfp),key_dfp },//FSB4 + { 1,0, sizeof(key_dfp),key_dfp },//untested + { 1,1, sizeof(key_dfp),key_dfp },//untested + { 1,0, sizeof(key_npp),key_npp },//FSB5 + { 1,0, sizeof(key_sms),key_sms },//FSB5 + { 1,0, sizeof(key_gfs),key_gfs },//FSB5 + { 1,0, sizeof(key_rev),key_rev },//FSB5 + { 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 + { 0,0, sizeof(key_sc2),key_sc2 },//untested + { 0,1, sizeof(key_sc2),key_sc2 },//untested + { 1,0, sizeof(key_sc2),key_sc2 },//untested + { 1,1, sizeof(key_sc2),key_sc2 },//untested + { 1,0, sizeof(key_cro),key_cro }, + { 0,1, sizeof(key_mtj),key_mtj },// FSB3 + { 0,1, sizeof(key_gh5),key_gh5 },// FSB4 + { 1,0, sizeof(key_sek),key_sek },// FSB5 + { 1,0, sizeof(key_scp),key_scp },// FSB5 + { 0,1, sizeof(key_ghm),key_ghm },// FSB4 + { 1,0, sizeof(key_wrb),key_wrb },// FSB5 +}; +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/gsp_gsb.c b/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c index 71f5bbf1c..b04fdf0a6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/gsp_gsb.c @@ -1,149 +1,147 @@ -#include "meta.h" -#include "../layout/layout.h" -#include "../coding/coding.h" - -/* GSP+GSB - from Tecmo's Super Swing Golf 1 & 2 (Wii), Quantum Theory (PS3/X360) */ -VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamHeader = NULL; - int loop_flag, channel_count, sample_rate, num_samples, loop_start, loop_end; - off_t start_offset, chunk_offset, first_offset; - size_t data_size; - int codec; - - - /* checks */ - if (!check_extensions(streamFile,"gsb")) - goto fail; - - streamHeader = open_streamfile_by_ext(streamFile, "gsp"); - if (!streamHeader) goto fail; - - if (read_32bitBE(0x00,streamHeader) != 0x47534E44) /* "GSND" */ - goto fail; - /* 0x04: version? */ - /* 0x08: 1? */ - /* 0x0c: 0? */ - first_offset = read_32bitBE(0x10,streamHeader); /* usually 0x14 */ - - if (!find_chunk_be(streamHeader, 0x48454144,first_offset,1, &chunk_offset,NULL)) /* "HEAD" */ - goto fail; - /* 0x00: header size */ - /* 0x04: num_chunks */ - - if (!find_chunk_be(streamHeader, 0x44415441,first_offset,1, &chunk_offset,NULL)) /* "DATA" */ - goto fail; - data_size = read_32bitBE(chunk_offset + 0x00,streamHeader); - codec = read_32bitBE(chunk_offset + 0x04,streamHeader); - sample_rate = read_32bitBE(chunk_offset + 0x08,streamHeader); - /* 0x0c: always 16? */ - channel_count = read_16bitBE(chunk_offset + 0x0e,streamHeader); - /* 0x10: always 0? */ - num_samples = read_32bitBE(chunk_offset + 0x14,streamHeader); - /* 0x18: always 0? */ - /* 0x1c: unk (varies with codec_id) */ - - if (!find_chunk_be(streamHeader, 0x42534943,first_offset,1, &chunk_offset,NULL)) /* "BSIC" */ - goto fail; - /* 0x00/0x04: probably volume/pan/etc floats (1.0) */ - /* 0x08: null? */ - loop_flag = read_8bit(chunk_offset+0x0c,streamHeader); - loop_start = read_32bitBE(chunk_offset+0x10,streamHeader); - loop_end = read_32bitBE(chunk_offset+0x14,streamHeader); - - //if (!find_chunk_be(streamHeader, 0x4E414D45,first_offset,1, &chunk_offset,NULL)) /* "NAME" */ - // goto fail; - /* 0x00: name_size */ - /* 0x04+: name (same as filename) */ - - - start_offset = 0x00; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_GSP_GSB; - - vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = num_samples; - vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = loop_end; - - switch (codec) { - case 0x04: { /* DSP [Super Swing Golf (Wii)] */ - size_t block_header_size; - size_t num_blocks; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_blocked_gsb; - - if (!find_chunk_be(streamHeader, 0x47434558,first_offset,1, &chunk_offset,NULL)) /* "GCEX" */ - goto fail; - - //vgmstream->current_block_size = read_32bitBE(chunk_offset+0x00,streamHeader); - block_header_size = read_32bitBE(chunk_offset+0x04,streamHeader); - num_blocks = read_32bitBE(chunk_offset+0x08,streamHeader); - vgmstream->num_samples = (data_size - block_header_size * num_blocks) / 8 / vgmstream->channels * 14; - /* 0x0c+: unk */ - - dsp_read_coefs_be(vgmstream, streamHeader, chunk_offset+0x18, 0x30); - break; - } -#ifdef VGM_USE_FFMPEG - case 0x08: { /* ATRAC3 [Quantum Theory (PS3)] */ - int block_align, encoder_delay; - - block_align = 0x98 * vgmstream->channels; - encoder_delay = 1024 + 69*2; /* observed default, matches XMA (needed as many files start with garbage) */ - vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_align) - encoder_delay; - /* fix num_samples as header samples seem to be modified to match altered (49999/48001) sample rates somehow */ - - vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ - vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_align); //- encoder_delay - vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_align) - encoder_delay; - break; - } - - case 0x09: { /* XMA2 [Quantum Theory (PS3)] */ - ffmpeg_codec_data *ffmpeg_data = NULL; - uint8_t buf[200]; - int32_t bytes; - - if (!find_chunk_be(streamHeader, 0x584D4558,first_offset,1, &chunk_offset,NULL)) /* "XMEX" */ - goto fail; - /* 0x00: fmt0x166 header (BE) */ - /* 0x34: seek table */ - - bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, chunk_offset,0x34, data_size, streamHeader, 1); - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - xma_fix_raw_samples(vgmstream, streamFile, start_offset,data_size, 0, 0,0); /* samples are ok */ - break; - } -#endif - default: - goto fail; - } - - - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) - goto fail; - close_streamfile(streamHeader); - return vgmstream; - -fail: - close_streamfile(streamHeader); - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + +/* GSP+GSB - from Tecmo's Super Swing Golf 1 & 2 (Wii), Quantum Theory (PS3/X360) */ +VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamHeader = NULL; + int loop_flag, channel_count, sample_rate, num_samples, loop_start, loop_end; + off_t start_offset, chunk_offset, first_offset; + size_t data_size; + int codec; + + + /* checks */ + if (!check_extensions(streamFile,"gsb")) + goto fail; + + streamHeader = open_streamfile_by_ext(streamFile, "gsp"); + if (!streamHeader) goto fail; + + if (read_32bitBE(0x00,streamHeader) != 0x47534E44) /* "GSND" */ + goto fail; + /* 0x04: version? */ + /* 0x08: 1? */ + /* 0x0c: 0? */ + first_offset = read_32bitBE(0x10,streamHeader); /* usually 0x14 */ + + if (!find_chunk_be(streamHeader, 0x48454144,first_offset,1, &chunk_offset,NULL)) /* "HEAD" */ + goto fail; + /* 0x00: header size */ + /* 0x04: num_chunks */ + + if (!find_chunk_be(streamHeader, 0x44415441,first_offset,1, &chunk_offset,NULL)) /* "DATA" */ + goto fail; + data_size = read_32bitBE(chunk_offset + 0x00,streamHeader); + codec = read_32bitBE(chunk_offset + 0x04,streamHeader); + sample_rate = read_32bitBE(chunk_offset + 0x08,streamHeader); + /* 0x0c: always 16? */ + channel_count = read_16bitBE(chunk_offset + 0x0e,streamHeader); + /* 0x10: always 0? */ + num_samples = read_32bitBE(chunk_offset + 0x14,streamHeader); + /* 0x18: always 0? */ + /* 0x1c: unk (varies with codec_id) */ + + if (!find_chunk_be(streamHeader, 0x42534943,first_offset,1, &chunk_offset,NULL)) /* "BSIC" */ + goto fail; + /* 0x00/0x04: probably volume/pan/etc floats (1.0) */ + /* 0x08: null? */ + loop_flag = read_8bit(chunk_offset+0x0c,streamHeader); + loop_start = read_32bitBE(chunk_offset+0x10,streamHeader); + loop_end = read_32bitBE(chunk_offset+0x14,streamHeader); + + //if (!find_chunk_be(streamHeader, 0x4E414D45,first_offset,1, &chunk_offset,NULL)) /* "NAME" */ + // goto fail; + /* 0x00: name_size */ + /* 0x04+: name (same as filename) */ + + + start_offset = 0x00; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_GSP_GSB; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + switch (codec) { + case 0x04: { /* DSP [Super Swing Golf (Wii)] */ + size_t block_header_size; + size_t num_blocks; + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_blocked_gsb; + + if (!find_chunk_be(streamHeader, 0x47434558,first_offset,1, &chunk_offset,NULL)) /* "GCEX" */ + goto fail; + + //vgmstream->current_block_size = read_32bitBE(chunk_offset+0x00,streamHeader); + block_header_size = read_32bitBE(chunk_offset+0x04,streamHeader); + num_blocks = read_32bitBE(chunk_offset+0x08,streamHeader); + vgmstream->num_samples = (data_size - block_header_size * num_blocks) / 8 / vgmstream->channels * 14; + /* 0x0c+: unk */ + + dsp_read_coefs_be(vgmstream, streamHeader, chunk_offset+0x18, 0x30); + break; + } +#ifdef VGM_USE_FFMPEG + case 0x08: { /* ATRAC3 [Quantum Theory (PS3)] */ + int block_align, encoder_delay; + + block_align = 0x98 * vgmstream->channels; + encoder_delay = 1024 + 69*2; /* observed default, matches XMA (needed as many files start with garbage) */ + vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_align) - encoder_delay; + /* fix num_samples as header samples seem to be modified to match altered (49999/48001) sample rates somehow */ + + vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ + vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_align); //- encoder_delay + vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_align) - encoder_delay; + break; + } + + case 0x09: { /* XMA2 [Quantum Theory (PS3)] */ + uint8_t buf[0x100]; + int32_t bytes; + + if (!find_chunk_be(streamHeader, 0x584D4558,first_offset,1, &chunk_offset,NULL)) /* "XMEX" */ + goto fail; + /* 0x00: fmt0x166 header (BE) */ + /* 0x34: seek table */ + + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, chunk_offset,0x34, data_size, streamHeader, 1); + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + xma_fix_raw_samples(vgmstream, streamFile, start_offset,data_size, 0, 0,0); /* samples are ok */ + break; + } +#endif + default: + goto fail; + } + + + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; + close_streamfile(streamHeader); + return vgmstream; + +fail: + close_streamfile(streamHeader); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca.c b/Frameworks/vgmstream/vgmstream/src/meta/hca.c index d4d032436..b68724328 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca.c @@ -10,69 +10,73 @@ static void find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t /* CRI HCA - streamed audio from CRI ADX2/Atom middleware */ -VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) { - return init_vgmstream_hca_subkey(streamFile, 0x0000); +VGMSTREAM * init_vgmstream_hca(STREAMFILE* sf) { + return init_vgmstream_hca_subkey(sf, 0x0000); } -VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey) { +VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE* sf, uint16_t subkey) { VGMSTREAM * vgmstream = NULL; - hca_codec_data * hca_data = NULL; + hca_codec_data* hca_data = NULL; + clHCA_stInfo* hca_info; /* checks */ - if ( !check_extensions(streamFile, "hca")) + if (!check_extensions(sf, "hca")) return NULL; - if (((uint32_t)read_32bitBE(0x00,streamFile) & 0x7f7f7f7f) != 0x48434100) /* "HCA\0", possibly masked */ + + if ((read_u32be(0x00,sf) & 0x7F7F7F7F) != 0x48434100) /* "HCA\0", possibly masked */ goto fail; /* init vgmstream and library's context, will validate the HCA */ - hca_data = init_hca(streamFile); + hca_data = init_hca(sf); if (!hca_data) goto fail; + hca_info = hca_get_info(hca_data); + /* find decryption key in external file or preloaded list */ - if (hca_data->info.encryptionEnabled) { + if (hca_info->encryptionEnabled) { uint64_t keycode = 0; uint8_t keybuf[0x08+0x02]; size_t keysize; - keysize = read_key_file(keybuf, 0x08+0x04, streamFile); + keysize = read_key_file(keybuf, 0x08+0x04, sf); if (keysize == 0x08) { /* standard */ - keycode = (uint64_t)get_64bitBE(keybuf+0x00); + keycode = get_u64be(keybuf+0x00); if (subkey) { keycode = keycode * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) ); } } else if (keysize == 0x08+0x02) { /* seed key + AWB subkey */ - uint64_t file_key = (uint64_t)get_64bitBE(keybuf+0x00); - uint16_t file_sub = (uint16_t)get_16bitBE(keybuf+0x08); + uint64_t file_key = get_u64be(keybuf+0x00); + uint16_t file_sub = get_u16be(keybuf+0x08); keycode = file_key * ( ((uint64_t)file_sub << 16u) | ((uint16_t)~file_sub + 2u) ); } #ifdef HCA_BRUTEFORCE else if (1) { - bruteforce_hca_key(streamFile, hca_data, &keycode, subkey); + bruteforce_hca_key(sf, hca_data, &keycode, subkey); } #endif else { find_hca_key(hca_data, &keycode, subkey); } - clHCA_SetKey(hca_data->handle, (unsigned long long)keycode); //maybe should be done through hca_decoder.c? + hca_set_encryption_key(hca_data, keycode); } /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(hca_data->info.channelCount, hca_data->info.loopEnabled); + vgmstream = allocate_vgmstream(hca_info->channelCount, hca_info->loopEnabled); if (!vgmstream) goto fail; vgmstream->meta_type = meta_HCA; - vgmstream->sample_rate = hca_data->info.samplingRate; + vgmstream->sample_rate = hca_info->samplingRate; - vgmstream->num_samples = hca_data->info.blockCount * hca_data->info.samplesPerBlock - - hca_data->info.encoderDelay - hca_data->info.encoderPadding; - vgmstream->loop_start_sample = hca_data->info.loopStartBlock * hca_data->info.samplesPerBlock - - hca_data->info.encoderDelay + hca_data->info.loopStartDelay; - vgmstream->loop_end_sample = hca_data->info.loopEndBlock * hca_data->info.samplesPerBlock - - hca_data->info.encoderDelay + (hca_data->info.samplesPerBlock - hca_data->info.loopEndPadding); + vgmstream->num_samples = hca_info->blockCount * hca_info->samplesPerBlock - + hca_info->encoderDelay - hca_info->encoderPadding; + vgmstream->loop_start_sample = hca_info->loopStartBlock * hca_info->samplesPerBlock - + hca_info->encoderDelay + hca_info->loopStartDelay; + vgmstream->loop_end_sample = hca_info->loopEndBlock * hca_info->samplesPerBlock - + hca_info->encoderDelay + (hca_info->samplesPerBlock - hca_info->loopEndPadding); /* After loop end CRI's encoder removes the rest of the original samples and puts some * garbage in the last frame that should be ignored. Optionally it can encode fully preserving * the file too, but it isn't detectable, so we'll allow the whole thing just in case */ @@ -80,10 +84,10 @@ VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey) { // vgmstream->num_samples = vgmstream->loop_end_sample; /* this can happen in preloading HCA from memory AWB */ - if (hca_data->info.blockCount * hca_data->info.blockSize > get_streamfile_size(streamFile)) { - unsigned int max_block = get_streamfile_size(streamFile) / hca_data->info.blockSize; - vgmstream->num_samples = max_block * hca_data->info.samplesPerBlock - - hca_data->info.encoderDelay - hca_data->info.encoderPadding; + if (hca_info->blockCount * hca_info->blockSize > get_streamfile_size(sf)) { + unsigned int max_block = get_streamfile_size(sf) / hca_info->blockSize; + vgmstream->num_samples = max_block * hca_info->samplesPerBlock - + hca_info->encoderDelay - hca_info->encoderPadding; } vgmstream->coding_type = coding_CRI_HCA; @@ -115,7 +119,7 @@ fail: } -static inline void test_key(hca_codec_data * hca_data, uint64_t key, uint16_t subkey, int *best_score, uint64_t *best_keycode) { +static inline void test_key(hca_codec_data* hca_data, uint64_t key, uint16_t subkey, int* best_score, uint64_t* best_keycode) { int score; if (subkey) { @@ -177,9 +181,10 @@ done: static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* out_keycode, uint16_t subkey) { STREAMFILE* sf_keys = NULL; uint8_t* buf = NULL; - int best_score = -1; + int best_score = 0xFFFFFF, cur_score; off_t keys_size, bytes; - int i, pos; + int pos; + uint64_t old_key = 0; VGM_LOG("HCA: test keys\n"); @@ -198,33 +203,46 @@ static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigne bytes = read_streamfile(buf, 0, keys_size, sf_keys); if (bytes != keys_size) goto done; + VGM_LOG("HCA: start\n"); + pos = 0; while (pos < keys_size - 4) { uint64_t key; + VGM_ASSERT(pos % 0x1000000 == 0, "HCA: pos %x...\n", pos); /* keys are usually u32le lower, u32le upper (u64le) but other orders may exist */ key = ((uint64_t)get_u32le(buf + pos + 0x00) << 0 ) | ((uint64_t)get_u32le(buf + pos + 0x04) << 32); //key = ((uint64_t)get_u32le(buf + pos + 0x00) << 32) | ((uint64_t)get_u32le(buf + pos + 0x04) << 0); //key = ((uint64_t)get_u32be(buf + pos + 0x00) << 0 ) | ((uint64_t)get_u32be(buf + pos + 0x04) << 32); //key = ((uint64_t)get_u32be(buf + pos + 0x00) << 32) | ((uint64_t)get_u32be(buf + pos + 0x04) << 0); - if (key == 0) - continue; + //key = ((uint64_t)get_u32le(buf + pos + 0x00) << 0 ) | 0; /* upper bytes not set, ex. P5 */ + //key = ((uint64_t)get_u32be(buf + pos + 0x00) << 0 ) | 0; /* upper bytes not set, ex. P5 */ - test_key(hca_data, keys[i], subkey, &best_score, out_keycode); - if (best_score == 1) + /* observed files have aligned keys, change if needed */ + pos += 0x04; //pos++; + + if (key == 0 || key == old_key) + continue; + old_key = key; + + cur_score = 0; + test_key(hca_data, key, subkey, &cur_score, out_keycode); + if (cur_score == 1) goto done; - VGM_ASSERT(pos % 0x100000 == 0, "HCA: pos %x...\n", pos); - - /* observed files have aligned keys in the .text section, change if needed */ - pos += 0x04; //pos++; + if (cur_score > 0 && cur_score <= 500) { + VGM_LOG("HCA: possible key=%08x%08x (score=%i) at %x\n", + (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), cur_score, pos-0x04); + if (best_score > cur_score) + best_score = cur_score; + } } done: VGM_ASSERT(best_score > 0, "HCA: best key=%08x%08x (score=%i)\n", (uint32_t)((*out_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*out_keycode & 0xFFFFFFFF), best_score); VGM_ASSERT(best_score < 0, "HCA: key not found\n"); - + close_streamfile(sf_keys); free(buf); } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index fd1ba4765..a8e7899ac 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -26,7 +26,7 @@ static const hcakey_info hcakey_list[] = { // Phantasy Star Online 2 (multi?) // used by most console games - {0xCC55463930DBE1AB}, // CC55463930DBE1AB / 14723751768204501419 + {14723751768204501419u}, // CC55463930DBE1AB // Old Phantasy Star Online 2 (multi?) {61891147883431481}, // 30DBE1ABCC554639 @@ -103,7 +103,7 @@ static const hcakey_info hcakey_list[] = { {8910}, // 00000000000022CE // Tokyo 7th Sisters (iOS/Android) *unconfirmed - {0xFDAE531AAB414BA1}, // FDAE531AAB414BA1 + {18279639311550860193u}, // FDAE531AAB414BA1 // One Piece Dance Battle (iOS/Android) {1905818}, // 00000000001D149A @@ -118,7 +118,7 @@ static const hcakey_info hcakey_list[] = { {4867249871962584729}, // 438BF1F883653699 // Tekken Mobile (iOS/Android) - {0xFFFFFFFFFFFFFFFF}, // FFFFFFFFFFFFFFFF / 18446744073709551615 + {18446744073709551615u}, // FFFFFFFFFFFFFFFF // Tales of the Rays (iOS/Android) {9516284}, // 00000000009134FC @@ -136,7 +136,7 @@ static const hcakey_info hcakey_list[] = { {1224}, // 00000000000004C8 // Schoolgirl Strikers ~Twinkle Melodies~ (iOS/Android) - {0xDB5B61B8343D0000}, // DB5B61B8343D0000 + {15806334760965177344u}, // DB5B61B8343D0000 // Bad Apple Wars (PSVita) {241352432}, // 000000000E62BEF0 @@ -163,7 +163,7 @@ static const hcakey_info hcakey_list[] = { {11708691}, // 0000000000B2A913 // Monster Gear Versus (iOS/Android) - {0xB1E30F346415B475}, // B1E30F346415B475 + {12818105682118423669u}, // B1E30F346415B475 // Yumeiro Cast (iOS/Android) {14418}, // 0000000000003852 @@ -172,13 +172,13 @@ static const hcakey_info hcakey_list[] = { {1000}, // 00000000000003E8 // Zero kara Hajimeru Mahou no Sho (iOS/Android) - {0xD2E836E662F20000}, // D2E836E662F20000 + {15197457305692143616u}, // D2E836E662F20000 // Soul Reverse Zero (iOS/Android) {2873513618}, // 00000000AB465692 // Jojo's Bizarre Adventure: Diamond Records (iOS/Android) [additional data] - {0x820212864CAB35DE}, // 820212864CAB35DE + {9368070542905259486u}, // 820212864CAB35DE // HUNTER x HUNTER: World Hunt (iOS/Android) {71777214294589695}, // 00FF00FF00FF00FF @@ -317,7 +317,7 @@ static const hcakey_info hcakey_list[] = { {3957325206121219506}, // 36EB3E4EE38E05B2 /* Idola: Phantasy Star Saga (Android) */ - {0xA86BF72B4C852CA7}, // A86BF72B4C852CA7 / 12136065386219383975 + {12136065386219383975u}, // A86BF72B4C852CA7 /* Arca Last (Android) */ {612310807}, // 00000000247F1F17 @@ -329,7 +329,7 @@ static const hcakey_info hcakey_list[] = { {3315495188}, // 00000000C59E7114 /* Inazuma Eleven SD (Android) */ - {0xC436E03737D55B5F}, // C436E03737D55B5F / 14138734607940803423 + {14138734607940803423u}, // C436E03737D55B5F /* Detective Conan Runner / Case Closed Runner (Android) */ {1175268187653273344}, // 104f643098e3f700 @@ -340,6 +340,42 @@ static const hcakey_info hcakey_list[] = { /* 22/7 Ongaku no Jikan (Android) */ {20190906}, // 00000000013416BA + /* Cardcaptor Sakura: Happiness Memories (Android) */ + {625144437747651}, // 00023890C8252FC3 + + /* Digimon Story: Cyber Sleuth (PC) */ + {2897314143465725881}, // 283553DCE3FD5FB9 + + /* Alice Re:Code (Android) */ + {9422596198430275382u}, // 82C3C951C561F736 + + /* Tokyo 7th Sisters (Android) */ + {18279639311550860193u}, // FDAE531AAB414BA1 + + /* High School Fleet: Kantai Battle de Pinch! (Mobile) */ + {43472919336422565}, // 009A7263CA658CA5 + + /* Disney's Twisted Wonderland (Android) */ + {2895000877}, // 00000000AC8E352D + + /* B-PROJECT Kaikan Everyday (Android) */ + {12316546176516217334u}, // AAED297DDEF1D9F6 + + /* HELIOS Rising Heroes (Android) */ + {311981570940334162}, // 04546195F85DF052 + + /* World Ends's Club (iOS) */ + {50979632184989243}, // 00B51DB4932A963B + + /* Kandagawa Jet Girls (PC) */ + {6235253715273671}, // 001626EE22C887C7 + + /* Re:Zero - Lost in Memories (Android) */ + {1611432018519751642}, // 165CF4E2138F7BDA + + /* D4DJ Groovy Mix (Android) [base files] */ + {393410674916959300}, // 0575ACECA945A444 + /* Dragalia Lost (iOS/Android) */ {2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ikm.c b/Frameworks/vgmstream/vgmstream/src/meta/ikm.c index d6044c9fd..7c2456ebd 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ikm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ikm.c @@ -3,24 +3,24 @@ /* IKM - MiCROViSiON PS2 container [Zwei (PS2)] */ -VGMSTREAM * init_vgmstream_ikm_ps2(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_ikm_ps2(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; int loop_flag, channel_count; /* checks */ - if ( !check_extensions(streamFile,"ikm") ) + if ( !check_extensions(sf,"ikm") ) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x494B4D00) /* "IKM\0" */ + if (read_u32be(0x00,sf) != 0x494B4D00) /* "IKM\0" */ goto fail; - if (read_32bitBE(0x40,streamFile) != 0x41535400) /* "AST\0" */ + if (read_u32be(0x40,sf) != 0x41535400) /* "AST\0" */ goto fail; /* 0x20: type 03? */ - loop_flag = (read_32bitLE(0x14, streamFile) > 0); - channel_count = read_32bitLE(0x50, streamFile); + loop_flag = (read_s32le(0x14, sf) > 0); + channel_count = read_s32le(0x50, sf); start_offset = 0x800; @@ -29,15 +29,15 @@ VGMSTREAM * init_vgmstream_ikm_ps2(STREAMFILE *streamFile) { if (!vgmstream) goto fail; vgmstream->meta_type = meta_IKM; - vgmstream->sample_rate = read_32bitLE(0x44, streamFile); - vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x4c, streamFile), channel_count); - vgmstream->loop_start_sample = read_32bitLE(0x14, streamFile); - vgmstream->loop_end_sample = read_32bitLE(0x18, streamFile); + vgmstream->sample_rate = read_s32le(0x44, sf); + vgmstream->num_samples = ps_bytes_to_samples(read_s32le(0x4c, sf), channel_count); + vgmstream->loop_start_sample = read_s32le(0x14, sf); + vgmstream->loop_end_sample = read_s32le(0x18, sf); vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; /* @0x40 / channels */ - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; @@ -46,34 +46,41 @@ fail: return NULL; } -/* IKM - MiCROViSiON PC container [Chaos Legion (PC)] */ -VGMSTREAM * init_vgmstream_ikm_pc(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +/* IKM - MiCROViSiON PC container [Chaos Legion (PC), Legend of Galactic Heroes (PC)] */ +VGMSTREAM* init_vgmstream_ikm_pc(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; /* checks */ - if ( !check_extensions(streamFile,"ikm") ) + if ( !check_extensions(sf,"ikm") ) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x494B4D00) /* "IKM\0" */ - goto fail; - if (read_32bitBE(0x30,streamFile) != 0x4F676753) /* "OggS" */ + if (read_u32be(0x00,sf) != 0x494B4D00) /* "IKM\0" */ goto fail; /* 0x20: type 01? */ - start_offset = 0x30; + /* find "OggS" start */ + if (read_u32be(0x30,sf) == 0x4F676753) { + start_offset = 0x30; /* Chaos Legion (PC) */ + } else if (read_u32be(0x800,sf) == 0x4F676753) { + start_offset = 0x800; /* Legend of Galactic Heroes (PC) */ + } else { + goto fail; + } + + #ifdef VGM_USE_VORBIS { ogg_vorbis_meta_info_t ovmi = {0}; ovmi.meta_type = meta_IKM; - ovmi.loop_start = read_32bitLE(0x14, streamFile); - ovmi.loop_end = read_32bitLE(0x18, streamFile); + ovmi.loop_start = read_s32le(0x14, sf); + ovmi.loop_end = read_s32le(0x18, sf); ovmi.loop_end_found = ovmi.loop_end; ovmi.loop_flag = ovmi.loop_end > 0; - ovmi.stream_size = read_32bitLE(0x24, streamFile); + ovmi.stream_size = read_s32le(0x24, sf); - vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); + vgmstream = init_vgmstream_ogg_vorbis_callbacks(sf, NULL, start_offset, &ovmi); } #else goto fail; @@ -87,39 +94,39 @@ fail: } /* IKM - MiCROViSiON PSP container [The Legend of Heroes: A Tear of Vermillion (PSP)] */ -VGMSTREAM * init_vgmstream_ikm_psp(STREAMFILE *streamFile) { - VGMSTREAM *vgmstream = NULL; - STREAMFILE *temp_streamFile = NULL; +VGMSTREAM* init_vgmstream_ikm_psp(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; off_t start_offset; size_t data_size; /* checks */ - if ( !check_extensions(streamFile,"ikm") ) + if (!check_extensions(sf,"ikm")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x494B4D00) /* "IKM\0" */ + if (read_u32be(0x00,sf) != 0x494B4D00) /* "IKM\0" */ goto fail; - if (read_32bitBE(0x800,streamFile) != 0x52494646) /* "RIFF" */ + if (read_u32be(0x800,sf) != 0x52494646) /* "RIFF" */ goto fail; /* 0x20: type 00? */ /* loop values (pre-adjusted without encoder delay) at 0x14/18 are found in the RIFF too */ - data_size = read_32bitLE(0x24, streamFile); + data_size = read_s32le(0x24, sf); start_offset = 0x800; - temp_streamFile = setup_subfile_streamfile(streamFile, start_offset, data_size, "at3"); - if (!temp_streamFile) goto fail; + temp_sf = setup_subfile_streamfile(sf, start_offset, data_size, "at3"); + if (!temp_sf) goto fail; - vgmstream = init_vgmstream_riff(temp_streamFile); + vgmstream = init_vgmstream_riff(temp_sf); if (!vgmstream) goto fail; vgmstream->meta_type = meta_IKM; - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); return vgmstream; fail: - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/imuse.c b/Frameworks/vgmstream/vgmstream/src/meta/imuse.c index 5b6222cc0..a8706ebec 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/imuse.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/imuse.c @@ -108,23 +108,19 @@ VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf) { if (!is_id4("DATA", head_offset + 0x10 + map_size + 0x00, sf)) goto fail; data_bytes = read_u32be(head_offset + 0x10 + map_size + 0x04, sf); - num_samples = data_bytes / channels / sizeof(int16_t); - //num_samples = (read_u32be(head_offset + 0x04,sf) - head_size) / channels / sizeof(int16_t); /* equivalent */ } else if (is_id4("RIFF", head_offset, sf)) { /* MCMP voices */ - /* standard (LE), with fake codec 1 and sizes also in decoded bytes (see above) */ + /* standard (LE), with fake codec 1 and sizes also in decoded bytes (see above), + * has standard RIFF chunks (may include extra), start offset in MCSC */ - if (!is_id4("fmt ", head_offset + 0x0c, sf)) + if (!find_chunk_le(sf, 0x666D7420, head_offset + 0x0c, 0, &offset, NULL)) /* "fmt " */ goto fail; - offset = head_offset + 0x14; channels = read_u16le(offset + 0x02,sf); sample_rate = read_u32le(offset + 0x04,sf); - if (!is_id4("data", head_offset + 0x24, sf)) + if (!find_chunk_le(sf, 0x64617461, head_offset + 0x0c, 0, NULL, &data_bytes)) /*"data"*/ goto fail; - data_bytes = read_u32le(head_offset + 0x28, sf); - num_samples = data_bytes / channels / sizeof(int16_t); } else { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ivaud.c b/Frameworks/vgmstream/vgmstream/src/meta/ivaud.c index 8a85a5567..0fd4a368c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ivaud.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ivaud.c @@ -1,192 +1,232 @@ -#include "meta.h" -#include "../layout/layout.h" -#include "../util.h" - -typedef struct { - int is_music; - - int total_subsongs; - - int channel_count; - int sample_rate; - int codec; - int num_samples; - - size_t block_count; - size_t block_size; - - off_t stream_offset; - size_t stream_size; - -} ivaud_header; - -static int parse_ivaud_header(STREAMFILE* streamFile, ivaud_header* ivaud); - - -/* .ivaud - from GTA IV (PC) */ -VGMSTREAM * init_vgmstream_ivaud(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - ivaud_header ivaud = {0}; - int loop_flag; - - /* checks */ - /* (hashed filenames are likely extensionless and .ivaud is added by tools) */ - if (!check_extensions(streamFile, "ivaud,")) - goto fail; - - /* check header */ - if (!parse_ivaud_header(streamFile, &ivaud)) - goto fail; - - - loop_flag = 0; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(ivaud.channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = ivaud.sample_rate; - vgmstream->num_samples = ivaud.num_samples; - vgmstream->num_streams = ivaud.total_subsongs; - vgmstream->stream_size = ivaud.stream_size; - vgmstream->meta_type = meta_IVAUD; - - switch(ivaud.codec) { - case 0x0001: /* common in sfx, uncommon in music (ex. EP2_SFX/MENU_MUSIC) */ - vgmstream->coding_type = coding_PCM16LE; - vgmstream->layout_type = ivaud.is_music ? layout_blocked_ivaud : layout_none; - vgmstream->full_block_size = ivaud.block_size; - break; - - case 0x0400: - vgmstream->coding_type = coding_IMA_int; - vgmstream->layout_type = ivaud.is_music ? layout_blocked_ivaud : layout_none; - vgmstream->full_block_size = ivaud.block_size; - break; - - default: - VGM_LOG("IVAUD: unknown codec 0x%x\n", ivaud.codec); - goto fail; - } - - - if (!vgmstream_open_stream(vgmstream,streamFile,ivaud.stream_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - -/* Parse Rockstar's .ivaud header (much info from SparkIV). */ -static int parse_ivaud_header(STREAMFILE* streamFile, ivaud_header* ivaud) { - int target_subsong = streamFile->stream_index; - - - /* use bank's stream count to detect */ - ivaud->is_music = (read_32bitLE(0x10,streamFile) == 0); - - if (ivaud->is_music) { - off_t block_table_offset, channel_table_offset, channel_info_offset; - - /* music header */ - block_table_offset = read_32bitLE(0x00,streamFile); /* 64b */ - ivaud->block_count = read_32bitLE(0x08,streamFile); - ivaud->block_size = read_32bitLE(0x0c,streamFile); /* 64b, uses padded blocks */ - channel_table_offset = read_32bitLE(0x14,streamFile); /* 64b */ - /* 0x1c(8): block_table_offset again? */ - ivaud->channel_count = read_32bitLE(0x24,streamFile); - /* 0x28(4): unknown entries? */ - ivaud->stream_offset = read_32bitLE(0x2c,streamFile); - channel_info_offset = channel_table_offset + ivaud->channel_count*0x10; - - if ((ivaud->block_count * ivaud->block_size) + ivaud->stream_offset != get_streamfile_size(streamFile)) { - VGM_LOG("IVAUD: bad file size\n"); - goto fail; - } - - /* channel table (one entry per channel, points to channel info) */ - /* 0x00(8): offset within channel_info_offset */ - /* 0x08(4): hash */ - /* 0x0c(4): size */ - - /* channel info (one entry per channel) */ - /* 0x00(8): offset within data (should be 0) */ - /* 0x08(4): hash */ - /* 0x0c(4): half num_samples? */ - ivaud->num_samples = read_32bitLE(channel_info_offset+0x10,streamFile); - /* 0x14(4): unknown (-1) */ - /* 0x18(2): sample rate */ - /* 0x1a(2): unknown */ - ivaud->codec = read_32bitLE(channel_info_offset+0x1c,streamFile); - /* (when codec is IMA) */ - /* 0x20(8): adpcm states offset, 0x38: num states? (reference for seeks?) */ - /* rest: unknown data */ - - /* block table (one entry per block) */ - /* 0x00: data size processed up to this block (doesn't count block padding) */ - ivaud->sample_rate = read_32bitLE(block_table_offset + 0x04,streamFile); - /* sample_rate should agree with each channel in the channel table */ - - - ivaud->total_subsongs = 1; - ivaud->stream_size = get_streamfile_size(streamFile); - } - else { - off_t stream_table_offset, stream_info_offset, stream_entry_offset; - - /* bank header */ - stream_table_offset = read_32bitLE(0x00,streamFile); /* 64b */ - /* 0x08(8): header size? start offset? */ - ivaud->total_subsongs = read_32bitLE(0x10,streamFile); - /* 0x14(4): unknown */ - ivaud->stream_offset = read_32bitLE(0x18,streamFile); /* base start_offset */ - - if (target_subsong == 0) target_subsong = 1; - if (target_subsong < 0 || target_subsong > ivaud->total_subsongs || ivaud->total_subsongs < 1) goto fail; - - if (stream_table_offset != 0x1c) - goto fail; - stream_info_offset = stream_table_offset + 0x10*ivaud->total_subsongs; - - /* stream table (one entry per stream, points to stream info) */ - stream_entry_offset = read_32bitLE(stream_table_offset + 0x10*(target_subsong-1) + 0x00,streamFile); /* within stream info */ - /* 0x00(8): offset within stream_info_offset */ - /* 0x08(4): hash */ - /* 0x0c(4): size */ - - /* stream info (one entry per stream) */ - ivaud->stream_offset += read_32bitLE(stream_info_offset+stream_entry_offset+0x00,streamFile); /* 64b, within data */ - /* 0x08(4): hash */ - /* 0x0c(4): half num_samples? */ - ivaud->num_samples = read_32bitLE(stream_info_offset+stream_entry_offset+0x10,streamFile); - /* 0x14(4): unknown (-1) */ - ivaud->sample_rate = (uint16_t)read_16bitLE(stream_info_offset+stream_entry_offset+0x18,streamFile); - /* 0x1a(2): unknown */ - ivaud->codec = read_32bitLE(stream_info_offset+stream_entry_offset+0x1c,streamFile); - /* (when codec is IMA) */ - /* 0x20(8): adpcm states offset, 0x38: num states? (reference for seeks?) */ - /* rest: unknown data */ - - ivaud->channel_count = 1; - - /* ghetto size calculator (could substract offsets but streams are not ordered) */ - switch(ivaud->codec) { - case 0x0001: - ivaud->stream_size = ivaud->num_samples * 2; /* double 16b PCM */ - break; - case 0x0400: - ivaud->stream_size = ivaud->num_samples / 2; /* half nibbles */ - break; - default: - break; - } - } - - - return 1; -fail: - return 0; -} +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + +typedef struct { + int is_music; + + int total_subsongs; + + int channel_count; + int sample_rate; + int codec; + int num_samples; + + size_t block_count; + size_t block_size; + + off_t stream_offset; + size_t stream_size; + + int big_endian; +} ivaud_header; + +static int parse_ivaud_header(STREAMFILE* sf, ivaud_header* ivaud); + + +/* .ivaud - from GTA IV (PC/PS3/X360) */ +VGMSTREAM* init_vgmstream_ivaud(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + ivaud_header ivaud = {0}; + int loop_flag; + + /* checks */ + /* (hashed filenames are likely extensionless and .ivaud is added by tools) */ + if (!check_extensions(sf, "ivaud,")) + goto fail; + + /* check header */ + if (!parse_ivaud_header(sf, &ivaud)) + goto fail; + + + loop_flag = 0; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(ivaud.channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = ivaud.sample_rate; + vgmstream->num_samples = ivaud.num_samples; + vgmstream->num_streams = ivaud.total_subsongs; + vgmstream->stream_size = ivaud.stream_size; + vgmstream->meta_type = meta_IVAUD; + + switch(ivaud.codec) { + case 0x0001: /* common in sfx, uncommon in music (ex. EP2_SFX/MENU_MUSIC) */ + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = ivaud.is_music ? layout_blocked_ivaud : layout_none; + vgmstream->full_block_size = ivaud.block_size; + break; + +#ifdef VGM_USE_FFMPEG + case 0x0000: { /* XMA2 (X360) */ + uint8_t buf[0x100]; + size_t bytes; + + if (ivaud.is_music) { + goto fail; + } + else { + /* regular XMA for sfx */ + bytes = ffmpeg_make_riff_xma1(buf, 0x100, ivaud.num_samples, ivaud.stream_size, ivaud.channel_count, ivaud.sample_rate, 0); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, ivaud.stream_offset, ivaud.stream_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + xma_fix_raw_samples(vgmstream, sf, ivaud.stream_offset, ivaud.stream_size, 0, 0,0); /* samples are ok? */ + } + break; + } +#endif + +#ifdef VGM_USE_MPEG + case 0x0100: { /* MPEG (PS3) */ + mpeg_custom_config cfg = {0}; + + if (ivaud.is_music) { + goto fail; + } + else { + cfg.chunk_size = ivaud.block_size; + cfg.big_endian = ivaud.big_endian; + + vgmstream->codec_data = init_mpeg_custom(sf, ivaud.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + } + break; + } +#endif + + case 0x0400: /* PC */ + vgmstream->coding_type = coding_IMA_int; + vgmstream->layout_type = ivaud.is_music ? layout_blocked_ivaud : layout_none; + vgmstream->full_block_size = ivaud.block_size; + break; + + default: + VGM_LOG("IVAUD: unknown codec 0x%x\n", ivaud.codec); + goto fail; + } + + + if (!vgmstream_open_stream(vgmstream,sf,ivaud.stream_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* Parse Rockstar's .ivaud header (much info from SparkIV). */ +static int parse_ivaud_header(STREAMFILE* sf, ivaud_header* ivaud) { + int target_subsong = sf->stream_index; + uint64_t (*read_u64)(off_t,STREAMFILE*); + uint32_t (*read_u32)(off_t,STREAMFILE*); + uint16_t (*read_u16)(off_t,STREAMFILE*); + + + ivaud->big_endian = read_u32be(0x00, sf) == 0; /* table offset at 0x04 > BE (64b) */ + read_u64 = ivaud->big_endian ? read_u64be : read_u64le; + read_u32 = ivaud->big_endian ? read_u32be : read_u32le; + read_u16 = ivaud->big_endian ? read_u16be : read_u16le; + + /* use bank's stream count to detect */ + ivaud->is_music = (read_u32(0x10,sf) == 0); + + if (ivaud->is_music) { + off_t block_table_offset, channel_table_offset, channel_info_offset; + + /* music header */ + block_table_offset = read_u64(0x00,sf); + ivaud->block_count = read_u32(0x08,sf); + ivaud->block_size = read_u32(0x0c,sf); /* uses padded blocks */ + /* 0x10(4): stream count */ + channel_table_offset = read_u64(0x14,sf); + /* 0x1c(8): block_table_offset again? */ + ivaud->channel_count = read_u32(0x24,sf); + /* 0x28(4): unknown entries? */ + ivaud->stream_offset = read_u32(0x2c,sf); + channel_info_offset = channel_table_offset + ivaud->channel_count * 0x10; + + if ((ivaud->block_count * ivaud->block_size) + ivaud->stream_offset != get_streamfile_size(sf)) { + VGM_LOG("IVAUD: bad file size\n"); + goto fail; + } + + /* channel table (one entry per channel, points to channel info) */ + /* 0x00(8): offset within channel_info_offset */ + /* 0x08(4): hash */ + /* 0x0c(4): size */ + + /* channel info (one entry per channel) */ + /* 0x00(8): offset within data (should be 0) */ + /* 0x08(4): hash */ + /* 0x0c(4): half num_samples? */ + ivaud->num_samples = read_u32(channel_info_offset+0x10,sf); + /* 0x14(4): unknown (-1) */ + /* 0x18(2): sample rate */ + /* 0x1a(2): unknown */ + ivaud->codec = read_u32(channel_info_offset+0x1c,sf); + /* (when codec is IMA) */ + /* 0x20(8): adpcm states offset, 0x38: num states? (reference for seeks?) */ + /* rest: unknown data */ + + /* block table (one entry per block) */ + /* 0x00: data size processed up to this block (doesn't count block padding) */ + ivaud->sample_rate = read_u32(block_table_offset + 0x04,sf); + /* sample_rate should agree with each channel in the channel table */ + + + ivaud->total_subsongs = 1; + ivaud->stream_size = get_streamfile_size(sf); + } + else { + off_t stream_table_offset, stream_info_offset, stream_entry_offset, offset; + + /* bank header */ + stream_table_offset = read_u64(0x00,sf); + /* 0x08(8): header size? start offset? */ + ivaud->total_subsongs = read_u32(0x10,sf); + /* 0x14(4): unknown */ + ivaud->stream_offset = read_u32(0x18,sf); /* base start_offset */ + + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > ivaud->total_subsongs || ivaud->total_subsongs < 1) goto fail; + + if (stream_table_offset != 0x1c) + goto fail; + stream_info_offset = stream_table_offset + 0x10*ivaud->total_subsongs; + + /* stream table (one entry per stream, points to stream info) */ + stream_entry_offset = read_u64(stream_table_offset + 0x10*(target_subsong-1) + 0x00,sf); /* within stream info */ + /* 0x00(8): offset within stream_info_offset */ + /* 0x08(4): hash */ + /* 0x0c(4): some offset/size */ + + /* stream info (one entry per stream) */ + offset = stream_info_offset + stream_entry_offset; + ivaud->stream_offset += read_u64(offset+0x00,sf); /* within data */ + /* 0x08(4): hash */ + ivaud->stream_size = read_u32(offset+0x0c,sf); + ivaud->num_samples = read_u32(offset+0x10,sf); + /* 0x14(4): unknown (-1) */ + ivaud->sample_rate = read_u16(offset+0x18,sf); + /* 0x1a(2): unknown */ + ivaud->codec = read_u32(offset+0x1c,sf); + /* (when codec is IMA) */ + /* 0x20(8): adpcm states offset, 0x38: num states? (reference for seeks?) */ + /* rest: unknown data */ + + ivaud->channel_count = 1; + } + + + return 1; +fail: + return 0; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktsc.c b/Frameworks/vgmstream/vgmstream/src/meta/ktsc.c new file mode 100644 index 000000000..e438075c4 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktsc.c @@ -0,0 +1,60 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../layout/layout.h" + + +/* KTSC - Koei Tecmo KTSR container */ +VGMSTREAM* init_vgmstream_ktsc(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE *temp_sf = NULL; + int target_subsong = sf->stream_index, total_subsongs; + off_t offset, subfile_offset; + size_t subfile_size; + + + /* checks */ + /* .ktsl2asbin: common [Atelier Ryza (PC)] */ + if (!check_extensions(sf, "ktsl2asbin")) + goto fail; + + /* KTSC is a container of KTSRs, but can't be extracted easily as they use absolute pointers to the + * same stream companion file. KTSRs may have subsongs, but only seem to have 1, so use KTSC's subsongs. */ + if (read_u32be(0x00, sf) != 0x4B545343) /* "KTSC" */ + goto fail; + if (read_u32be(0x04, sf) != 0x01000001) /* version? */ + goto fail; + + if (target_subsong == 0) target_subsong = 1; + total_subsongs = read_u32le(0x08, sf); + if (target_subsong > total_subsongs) + goto fail; + + /* 0x0c: CRC(?) table start */ + offset = read_u32le(0x10, sf); + /* 0x14: file size */ + /* 0x18: header end */ + /* 0x1c: null */ + /* 0x20+: CRC(?) table, 1 entry per file */ + + subfile_offset = read_u32le(offset + 0x04 * (target_subsong - 1), sf); + subfile_size = read_u32le(subfile_offset + 0x1c, sf); /* from header, meh */ + + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, NULL); + if (!temp_sf) goto fail; + + temp_sf->stream_index = 1; + vgmstream = init_vgmstream_ktsr(temp_sf); + if (!vgmstream) goto fail; + + if (vgmstream->num_streams > 1) + goto fail; + vgmstream->num_streams = total_subsongs; + + close_streamfile(temp_sf); + return vgmstream; + +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktsr.c b/Frameworks/vgmstream/vgmstream/src/meta/ktsr.c index 63223977c..ce32b772b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ktsr.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktsr.c @@ -124,29 +124,6 @@ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) { vgmstream->layout_type = layout_layered; vgmstream->coding_type = coding_ATRAC9; break; - -#if 0 - atrac9_config cfg = {0}; - if (ktsr.channels > 1) { - VGM_LOG("1\n"); - goto fail; - } - - /* 0x00: samples per frame */ - /* 0x02: frame size */ - cfg.config_data = read_u32be(ktsr.extra_offset + 0x04, sf_b); - if ((cfg.config_data & 0xFF) == 0xFE) /* later versions(?) in LE */ - cfg.config_data = read_u32le(ktsr.extra_offset + 0x04, sf_b); - - cfg.channels = vgmstream->channels; - cfg.encoder_delay = 256; /* observed default (ex. Attack on Titan PC vs Vita) */ - - vgmstream->codec_data = init_atrac9(&cfg); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_ATRAC9; - vgmstream->layout_type = layout_none; - break; -#endif } #endif @@ -312,6 +289,8 @@ static int parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, off_t offset) { switch(type) { /* hash-id? */ case 0x38D0437D: /* external [Nioh (PC), Atelier Ryza (PC)] */ + case 0xDF92529F: /* external [Atelier Ryza (PC)] */ + case 0x6422007C: /* external [Atelier Ryza (PC)] */ /* 08 subtype? (ex. 0x522B86B9) * 0c channels * 10 ? (always 0x002706B8) @@ -465,7 +444,6 @@ static void parse_longname(ktsr_header* ktsr, STREAMFILE* sf, uint32_t target_id offset += size; } - } static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { @@ -503,6 +481,7 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { case 0x6172DBA8: /* padding (empty) */ case 0xBD888C36: /* config (floats, stream id, etc, may have extended name) */ case 0xC9C48EC1: /* unknown (has some string inside like "boss") */ + case 0xA9D23BF1: /* "state container", some kind of config/floats, witgh names like 'State_bgm01'..N */ break; case 0xC5CCCB70: /* sound (internal data or external stream) */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/kwb.c b/Frameworks/vgmstream/vgmstream/src/meta/kwb.c index 6a9c14387..d40aac1d7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/kwb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/kwb.c @@ -1,9 +1,10 @@ #include "meta.h" #include "../coding/coding.h" -typedef enum { PCM16, MSADPCM, DSP, AT9 } kwb_codec; +typedef enum { PCM16, MSADPCM, DSP_HEAD, DSP_BODY, AT9, MSF } kwb_codec; typedef struct { + int big_endian; int total_subsongs; int target_subsong; kwb_codec codec; @@ -24,13 +25,15 @@ typedef struct { } kwb_header; static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b); +static int parse_xws(kwb_header* kwb, STREAMFILE* sf); /* KWB - WaveBank from Koei games */ -VGMSTREAM * init_vgmstream_kwb(STREAMFILE* sf) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_kwb(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; STREAMFILE *sf_h = NULL, *sf_b = NULL; kwb_header kwb = {0}; + int32_t (*read_s32)(off_t,STREAMFILE*) = NULL; int target_subsong = sf->stream_index; @@ -67,6 +70,7 @@ VGMSTREAM * init_vgmstream_kwb(STREAMFILE* sf) { if (!parse_kwb(&kwb, sf_h, sf_b)) goto fail; + read_s32 = kwb.big_endian ? read_s32be : read_s32le; /* build the VGMSTREAM */ @@ -92,13 +96,24 @@ VGMSTREAM * init_vgmstream_kwb(STREAMFILE* sf) { vgmstream->frame_size = kwb.block_size; break; - case DSP: + case DSP_HEAD: + case DSP_BODY: if (kwb.channels > 1) goto fail; vgmstream->coding_type = coding_NGC_DSP; /* subinterleave? */ vgmstream->layout_type = layout_interleave; - vgmstream->layout_type = 0x08; - dsp_read_coefs_le(vgmstream, sf_h, kwb.dsp_offset + 0x1c, 0x60); - dsp_read_hist_le (vgmstream, sf_h, kwb.dsp_offset + 0x40, 0x60); + vgmstream->interleave_block_size = 0x08; + if (kwb.codec == DSP_HEAD) { + dsp_read_coefs(vgmstream, sf_h, kwb.dsp_offset + 0x1c, 0x60, kwb.big_endian); + dsp_read_hist (vgmstream, sf_h, kwb.dsp_offset + 0x40, 0x60, kwb.big_endian); + } + else { + /* typical DSP header + data */ + vgmstream->num_samples = read_s32(kwb.stream_offset + 0x00, sf_b); + dsp_read_coefs(vgmstream, sf_b, kwb.stream_offset + 0x1c, 0x60, kwb.big_endian); + dsp_read_hist (vgmstream, sf_b, kwb.stream_offset + 0x40, 0x60, kwb.big_endian); + kwb.stream_offset += 0x60; + } + break; #ifdef VGM_USE_ATRAC9 @@ -130,8 +145,6 @@ VGMSTREAM * init_vgmstream_kwb(STREAMFILE* sf) { break; } #endif - - default: goto fail; } @@ -148,6 +161,54 @@ fail: return NULL; } +/* XWS - WaveStream? from Koei games */ +VGMSTREAM* init_vgmstream_xws(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + kwb_header kwb = {0}; + int target_subsong = sf->stream_index; + + + /* checks */ + if (!check_extensions(sf, "xws")) + goto fail; + + if (target_subsong == 0) target_subsong = 1; + kwb.target_subsong = target_subsong; + + if (!parse_xws(&kwb, sf)) + goto fail; + + if (kwb.codec == MSF) { + if (kwb.stream_offset == 0) { + vgmstream = init_vgmstream_silence(0,0,0); /* dummy, whatevs */ + if (!vgmstream) goto fail; + } + else { + kwb.stream_size = read_u32be(kwb.stream_offset + 0x0c, sf) + 0x40; + + temp_sf = setup_subfile_streamfile(sf, kwb.stream_offset, kwb.stream_size, "msf"); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_msf(temp_sf); + if (!vgmstream) goto fail; + } + + vgmstream->num_streams = kwb.total_subsongs; + } + else { + goto fail; + } + + close_streamfile(temp_sf); + return vgmstream; +fail: + close_streamfile(temp_sf); + return NULL; +} + + + static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { int i, j, sounds; @@ -220,7 +281,7 @@ static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { kwb->codec = MSADPCM; break; case 0x90: - kwb->codec = DSP; + kwb->codec = DSP_HEAD; kwb->dsp_offset = subsound_offset + 0x4c; break; default: @@ -257,8 +318,8 @@ static int parse_type_k4hd(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { /* a format mimicking PSVita's hd4+bd4 format */ - /* 00 K4HD id */ - /* 04 chunk size */ + /* 00: K4HD id */ + /* 04: chunk size */ /* 08: ? */ /* 0c: ? */ /* 10: PPPG offset ('program'? cues?) */ @@ -271,7 +332,7 @@ static int parse_type_k4hd(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { if (read_u32be(ppva_offset + 0x00, sf_h) != 0x50505641) /* "PPVA" */ goto fail; - entry_size = read_u32le(ppva_offset + 0x08, sf_h); /* */ + entry_size = read_u32le(ppva_offset + 0x08, sf_h); /* 0x0c: -1? */ /* 0x10: 0? */ entries = read_u32le(ppva_offset + 0x14, sf_h) + 1; @@ -314,16 +375,78 @@ static int parse_type_sdsd(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { return 0; } +static int parse_type_sdwi(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { + off_t smpl_offset, header_offset; + int entries; + size_t entry_size; + + + /* variation of SDsd */ + /* 00: SDWiVers */ + /* 08: chunk size */ + /* 0c: null */ + /* 10: SDsdHead */ + /* 18: chunk size */ + /* 1c: WBH_ size */ + /* 20: WBD_ size */ + /* 24: SDsdProg offset ('program'? cues?) */ + /* 28: SDsdSmpl offset ('samples'? waves?) */ + /* rest: ? */ + smpl_offset = read_u32be(offset + 0x28, sf_h); + smpl_offset += offset; + + /* Smpl table: */ + if (read_u32be(smpl_offset + 0x00, sf_h) != 0x53447364 && /* "SDsd" */ + read_u32be(smpl_offset + 0x04, sf_h) != 0x536D706C) /* "Smpl" */ + goto fail; + + /* 0x08: ? */ + entries = read_u32le(smpl_offset + 0x0c, sf_h); /* LE! */ + entry_size = 0x40; + + kwb->total_subsongs = entries; + if (kwb->target_subsong < 0 || kwb->target_subsong > kwb->total_subsongs || kwb->total_subsongs < 1) goto fail; + + header_offset = smpl_offset + 0x10 + (kwb->target_subsong-1) * entry_size; + + /* 00: "SS" + ID (0..N) */ + kwb->stream_offset = read_u32be(header_offset + 0x04, sf_h); + /* 08: flag? */ + /* 0c: ? + channels? */ + kwb->sample_rate = read_u32be(header_offset + 0x10, sf_h); + /* 14: bitrate */ + /* 18: codec? + bps */ + /* 1c: null? */ + /* 20: null? */ + kwb->stream_size = read_u32be(header_offset + 0x24, sf_h); + /* 28: full stream size (with padding) */ + /* 2c: related to samples? */ + /* 30: ID */ + /* 34-38: null */ + + kwb->codec = DSP_BODY; + kwb->channels = 1; + + return 1; +fail: + return 0; +} + static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) { off_t head_offset, body_offset, start; uint32_t type; + uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; if (read_u32be(0x00, sf_h) == 0x57484431) { /* "WHD1" */ - /* container of wbh+wbd */ + /* container of fused .wbh+wbd */ /* 0x04: fixed value? */ - /* 0x08: version? */ - start = read_u32le(0x0c, sf_h); + kwb->big_endian = read_u8(0x08, sf_h) == 0xFF; + /* 0x0a: version? */ + + read_u32 = kwb->big_endian ? read_u32be : read_u32le; + + start = read_u32(0x0c, sf_h); /* 0x10: file size */ /* 0x14: subfiles? */ /* 0x18: subfiles? */ @@ -331,8 +454,8 @@ static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) { /* 0x20: some size? */ /* 0x24: some size? */ - head_offset = read_u32le(start + 0x00, sf_h); - body_offset = read_u32le(start + 0x04, sf_h); + head_offset = read_u32(start + 0x00, sf_h); + body_offset = read_u32(start + 0x04, sf_h); /* 0x10: head size */ /* 0x14: body size */ } @@ -340,13 +463,17 @@ static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) { /* dual file */ head_offset = 0x00; body_offset = 0x00; + + kwb->big_endian = guess_endianness32bit(head_offset + 0x08, sf_h); + + read_u32 = kwb->big_endian ? read_u32be : read_u32le; } - if (read_u32be(head_offset + 0x00, sf_h) != 0x5F484257 || /* "_HBW" */ - read_u32be(head_offset + 0x04, sf_h) != 0x30303030) /* "0000" */ + if (read_u32(head_offset + 0x00, sf_h) != 0x5742485F || /* "WBH_" */ + read_u32(head_offset + 0x04, sf_h) != 0x30303030) /* "0000" */ goto fail; - if (read_u32be(body_offset + 0x00, sf_b) != 0x5F444257 || /* "_DBW" */ - read_u32be(body_offset + 0x04, sf_b) != 0x30303030) /* "0000" */ + if (read_u32(body_offset + 0x00, sf_b) != 0x5742445F || /* "WBD_" */ + read_u32(body_offset + 0x04, sf_b) != 0x30303030) /* "0000" */ goto fail; /* 0x08: head/body size */ @@ -356,26 +483,109 @@ static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) { /* format has multiple bank subtypes that are quite different from each other */ type = read_u32be(head_offset + 0x00, sf_h); switch(type) { - case 0x4B574232: /* "KWB2" (PC) */ - case 0x4B57424E: /* "KWBN" (Switch) */ + case 0x4B574232: /* "KWB2" [Bladestorm Nightmare (PC), Dissidia NT (PC)] */ + case 0x4B57424E: /* "KWBN" [Fire Emblem Warriors (Switch)] */ if (!parse_type_kwb2(kwb, head_offset, sf_h)) goto fail; break; - case 0x4B344844: /* "K4HD" (PS4/Vita) */ + case 0x4B344844: /* "K4HD" [Dissidia NT (PS4), (Vita) */ if (!parse_type_k4hd(kwb, head_offset, sf_h)) goto fail; break; - case 0x53447364: /* "SDsd" (PS3?) */ + case 0x53447364: /* "SDsd" (PS3? leftover files) */ if (!parse_type_sdsd(kwb, head_offset, sf_h)) goto fail; break; + case 0x53445769: /* "SDWi" [Fatal Frame 5 (WiiU)] */ + if (!parse_type_sdwi(kwb, head_offset, sf_h)) + goto fail; + break; + default: goto fail; } + kwb->stream_offset += body_offset; + + return 1; +fail: + return 0; +} + +static int parse_type_msfbank(kwb_header* kwb, off_t offset, STREAMFILE* sf) { + /* this is just like XWSF, abridged: */ + off_t header_offset; + + kwb->total_subsongs = read_u32be(offset + 0x14, sf); + if (kwb->target_subsong < 0 || kwb->target_subsong > kwb->total_subsongs || kwb->total_subsongs < 1) goto fail; + + header_offset = offset + 0x30 + (kwb->target_subsong-1) * 0x04; + + /* just a dumb table pointing to MSF, entries can be dummy */ + kwb->stream_offset = read_u32be(header_offset, sf); + kwb->codec = MSF; + + return 1; +fail: + return 0; +} + + +static int parse_xws(kwb_header* kwb, STREAMFILE* sf) { + off_t head_offset, body_offset, start; + uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; + int chunks, chunks2; + off_t msfb_offset; + + /* format is similar to WHD1 with some annoyances of its own + * variations: + * - tdpack: points to N XWSFILE + * - XWSFILE w/ 4 chunks: CUEBANK offset, ? offset, MSFBANK offset, end offset (PS3) + * [Ninja Gaiden Sigma 2 (PS3), Ninja Gaiden 3 Razor's Edge (PS3)] + * - XWSFILE w/ 2*N chunks: KWB2 offset + data offset * N (ex. 3 pairs = 6 chunks) + * [Dead or Alive 5 Last Round (PC)] + * + * for now basic support for the second case, others we'd have to map subsong N to internal bank M + */ + + if (read_u32be(0x00, sf) != 0x58575346 || /* "XWSF" */ + read_u32be(0x04, sf) != 0x494C4500) /* "ILE\0" */ + goto fail; + + kwb->big_endian = read_u8(0x08, sf) == 0xFF; + /* 0x0a: version? */ + + read_u32 = kwb->big_endian ? read_u32be : read_u32le; + + start = read_u32(0x0c, sf); + /* 0x10: file size */ + chunks = read_u32(0x14, sf); + chunks2 = read_u32(0x18, sf); + /* 0x1c: null */ + /* 0x20: some size? */ + /* 0x24: some size? */ + if (chunks != chunks2) + goto fail; + + if (chunks != 4) + goto fail; + + msfb_offset = read_u32(start + 0x08, sf); + if (read_u32be(msfb_offset, sf) == 0x4D534642) { /* "MSFB" + "ANK\0" */ + head_offset = msfb_offset; + body_offset = msfb_offset; /* relative to start */ + + if (!parse_type_msfbank(kwb, head_offset, sf)) + goto fail; + } + else { + goto fail; + } + + kwb->stream_offset += body_offset; return 1; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 27e4d3d31..c19b9c136 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -3,6 +3,9 @@ #include "../vgmstream.h" +VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_samples); + + VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_afc(STREAMFILE *streamFile); @@ -23,34 +26,36 @@ VGMSTREAM * init_vgmstream_nds_strm(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ngc_adpdtk(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ngc_mdsp_std(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_stm(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ngc_mpdsp(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_idsp_namco(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_sadb(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_sadf(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_wii_wsd(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_dsp_ddsp(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_wii_was(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_dsp_str_ig(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_dsp_xiii(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_dsp_cabelas(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_wii_ndp(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_aaap(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_iadp(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_dsp_mcadpcm(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_dsp_sps_n1(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_dsp_itl_ch(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_dsp_adpy(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_dsp_adpx(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_dsp_ds2(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_dsp_itl(STREAMFILE *streamFile); +VGMSTREAM* init_vgmstream_ngc_dsp_std(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_ngc_mdsp_std(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_ngc_dsp_stm(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_ngc_mpdsp(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_ngc_dsp_std_int(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_idsp_namco(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_sadb(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_sadf(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_idsp_tt(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_idsp_nl(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_wii_wsd(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_ddsp(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_wii_was(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_str_ig(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_xiii(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_cabelas(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_wii_ndp(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_ngc_dsp_aaap(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_dspw(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_ngc_dsp_iadp(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_mcadpcm(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_switch_audio(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_sps_n1(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_itl_ch(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_adpy(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_adpx(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_ds2(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_itl(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_sqex(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_dsp_wiivoice(STREAMFILE* sf); VGMSTREAM * init_vgmstream_csmp(STREAMFILE *streamFile); @@ -76,7 +81,7 @@ VGMSTREAM * init_vgmstream_raw_int(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_exst(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ps2_svag(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_svag_kcet(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps_headerless(STREAMFILE *streamFile); @@ -363,7 +368,7 @@ VGMSTREAM * init_vgmstream_ngc_ffcc_str(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_sat_baka(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_nds_swav(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_swav(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_vsf(STREAMFILE *streamFile); @@ -512,7 +517,8 @@ VGMSTREAM * init_vgmstream_vs_str(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_lsf_n1nj4n(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_vawx(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_xwav_new(STREAMFILE* sf); +VGMSTREAM * init_vgmstream_xwav_old(STREAMFILE* sf); VGMSTREAM * init_vgmstream_raw_snds(STREAMFILE* streamFile); @@ -565,7 +571,7 @@ VGMSTREAM * init_vgmstream_mca(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_btsnd(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ps2_svag_snk(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_svag_snk(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_xma(STREAMFILE* streamFile); @@ -596,11 +602,7 @@ VGMSTREAM * init_vgmstream_mc3(STREAMFILE *streamFile); 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_ta_aac_mobile_vorbis(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ta_aac_vita(STREAMFILE *streamFile); +VGMSTREAM* init_vgmstream_ta_aac(STREAMFILE* sf); VGMSTREAM * init_vgmstream_va3(STREAMFILE *streamFile); @@ -632,18 +634,19 @@ VGMSTREAM * init_vgmstream_stm(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_awc(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_std(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_n1(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_nop(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_shinen(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_nus3(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_sps_n1(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_nxa(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_opusx(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_prototype(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_opusnx(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_opus_sqex(STREAMFILE* streamFile); +VGMSTREAM* init_vgmstream_opus_std(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_n1(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_capcom(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_nop(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_shinen(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_nus3(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_sps_n1(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_nxa(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_opusx(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_prototype(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_opusnx(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_nsopus(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_opus_sqex(STREAMFILE* sf); VGMSTREAM * init_vgmstream_raw_al(STREAMFILE * streamFile); @@ -687,6 +690,7 @@ VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_sps_n1_segmented(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_atx(STREAMFILE *streamFile); @@ -723,8 +727,6 @@ VGMSTREAM * init_vgmstream_smc_smh(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ppst(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_opus_sps_n1_segmented(STREAMFILE *streamFile); - VGMSTREAM * init_vgmstream_ubi_bao_pk(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ubi_bao_atomic(STREAMFILE *streamFile); @@ -886,7 +888,8 @@ VGMSTREAM* init_vgmstream_fda(STREAMFILE *sf); VGMSTREAM * init_vgmstream_tgc(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_kwb(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_kwb(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_xws(STREAMFILE* sf); VGMSTREAM * init_vgmstream_lrmd(STREAMFILE* sf); @@ -907,4 +910,22 @@ VGMSTREAM* init_vgmstream_kat(STREAMFILE* sf); VGMSTREAM* init_vgmstream_pcm_success(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_ktsc(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_adp_konami(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_zwv(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_dsb(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_bsf(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_xse_new(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_xse_old(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_wady(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_cpk(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/msf.c b/Frameworks/vgmstream/vgmstream/src/meta/msf.c index 9dc94fd56..a8d2ffb4f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/msf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/msf.c @@ -2,59 +2,59 @@ #include "../coding/coding.h" /* MSF - Sony's PS3 SDK format (MultiStream File) */ -VGMSTREAM * init_vgmstream_msf(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_msf(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; uint32_t data_size, loop_start = 0, loop_end = 0; uint32_t codec, flags; - int loop_flag, channel_count, sample_rate; + int loop_flag, channels, sample_rate; /* checks */ /* .msf: standard * .msa: Sonic & Sega All-Stars Racing (PS3) - * .at3: Silent Hill HD Collection (PS3) + * .at3: Silent Hill HD Collection (PS3), Z/X Zekkai no Crusade (PS3) * .mp3: Darkstalkers Resurrection (PS3) */ - if (!check_extensions(streamFile,"msf,msa,at3,mp3")) + if (!check_extensions(sf,"msf,msa,at3,mp3")) goto fail; /* check header "MSF" + version-char, usually: - * 0x01, 0x02, 0x30 ("0"), 0x35 ("5"), 0x43 ("C") (last/most common version) */ - if ((read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x4D534600) /* "MSF\0" */ + * 0x01, 0x02, 0x30="0", 0x35="5", 0x43="C" (last/most common version) */ + if ((read_u32be(0x00,sf) & 0xffffff00) != 0x4D534600) /* "MSF\0" */ goto fail; start_offset = 0x40; - codec = read_32bitBE(0x04,streamFile); - channel_count = read_32bitBE(0x08,streamFile); - data_size = read_32bitBE(0x0C,streamFile); /* without header */ + codec = read_u32be(0x04,sf); + channels = read_s32be(0x08,sf); + data_size = read_u32be(0x0C,sf); /* without header */ if (data_size == 0xFFFFFFFF) /* unneeded? */ - data_size = get_streamfile_size(streamFile) - start_offset; - sample_rate = read_32bitBE(0x10,streamFile); + data_size = get_streamfile_size(sf) - start_offset; + sample_rate = read_s32be(0x10,sf); /* byte flags, not in MSFv1 or v2 * 0x01/02/04/08: loop marker 0/1/2/3 * 0x10: resample options (force 44/48khz) - * 0x20: VBR MP3 source (changed into simplified 0x1a1 CBR) + * 0x20: VBR MP3 source (encoded with min/max quality options, may end up being CBR) * 0x40: joint stereo MP3 (apparently interleaved stereo for other formats) * 0x80+: (none/reserved) */ - flags = read_32bitBE(0x14,streamFile); + flags = read_u32be(0x14,sf); /* sometimes loop_start/end is set with flag 0x10, but from tests it only loops if 0x01/02 is set * 0x10 often goes with 0x01 but not always (Castlevania HoD); Malicious PS3 uses flag 0x2 instead */ - loop_flag = flags != 0xffffffff && ((flags & 0x01) || (flags & 0x02)); + loop_flag = (flags != 0xffffffff) && ((flags & 0x01) || (flags & 0x02)); /* loop markers (marker N @ 0x18 + N*(4+4), but in practice only marker 0 is used) */ if (loop_flag) { - loop_start = read_32bitBE(0x18,streamFile); - loop_end = read_32bitBE(0x1C,streamFile); /* loop duration */ + loop_start = read_u32be(0x18,sf); + loop_end = read_u32be(0x1C,sf); /* loop duration */ loop_end = loop_start + loop_end; /* usually equals data_size but not always */ - if (loop_end > data_size)/* not seen */ + if (loop_end > data_size) /* not seen */ loop_end = data_size; } /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_MSF; @@ -63,16 +63,16 @@ VGMSTREAM * init_vgmstream_msf(STREAMFILE *streamFile) { vgmstream->sample_rate = 48000; switch (codec) { - case 0x00: /* PCM (Big Endian) */ + case 0x00: /* PCM (Big Endian) [MSEnc tests] */ case 0x01: { /* PCM (Little Endian) [Smash Cars (PS3)] */ vgmstream->coding_type = codec==0 ? coding_PCM16BE : coding_PCM16LE; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x02; - vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count,16); + vgmstream->num_samples = pcm_bytes_to_samples(data_size, channels, 16); if (loop_flag){ - 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); + vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, channels, 16); + vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, channels, 16); } break; @@ -87,10 +87,10 @@ VGMSTREAM * init_vgmstream_msf(STREAMFILE *streamFile) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; - vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + vgmstream->num_samples = ps_bytes_to_samples(data_size, channels); 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); + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start,channels); + vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end,channels); } break; @@ -111,7 +111,7 @@ VGMSTREAM * init_vgmstream_msf(STREAMFILE *streamFile) { if (vgmstream->sample_rate == 0xFFFFFFFF) /* some MSFv1 (Digi World SP) */ vgmstream->sample_rate = 44100; /* voice tracks seems to use 44khz, not sure about other tracks */ - vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -127,43 +127,37 @@ VGMSTREAM * init_vgmstream_msf(STREAMFILE *streamFile) { } #endif #if defined(VGM_USE_MPEG) - case 0x07: { /* MPEG (CBR LAME MP3) [Dengeki Bunko Fighting Climax (PS3)] */ - mpeg_codec_data *mpeg_data = NULL; + case 0x07: { /* MPEG (LAME MP3) [Dengeki Bunko Fighting Climax (PS3), Asura's Wrath (PS3)-vbr] */ + int is_vbr = (flags & 0x20); /* must calc samples/loop offsets manually */ - mpeg_data = init_mpeg(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels); - if (!mpeg_data) goto fail; - vgmstream->codec_data = mpeg_data; + vgmstream->codec_data = init_mpeg(sf, start_offset, &vgmstream->coding_type, vgmstream->channels); + if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; - vgmstream->num_samples = mpeg_bytes_to_samples(data_size, mpeg_data); - if (loop_flag) { - vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, mpeg_data); - vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, mpeg_data); - /* loops are always aligned to CBR frame beginnings */ - } + vgmstream->num_samples = mpeg_get_samples_clean(sf, start_offset, data_size, &loop_start, &loop_end, is_vbr); + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + /* MPEG here seems stripped from ID3/Xing headers, loops are frame offsets */ /* encoder delay varies between 1152 (1f), 528, 576, etc; probably not actually skipped */ break; } #elif defined(VGM_USE_FFMPEG) - case 0x07: - { /* MPEG (CBR LAME MP3) [Dengeki Bunko Fighting Climax (PS3)] */ + case 0x07: { /* MPEG (LAME MP3) [Dengeki Bunko Fighting Climax (PS3), Asura's Wrath (PS3)-vbr] */ ffmpeg_codec_data *ffmpeg_data = NULL; - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, streamFile->get_size(streamFile)); + ffmpeg_data = init_ffmpeg_offset(sf, start_offset, sf->get_size(sf)); if (!ffmpeg_data) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; + //todo use same calcs as above vgmstream->num_samples = (int64_t)data_size * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; if (loop_flag) { vgmstream->loop_start_sample = (int64_t)loop_start * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; vgmstream->loop_end_sample = (int64_t)loop_end * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; - /* loops are always aligned to CBR frame beginnings */ } - - /* encoder delay varies between 1152 (1f), 528, 576, etc; probably not actually skipped */ break; } #endif @@ -173,7 +167,7 @@ VGMSTREAM * init_vgmstream_msf(STREAMFILE *streamFile) { } - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mul.c b/Frameworks/vgmstream/vgmstream/src/meta/mul.c index 7d2bf3e9f..1b3c5ed54 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mul.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mul.c @@ -3,15 +3,15 @@ #include "../coding/coding.h" #include "mul_streamfile.h" -typedef enum { PSX, DSP, XBOX, XMA1 } mul_codec; +typedef enum { PSX, DSP, IMA, XMA1, FSB4 } mul_codec; -static int guess_codec(STREAMFILE* sf, int big_endian, int channels, mul_codec* p_codec, off_t* p_extra_offset); - -static layered_layout_data* build_layered_mul(STREAMFILE *sf_data, int big_endian, VGMSTREAM* vgmstream); +static off_t get_start_offset(STREAMFILE* sf); +static int guess_codec(STREAMFILE* sf, off_t start_offset, int big_endian, int channels, mul_codec* p_codec, off_t* p_extra_offset); +static layered_layout_data* build_layered_mul(STREAMFILE* sf, off_t offset, int big_endian, VGMSTREAM* vgmstream, mul_codec codec); /* .MUL - from Crystal Dynamics games [Legacy of Kain: Defiance (PS2), Tomb Raider Underworld (multi)] */ -VGMSTREAM * init_vgmstream_mul(STREAMFILE *sf) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_mul(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset, coefs_offset = 0; int loop_flag, channel_count, sample_rate, num_samples, loop_start; int big_endian; @@ -48,21 +48,24 @@ VGMSTREAM * init_vgmstream_mul(STREAMFILE *sf) { /* 0x34: id? */ /* 0x38+: channel config until ~0x100? (multiple 0x3F800000 / 1.0f depending on the number of channels) */ - /* test known versions (later versions start from 0x24 instead of 0x20) */ - if (!(read_u32(0x38,sf) == 0x3F800000 || - read_u32(0x3c,sf) == 0x3F800000)) /* Tomb Raider Underworld */ + /* test known "version" (some float) later versions start from 0x24 instead of 0x20 */ + if (!(read_u32(0x38,sf) == 0x3F800000 || /* common */ + read_u32(0x38,sf) == 0x4530F000 || /* Avengers */ + read_u32(0x3c,sf) == 0x3F800000)) /* Tomb Raider Underworld */ goto fail; loop_flag = (loop_start >= 0); /* 0xFFFFFFFF when not looping */ - start_offset = 0x800; + + start_offset = get_start_offset(sf); + if (!start_offset) goto fail; /* format is pretty limited so we need to guess codec */ - if (!guess_codec(sf, big_endian, channel_count, &codec, &coefs_offset)) + if (!guess_codec(sf, start_offset, big_endian, channel_count, &codec, &coefs_offset)) goto fail; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_MUL; @@ -86,21 +89,18 @@ VGMSTREAM * init_vgmstream_mul(STREAMFILE *sf) { dsp_read_hist_be (vgmstream,sf,coefs_offset+0x24,0x2e); break; - case XBOX: - vgmstream->coding_type = coding_XBOX_IMA_int; + case IMA: + vgmstream->coding_type = coding_CD_IMA; vgmstream->layout_type = layout_blocked_mul; break; -#ifdef VGM_USE_FFMPEG - case XMA1: { - - vgmstream->layout_data = build_layered_mul(sf, big_endian, vgmstream); + case XMA1: + case FSB4: + vgmstream->layout_data = build_layered_mul(sf, start_offset, big_endian, vgmstream, codec); if (!vgmstream->layout_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; + //vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_layered; break; - } -#endif default: goto fail; @@ -115,7 +115,19 @@ fail: return NULL; } -static int guess_codec(STREAMFILE* sf, int big_endian, int channels, mul_codec* p_codec, off_t* p_extra_offset) { +static off_t get_start_offset(STREAMFILE* sf) { + + /* find first block with header info */ + if (read_u32be(0x0800,sf) != 0 || read_u32be(0x0804,sf) != 0) /* earlier games */ + return 0x800; + + if (read_u32be(0x2000,sf) != 0 || read_u32be(0x2004,sf) != 0) /* later games */ + return 0x2000; + + return 0; +} + +static int guess_codec(STREAMFILE* sf, off_t start_offset, int big_endian, int channels, mul_codec* p_codec, off_t* p_extra_offset) { uint32_t (*read_u32)(off_t,STREAMFILE*); read_u32 = big_endian ? read_u32be : read_u32le; @@ -141,14 +153,11 @@ static int guess_codec(STREAMFILE* sf, int big_endian, int channels, mul_codec* else { //if (ps_check_format(sf, 0x820, 0x100)) { /* may be PS3/X360, tested below [Tomb Raider 7 (PS3)] */ } - - // todo test XMA1 (X360): N mono streams (layered), each block has 1 sub-blocks of 0x800 packet per channel } - { int i; - off_t offset = 0x800; + off_t offset = start_offset; size_t file_size = get_streamfile_size(sf); size_t frame_size; @@ -163,6 +172,12 @@ static int guess_codec(STREAMFILE* sf, int big_endian, int channels, mul_codec* continue; /* not audio */ } + /* test FSB4 header */ + if (read_u32be(offset + 0x10, sf) == 0x46534234 || read_u32be(offset + 0x20, sf) == 0x46534234) { + *p_codec = FSB4; + return 1; + } + /* test XMA1 (X360): has sub-blocks of 0x800 per channel */ if (block_size == 0x810 * channels) { for (i = 0; i < channels; i++) { @@ -195,7 +210,7 @@ static int guess_codec(STREAMFILE* sf, int big_endian, int channels, mul_codec* break; } if (i == data_size / frame_size) { - *p_codec = XBOX; + *p_codec = IMA; return 1; } @@ -207,8 +222,8 @@ static int guess_codec(STREAMFILE* sf, int big_endian, int channels, mul_codec* } -/* MUL contain one XMA1 streams per channel so we need the usual voodoo */ -static layered_layout_data* build_layered_mul(STREAMFILE *sf_data, int big_endian, VGMSTREAM* vgmstream) { +/* MUL contain one XMA1/FSB streams per channel so we need the usual voodoo */ +static layered_layout_data* build_layered_mul(STREAMFILE* sf, off_t offset, int big_endian, VGMSTREAM* vgmstream, mul_codec codec) { layered_layout_data* data = NULL; STREAMFILE* temp_sf = NULL; int i, layers = vgmstream->channels; @@ -219,47 +234,60 @@ static layered_layout_data* build_layered_mul(STREAMFILE *sf_data, int big_endia if (!data) goto fail; for (i = 0; i < layers; i++) { - int layer_channels = 1; - - - /* build the layer VGMSTREAM */ - data->layers[i] = allocate_vgmstream(layer_channels, 0); - if (!data->layers[i]) goto fail; - - data->layers[i]->sample_rate = vgmstream->sample_rate; - data->layers[i]->num_samples = vgmstream->num_samples; + switch(codec) { #ifdef VGM_USE_FFMPEG - { - uint8_t buf[0x100]; - int bytes; - size_t stream_size; + case XMA1: { + uint8_t buf[0x100]; + int bytes; + size_t stream_size; + int layer_channels = 1; - temp_sf = setup_mul_streamfile(sf_data, big_endian, i, layers); - if (!temp_sf) goto fail; + temp_sf = setup_mul_streamfile(sf, offset, big_endian, i, layers, NULL); + if (!temp_sf) goto fail; - stream_size = get_streamfile_size(temp_sf); + stream_size = get_streamfile_size(temp_sf); - bytes = ffmpeg_make_riff_xma1(buf, 0x100, data->layers[i]->num_samples, stream_size, data->layers[i]->channels, data->layers[i]->sample_rate, 0); - data->layers[i]->codec_data = init_ffmpeg_header_offset(temp_sf, buf,bytes, 0x00, stream_size); - if (!data->layers[i]->codec_data) goto fail; + /* build the layer VGMSTREAM */ + data->layers[i] = allocate_vgmstream(layer_channels, 0); + if (!data->layers[i]) goto fail; - data->layers[i]->coding_type = coding_FFmpeg; - data->layers[i]->layout_type = layout_none; - data->layers[i]->stream_size = stream_size; + data->layers[i]->sample_rate = vgmstream->sample_rate; + data->layers[i]->num_samples = vgmstream->num_samples; - xma_fix_raw_samples(data->layers[i], temp_sf, 0x00,stream_size, 0, 0,0); /* ? */ + bytes = ffmpeg_make_riff_xma1(buf, 0x100, data->layers[i]->num_samples, stream_size, data->layers[i]->channels, data->layers[i]->sample_rate, 0); + data->layers[i]->codec_data = init_ffmpeg_header_offset(temp_sf, buf,bytes, 0x00, stream_size); + if (!data->layers[i]->codec_data) goto fail; - close_streamfile(temp_sf); - temp_sf = NULL; - } -#else - goto fail; + data->layers[i]->coding_type = coding_FFmpeg; + data->layers[i]->layout_type = layout_none; + data->layers[i]->stream_size = stream_size; + + xma_fix_raw_samples(data->layers[i], temp_sf, 0x00,stream_size, 0, 0,0); /* ? */ + break; + } #endif + case FSB4: { /* FSB4 w/ mono MP3 [Tomb Raider 8 (PS3), Avengers (PC)] */ + temp_sf = setup_mul_streamfile(sf, offset, big_endian, i, layers, "fsb"); + if (!temp_sf) goto fail; + + data->layers[i] = init_vgmstream_fsb(temp_sf); + if (!data->layers[i]) goto fail; + break; + } + + default: + goto fail; + } + + close_streamfile(temp_sf); + temp_sf = NULL; } if (!setup_layout_layered(data)) goto fail; + + vgmstream->coding_type = data->layers[0]->coding_type; return data; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mul_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/mul_streamfile.h index 842a86580..2e5489bb2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mul_streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/mul_streamfile.h @@ -2,56 +2,71 @@ #define _MUL_STREAMFILE_H_ #include "deblock_streamfile.h" + static void block_callback(STREAMFILE* sf, deblock_io_data* data) { - uint32_t block_type; - size_t block_size; uint32_t (*read_u32)(off_t,STREAMFILE*) = data->cfg.big_endian ? read_u32be : read_u32le; + /* Blocks have base header + sub-blocks with track sub-header. + * Some blocks don't contain all channels (instead they begin next block). */ - if (data->physical_offset == 0) { - data->block_size = 0x800; + if (data->chunk_size && data->chunk_size < 0x10) { + /* padding after all sub-blocks */ + data->block_size = data->chunk_size; data->data_size = 0; data->skip_size = 0; - return; + data->chunk_size = 0; } + else if (data->chunk_size) { + /* audio block sub-headers, ignore data for other tracks */ + uint32_t track_size = read_u32(data->physical_offset + 0x00, sf); + uint32_t track_number = read_u32(data->physical_offset + 0x04, sf); + /* 0x08: dummy (may contain un-init'd data) */ + /* 0x0c: dummy (may contain un-init'd data) */ - block_type = read_u32(data->physical_offset + 0x00, sf); - block_size = read_u32(data->physical_offset + 0x04, sf); /* not including main header */ - - /* some blocks only contain half of data (continues in next block) so use track numbers */ - - if (block_type == 0x00 && block_size != 0) { - /* header block */ - data->block_size = 0x10; + data->block_size = 0x10 + track_size; data->data_size = 0; data->skip_size = 0; - } - else if (block_type == 0x00000800) { - data->block_size = 0x810; - /* actually sub-block with size + number, kinda half-assed but meh... */ - if (block_size == data->cfg.track_number) { - data->data_size = 0x800; + if (track_number == data->cfg.track_number) { + data->data_size = track_size; data->skip_size = 0x10; } - else{ - data->data_size = 0; - data->skip_size = 0; - } + + data->chunk_size -= data->block_size; } else { - /* non-audio block */ - data->block_size = block_size + 0x10; + /* base block header */ + uint32_t block_type = read_u32(data->physical_offset + 0x00, sf); + uint32_t block_size = read_u32(data->physical_offset + 0x04, sf); + /* 0x08: dummy */ + /* 0x0c: dummy */ + + /* blocks are padded after all sub-blocks */ + if (block_size % 0x10) { + block_size = block_size + 0x10 - (block_size % 0x10); + } + data->data_size = 0; data->skip_size = 0; + + if (block_type == 0x00 && block_size != 0) { + /* audio block */ + data->block_size = 0x10; + data->chunk_size = block_size; + } + else { + /* non-audio block (or empty audio block) */ + data->block_size = block_size + 0x10; + } } } /* Deinterleaves MUL streams */ -static STREAMFILE* setup_mul_streamfile(STREAMFILE* sf, int big_endian, int track_number, int track_count) { +static STREAMFILE* setup_mul_streamfile(STREAMFILE* sf, off_t offset, int big_endian, int track_number, int track_count, const char* extension) { STREAMFILE *new_sf = NULL; deblock_config_t cfg = {0}; + cfg.stream_start = offset; cfg.big_endian = big_endian; cfg.track_number = track_number; cfg.track_count = track_count; @@ -59,6 +74,8 @@ static STREAMFILE* setup_mul_streamfile(STREAMFILE* sf, int big_endian, int trac new_sf = open_wrap_streamfile(sf); new_sf = open_io_deblock_streamfile_f(new_sf, &cfg); + if (extension) + new_sf = open_fakename_streamfile_f(new_sf, NULL, extension); return new_sf; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mus_acm.c b/Frameworks/vgmstream/vgmstream/src/meta/mus_acm.c index 88b02a1a1..963c67234 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mus_acm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mus_acm.c @@ -194,7 +194,7 @@ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_lo char dir_name[NAME_LENGTH]; char subdir_name[NAME_LENGTH]; - int file_count; + int file_count = 0; size_t bytes_read; int line_ok = 0; off_t mus_offset = 0; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/naac.c b/Frameworks/vgmstream/vgmstream/src/meta/naac.c index b657e598b..9f649cf45 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/naac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/naac.c @@ -1,66 +1,61 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* .NAAC - from Namco 3DS games (Ace Combat - Assault Horizon Legacy, Taiko no Tatsujin Don to Katsu no Jikuu Daibouken) */ -VGMSTREAM * init_vgmstream_naac(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - size_t data_size; - - /* check extension */ - if ( !check_extensions(streamFile,"naac") ) - goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x41414320) /* "AAC " */ - goto fail; - if (read_32bitLE(0x04,streamFile) != 0x01) /* version? */ - goto fail; - - start_offset = 0x1000; - loop_flag = (read_32bitLE(0x18,streamFile) != 0); - channel_count = read_32bitLE(0x08,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = read_32bitLE(0x0c,streamFile); - vgmstream->num_samples = read_32bitLE(0x10,streamFile); /* without skip_samples */ - vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile); /* with skip_samples */ - vgmstream->loop_end_sample = read_32bitLE(0x18,streamFile); - /* 0x1c: loop start offset, 0x20: loop end offset (within data) */ - data_size = read_32bitLE(0x24,streamFile); - /* 0x28: unknown; 0x2c: table start offset?; 0x30: seek table (always 0xFD0, padded) */ - - vgmstream->meta_type = meta_NAAC; - -#ifdef VGM_USE_FFMPEG - { - ffmpeg_codec_data *ffmpeg_data = NULL; - - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,data_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - /* observed default, some files start without silence though seems correct when loop_start=0 */ - if (!ffmpeg_data->skipSamples) /* FFmpeg doesn't seem to use not report it */ - ffmpeg_set_skip_samples(ffmpeg_data, 1024); - vgmstream->num_samples -= 1024; - } -#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; -} +#include "meta.h" +#include "../coding/coding.h" + +/* .NAAC - from Namco 3DS games (Ace Combat - Assault Horizon Legacy, Taiko no Tatsujin Don to Katsu no Jikuu Daibouken) */ +VGMSTREAM* init_vgmstream_naac(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + size_t data_size; + + /* check extension */ + if ( !check_extensions(sf,"naac") ) + goto fail; + + /* check header */ + if (read_32bitBE(0x00,sf) != 0x41414320) /* "AAC " */ + goto fail; + if (read_32bitLE(0x04,sf) != 0x01) /* version? */ + goto fail; + + start_offset = 0x1000; + loop_flag = (read_32bitLE(0x18,sf) != 0); + channel_count = read_32bitLE(0x08,sf); + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitLE(0x0c,sf); + vgmstream->num_samples = read_32bitLE(0x10,sf); /* without skip_samples */ + vgmstream->loop_start_sample = read_32bitLE(0x14,sf); /* with skip_samples */ + vgmstream->loop_end_sample = read_32bitLE(0x18,sf); + /* 0x1c: loop start offset, 0x20: loop end offset (within data) */ + data_size = read_32bitLE(0x24,sf); + /* 0x28: unknown; 0x2c: table start offset?; 0x30: seek table (always 0xFD0, padded) */ + + vgmstream->meta_type = meta_NAAC; + +#ifdef VGM_USE_FFMPEG + { + vgmstream->codec_data = init_ffmpeg_offset(sf, start_offset,data_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + /* observed default, some files start without silence though seems correct when loop_start=0 */ + ffmpeg_set_skip_samples(vgmstream->codec_data, 1024); /* raw AAC doesn't set this */ + vgmstream->num_samples -= 1024; + } +#else + goto fail; +#endif + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nds_swav.c b/Frameworks/vgmstream/vgmstream/src/meta/nds_swav.c deleted file mode 100644 index 2840641cd..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/nds_swav.c +++ /dev/null @@ -1,127 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* 28.01.2009 - bxaimc : - SWAV - found in Asphalt Urban GT & Asphalt Urban GT 2 */ -VGMSTREAM * init_vgmstream_nds_swav(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int codec_number; - int channel_count; - int loop_flag; - coding_t coding_type; - off_t start_offset; - int bits_per_sample; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("swav",filename_extension(filename))) goto fail; - - /* check header */ - if ((uint32_t)read_32bitBE(0x00,streamFile)!=0x53574156) /* SWAV */ - goto fail; - - /* check for DATA section */ - if ((uint32_t)read_32bitBE(0x10,streamFile)!=0x44415441) /* "DATA" */ - goto fail; - - /* check type details */ - codec_number = read_8bit(0x18,streamFile); - loop_flag = read_8bit(0x19,streamFile); - - channel_count = 1; - if (get_streamfile_size(streamFile) != read_32bitLE(0x8,streamFile)) - { - if (get_streamfile_size(streamFile) != - (read_32bitLE(0x8,streamFile) - 0x24) * 2 + 0x24) - goto fail; - - channel_count = 2; - } - - switch (codec_number) { - case 0: - coding_type = coding_PCM8; - bits_per_sample = 8; - break; - case 1: - coding_type = coding_PCM16LE; - bits_per_sample = 16; - break; - case 2: - coding_type = coding_IMA_int; - bits_per_sample = 4; - break; - default: - goto fail; - } - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x24; - vgmstream->num_samples = - (read_32bitLE(0x14,streamFile) - 0x14) * 8 / bits_per_sample; - vgmstream->sample_rate = (uint16_t)read_16bitLE(0x1A,streamFile); - - if (loop_flag) { - vgmstream->loop_start_sample = - (uint16_t)read_16bitLE(0x1E,streamFile) * 32 / bits_per_sample; - vgmstream->loop_end_sample = - read_32bitLE(0x20,streamFile) * 32 / bits_per_sample + - vgmstream->loop_start_sample; - } - - if (coding_type == coding_IMA_int) { - /* handle IMA frame header */ - vgmstream->loop_start_sample -= 32 / bits_per_sample; - vgmstream->loop_end_sample -= 32 / bits_per_sample; - vgmstream->num_samples -= 32 / bits_per_sample; - - { - int i; - for (i=0; ich[i].adpcm_history1_32 = - read_16bitLE(start_offset + 0 + 4*i, streamFile); - vgmstream->ch[i].adpcm_step_index = - read_16bitLE(start_offset + 2 + 4*i, streamFile); - } - } - - start_offset += 4 * channel_count; - } - - vgmstream->coding_type = coding_type; - vgmstream->meta_type = meta_NDS_SWAV; - if (channel_count == 2) { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 1; - } else { - vgmstream->layout_type = layout_none; - } - - /* open the file for reading by each channel */ - - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c index b1a9e760e..50a8cb081 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c @@ -28,15 +28,15 @@ struct dsp_header { }; /* read the above struct; returns nonzero on failure */ -static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREAMFILE *streamFile, int big_endian) { - int32_t (*get_32bit)(uint8_t *) = big_endian ? get_32bitBE : get_32bitLE; - int16_t (*get_16bit)(uint8_t *) = big_endian ? get_16bitBE : get_16bitLE; +static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREAMFILE* sf, int big_endian) { + int32_t (*get_32bit)(const uint8_t *) = big_endian ? get_32bitBE : get_32bitLE; + int16_t (*get_16bit)(const uint8_t *) = big_endian ? get_16bitBE : get_16bitLE; int i; uint8_t buf[0x4e]; - if (offset > get_streamfile_size(streamFile)) + if (offset > get_streamfile_size(sf)) return 1; - if (read_streamfile(buf, offset, 0x4e, streamFile) != 0x4e) + if (read_streamfile(buf, offset, 0x4e, sf) != 0x4e) return 1; header->sample_count = get_32bit(buf+0x00); header->nibble_count = get_32bit(buf+0x04); @@ -59,10 +59,10 @@ static int read_dsp_header_endian(struct dsp_header *header, off_t offset, STREA header->block_size = get_16bit(buf+0x4c); return 0; } -static int read_dsp_header(struct dsp_header *header, off_t offset, STREAMFILE *file) { +static int read_dsp_header(struct dsp_header *header, off_t offset, STREAMFILE* file) { return read_dsp_header_endian(header, offset, file, 1); } -static int read_dsp_header_le(struct dsp_header *header, off_t offset, STREAMFILE *file) { +static int read_dsp_header_le(struct dsp_header *header, off_t offset, STREAMFILE* file) { return read_dsp_header_endian(header, offset, file, 0); } @@ -98,8 +98,8 @@ typedef struct { /* Common parser for most DSPs that are basically the same with minor changes. * Custom variants will just concatenate or interleave standard DSP headers and data, * so we make sure to validate read vs expected values, based on dsp_meta config. */ -static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *dspm) { - VGMSTREAM * vgmstream = NULL; +static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) { + VGMSTREAM* vgmstream = NULL; int i, j; int loop_flag; struct dsp_header ch_header[COMMON_DSP_MAX_CHANNELS]; @@ -113,7 +113,7 @@ static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *d /* load standard DSP header per channel */ { for (i = 0; i < dspm->channel_count; i++) { - if (read_dsp_header_endian(&ch_header[i], dspm->header_offset + i*dspm->header_spacing, streamFile, !dspm->little_endian)) + if (read_dsp_header_endian(&ch_header[i], dspm->header_offset + i*dspm->header_spacing, sf, !dspm->little_endian)) goto fail; } } @@ -156,7 +156,7 @@ static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *d for (i = 0; i < channels; i++) { off_t channel_offset = dspm->start_offset + i*dspm->interleave; - if (ch_header[i].initial_ps != (uint8_t)read_8bit(channel_offset, streamFile)) + if (ch_header[i].initial_ps != (uint8_t)read_8bit(channel_offset, sf)) goto fail; } } @@ -174,7 +174,7 @@ static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *d loop_offset = (loop_offset / dspm->interleave * dspm->interleave * channels) + (loop_offset % dspm->interleave); } - if (ch_header[i].loop_ps != (uint8_t)read_8bit(dspm->start_offset + i*dspm->interleave + loop_offset,streamFile)) + if (ch_header[i].loop_ps != (uint8_t)read_8bit(dspm->start_offset + i*dspm->interleave + loop_offset,sf)) goto fail; } } @@ -235,7 +235,7 @@ static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *d } - if (!vgmstream_open_stream(vgmstream,streamFile,dspm->start_offset)) + if (!vgmstream_open_stream(vgmstream,sf,dspm->start_offset)) goto fail; return vgmstream; @@ -247,8 +247,8 @@ fail: /* ********************************* */ /* .dsp - standard dsp as generated by DSPADPCM.exe */ -VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_ngc_dsp_std(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; struct dsp_header header; const size_t header_size = 0x60; off_t start_offset; @@ -258,16 +258,16 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { /* .dsp: standard * .adp: Dr. Muto/Battalion Wars (GC) mono files * (extensionless): Tony Hawk's Downhill Jam (Wii) */ - if (!check_extensions(streamFile, "dsp,adp,")) + if (!check_extensions(sf, "dsp,adp,")) goto fail; - if (read_dsp_header(&header, 0x00, streamFile)) + if (read_dsp_header(&header, 0x00, sf)) goto fail; channel_count = 1; start_offset = header_size; - if (header.initial_ps != (uint8_t)read_8bit(start_offset,streamFile)) + if (header.initial_ps != (uint8_t)read_8bit(start_offset,sf)) goto fail; /* check initial predictor/scale */ if (header.format || header.gain) goto fail; /* check type==0 and gain==0 */ @@ -282,7 +282,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { struct dsp_header header2; /* ignore headers one after another */ - ko = read_dsp_header(&header2, header_size, streamFile); + ko = read_dsp_header(&header2, header_size, sf); if (!ko && header.sample_count == header2.sample_count && header.nibble_count == header2.nibble_count && @@ -293,7 +293,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { /* ignore headers after interleave [Ultimate Board Collection (Wii)] */ - ko = read_dsp_header(&header2, 0x10000, streamFile); + ko = read_dsp_header(&header2, 0x10000, sf); if (!ko && header.sample_count == header2.sample_count && header.nibble_count == header2.nibble_count && @@ -307,7 +307,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { off_t loop_off; /* check loop predictor/scale */ loop_off = header.loop_start_offset/16*8; - if (header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,streamFile)) { + if (header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,sf)) { /* rarely won't match (ex ESPN 2002), not sure if header or calc problem, but doesn't seem to matter * (there may be a "click" when looping, or loop values may be too big and loop disabled anyway) */ VGM_LOG("DSP (std): bad loop_predictor\n"); @@ -341,7 +341,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { vgmstream->ch[0].adpcm_history2_16 = header.initial_hist2; } - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) goto fail; return vgmstream; @@ -351,8 +351,8 @@ fail: } /* .dsp - little endian dsp, possibly main Switch .dsp [LEGO Worlds (Switch)] */ -VGMSTREAM * init_vgmstream_ngc_dsp_std_le(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_ngc_dsp_std_le(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; struct dsp_header header; const size_t header_size = 0x60; off_t start_offset; @@ -360,16 +360,16 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_le(STREAMFILE *streamFile) { /* checks */ /* .adpcm: LEGO Worlds */ - if (!check_extensions(streamFile, "adpcm")) + if (!check_extensions(sf, "adpcm")) goto fail; - if (read_dsp_header_le(&header, 0x00, streamFile)) + if (read_dsp_header_le(&header, 0x00, sf)) goto fail; channel_count = 1; start_offset = header_size; - if (header.initial_ps != (uint8_t)read_8bit(start_offset,streamFile)) + if (header.initial_ps != (uint8_t)read_8bit(start_offset,sf)) goto fail; /* check initial predictor/scale */ if (header.format || header.gain) goto fail; /* check type==0 and gain==0 */ @@ -380,7 +380,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_le(STREAMFILE *streamFile) { * predictor/scale check if the first byte is 0 */ { struct dsp_header header2; - read_dsp_header_le(&header2, header_size, streamFile); + read_dsp_header_le(&header2, header_size, sf); if (header.sample_count == header2.sample_count && header.nibble_count == header2.nibble_count && @@ -394,7 +394,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_le(STREAMFILE *streamFile) { off_t loop_off; /* check loop predictor/scale */ loop_off = header.loop_start_offset/16*8; - if (header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,streamFile)) { + if (header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,sf)) { /* rarely won't match (ex ESPN 2002), not sure if header or calc problem, but doesn't seem to matter * (there may be a "click" when looping, or loop values may be too big and loop disabled anyway) */ VGM_LOG("DSP (std): bad loop_predictor\n"); @@ -428,7 +428,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_le(STREAMFILE *streamFile) { vgmstream->ch[0].adpcm_history2_16 = header.initial_hist2; } - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) goto fail; return vgmstream; @@ -438,28 +438,28 @@ fail: } /* .dsp - standard multi-channel dsp as generated by DSPADPCM.exe (later revisions) */ -VGMSTREAM * init_vgmstream_ngc_mdsp_std(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_ngc_mdsp_std(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; struct dsp_header header; const size_t header_size = 0x60; off_t start_offset; int i, c, channel_count; /* checks */ - if (!check_extensions(streamFile, "dsp,mdsp")) + if (!check_extensions(sf, "dsp,mdsp")) goto fail; - if (read_dsp_header(&header, 0x00, streamFile)) + if (read_dsp_header(&header, 0x00, sf)) goto fail; channel_count = header.channel_count==0 ? 1 : header.channel_count; start_offset = header_size * channel_count; /* named .dsp and no channels? likely another interleaved dsp */ - if (check_extensions(streamFile,"dsp") && header.channel_count == 0) + if (check_extensions(sf,"dsp") && header.channel_count == 0) goto fail; - if (header.initial_ps != (uint8_t)read_8bit(start_offset, streamFile)) + if (header.initial_ps != (uint8_t)read_8bit(start_offset, sf)) goto fail; /* check initial predictor/scale */ if (header.format || header.gain) goto fail; /* check type==0 and gain==0 */ @@ -483,7 +483,7 @@ VGMSTREAM * init_vgmstream_ngc_mdsp_std(STREAMFILE *streamFile) { vgmstream->interleave_last_block_size = (header.nibble_count / 2 % vgmstream->interleave_block_size + 7) / 8 * 8; for (i = 0; i < channel_count; i++) { - if (read_dsp_header(&header, header_size * i, streamFile)) goto fail; + if (read_dsp_header(&header, header_size * i, sf)) goto fail; /* adpcm coeffs/history */ for (c = 0; c < 16; c++) @@ -492,7 +492,7 @@ VGMSTREAM * init_vgmstream_ngc_mdsp_std(STREAMFILE *streamFile) { vgmstream->ch[i].adpcm_history2_16 = header.initial_hist2; } - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; @@ -504,39 +504,39 @@ fail: /* ********************************* */ /* .stm - Intelligent Systems + others (same programmers) full interleaved dsp [Paper Mario TTYD (GC), Fire Emblem: POR (GC), Cubivore (GC)] */ -VGMSTREAM * init_vgmstream_ngc_dsp_stm(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ngc_dsp_stm(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ /* .lstm/dsp: renamed to avoid hijacking Scream Tracker 2 Modules */ - if (!check_extensions(streamFile, "stm,lstm,dsp")) + if (!check_extensions(sf, "stm,lstm,dsp")) goto fail; - if (read_16bitBE(0x00, streamFile) != 0x0200) + if (read_16bitBE(0x00, sf) != 0x0200) goto fail; /* 0x02(2): sample rate, 0x08+: channel sizes/loop offsets? */ - dspm.channel_count = read_32bitBE(0x04, streamFile); + dspm.channel_count = read_32bitBE(0x04, sf); dspm.max_channels = 2; dspm.fix_looping = 1; dspm.header_offset = 0x40; dspm.header_spacing = 0x60; dspm.start_offset = 0x100; - dspm.interleave = (read_32bitBE(0x08, streamFile) + 0x20) / 0x20 * 0x20; /* strange rounding, but works */ + dspm.interleave = (read_32bitBE(0x08, sf) + 0x20) / 0x20 * 0x20; /* strange rounding, but works */ dspm.meta_type = meta_DSP_STM; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .(mp)dsp - single header + interleaved dsp [Monopoly Party! (GC)] */ -VGMSTREAM * init_vgmstream_ngc_mpdsp(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ngc_mpdsp(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ /* .mpdsp: renamed since standard .dsp would catch it otherwise */ - if (!check_extensions(streamFile, "mpdsp")) + if (!check_extensions(sf, "mpdsp")) goto fail; /* at 0x48 is extra data that could help differenciating these DSPs, but seems like @@ -553,18 +553,18 @@ VGMSTREAM * init_vgmstream_ngc_mpdsp(STREAMFILE *streamFile) { dspm.interleave = 0xf000; dspm.meta_type = meta_DSP_MPDSP; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* various dsp with differing extensions and interleave values */ -VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ngc_dsp_std_int(STREAMFILE* sf) { dsp_meta dspm = {0}; char filename[PATH_LIMIT]; /* checks */ - if (!check_extensions(streamFile, "dsp,mss,gcm")) + if (!check_extensions(sf, "dsp,mss,gcm")) goto fail; dspm.channel_count = 2; @@ -575,16 +575,16 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile) { dspm.header_spacing = 0x60; dspm.start_offset = 0xc0; - streamFile->get_name(streamFile,filename,sizeof(filename)); + sf->get_name(sf,filename,sizeof(filename)); if (strlen(filename) > 7 && !strcasecmp("_lr.dsp",filename+strlen(filename)-7)) { //todo improve dspm.interleave = 0x14180; dspm.meta_type = meta_DSP_JETTERS; /* Bomberman Jetters (GC) */ - } else if (check_extensions(streamFile, "mss")) { + } else if (check_extensions(sf, "mss")) { dspm.interleave = 0x1000; dspm.meta_type = meta_DSP_MSS; /* Free Radical GC games */ /* Timesplitters 2 GC's ts2_atom_smasher_44_fx.mss differs slightly in samples but plays ok */ dspm.ignore_header_agreement = 1; - } else if (check_extensions(streamFile, "gcm")) { + } else if (check_extensions(sf, "gcm")) { /* older Traveller's Tales games [Lego Star Wars (GC), The Chronicles of Narnia (GC), Sonic R (GC)] */ dspm.interleave = 0x8000; dspm.meta_type = meta_DSP_GCM; @@ -592,19 +592,19 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile) { goto fail; } - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* IDSP - Namco header (from NUB/NUS3) + interleaved dsp [SSB4 (3DS), Tekken Tag Tournament 2 (WiiU)] */ -VGMSTREAM * init_vgmstream_idsp_namco(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_idsp_namco(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "idsp")) + if (!check_extensions(sf, "idsp")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x49445350) /* "IDSP" */ + if (read_32bitBE(0x00,sf) != 0x49445350) /* "IDSP" */ goto fail; dspm.max_channels = 8; @@ -612,52 +612,52 @@ VGMSTREAM * init_vgmstream_idsp_namco(STREAMFILE *streamFile) { dspm.fix_looping = 1; /* 0x04: null */ - dspm.channel_count = read_32bitBE(0x08, streamFile); + dspm.channel_count = read_32bitBE(0x08, sf); /* 0x0c: sample rate */ /* 0x10: num_samples */ /* 0x14: loop start */ /* 0x18: loop end */ - dspm.interleave = read_32bitBE(0x1c,streamFile); /* usually 0x10 */ - dspm.header_offset = read_32bitBE(0x20,streamFile); - dspm.header_spacing = read_32bitBE(0x24,streamFile); - dspm.start_offset = read_32bitBE(0x28,streamFile); + dspm.interleave = read_32bitBE(0x1c,sf); /* usually 0x10 */ + dspm.header_offset = read_32bitBE(0x20,sf); + dspm.header_spacing = read_32bitBE(0x24,sf); + dspm.start_offset = read_32bitBE(0x28,sf); /* Soul Calibur: Broken destiny (PSP), Taiko no Tatsujin: Atsumete Tomodachi Daisakusen (WiiU) */ if (dspm.interleave == 0) /* half interleave (happens sometimes), use channel size */ - dspm.interleave = read_32bitBE(0x2c,streamFile); + dspm.interleave = read_32bitBE(0x2c,sf); dspm.meta_type = meta_IDSP_NAMCO; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* sadb - Procyon Studio header + interleaved dsp [Shiren the Wanderer 3 (Wii), Disaster: Day of Crisis (Wii)] */ -VGMSTREAM * init_vgmstream_sadb(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_sadb(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "sad")) + if (!check_extensions(sf, "sad")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x73616462) /* "sadb" */ + if (read_32bitBE(0x00,sf) != 0x73616462) /* "sadb" */ goto fail; - dspm.channel_count = read_8bit(0x32, streamFile); + dspm.channel_count = read_8bit(0x32, sf); dspm.max_channels = 2; dspm.header_offset = 0x80; dspm.header_spacing = 0x60; - dspm.start_offset = read_32bitBE(0x48,streamFile); + dspm.start_offset = read_32bitBE(0x48,sf); dspm.interleave = 0x10; dspm.meta_type = meta_DSP_SADB; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* IDSP - Traveller's Tales header + interleaved dsps [Lego Batman (Wii), Lego Dimensions (Wii U)] */ -VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_idsp_tt(STREAMFILE* sf) { dsp_meta dspm = {0}; int version_main, version_sub; @@ -665,14 +665,14 @@ VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile) { /* .gcm: standard * .idsp: header id? * .wua: Lego Dimensions (Wii U) */ - if (!check_extensions(streamFile, "gcm,idsp,wua")) + if (!check_extensions(sf, "gcm,idsp,wua")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x49445350) /* "IDSP" */ + if (read_32bitBE(0x00,sf) != 0x49445350) /* "IDSP" */ goto fail; - version_main = read_32bitBE(0x04, streamFile); - version_sub = read_32bitBE(0x08, streamFile); /* extra check since there are other IDSPs */ + version_main = read_32bitBE(0x04, sf); + version_sub = read_32bitBE(0x08, sf); /* extra check since there are other IDSPs */ if (version_main == 0x01 && version_sub == 0xc8) { /* Transformers: The Game (Wii) */ dspm.channel_count = 2; @@ -694,7 +694,7 @@ VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile) { else if (version_main == 0x03 && version_sub == 0x12c) { /* Lego The Lord of the Rings (Wii) */ /* Lego Dimensions (Wii U) */ - dspm.channel_count = read_32bitBE(0x10, streamFile); + dspm.channel_count = read_32bitBE(0x10, sf); dspm.max_channels = 2; dspm.header_offset = 0x20; /* 0x14+: "I_AM_PADDING" */ @@ -705,22 +705,22 @@ VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile) { dspm.header_spacing = 0x60; dspm.start_offset = dspm.header_offset + 0x60 * dspm.channel_count; - dspm.interleave = read_32bitBE(0x0c, streamFile); + dspm.interleave = read_32bitBE(0x0c, sf); dspm.meta_type = meta_IDSP_TT; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* IDSP - from Next Level games [Super Mario Strikers (GC), Mario Strikers: Charged (Wii)] */ -VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_idsp_nl(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "idsp")) + if (!check_extensions(sf, "idsp")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x49445350) /* "IDSP" */ + if (read_32bitBE(0x00,sf) != 0x49445350) /* "IDSP" */ goto fail; dspm.channel_count = 2; @@ -729,11 +729,11 @@ VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE *streamFile) { dspm.header_offset = 0x0c; dspm.header_spacing = 0x60; dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channel_count; - dspm.interleave = read_32bitBE(0x04,streamFile); + dspm.interleave = read_32bitBE(0x04,sf); /* 0x08: usable channel size */ { - size_t stream_size = get_streamfile_size(streamFile); - if (read_32bitBE(stream_size - 0x04,streamFile) == 0x30303030) + size_t stream_size = get_streamfile_size(sf); + if (read_32bitBE(stream_size - 0x04,sf) == 0x30303030) stream_size -= 0x14; /* remove padding */ stream_size -= dspm.start_offset; @@ -746,87 +746,87 @@ VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE *streamFile) { dspm.force_loop_seconds = 15; dspm.meta_type = meta_IDSP_NL; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .wsd - Custom header + full interleaved dsp [Phantom Brave (Wii)] */ -VGMSTREAM * init_vgmstream_wii_wsd(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_wii_wsd(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "wsd")) + if (!check_extensions(sf, "wsd")) goto fail; - if (read_32bitBE(0x08,streamFile) != read_32bitBE(0x0c,streamFile)) /* channel sizes */ + if (read_32bitBE(0x08,sf) != read_32bitBE(0x0c,sf)) /* channel sizes */ goto fail; dspm.channel_count = 2; dspm.max_channels = 2; - dspm.header_offset = read_32bitBE(0x00,streamFile); - dspm.header_spacing = read_32bitBE(0x04,streamFile) - dspm.header_offset; + dspm.header_offset = read_32bitBE(0x00,sf); + dspm.header_spacing = read_32bitBE(0x04,sf) - dspm.header_offset; dspm.start_offset = dspm.header_offset + 0x60; dspm.interleave = dspm.header_spacing; dspm.meta_type = meta_DSP_WII_WSD; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .ddsp - full interleaved dsp [The Sims 2 - Pets (Wii)] */ -VGMSTREAM * init_vgmstream_dsp_ddsp(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_ddsp(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "ddsp")) + if (!check_extensions(sf, "ddsp")) goto fail; dspm.channel_count = 2; dspm.max_channels = 2; dspm.header_offset = 0x00; - dspm.header_spacing = (get_streamfile_size(streamFile) / dspm.channel_count); + dspm.header_spacing = (get_streamfile_size(sf) / dspm.channel_count); dspm.start_offset = 0x60; dspm.interleave = dspm.header_spacing; dspm.meta_type = meta_DSP_DDSP; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* iSWS - Sumo Digital header + interleaved dsp [DiRT 2 (Wii), F1 2009 (Wii)] */ -VGMSTREAM * init_vgmstream_wii_was(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_wii_was(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "was,dsp,isws")) + if (!check_extensions(sf, "was,dsp,isws")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x69535753) /* "iSWS" */ + if (read_32bitBE(0x00,sf) != 0x69535753) /* "iSWS" */ goto fail; - dspm.channel_count = read_32bitBE(0x08,streamFile); + dspm.channel_count = read_32bitBE(0x08,sf); dspm.max_channels = 2; - dspm.header_offset = 0x08 + read_32bitBE(0x04,streamFile); + dspm.header_offset = 0x08 + read_32bitBE(0x04,sf); dspm.header_spacing = 0x60; dspm.start_offset = dspm.header_offset + dspm.channel_count*dspm.header_spacing; - dspm.interleave = read_32bitBE(0x10,streamFile); + dspm.interleave = read_32bitBE(0x10,sf); dspm.meta_type = meta_WII_WAS; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .str - Infogrames raw interleaved dsp [Micro Machines (GC), Superman: Shadow of Apokolips (GC)] */ -VGMSTREAM * init_vgmstream_dsp_str_ig(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_str_ig(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "str")) + if (!check_extensions(sf, "str")) goto fail; dspm.channel_count = 2; @@ -838,17 +838,17 @@ VGMSTREAM * init_vgmstream_dsp_str_ig(STREAMFILE *streamFile) { dspm.interleave = 0x4000; dspm.meta_type = meta_DSP_STR_IG; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .dsp - Ubisoft interleaved dsp with bad loop start [Speed Challenge: Jacques Villeneuve's Racing Vision (GC), XIII (GC)] */ -VGMSTREAM * init_vgmstream_dsp_xiii(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_xiii(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "dsp")) + if (!check_extensions(sf, "dsp")) goto fail; dspm.channel_count = 2; @@ -861,25 +861,25 @@ VGMSTREAM * init_vgmstream_dsp_xiii(STREAMFILE *streamFile) { dspm.interleave = 0x08; dspm.meta_type = meta_DSP_XIII; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* NPD - Icon Games header + subinterleaved DSPs [Vertigo (Wii), Build n' Race (Wii)] */ -VGMSTREAM * init_vgmstream_wii_ndp(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_wii_ndp(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "ndp")) + if (!check_extensions(sf, "ndp")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x4E445000) /* "NDP\0" */ + if (read_32bitBE(0x00,sf) != 0x4E445000) /* "NDP\0" */ goto fail; - if (read_32bitLE(0x08,streamFile) + 0x18 != get_streamfile_size(streamFile)) + if (read_32bitLE(0x08,sf) + 0x18 != get_streamfile_size(sf)) goto fail; /* 0x0c: sample rate */ - dspm.channel_count = read_32bitLE(0x10,streamFile); + dspm.channel_count = read_32bitLE(0x10,sf); dspm.max_channels = 2; dspm.header_offset = 0x18; @@ -888,25 +888,25 @@ VGMSTREAM * init_vgmstream_wii_ndp(STREAMFILE *streamFile) { dspm.interleave = 0x04; dspm.meta_type = meta_WII_NDP; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* Cabela's series (Magic Wand dev?) - header + interleaved dsp * [Cabela's Big Game Hunt 2005 Adventures (GC), Cabela's Outdoor Adventures (GC)] */ -VGMSTREAM * init_vgmstream_dsp_cabelas(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_cabelas(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "dsp")) + if (!check_extensions(sf, "dsp")) goto fail; /* has extra stuff in the reserved data, without it this meta may catch other DSPs it shouldn't */ - if (read_32bitBE(0x50,streamFile) == 0 || read_32bitBE(0x54,streamFile) == 0) + if (read_32bitBE(0x50,sf) == 0 || read_32bitBE(0x54,sf) == 0) goto fail; /* sfx are mono, but standard dsp will catch them tho */ - dspm.channel_count = read_32bitBE(0x00,streamFile) == read_32bitBE(0x60,streamFile) ? 2 : 1; + dspm.channel_count = read_32bitBE(0x00,sf) == read_32bitBE(0x60,sf) ? 2 : 1; dspm.max_channels = 2; dspm.force_loop = (dspm.channel_count > 1); @@ -916,49 +916,49 @@ VGMSTREAM * init_vgmstream_dsp_cabelas(STREAMFILE *streamFile) { dspm.interleave = 0x10; dspm.meta_type = meta_DSP_CABELAS; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* AAAp - Acclaim Austin Audio header + interleaved dsp [Vexx (GC), Turok: Evolution (GC)] */ -VGMSTREAM * init_vgmstream_ngc_dsp_aaap(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ngc_dsp_aaap(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "dsp")) + if (!check_extensions(sf, "dsp")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x41414170) /* "AAAp" */ + if (read_32bitBE(0x00,sf) != 0x41414170) /* "AAAp" */ goto fail; - dspm.channel_count = read_16bitBE(0x06,streamFile); + dspm.channel_count = read_16bitBE(0x06,sf); dspm.max_channels = 2; dspm.header_offset = 0x08; dspm.header_spacing = 0x60; dspm.start_offset = dspm.header_offset + dspm.channel_count*dspm.header_spacing; - dspm.interleave = (uint16_t)read_16bitBE(0x04,streamFile); + dspm.interleave = (uint16_t)read_16bitBE(0x04,sf); dspm.meta_type = meta_NGC_DSP_AAAP; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* DSPW - Capcom header + full interleaved DSP [Sengoku Basara 3 (Wii), Monster Hunter 3 Ultimate (WiiU)] */ -VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_dspw(STREAMFILE* sf) { dsp_meta dspm = {0}; size_t data_size; /* check extension */ - if (!check_extensions(streamFile, "dspw")) + if (!check_extensions(sf, "dspw")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x44535057) /* "DSPW" */ + if (read_32bitBE(0x00,sf) != 0x44535057) /* "DSPW" */ goto fail; /* ignore time marker */ - data_size = read_32bitBE(0x08, streamFile); - if (read_32bitBE(data_size - 0x10, streamFile) == 0x74494D45) /* "tIME" */ + data_size = read_32bitBE(0x08, sf); + if (read_32bitBE(data_size - 0x10, sf) == 0x74494D45) /* "tIME" */ data_size -= 0x10; /* (ignore, 2 ints in YYYYMMDD hhmmss00) */ /* some files have a mrkr section with multiple loop regions added at the end (variable size) */ @@ -966,7 +966,7 @@ VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE *streamFile) { off_t mrkr_offset = data_size - 0x04; off_t max_offset = data_size - 0x1000; while (mrkr_offset > max_offset) { - if (read_32bitBE(mrkr_offset, streamFile) != 0x6D726B72) { /* "mrkr" */ + if (read_32bitBE(mrkr_offset, sf) != 0x6D726B72) { /* "mrkr" */ mrkr_offset -= 0x04; } else { data_size = mrkr_offset; @@ -977,7 +977,7 @@ VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE *streamFile) { data_size -= 0x20; /* header size */ /* 0x10: loop start, 0x14: loop end, 0x1c: num_samples */ - dspm.channel_count = read_32bitBE(0x18, streamFile); + dspm.channel_count = read_32bitBE(0x18, sf); dspm.max_channels = 6; /* 6ch in Monster Hunter 3 Ultimate */ dspm.header_offset = 0x20; @@ -986,74 +986,74 @@ VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE *streamFile) { dspm.interleave = data_size / dspm.channel_count; dspm.meta_type = meta_DSP_DSPW; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* iadp - custom header + interleaved dsp [Dr. Muto (GC)] */ -VGMSTREAM * init_vgmstream_ngc_dsp_iadp(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ngc_dsp_iadp(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ /* .adp: actual extension, .iadp: header id */ - if (!check_extensions(streamFile, "adp,iadp")) + if (!check_extensions(sf, "adp,iadp")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x69616470) /* "iadp" */ + if (read_32bitBE(0x00,sf) != 0x69616470) /* "iadp" */ goto fail; - dspm.channel_count = read_32bitBE(0x04,streamFile); + dspm.channel_count = read_32bitBE(0x04,sf); dspm.max_channels = 2; dspm.header_offset = 0x20; dspm.header_spacing = 0x60; - dspm.start_offset = read_32bitBE(0x1C,streamFile); - dspm.interleave = read_32bitBE(0x08,streamFile); + dspm.start_offset = read_32bitBE(0x1C,sf); + dspm.interleave = read_32bitBE(0x08,sf); dspm.meta_type = meta_NGC_DSP_IADP; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .mcadpcm - Custom header + full interleaved dsp [Skyrim (Switch)] */ -VGMSTREAM * init_vgmstream_dsp_mcadpcm(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_mcadpcm(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "mcadpcm")) + if (!check_extensions(sf, "mcadpcm")) goto fail; /* could validate dsp sizes but only for +1ch, check_dsp_samples will do it anyway */ - //if (read_32bitLE(0x08,streamFile) != read_32bitLE(0x10,streamFile)) + //if (read_32bitLE(0x08,sf) != read_32bitLE(0x10,sf)) // goto fail; - dspm.channel_count = read_32bitLE(0x00,streamFile); + dspm.channel_count = read_32bitLE(0x00,sf); dspm.max_channels = 2; dspm.little_endian = 1; - dspm.header_offset = read_32bitLE(0x04,streamFile); + dspm.header_offset = read_32bitLE(0x04,sf); dspm.header_spacing = dspm.channel_count == 1 ? 0 : - read_32bitLE(0x0c,streamFile) - dspm.header_offset; /* channel 2 start, only with Nch */ + read_32bitLE(0x0c,sf) - dspm.header_offset; /* channel 2 start, only with Nch */ dspm.start_offset = dspm.header_offset + 0x60; dspm.interleave = dspm.header_spacing; dspm.meta_type = meta_DSP_MCADPCM; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .switch_audio - UE4 standard LE header + full interleaved dsp [Gal Gun 2 (Switch)] */ -VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_switch_audio(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ /* .switch_audio: possibly UE4 class name rather than extension, .dsp: assumed */ - if (!check_extensions(streamFile, "switch_audio,dsp")) + if (!check_extensions(sf, "switch_audio,dsp")) goto fail; /* manual double header test */ - if (read_32bitLE(0x00, streamFile) == read_32bitLE(get_streamfile_size(streamFile) / 2, streamFile)) + if (read_32bitLE(0x00, sf) == read_32bitLE(get_streamfile_size(sf) / 2, sf)) dspm.channel_count = 2; else dspm.channel_count = 1; @@ -1061,28 +1061,28 @@ VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile) { dspm.little_endian = 1; dspm.header_offset = 0x00; - dspm.header_spacing = get_streamfile_size(streamFile) / dspm.channel_count; + dspm.header_spacing = get_streamfile_size(sf) / dspm.channel_count; dspm.start_offset = dspm.header_offset + 0x60; dspm.interleave = dspm.header_spacing; dspm.meta_type = meta_DSP_SWITCH_AUDIO; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .vag - Nippon Ichi SPS wrapper [Penny-Punching Princess (Switch), Ys VIII (Switch)] */ -VGMSTREAM * init_vgmstream_dsp_sps_n1(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_sps_n1(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ /* .vag: Penny-Punching Princess (Switch) * .nlsd: Ys VIII (Switch) */ - if (!check_extensions(streamFile, "vag,nlsd")) + if (!check_extensions(sf, "vag,nlsd")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x08000000) /* file type (see other N1 SPS) */ + if (read_32bitBE(0x00,sf) != 0x08000000) /* file type (see other N1 SPS) */ goto fail; - if ((uint16_t)read_16bitLE(0x08,streamFile) != read_32bitLE(0x24,streamFile)) /* header has various repeated values */ + if ((uint16_t)read_16bitLE(0x08,sf) != read_32bitLE(0x24,sf)) /* header has various repeated values */ goto fail; dspm.channel_count = 1; @@ -1097,17 +1097,17 @@ VGMSTREAM * init_vgmstream_dsp_sps_n1(STREAMFILE *streamFile) { dspm.fix_loop_start = 1; dspm.meta_type = meta_DSP_VAG; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .itl - from Chanrinko Hero (GC) */ -VGMSTREAM * init_vgmstream_dsp_itl_ch(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_itl_ch(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "itl")) + if (!check_extensions(sf, "itl")) goto fail; dspm.channel_count = 2; @@ -1121,26 +1121,26 @@ VGMSTREAM * init_vgmstream_dsp_itl_ch(STREAMFILE *streamFile) { dspm.fix_looping = 1; dspm.meta_type = meta_DSP_ITL; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* ADPY - AQUASTYLE wrapper [Touhou Genso Wanderer -Reloaded- (Switch)] */ -VGMSTREAM * init_vgmstream_dsp_adpy(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_adpy(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "adpcmx")) + if (!check_extensions(sf, "adpcmx")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x41445059) /* "ADPY" */ + if (read_32bitBE(0x00,sf) != 0x41445059) /* "ADPY" */ goto fail; /* 0x04(2): 1? */ /* 0x08: some size? */ /* 0x0c: null */ - dspm.channel_count = read_16bitLE(0x06,streamFile); + dspm.channel_count = read_16bitLE(0x06,sf); dspm.max_channels = 2; dspm.little_endian = 1; @@ -1150,56 +1150,56 @@ VGMSTREAM * init_vgmstream_dsp_adpy(STREAMFILE *streamFile) { dspm.interleave = 0x08; dspm.meta_type = meta_DSP_ADPY; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* ADPX - AQUASTYLE wrapper [Fushigi no Gensokyo: Lotus Labyrinth (Switch)] */ -VGMSTREAM * init_vgmstream_dsp_adpx(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_adpx(STREAMFILE* sf) { dsp_meta dspm = {0}; /* checks */ - if (!check_extensions(streamFile, "adpcmx")) + if (!check_extensions(sf, "adpcmx")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x41445058) /* "ADPX" */ + if (read_32bitBE(0x00,sf) != 0x41445058) /* "ADPX" */ goto fail; /* from 0x04 *6 are probably channel sizes, so max would be 6ch; this assumes 2ch */ - if (read_32bitLE(0x04,streamFile) != read_32bitLE(0x08,streamFile) && - read_32bitLE(0x0c,streamFile) != 0) + if (read_32bitLE(0x04,sf) != read_32bitLE(0x08,sf) && + read_32bitLE(0x0c,sf) != 0) goto fail; dspm.channel_count = 2; dspm.max_channels = 2; dspm.little_endian = 1; dspm.header_offset = 0x1c; - dspm.header_spacing = read_32bitLE(0x04,streamFile); + dspm.header_spacing = read_32bitLE(0x04,sf); dspm.start_offset = dspm.header_offset + 0x60; dspm.interleave = dspm.header_spacing; dspm.meta_type = meta_DSP_ADPX; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .ds2 - LucasArts wrapper [Star Wars: Bounty Hunter (GC)] */ -VGMSTREAM * init_vgmstream_dsp_ds2(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_ds2(STREAMFILE* sf) { dsp_meta dspm = {0}; size_t file_size, channel_offset; /* checks */ /* .ds2: real extension, dsp: fake/renamed */ - if (!check_extensions(streamFile, "ds2,dsp")) + if (!check_extensions(sf, "ds2,dsp")) goto fail; - if (!(read_32bitBE(0x50,streamFile) == 0 && - read_32bitBE(0x54,streamFile) == 0 && - read_32bitBE(0x58,streamFile) == 0 && - read_32bitBE(0x5c,streamFile) != 0)) + if (!(read_32bitBE(0x50,sf) == 0 && + read_32bitBE(0x54,sf) == 0 && + read_32bitBE(0x58,sf) == 0 && + read_32bitBE(0x5c,sf) != 0)) goto fail; - file_size = get_streamfile_size(streamFile); - channel_offset = read_32bitBE(0x5c,streamFile); /* absolute offset to 2nd channel */ + file_size = get_streamfile_size(sf); + channel_offset = read_32bitBE(0x5c,sf); /* absolute offset to 2nd channel */ if (channel_offset < file_size / 2 || channel_offset > file_size) /* just to make sure */ goto fail; @@ -1213,23 +1213,23 @@ VGMSTREAM * init_vgmstream_dsp_ds2(STREAMFILE *streamFile) { dspm.interleave = channel_offset - dspm.start_offset; dspm.meta_type = meta_DSP_DS2; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } /* .itl - Incinerator Studios interleaved dsp [Cars Race-o-rama (Wii), MX vs ATV Untamed (Wii)] */ -VGMSTREAM * init_vgmstream_dsp_itl(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_dsp_itl(STREAMFILE* sf) { dsp_meta dspm = {0}; size_t stream_size; /* checks */ /* .itl: standard * .dsp: default to catch a similar file, not sure which devs */ - if (!check_extensions(streamFile, "itl,dsp")) + if (!check_extensions(sf, "itl,dsp")) goto fail; - stream_size = get_streamfile_size(streamFile); + stream_size = get_streamfile_size(sf); dspm.channel_count = 2; dspm.max_channels = 2; @@ -1244,7 +1244,65 @@ VGMSTREAM * init_vgmstream_dsp_itl(STREAMFILE *streamFile) { //todo some files end in half a frame and may click at the very end //todo when .dsp should refer to Ultimate Board Collection (Wii), not sure about dev dspm.meta_type = meta_DSP_ITL_i; - return init_vgmstream_dsp_common(streamFile, &dspm); + return init_vgmstream_dsp_common(sf, &dspm); +fail: + return NULL; +} + +/* .wav - Square Enix wrapper [Dragon Quest I-III (Switch)] */ +VGMSTREAM* init_vgmstream_dsp_sqex(STREAMFILE* sf) { + dsp_meta dspm = {0}; + + /* checks */ + if (!check_extensions(sf, "wav,lwav")) + goto fail; + if (read_u32be(0x00,sf) != 0x00000000) + goto fail; + + dspm.channel_count = read_u32le(0x04,sf); + dspm.header_offset = read_u32le(0x08,sf); + /* 0x0c: channel size */ + dspm.start_offset = dspm.header_offset + 0x60; + + if (dspm.channel_count > 1) { + dspm.interleave = read_u32le(0x10,sf) - dspm.header_offset; + dspm.header_spacing = dspm.interleave; + } + + + dspm.max_channels = 2; + dspm.little_endian = 1; + + dspm.meta_type = meta_DSP_SQEX; + return init_vgmstream_dsp_common(sf, &dspm); +fail: + return NULL; +} + +/* WiiVoice - Koei Tecmo wrapper [Fatal Frame 5 (WiiU)] */ +VGMSTREAM* init_vgmstream_dsp_wiivoice(STREAMFILE* sf) { + dsp_meta dspm = {0}; + /* also see g1l.c for WiiBGM weirder variation */ + + /* checks */ + /* .dsp: assumed */ + if (!check_extensions(sf, "dsp")) + goto fail; + if (read_u32be(0x00,sf) != 0x57696956 && /* "WiiV" */ + read_u32be(0x04,sf) != 0x6F696365) /* "oice" */ + goto fail; + + dspm.channel_count = 1; + dspm.max_channels = 1; + + dspm.header_offset = read_u32be(0x08,sf); + /* 0x10: file size */ + /* 0x14: data size */ + dspm.header_spacing = 0x60; + dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channel_count; + + dspm.meta_type = meta_DSP_WIIVOICE; + return init_vgmstream_dsp_common(sf, &dspm); fail: return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nus3bank.c b/Frameworks/vgmstream/vgmstream/src/meta/nus3bank.c index 81baf9fb2..277cab413 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nus3bank.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nus3bank.c @@ -3,28 +3,29 @@ #include "nus3bank_streamfile.h" -typedef enum { IDSP, IVAG, BNSF, RIFF, OPUS, RIFF_ENC, } nus3bank_codec; +typedef enum { IDSP, IVAG, BNSF, RIFF, RIFF_XMA2, OPUS, RIFF_ENC, } nus3bank_codec; -/* .nus3bank - Namco's newest audio container [Super Smash Bros (Wii U), idolmaster (PS4))] */ -VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { - VGMSTREAM *vgmstream = NULL; - STREAMFILE *temp_sf = NULL; +/* .nus3bank - Namco's newest audio container [Super Smash Bros (Wii U), THE iDOLM@STER 2 (PS3/X360)] */ +VGMSTREAM* init_vgmstream_nus3bank(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; off_t tone_offset = 0, pack_offset = 0, name_offset = 0, subfile_offset = 0; size_t name_size = 0, subfile_size = 0; nus3bank_codec codec; const char* fake_ext; - int total_subsongs, target_subsong = streamFile->stream_index; + int total_subsongs, target_subsong = sf->stream_index; + /* checks */ /* .nub2: early [THE iDOLM@STER 2 (PS3/X360)] * .nus3bank: standard */ - if (!check_extensions(streamFile, "nub2,nus3bank")) + if (!check_extensions(sf, "nub2,nus3bank")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x4E555333) /* "NUS3" */ + if (read_u32be(0x00,sf) != 0x4E555333) /* "NUS3" */ goto fail; - if (read_32bitBE(0x08,streamFile) != 0x42414E4B) /* "BANK" */ + if (read_u32be(0x08,sf) != 0x42414E4B) /* "BANK" */ goto fail; - if (read_32bitBE(0x0c,streamFile) != 0x544F4320) /* "TOC\0" */ + if (read_u32be(0x0c,sf) != 0x544F4320) /* "TOC\0" */ goto fail; /* header is always LE, while contained files may use another endianness */ @@ -32,12 +33,12 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { /* parse TOC with all existing chunks and sizes (offsets must be derived) */ { int i; - off_t offset = 0x14 + read_32bitLE(0x10, streamFile); /* TOC size */ - size_t chunk_count = read_32bitLE(0x14, streamFile); /* rarely not 7 (ex. SMB U's snd_bgm_CRS12_Simple_Result_Final) */ + off_t offset = 0x14 + read_u32le(0x10, sf); /* TOC size */ + size_t chunk_count = read_u32le(0x14, sf); /* rarely not 7 (ex. SMB U's snd_bgm_CRS12_Simple_Result_Final) */ for (i = 0; i < chunk_count; i++) { - uint32_t chunk_id = (uint32_t)read_32bitBE(0x18+(i*0x08)+0x00, streamFile); - size_t chunk_size = (size_t)read_32bitLE(0x18+(i*0x08)+0x04, streamFile); + uint32_t chunk_id = read_u32be(0x18+(i*0x08)+0x00, sf); + size_t chunk_size = read_u32le(0x18+(i*0x08)+0x04, sf); switch(chunk_id) { case 0x544F4E45: /* "TONE": stream info */ @@ -71,7 +72,7 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { { int i; uint32_t codec_id = 0; - size_t entries = read_32bitLE(tone_offset+0x00, streamFile); + size_t entries = read_u32le(tone_offset+0x00, sf); /* get actual number of subsongs */ total_subsongs = 0; @@ -82,8 +83,8 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { size_t tone_header_size, stream_name_size, stream_size; uint8_t flags2; - tone_header_offset = read_32bitLE(tone_offset+0x04+(i*0x08)+0x00, streamFile); - tone_header_size = read_32bitLE(tone_offset+0x04+(i*0x08)+0x04, streamFile); + tone_header_offset = read_u32le(tone_offset+0x04+(i*0x08)+0x00, sf); + tone_header_size = read_u32le(tone_offset+0x04+(i*0x08)+0x04, sf); offset = tone_offset + tone_header_offset; //;VGM_LOG("NUS3BANK: tone at %lx, size %x\n", tone_offset + tone_header_offset, tone_header_size); @@ -96,7 +97,7 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { /* 0x00: type? normally 0x00 and rarely 0x09 */ /* 0x04: usually -1, found when tone is not a stream (most flags are off too) */ /* 0x06: flags1 */ - flags2 = read_8bit(offset + 0x07, streamFile); + flags2 = read_8bit(offset + 0x07, sf); offset += 0x08; /* flags3-6 (early .nub2 and some odd non-stream don't have them) */ @@ -104,18 +105,18 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { offset += 0x04; } - stream_name_size = read_8bit(offset + 0x00, streamFile); /* includes null */ + stream_name_size = read_8bit(offset + 0x00, sf); /* includes null */ stream_name_offset = offset + 0x01; offset += align_size_to_block(0x01 + stream_name_size, 0x04); /* padded if needed */ /* 0x00: subtype? should be 0 */ - if (read_32bitLE(offset + 0x04, streamFile) != 0x08) { /* flag? */ + if (read_u32le(offset + 0x04, sf) != 0x08) { /* flag? */ //;VGM_LOG("NUS3BANK: bad tone type at %lx, size %x\n", tone_offset + tone_header_offset, tone_header_size); continue; } - stream_offset = read_32bitLE(offset + 0x08, streamFile) + pack_offset; - stream_size = read_32bitLE(offset + 0x0c, streamFile); + stream_offset = read_u32le(offset + 0x08, sf) + pack_offset; + stream_size = read_u32le(offset + 0x0c, sf); //;VGM_LOG("NUS3BANK: so=%lx, ss=%x\n", stream_offset, stream_size); /* Beyond are a bunch of sub-chunks of unknown size with floats and stuff, that seemingly @@ -148,17 +149,25 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { } //todo improve, codec may be in one of the tone sub-chunks (or other chunk? one bank seems to use one codec) - codec_id = read_32bitBE(subfile_offset, streamFile); + codec_id = read_u32be(subfile_offset + 0x00, sf); switch(codec_id) { case 0x49445350: /* "IDSP" [Super Smash Bros. for 3DS (3DS)] */ codec = IDSP; fake_ext = "idsp"; break; - case 0x52494646: /* "RIFF" [THE iDOLM@STER 2 (PS3), Mario Kart Arcade GP DX (PC), idolm@ster: Platinum Stars (PS4)] */ - codec = RIFF; - fake_ext = "wav"; //TODO: works but should have better detection + case 0x52494646: { /* "RIFF" [THE iDOLM@STER 2 (PS3), Mario Kart Arcade GP DX (PC), idolm@ster: Platinum Stars (PS4)] */ + uint16_t format = read_u16le(subfile_offset + 0x14, sf); + if (format == 0x0166) { /* Tekken Tag Tournament 2 (X360) */ + codec = RIFF_XMA2; + fake_ext = "xma"; + } + else { + codec = RIFF; + fake_ext = "wav"; //TODO: works but should have better detection + } break; + } case 0x4F505553: /* "OPUS" [Taiko no Tatsujin (Switch)] */ codec = OPUS; @@ -186,9 +195,9 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { } } - //;VGM_LOG("NUS3BANK: subfile=%lx, size=%x\n", subfile_offset, subfile_size); + //;VGM_LOG("NUS3BANK: subfile=%lx, size=%x, %s\n", subfile_offset, subfile_size, fake_ext); - temp_sf = setup_subfile_streamfile(streamFile, subfile_offset, subfile_size, fake_ext); + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, fake_ext); if (!temp_sf) goto fail; /* init the VGMSTREAM */ @@ -218,6 +227,11 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { if (!vgmstream) goto fail; break; + case RIFF_XMA2: + vgmstream = init_vgmstream_xma(temp_sf); + if (!vgmstream) goto fail; + break; + case RIFF_ENC: vgmstream = init_vgmstream_nus3bank_encrypted(temp_sf); if (!vgmstream) goto fail; @@ -229,7 +243,7 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile) { vgmstream->num_streams = total_subsongs; if (name_offset) - read_string(vgmstream->stream_name,name_size, name_offset,streamFile); + read_string(vgmstream->stream_name, name_size, name_offset, sf); close_streamfile(temp_sf); @@ -242,9 +256,9 @@ fail: } /* encrypted RIFF from the above, in case kids try to extract and play single files */ -VGMSTREAM* init_vgmstream_nus3bank_encrypted(STREAMFILE *sf) { - VGMSTREAM *vgmstream = NULL; - STREAMFILE *temp_sf = NULL; +VGMSTREAM* init_vgmstream_nus3bank_encrypted(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; /* checks */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nwa.c b/Frameworks/vgmstream/vgmstream/src/meta/nwa.c index 26003fbbd..ca554442f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nwa.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nwa.c @@ -1,18 +1,15 @@ #include "meta.h" -#include "../util.h" -#include "../coding/nwa_decoder.h" +#include "../coding/coding.h" #include #include -static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start); -static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start, int32_t *out_loop_end); -static nwa_codec_data *open_nwa_vgmstream(STREAMFILE *streamFile); -static void free_nwa_vgmstream(nwa_codec_data *data); +static int get_loops_nwainfo_ini(STREAMFILE *sf, int *p_loop_flag, int32_t *p_loop_start); +static int get_loops_gameexe_ini(STREAMFILE *sf, int *p_loop_flag, int32_t *p_loop_start, int32_t *p_loop_end); /* NWA - Visual Art's streams [Air (PC), Clannad (PC)] */ -VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_nwa(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; int channel_count, loop_flag = 0; int32_t loop_start_sample = 0, loop_end_sample = 0; @@ -21,26 +18,26 @@ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) { /* checks */ - if (!check_extensions(streamFile, "nwa")) + if (!check_extensions(sf, "nwa")) goto fail; - channel_count = read_16bitLE(0x00,streamFile); + channel_count = read_16bitLE(0x00,sf); if (channel_count != 1 && channel_count != 2) goto fail; /* check if we're using raw pcm */ - if ( read_32bitLE(0x08,streamFile)==-1 || /* compression level */ - read_32bitLE(0x10,streamFile)==0 || /* block count */ - read_32bitLE(0x18,streamFile)==0 || /* compressed data size */ - read_32bitLE(0x20,streamFile)==0 || /* block size */ - read_32bitLE(0x24,streamFile)==0 ) { /* restsize */ + if ( read_32bitLE(0x08,sf)==-1 || /* compression level */ + read_32bitLE(0x10,sf)==0 || /* block count */ + read_32bitLE(0x18,sf)==0 || /* compressed data size */ + read_32bitLE(0x20,sf)==0 || /* block size */ + read_32bitLE(0x24,sf)==0 ) { /* restsize */ compression_level = -1; } else { - compression_level = read_32bitLE(0x08,streamFile); + compression_level = read_32bitLE(0x08,sf); } /* loop points come from external files */ - nwainfo_ini_found = get_loops_nwainfo_ini(streamFile, &loop_flag, &loop_start_sample); - gameexe_ini_found = !nwainfo_ini_found && get_loops_gameexe_ini(streamFile, &loop_flag, &loop_start_sample, &loop_end_sample); + nwainfo_ini_found = get_loops_nwainfo_ini(sf, &loop_flag, &loop_start_sample); + gameexe_ini_found = !nwainfo_ini_found && get_loops_gameexe_ini(sf, &loop_flag, &loop_start_sample, &loop_end_sample); start_offset = 0x2c; @@ -48,12 +45,12 @@ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = read_32bitLE(0x04,streamFile); - vgmstream->num_samples = read_32bitLE(0x1c,streamFile) / channel_count; + vgmstream->sample_rate = read_32bitLE(0x04,sf); + vgmstream->num_samples = read_32bitLE(0x1c,sf) / channel_count; switch(compression_level) { case -1: - switch (read_16bitLE(0x02,streamFile)) { + switch (read_16bitLE(0x02,sf)) { case 8: vgmstream->coding_type = coding_PCM8; vgmstream->interleave_block_size = 0x01; @@ -76,7 +73,7 @@ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) { case 5: vgmstream->coding_type = coding_NWA; vgmstream->layout_type = layout_none; - vgmstream->codec_data = open_nwa_vgmstream(streamFile); + vgmstream->codec_data = init_nwa(sf); if (!vgmstream->codec_data) goto fail; break; @@ -103,7 +100,7 @@ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) { } - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) goto fail; return vgmstream; @@ -114,8 +111,8 @@ fail: /* try to locate NWAINFO.INI in the same directory */ -static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start) { - STREAMFILE *streamLoops; +static int get_loops_nwainfo_ini(STREAMFILE *sf, int *p_loop_flag, int32_t *p_loop_start) { + STREAMFILE *sf_loop; char namebase[PATH_LIMIT]; const char * ext; int length; @@ -127,15 +124,15 @@ static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int int32_t loop_start_sample = 0; - streamLoops = open_streamfile_by_filename(streamFile, "NWAINFO.INI"); - if (!streamLoops) goto fail; + sf_loop = open_streamfile_by_filename(sf, "NWAINFO.INI"); + if (!sf_loop) goto fail; - get_streamfile_filename(streamFile,namebase,PATH_LIMIT); + get_streamfile_filename(sf,namebase,PATH_LIMIT); /* ini found, try to find our name */ ext = filename_extension(namebase); length = ext - 1 - namebase; - file_size = get_streamfile_size(streamLoops); + file_size = get_streamfile_size(sf_loop); for (found = 0, offset = 0; !found && offset < file_size; offset++) { off_t suboffset; @@ -145,12 +142,12 @@ static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int for (suboffset = offset; suboffset < file_size && suboffset-offset < length && - read_8bit(suboffset,streamLoops) == namebase[suboffset-offset]; + read_8bit(suboffset,sf_loop) == namebase[suboffset-offset]; suboffset++) { /* skip */ } - if (suboffset-offset==length && read_8bit(suboffset,streamLoops)==0x09) { /* tab */ + if (suboffset-offset==length && read_8bit(suboffset,sf_loop)==0x09) { /* tab */ found = 1; found_off = suboffset+1; } @@ -160,7 +157,7 @@ static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int if (found) { char loopstring[9] = {0}; - if (read_streamfile((uint8_t*)loopstring,found_off,8,streamLoops) == 8) { + if (read_streamfile((uint8_t*)loopstring,found_off,8,sf_loop) == 8) { loop_start_sample = atol(loopstring); if (loop_start_sample > 0) loop_flag = 1; @@ -168,22 +165,22 @@ static int get_loops_nwainfo_ini(STREAMFILE *streamFile, int *out_loop_flag, int } - *out_loop_flag = loop_flag; - *out_loop_start = loop_start_sample; + *p_loop_flag = loop_flag; + *p_loop_start = loop_start_sample; - close_streamfile(streamLoops); + close_streamfile(sf_loop); return 1; fail: - close_streamfile(streamLoops); + close_streamfile(sf_loop); return 0; } /* try to locate Gameexe.ini in the same directory */ -static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int32_t *out_loop_start, int32_t *out_loop_end) { - STREAMFILE *streamLoops; +static int get_loops_gameexe_ini(STREAMFILE* sf, int* p_loop_flag, int32_t* p_loop_start, int32_t* p_loop_end) { + STREAMFILE*sf_loop; char namebase[PATH_LIMIT]; - const char * ext; + const char* ext; int length; int found; off_t offset; @@ -193,15 +190,15 @@ static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int int32_t loop_start_sample = 0, loop_end_sample = 0; - streamLoops = open_streamfile_by_filename(streamFile, "Gameexe.ini"); - if (!streamLoops) goto fail; + sf_loop = open_streamfile_by_filename(sf, "Gameexe.ini"); + if (!sf_loop) goto fail; - get_streamfile_filename(streamFile,namebase,PATH_LIMIT); + get_streamfile_filename(sf,namebase,PATH_LIMIT); /* ini found, try to find our name */ ext = filename_extension(namebase); length = ext-1-namebase; - file_size = get_streamfile_size(streamLoops); + file_size = get_streamfile_size(sf_loop); /* format of line is: * #DSTRACK = 00000000 - eeeeeeee - ssssssss = "name" = "name2?" @@ -212,20 +209,20 @@ static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int off_t suboffset; uint8_t buf[10]; - if (read_8bit(offset,streamLoops)!='#') continue; - if (read_streamfile(buf,offset+1,10,streamLoops)!=10) break; + if (read_8bit(offset,sf_loop)!='#') continue; + if (read_streamfile(buf,offset+1,10,sf_loop)!=10) break; if (memcmp("DSTRACK = ",buf,10)) continue; - if (read_8bit(offset+44,streamLoops)!='\"') continue; + if (read_8bit(offset+44,sf_loop)!='\"') continue; for (suboffset = offset+45; suboffset < file_size && suboffset-offset-45 < length && - tolower(read_8bit(suboffset,streamLoops)) == tolower(namebase[suboffset-offset-45]); + tolower(read_8bit(suboffset,sf_loop)) == tolower(namebase[suboffset-offset-45]); suboffset++) { /* skip */ } - if (suboffset-offset-45==length && read_8bit(suboffset,streamLoops)=='\"') { /* tab */ + if (suboffset-offset-45==length && read_8bit(suboffset,sf_loop)=='\"') { /* tab */ found = 1; found_off = offset+22; /* loop end */ } @@ -234,9 +231,9 @@ static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int if (found) { char loopstring[9] = {0}; int start_ok = 0, end_ok = 0; - int32_t total_samples = read_32bitLE(0x1c,streamFile) / read_16bitLE(0x00,streamFile); + int32_t total_samples = read_32bitLE(0x1c,sf) / read_16bitLE(0x00,sf); - if (read_streamfile((uint8_t*)loopstring,found_off,8,streamLoops)==8) + if (read_streamfile((uint8_t*)loopstring,found_off,8,sf_loop)==8) { if (!memcmp("99999999",loopstring,8)) { loop_end_sample = total_samples; @@ -246,7 +243,7 @@ static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int } end_ok = 1; } - if (read_streamfile((uint8_t*)loopstring,found_off+11,8,streamLoops)==8) + if (read_streamfile((uint8_t*)loopstring,found_off+11,8,sf_loop)==8) { if (!memcmp("99999999",loopstring,8)) { /* not ok to start at last sample, @@ -265,43 +262,14 @@ static int get_loops_gameexe_ini(STREAMFILE *streamFile, int *out_loop_flag, int } /* if found file name in INI */ - *out_loop_flag = loop_flag; - *out_loop_start = loop_start_sample; - *out_loop_end = loop_end_sample; + *p_loop_flag = loop_flag; + *p_loop_start = loop_start_sample; + *p_loop_end = loop_end_sample; - close_streamfile(streamLoops); + close_streamfile(sf_loop); return 1; fail: - close_streamfile(streamLoops); + close_streamfile(sf_loop); return 0; } - - -static nwa_codec_data *open_nwa_vgmstream(STREAMFILE *streamFile) { - nwa_codec_data *data = NULL; - char filename[PATH_LIMIT]; - - streamFile->get_name(streamFile,filename,sizeof(filename)); - - data = malloc(sizeof(nwa_codec_data)); - if (!data) goto fail; - - data->nwa = open_nwa(streamFile,filename); - if (!data->nwa) goto fail; - - return data; - -fail: - free_nwa_vgmstream(data); - return NULL; -} - -static void free_nwa_vgmstream(nwa_codec_data *data) { - if (data) { - if (data->nwa) { - close_nwa(data->nwa); - } - free(data); - } -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c index d1ed10f44..5cfebf283 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c @@ -6,7 +6,7 @@ #include "ogg_vorbis_streamfile.h" -static void um3_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { +static void um3_ogg_decryption_callback(void* ptr, size_t size, size_t nmemb, void* datasource) { uint8_t *ptr8 = ptr; size_t bytes_read = size * nmemb; ogg_vorbis_io *io = datasource; @@ -23,7 +23,7 @@ static void um3_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo } } -static void kovs_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { +static void kovs_ogg_decryption_callback(void* ptr, size_t size, size_t nmemb, void* datasource) { uint8_t *ptr8 = ptr; size_t bytes_read = size * nmemb; ogg_vorbis_io *io = datasource; @@ -41,7 +41,7 @@ static void kovs_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, v } } -static void psychic_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { +static void psychic_ogg_decryption_callback(void* ptr, size_t size, size_t nmemb, void* datasource) { static const uint8_t key[6] = { 0x23,0x31,0x20,0x2e,0x2e,0x28 }; @@ -58,7 +58,7 @@ static void psychic_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb } } -static void rpgmvo_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) { +static void rpgmvo_ogg_decryption_callback(void* ptr, size_t size, size_t nmemb, void* datasource) { static const uint8_t header[16] = { /* OggS, packet type, granule, stream id(empty) */ 0x4F,0x67,0x67,0x53,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; @@ -97,9 +97,9 @@ static const uint32_t xiph_mappings[] = { /* Ogg Vorbis, may contain loop comments */ -VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE *temp_streamFile = NULL; +VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; ogg_vorbis_io_config_data cfg = {0}; ogg_vorbis_meta_info_t ovmi = {0}; off_t start_offset = 0; @@ -124,54 +124,59 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { * .acm: Planescape Torment Enhanced Edition (PC) * .sod: Zone 4 (PC) * .aif/laif/aif-Loop: Psychonauts (PC) raw extractions (named) */ - if (check_extensions(streamFile,"ogg,logg,adx,rof,acm,sod,aif,laif,aif-Loop")) { + if (check_extensions(sf,"ogg,logg,adx,rof,acm,sod,aif,laif,aif-Loop")) { is_ogg = 1; - } else if (check_extensions(streamFile,"um3")) { + } else if (check_extensions(sf,"um3")) { is_um3 = 1; - } else if (check_extensions(streamFile,"kvs,kovs")) { + } else if (check_extensions(sf,"kvs,kovs")) { /* .kvs: Atelier Sophie (PC), kovs: header id only? */ is_kovs = 1; - } else if (check_extensions(streamFile,"sngw")) { /* .sngw: Capcom [Devil May Cry 4 SE (PC), Biohazard 6 (PC)] */ + } else if (check_extensions(sf,"sngw")) { /* .sngw: Capcom [Devil May Cry 4 SE (PC), Biohazard 6 (PC)] */ is_sngw = 1; - } else if (check_extensions(streamFile,"isd")) { /* .isd: Inti Creates PC games */ + } else if (check_extensions(sf,"isd")) { /* .isd: Inti Creates PC games */ is_isd = 1; - } else if (check_extensions(streamFile,"rpgmvo")) { /* .rpgmvo: RPG Maker MV games (PC) */ + } else if (check_extensions(sf,"rpgmvo")) { /* .rpgmvo: RPG Maker MV games (PC) */ is_rpgmvo = 1; - } else if (check_extensions(streamFile,"eno")) { /* .eno: Metronomicon (PC) */ + } else if (check_extensions(sf,"eno")) { /* .eno: Metronomicon (PC) */ is_eno = 1; - } else if (check_extensions(streamFile,"gwm")) { /* .gwm: Adagio: Cloudburst (PC) */ + } else if (check_extensions(sf,"gwm")) { /* .gwm: Adagio: Cloudburst (PC) */ is_gwm = 1; - } else if (check_extensions(streamFile,"mus")) { /* .mus: Redux - Dark Matters (PC) */ + } else if (check_extensions(sf,"mus")) { /* .mus: Redux - Dark Matters (PC) */ is_mus = 1; - } else if (check_extensions(streamFile,"lse")) { /* .lse: Labyrinth of Refrain: Coven of Dusk (PC) */ + } else if (check_extensions(sf,"lse")) { /* .lse: Labyrinth of Refrain: Coven of Dusk (PC) */ is_lse = 1; - } else if (check_extensions(streamFile,"bgm")) { /* .bgm: Fortissimo (PC) */ + } else if (check_extensions(sf,"bgm")) { /* .bgm: Fortissimo (PC) */ is_bgm = 1; } else { goto fail; } if (is_ogg) { - if (read_32bitBE(0x00,streamFile) == 0x2c444430) { /* Psychic Software [Darkwind: War on Wheels (PC)] */ + if (read_32bitBE(0x00,sf) == 0x2c444430) { /* Psychic Software [Darkwind: War on Wheels (PC)] */ ovmi.decryption_callback = psychic_ogg_decryption_callback; } - else if (read_32bitBE(0x00,streamFile) == 0x4C325344) { /* "L2SD" instead of "OggS" [Lineage II Chronicle 4 (PC)] */ + else if (read_32bitBE(0x00,sf) == 0x4C325344) { /* "L2SD" instead of "OggS" [Lineage II Chronicle 4 (PC)] */ cfg.is_header_swap = 1; cfg.is_encrypted = 1; } - else if (read_32bitBE(0x00,streamFile) == 0x048686C5) { /* "OggS" XOR'ed + bitswapped [Ys VIII (PC)] */ + else if (read_32bitBE(0x00,sf) == 0x048686C5) { /* "OggS" XOR'ed + bitswapped [Ys VIII (PC)] */ cfg.key[0] = 0xF0; cfg.key_len = 1; cfg.is_nibble_swap = 1; cfg.is_encrypted = 1; } - else if (read_32bitBE(0x00,streamFile) == 0x00000000 && /* null instead of "OggS" [Yuppie Psycho (PC)] */ - read_32bitBE(0x3a,streamFile) == 0x4F676753) { /* "OggS" in next page */ + else if (read_32bitBE(0x00,sf) == 0x00000000 && /* null instead of "OggS" [Yuppie Psycho (PC)] */ + read_32bitBE(0x3a,sf) == 0x4F676753) { /* "OggS" in next page */ cfg.is_header_swap = 1; cfg.is_encrypted = 1; } - else if (read_32bitBE(0x00,streamFile) == 0x4F676753) { /* "OggS" (standard) */ + else if (read_32bitBE(0x00,sf) != 0x4F676753 && /* random(?) swap instead of "OggS" [Tobi Tsukihime (PC)] */ + read_32bitBE(0x3a,sf) == 0x4F676753) { /* "OggS" in next page */ + cfg.is_header_swap = 1; + cfg.is_encrypted = 1; + } + else if (read_32bitBE(0x00,sf) == 0x4F676753) { /* "OggS" (standard) */ ; } else { @@ -180,16 +185,16 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { } if (is_um3) { /* ["Ultramarine3" (???)] */ - if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" (optionally encrypted) */ + if (read_32bitBE(0x00,sf) != 0x4f676753) { /* "OggS" (optionally encrypted) */ ovmi.decryption_callback = um3_ogg_decryption_callback; } } if (is_kovs) { /* Koei Tecmo PC games */ - if (read_32bitBE(0x00,streamFile) != 0x4b4f5653) { /* "KOVS" */ + if (read_32bitBE(0x00,sf) != 0x4b4f5653) { /* "KOVS" */ goto fail; } - ovmi.loop_start = read_32bitLE(0x08,streamFile); + ovmi.loop_start = read_32bitLE(0x08,sf); ovmi.loop_flag = (ovmi.loop_start != 0); ovmi.decryption_callback = kovs_ogg_decryption_callback; ovmi.meta_type = meta_OGG_KOVS; @@ -198,8 +203,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { } if (is_sngw) { /* [Capcom's MT Framework PC games] */ - if (read_32bitBE(0x00,streamFile) != 0x4f676753) { /* "OggS" (optionally encrypted) */ - cfg.key_len = read_streamfile(cfg.key, 0x00, 0x04, streamFile); + if (read_32bitBE(0x00,sf) != 0x4f676753) { /* "OggS" (optionally encrypted) */ + cfg.key_len = read_streamfile(cfg.key, 0x00, 0x04, sf); cfg.is_header_swap = 1; cfg.is_nibble_swap = 1; cfg.is_encrypted = 1; @@ -212,7 +217,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { const char *isl_name = NULL; /* check various encrypted "OggS" values */ - if (read_32bitBE(0x00,streamFile) == 0xAF678753) { /* Azure Striker Gunvolt (PC) */ + if (read_32bitBE(0x00,sf) == 0xAF678753) { /* Azure Striker Gunvolt (PC) */ static const uint8_t isd_gv_key[16] = { 0xe0,0x00,0xe0,0x00,0xa0,0x00,0x00,0x00,0xe0,0x00,0xe0,0x80,0x40,0x40,0x40,0x00 }; @@ -220,7 +225,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { memcpy(cfg.key, isd_gv_key, cfg.key_len); isl_name = "GV_steam.isl"; } - else if (read_32bitBE(0x00,streamFile) == 0x0FE787D3) { /* Mighty Gunvolt (PC) */ + else if (read_32bitBE(0x00,sf) == 0x0FE787D3) { /* Mighty Gunvolt (PC) */ static const uint8_t isd_mgv_key[120] = { 0x40,0x80,0xE0,0x80,0x40,0x40,0xA0,0x00,0xA0,0x40,0x00,0x80,0x00,0x40,0xA0,0x00, 0xC0,0x40,0xE0,0x00,0x60,0x40,0x80,0x00,0xA0,0x00,0xE0,0x00,0x60,0x40,0xC0,0x00, @@ -235,7 +240,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { memcpy(cfg.key, isd_mgv_key, cfg.key_len); isl_name = "MGV_steam.isl"; } - else if (read_32bitBE(0x00,streamFile) == 0x0FA74753) { /* Blaster Master Zero (PC) */ + else if (read_32bitBE(0x00,sf) == 0x0FA74753) { /* Blaster Master Zero (PC) */ static const uint8_t isd_bmz_key[120] = { 0x40,0xC0,0x20,0x00,0x40,0xC0,0xC0,0x00,0x00,0x80,0xE0,0x80,0x80,0x40,0x20,0x00, 0x60,0xC0,0xC0,0x00,0xA0,0x80,0x60,0x00,0x40,0x40,0x20,0x00,0x60,0x40,0xC0,0x00, @@ -263,19 +268,19 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { * 0x0c(2): PCM block size, 0x0e(2): PCM bps, 0x10: null, 0x18: samples (in PCM bytes) * - .isl: looping table (encrypted like the files) */ if (isl_name) { - STREAMFILE *islFile = NULL; + STREAMFILE* islFile = NULL; - islFile = open_streamfile_by_filename(streamFile, isl_name); + islFile = open_streamfile_by_filename(sf, isl_name); if (!islFile) { /* try in ../(file) too since that's how the .isl is stored on disc */ char isl_path[PATH_LIMIT]; snprintf(isl_path, sizeof(isl_path), "../%s", isl_name); - islFile = open_streamfile_by_filename(streamFile, isl_path); + islFile = open_streamfile_by_filename(sf, isl_path); } if (islFile) { - STREAMFILE *dec_sf = NULL; + STREAMFILE* dec_sf = NULL; dec_sf = setup_ogg_vorbis_streamfile(islFile, cfg); if (dec_sf) { @@ -284,7 +289,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { /* has a bunch of tables then a list with file names without extension and loops */ loop_offset = read_32bitLE(0x18, dec_sf); - get_streamfile_basename(streamFile, basename, sizeof(basename)); + get_streamfile_basename(sf, basename, sizeof(basename)); while (loop_offset < get_streamfile_size(dec_sf)) { char testname[0x20]; @@ -310,8 +315,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { } if (is_rpgmvo) { /* [RPG Maker MV (PC)] */ - if (read_32bitBE(0x00,streamFile) != 0x5250474D && /* "RPGM" */ - read_32bitBE(0x00,streamFile) != 0x56000000) { /* "V\0\0\0" */ + if (read_32bitBE(0x00,sf) != 0x5250474D && /* "RPGM" */ + read_32bitBE(0x00,sf) != 0x56000000) { /* "V\0\0\0" */ goto fail; } ovmi.decryption_callback = rpgmvo_ogg_decryption_callback; @@ -321,7 +326,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { if (is_eno) { /* [Metronomicon (PC)] */ /* first byte probably derives into key, but this works too */ - cfg.key[0] = (uint8_t)read_8bit(0x05,streamFile); /* regular ogg have a zero at this offset = easy key */ + cfg.key[0] = (uint8_t)read_8bit(0x05,sf); /* regular ogg have a zero at this offset = easy key */ cfg.key_len = 1; cfg.is_encrypted = 1; start_offset = 0x01; /* "OggS" starts after key-thing */ @@ -344,7 +349,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { } if (is_lse) { /* [Nippon Ichi PC games] */ - if (read_32bitBE(0x00,streamFile) == 0xFFFFFFFF) { /* [Operation Abyss: New Tokyo Legacy (PC)] */ + if (read_32bitBE(0x00,sf) == 0xFFFFFFFF) { /* [Operation Abyss: New Tokyo Legacy (PC)] */ cfg.key[0] = 0xFF; cfg.key_len = 1; cfg.is_header_swap = 1; @@ -353,7 +358,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { else { /* [Operation Babel: New Tokyo Legacy (PC), Labyrinth of Refrain: Coven of Dusk (PC)] */ int i; /* found at file_size-1 but this works too (same key for most files but can vary) */ - uint8_t base_key = (uint8_t)read_8bit(0x04,streamFile) - 0x04; + uint8_t base_key = (uint8_t)read_8bit(0x04,sf) - 0x04; cfg.key_len = 256; for (i = 0; i < cfg.key_len; i++) { @@ -364,13 +369,13 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { } if (is_bgm) { /* [Fortissimo (PC)] */ - size_t file_size = get_streamfile_size(streamFile); + size_t file_size = get_streamfile_size(sf); uint8_t key[0x04]; uint32_t xor_be; put_32bitLE(key, (uint32_t)file_size); xor_be = (uint32_t)get_32bitBE(key); - if ((read_32bitBE(0x00,streamFile) ^ xor_be) == 0x4F676753) { /* "OggS" */ + if ((read_32bitBE(0x00,sf) ^ xor_be) == 0x4F676753) { /* "OggS" */ int i; cfg.key_len = 4; for (i = 0; i < cfg.key_len; i++) { @@ -382,8 +387,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { if (cfg.is_encrypted) { - temp_streamFile = setup_ogg_vorbis_streamfile(streamFile, cfg); - if (!temp_streamFile) goto fail; + temp_sf = setup_ogg_vorbis_streamfile(sf, cfg); + if (!temp_sf) goto fail; } if (ovmi.meta_type == 0) { @@ -393,18 +398,18 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) { ovmi.meta_type = meta_OGG_VORBIS; } - vgmstream = init_vgmstream_ogg_vorbis_callbacks(temp_streamFile != NULL ? temp_streamFile : streamFile, NULL, start_offset, &ovmi); + vgmstream = init_vgmstream_ogg_vorbis_callbacks(temp_sf != NULL ? temp_sf : sf, NULL, start_offset, &ovmi); - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); return vgmstream; fail: - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); return NULL; } -VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callbacks *callbacks, off_t start, const ogg_vorbis_meta_info_t *ovmi) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_ogg_vorbis_callbacks(STREAMFILE* sf, ov_callbacks* callbacks, off_t start, const ogg_vorbis_meta_info_t *ovmi) { + VGMSTREAM* vgmstream = NULL; ogg_vorbis_codec_data* data = NULL; ogg_vorbis_io io = {0}; char name[STREAM_NAME_SIZE] = {0}; @@ -418,7 +423,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb int32_t loop_end = ovmi->loop_end; size_t stream_size = ovmi->stream_size ? ovmi->stream_size : - get_streamfile_size(streamFile) - start; + get_streamfile_size(sf) - start; int disable_reordering = ovmi->disable_reordering; @@ -428,22 +433,27 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb io.scd_xor_length = ovmi->scd_xor_length; io.xor_value = ovmi->xor_value; - data = init_ogg_vorbis(streamFile, start, stream_size, &io); + data = init_ogg_vorbis(sf, start, stream_size, &io); if (!data) goto fail; + ogg_vorbis_get_info(data, &channels, &sample_rate); + ogg_vorbis_get_samples(data, &num_samples); /* let libvorbisfile find total samples */ + /* search for loop comments */ {//todo ignore if loop flag already set? - const char * comment = NULL; + const char* comment = NULL; while (ogg_vorbis_get_comment(data, &comment)) { if (strstr(comment,"loop_start=") == comment || /* PSO4 */ strstr(comment,"LOOP_START=") == comment || /* PSO4 */ + strstr(comment,"LOOPPOINT=") == comment || /* Sonic Robo Blast 2 */ strstr(comment,"COMMENT=LOOPPOINT=") == comment || strstr(comment,"LOOPSTART=") == comment || strstr(comment,"um3.stream.looppoint.start=") == comment || strstr(comment,"LOOP_BEGIN=") == comment || /* Hatsune Miku: Project Diva F (PS3) */ strstr(comment,"LoopStart=") == comment || /* Devil May Cry 4 (PC) */ + strstr(comment,"LOOP=") == comment || /* Duke Nukem 3D: 20th Anniversary World Tour */ strstr(comment,"XIPH_CUE_LOOPSTART=") == comment) { /* Super Mario Run (Android) */ loop_start = atol(strrchr(comment,'=')+1); loop_flag = (loop_start >= 0); @@ -512,6 +522,12 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb loop_end_found = 1; } } + else if (strstr(comment,"LOOPMS=") == comment) { /* Sonic Robo Blast 2 */ + /* Convert from milliseconds to samples. */ + /* (x ms) * (y samples/s) / (1000 ms/s) */ + loop_start = atol(strrchr(comment,'=')+1) * sample_rate / 1000; + loop_flag = (loop_start >= 0); + } /* Hatsune Miku Project DIVA games, though only 'Arcade Future Tone' has >4ch files * ENCODER tag is common but ogg_vorbis_encode looks unique enough @@ -529,8 +545,6 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb } ogg_vorbis_set_disable_reordering(data, disable_reordering); - ogg_vorbis_get_info(data, &channels, &sample_rate); - ogg_vorbis_get_samples(data, &num_samples); /* let libvorbisfile find total samples */ /* build the VGMSTREAM */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/opus.c b/Frameworks/vgmstream/vgmstream/src/meta/opus.c index 932ecef2e..b2fff433f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/opus.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/opus.c @@ -5,32 +5,32 @@ /* Nintendo OPUS - from Switch games, including header variations (not the same as Ogg Opus) */ -static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type, off_t offset, int32_t num_samples, int32_t loop_start, int32_t loop_end) { - VGMSTREAM * vgmstream = NULL; +static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t offset, int32_t num_samples, int32_t loop_start, int32_t loop_end) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; int loop_flag = 0, channel_count; off_t data_offset, multichannel_offset = 0; size_t data_size, skip = 0; - if ((uint32_t)read_32bitLE(offset + 0x00,streamFile) != 0x80000001) + if ((uint32_t)read_32bitLE(offset + 0x00,sf) != 0x80000001) goto fail; - channel_count = read_8bit(offset + 0x09, streamFile); + channel_count = read_8bit(offset + 0x09, sf); /* 0x0a: packet size if CBR, 0 if VBR */ - data_offset = offset + read_32bitLE(offset + 0x10, streamFile); - skip = read_16bitLE(offset + 0x1c, streamFile); + data_offset = offset + read_32bitLE(offset + 0x10, sf); + skip = read_16bitLE(offset + 0x1c, sf); /* 0x1e: ? (seen in Lego Movie 2 (Switch)) */ /* recent >2ch info [Clannad (Switch)] */ - if ((uint32_t)read_32bitLE(offset + 0x20, streamFile) == 0x80000005) { + if ((uint32_t)read_32bitLE(offset + 0x20, sf) == 0x80000005) { multichannel_offset = offset + 0x20; } - if ((uint32_t)read_32bitLE(data_offset, streamFile) != 0x80000004) + if ((uint32_t)read_32bitLE(data_offset, sf) != 0x80000004) goto fail; - data_size = read_32bitLE(data_offset + 0x04, streamFile); + data_size = read_32bitLE(data_offset + 0x04, sf); start_offset = data_offset + 0x08; loop_flag = (loop_end > 0); /* -1 when not set */ @@ -41,7 +41,7 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type, if (!vgmstream) goto fail; vgmstream->meta_type = meta_type; - vgmstream->sample_rate = read_32bitLE(offset + 0x0c,streamFile); + vgmstream->sample_rate = read_32bitLE(offset + 0x0c,sf); if (vgmstream->sample_rate == 16000) vgmstream->sample_rate = 48000; // Grandia HD Collection contains a false sample_rate in header vgmstream->num_samples = num_samples; @@ -59,28 +59,28 @@ static VGMSTREAM * init_vgmstream_opus(STREAMFILE *streamFile, meta_t meta_type, if (multichannel_offset && vgmstream->channels <= 8) { int i; - cfg.stream_count = read_8bit(multichannel_offset + 0x08,streamFile); - cfg.coupled_count = read_8bit(multichannel_offset + 0x09,streamFile); + cfg.stream_count = read_8bit(multichannel_offset + 0x08,sf); + cfg.coupled_count = read_8bit(multichannel_offset + 0x09,sf); for (i = 0; i < vgmstream->channels; i++) { - cfg.channel_mapping[i] = read_8bit(multichannel_offset + 0x0a + i,streamFile); + cfg.channel_mapping[i] = read_8bit(multichannel_offset + 0x0a + i,sf); } } - vgmstream->codec_data = init_ffmpeg_switch_opus_config(streamFile, start_offset,data_size, &cfg); + vgmstream->codec_data = init_ffmpeg_switch_opus_config(sf, start_offset,data_size, &cfg); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; vgmstream->channel_layout = ffmpeg_get_channel_layout(vgmstream->codec_data); if (vgmstream->num_samples == 0) { - vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, streamFile) - skip; + vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, sf) - skip; } } #else goto fail; #endif - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + if ( !vgmstream_open_stream(vgmstream, sf, start_offset) ) goto fail; return vgmstream; @@ -91,20 +91,20 @@ fail: /* standard Switch Opus, Nintendo header + raw data (generated by opus_test.c?) [Lego City Undercover (Switch)] */ -VGMSTREAM * init_vgmstream_opus_std(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_std(STREAMFILE* sf) { STREAMFILE * PSIFile = NULL; off_t offset; int num_samples, loop_start, loop_end; /* checks */ - if (!check_extensions(streamFile,"opus,lopus")) + if (!check_extensions(sf,"opus,lopus")) goto fail; offset = 0x00; /* BlazBlue: Cross Tag Battle (Switch) PSI Metadata for corresponding Opus */ /* Maybe future Arc System Works games will use this too? */ - PSIFile = open_streamfile_by_ext(streamFile, "psi"); + PSIFile = open_streamfile_by_ext(sf, "psi"); if (PSIFile) { num_samples = read_32bitLE(0x8C, PSIFile); loop_start = read_32bitLE(0x84, PSIFile); @@ -117,56 +117,56 @@ VGMSTREAM * init_vgmstream_opus_std(STREAMFILE *streamFile) { loop_end = 0; } - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples,loop_start,loop_end); fail: return NULL; } /* Nippon1 variation [Disgaea 5 (Switch)] */ -VGMSTREAM * init_vgmstream_opus_n1(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_n1(STREAMFILE* sf) { off_t offset; int num_samples, loop_start, loop_end; /* checks */ - if ( !check_extensions(streamFile,"opus,lopus")) + if ( !check_extensions(sf,"opus,lopus")) goto fail; - if (!((read_32bitBE(0x04,streamFile) == 0x00000000 && read_32bitBE(0x0c,streamFile) == 0x00000000) || - (read_32bitBE(0x04,streamFile) == 0xFFFFFFFF && read_32bitBE(0x0c,streamFile) == 0xFFFFFFFF))) + if (!((read_32bitBE(0x04,sf) == 0x00000000 && read_32bitBE(0x0c,sf) == 0x00000000) || + (read_32bitBE(0x04,sf) == 0xFFFFFFFF && read_32bitBE(0x0c,sf) == 0xFFFFFFFF))) goto fail; offset = 0x10; num_samples = 0; - loop_start = read_32bitLE(0x00,streamFile); - loop_end = read_32bitLE(0x08,streamFile); + loop_start = read_32bitLE(0x00,sf); + loop_end = read_32bitLE(0x08,sf); - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples,loop_start,loop_end); fail: return NULL; } /* Capcom variation [Ultra Street Fighter II (Switch), Resident Evil: Revelations (Switch)] */ -VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_capcom(STREAMFILE* sf) { VGMSTREAM *vgmstream = NULL; off_t offset; int num_samples, loop_start, loop_end; int channel_count; /* checks */ - if ( !check_extensions(streamFile,"opus,lopus")) + if ( !check_extensions(sf,"opus,lopus")) goto fail; - channel_count = read_32bitLE(0x04,streamFile); + channel_count = read_32bitLE(0x04,sf); if (channel_count != 1 && channel_count != 2 && channel_count != 6) goto fail; /* unknown stream layout */ - num_samples = read_32bitLE(0x00,streamFile); + num_samples = read_32bitLE(0x00,sf); /* 0x04: channels, >2 uses interleaved streams (2ch+2ch+2ch) */ - loop_start = read_32bitLE(0x08,streamFile); - loop_end = read_32bitLE(0x0c,streamFile); + loop_start = read_32bitLE(0x08,sf); + loop_end = read_32bitLE(0x0c,sf); /* 0x10: frame size (with extra data) */ /* 0x14: extra chunk count */ /* 0x18: null */ - offset = read_32bitLE(0x1c,streamFile); + offset = read_32bitLE(0x1c,sf); /* 0x20-8: config? (0x0077C102 04000000 E107070C) */ /* 0x2c: some size? */ /* 0x30+: extra chunks (0x00: 0x7f, 0x04: num_sample), alt loop starts/regions? */ @@ -193,7 +193,7 @@ VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE *streamFile) { /* open each layer subfile */ for (i = 0; i < layers; i++) { - STREAMFILE* temp_sf = setup_opus_interleave_streamfile(streamFile, offset, i, layers); + STREAMFILE* temp_sf = setup_opus_interleave_streamfile(sf, offset, i, layers); if (!temp_sf) goto fail; data->layers[i] = init_vgmstream_opus(temp_sf, meta_OPUS, 0x00, num_samples,loop_start,loop_end); @@ -215,7 +215,7 @@ VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE *streamFile) { return vgmstream; } else { - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples,loop_start,loop_end); } @@ -225,85 +225,85 @@ fail: } /* Procyon Studio variation [Xenoblade Chronicles 2 (Switch)] */ -VGMSTREAM * init_vgmstream_opus_nop(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_nop(STREAMFILE* sf) { off_t offset; int num_samples, loop_start = 0, loop_end = 0, loop_flag; /* checks */ - if (!check_extensions(streamFile,"nop")) + if (!check_extensions(sf,"nop")) goto fail; - if (read_32bitBE(0x00, streamFile) != 0x73616466 || /* "sadf" */ - read_32bitBE(0x08, streamFile) != 0x6f707573) /* "opus" */ + if (read_32bitBE(0x00, sf) != 0x73616466 || /* "sadf" */ + read_32bitBE(0x08, sf) != 0x6f707573) /* "opus" */ goto fail; - offset = read_32bitLE(0x1c, streamFile); - num_samples = read_32bitLE(0x28, streamFile); - loop_flag = read_8bit(0x19, streamFile); + offset = read_32bitLE(0x1c, sf); + num_samples = read_32bitLE(0x28, sf); + loop_flag = read_8bit(0x19, sf); if (loop_flag) { - loop_start = read_32bitLE(0x2c, streamFile); - loop_end = read_32bitLE(0x30, streamFile); + loop_start = read_32bitLE(0x2c, sf); + loop_end = read_32bitLE(0x30, sf); } - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples,loop_start,loop_end); fail: return NULL; } /* Shin'en variation [Fast RMX (Switch)] */ -VGMSTREAM * init_vgmstream_opus_shinen(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_shinen(STREAMFILE* sf) { off_t offset = 0; int num_samples, loop_start, loop_end; /* checks */ - if ( !check_extensions(streamFile,"opus,lopus")) + if ( !check_extensions(sf,"opus,lopus")) goto fail; - if (read_32bitBE(0x08,streamFile) != 0x01000080) + if (read_32bitBE(0x08,sf) != 0x01000080) goto fail; offset = 0x08; num_samples = 0; - loop_start = read_32bitLE(0x00,streamFile); - loop_end = read_32bitLE(0x04,streamFile); /* 0 if no loop */ + loop_start = read_32bitLE(0x00,sf); + loop_end = read_32bitLE(0x04,sf); /* 0 if no loop */ if (loop_start > loop_end) goto fail; /* just in case */ - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples,loop_start,loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples,loop_start,loop_end); fail: return NULL; } /* Bandai Namco Opus (found in NUS3Banks) [Taiko no Tatsujin: Nintendo Switch Version!] */ -VGMSTREAM * init_vgmstream_opus_nus3(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_nus3(STREAMFILE* sf) { off_t offset = 0; int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag; /* checks */ /* .opus: header ID (they only exist inside .nus3bank) */ - if (!check_extensions(streamFile, "opus,lopus")) + if (!check_extensions(sf, "opus,lopus")) goto fail; - if (read_32bitBE(0x00, streamFile) != 0x4F505553) /* "OPUS" */ + if (read_32bitBE(0x00, sf) != 0x4F505553) /* "OPUS" */ goto fail; /* Here's an interesting quirk, OPUS header contains big endian values while the Nintendo Opus header and data that follows remain little endian as usual */ - offset = read_32bitBE(0x20, streamFile); - num_samples = read_32bitBE(0x08, streamFile); + offset = read_32bitBE(0x20, sf); + num_samples = read_32bitBE(0x08, sf); /* Check if there's a loop end value to determine loop_flag*/ - loop_flag = read_32bitBE(0x18, streamFile); + loop_flag = read_32bitBE(0x18, sf); if (loop_flag) { - loop_start = read_32bitBE(0x14, streamFile); - loop_end = read_32bitBE(0x18, streamFile); + loop_start = read_32bitBE(0x14, sf); + loop_end = read_32bitBE(0x18, sf); } - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples, loop_start, loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples, loop_start, loop_end); fail: return NULL; } /* Nippon Ichi SPS wrapper (non-segmented) [Ys VIII: Lacrimosa of Dana (Switch)] */ -VGMSTREAM * init_vgmstream_opus_sps_n1(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_sps_n1(STREAMFILE* sf) { off_t offset; int num_samples, loop_start = 0, loop_end = 0, loop_flag; @@ -311,28 +311,28 @@ VGMSTREAM * init_vgmstream_opus_sps_n1(STREAMFILE *streamFile) { /* .sps: Labyrinth of Refrain: Coven of Dusk (Switch) * .nlsd: Disgaea Refine (Switch), Ys VIII (Switch) * .at9: void tRrLM(); //Void Terrarium (Switch) */ - if (!check_extensions(streamFile, "sps,nlsd,at9")) + if (!check_extensions(sf, "sps,nlsd,at9")) goto fail; - if (read_32bitBE(0x00, streamFile) != 0x09000000) /* file type (see other N1 SPS) */ + if (read_32bitBE(0x00, sf) != 0x09000000) /* file type (see other N1 SPS) */ goto fail; - num_samples = read_32bitLE(0x0C, streamFile); + num_samples = read_32bitLE(0x0C, sf); - if (read_32bitBE(0x1c, streamFile) == 0x01000080) { + if (read_32bitBE(0x1c, sf) == 0x01000080) { offset = 0x1C; /* older games loop section (remnant of segmented opus_sps_n1): */ - loop_start = read_32bitLE(0x10, streamFile); /* intro samples */ - loop_end = loop_start + read_32bitLE(0x14, streamFile); /* loop samples */ + loop_start = read_32bitLE(0x10, sf); /* intro samples */ + loop_end = loop_start + read_32bitLE(0x14, sf); /* loop samples */ /* 0x18: end samples (all must add up to num_samples) */ - loop_flag = read_32bitLE(0x18, streamFile); /* with loop disabled only loop_end has a value */ + loop_flag = read_32bitLE(0x18, sf); /* with loop disabled only loop_end has a value */ } else { offset = 0x18; /* newer games loop section: */ - loop_start = read_32bitLE(0x10, streamFile); - loop_end = read_32bitLE(0x14, streamFile); + loop_start = read_32bitLE(0x10, sf); + loop_end = read_32bitLE(0x14, sf); loop_flag = loop_start != loop_end; /* with loop disabled start and end are the same as num samples */ } @@ -341,29 +341,29 @@ VGMSTREAM * init_vgmstream_opus_sps_n1(STREAMFILE *streamFile) { loop_end = 0; } - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples, loop_start, loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples, loop_start, loop_end); fail: return NULL; } /* AQUASTYLE wrapper [Touhou Genso Wanderer -Reloaded- (Switch)] */ -VGMSTREAM * init_vgmstream_opus_opusx(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_opusx(STREAMFILE* sf) { off_t offset; int num_samples, loop_start = 0, loop_end = 0; float modifier; /* checks */ - if (!check_extensions(streamFile, "opusx")) + if (!check_extensions(sf, "opusx")) goto fail; - if (read_32bitBE(0x00, streamFile) != 0x4F505553) /* "OPUS" */ + if (read_32bitBE(0x00, sf) != 0x4F505553) /* "OPUS" */ goto fail; offset = 0x10; /* values are for the original 44100 files, but Opus resamples to 48000 */ modifier = 48000.0f / 44100.0f; - num_samples = 0;//read_32bitLE(0x04, streamFile) * modifier; /* better use calc'd num_samples */ - loop_start = read_32bitLE(0x08, streamFile) * modifier; - loop_end = read_32bitLE(0x0c, streamFile) * modifier; + num_samples = 0;//read_32bitLE(0x04, sf) * modifier; /* better use calc'd num_samples */ + loop_start = read_32bitLE(0x08, sf) * modifier; + loop_end = read_32bitLE(0x0c, sf) * modifier; /* resampling calcs are slighly off and may to over num_samples, but by removing delay seems ok */ if (loop_start >= 120) { @@ -374,81 +374,102 @@ VGMSTREAM * init_vgmstream_opus_opusx(STREAMFILE *streamFile) { loop_end = 0; } - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples, loop_start, loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples, loop_start, loop_end); fail: return NULL; } /* Prototype variation [Clannad (Switch)] */ -VGMSTREAM * init_vgmstream_opus_prototype(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_prototype(STREAMFILE* sf) { off_t offset = 0; int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag; /* checks */ - if (!check_extensions(streamFile, "opus,lopus")) + if (!check_extensions(sf, "opus,lopus")) goto fail; - if (read_32bitBE(0x00, streamFile) != 0x4F505553 || /* "OPUS" */ - read_32bitBE(0x18, streamFile) != 0x01000080) + if (read_32bitBE(0x00, sf) != 0x4F505553 || /* "OPUS" */ + read_32bitBE(0x18, sf) != 0x01000080) goto fail; offset = 0x18; - num_samples = read_32bitLE(0x08, streamFile); + num_samples = read_32bitLE(0x08, sf); /* Check if there's a loop end value to determine loop_flag*/ - loop_flag = read_32bitLE(0x10, streamFile); + loop_flag = read_32bitLE(0x10, sf); if (loop_flag) { - loop_start = read_32bitLE(0x0C, streamFile); - loop_end = read_32bitLE(0x10, streamFile); + loop_start = read_32bitLE(0x0C, sf); + loop_end = read_32bitLE(0x10, sf); } - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples, loop_start, loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples, loop_start, loop_end); fail: return NULL; } /* Edelweiss variation [Astebreed (Switch)] */ -VGMSTREAM * init_vgmstream_opus_opusnx(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_opusnx(STREAMFILE* sf) { off_t offset = 0; int num_samples = 0, loop_start = 0, loop_end = 0; /* checks */ - if (!check_extensions(streamFile, "opus,lopus")) + if (!check_extensions(sf, "opus,lopus")) goto fail; - if (read_64bitBE(0x00, streamFile) != 0x4F5055534E580000) /* "OPUSNX\0\0" */ + if (read_64bitBE(0x00, sf) != 0x4F5055534E580000) /* "OPUSNX\0\0" */ goto fail; offset = 0x10; - num_samples = 0; //read_32bitLE(0x08, streamFile); /* samples with encoder delay */ - if (read_32bitLE(0x0c, streamFile) != 0) + num_samples = 0; //read_32bitLE(0x08, sf); /* samples with encoder delay */ + if (read_32bitLE(0x0c, sf) != 0) goto fail; - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples, loop_start, loop_end); + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples, loop_start, loop_end); +fail: + return NULL; +} + +/* Edelweiss variation [Sakuna: Of Rice and Ruin (Switch)] */ +VGMSTREAM* init_vgmstream_opus_nsopus(STREAMFILE* sf) { + off_t offset = 0; + int num_samples = 0, loop_start = 0, loop_end = 0; + + /* checks */ + if (!check_extensions(sf, "nsopus")) + goto fail; + if (read_u32be(0x00, sf) != 0x45574E4F) /* "EWNO" */ + goto fail; + + offset = 0x08; + num_samples = 0; //read_32bitLE(0x08, sf); /* samples without encoder delay? (lower than count) */ + + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples, loop_start, loop_end); fail: return NULL; } /* Square Enix variation [Dragon Quest I-III (Switch)] */ -VGMSTREAM * init_vgmstream_opus_sqex(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_opus_sqex(STREAMFILE* sf) { off_t offset = 0; int num_samples = 0, loop_start = 0, loop_end = 0, loop_flag; - + /* checks */ - if (!check_extensions(streamFile, "opus,lopus")) + /* .wav: default + * .opus: fake? */ + if (!check_extensions(sf, "wav,lwav,opus,lopus")) goto fail; - if (read_64bitBE(0x00, streamFile) != 0x0100000002000000) + if (read_u32be(0x00, sf) != 0x01000000) goto fail; - - offset = read_32bitLE(0x0C, streamFile); - num_samples = read_32bitLE(0x1C, streamFile); - - /* Check if there's a loop end value to determine loop_flag*/ - loop_flag = read_32bitLE(0x18, streamFile); + /* 0x04: channels */ + /* 0x08: data_size */ + offset = read_32bitLE(0x0C, sf); + num_samples = read_32bitLE(0x1C, sf); + + loop_flag = read_32bitLE(0x18, sf); if (loop_flag) { - loop_start = read_32bitLE(0x14, streamFile); - loop_end = read_32bitLE(0x18, streamFile); + loop_start = read_32bitLE(0x14, sf); + loop_end = read_32bitLE(0x18, sf); } - - return init_vgmstream_opus(streamFile, meta_OPUS, offset, num_samples, loop_start, loop_end); + + return init_vgmstream_opus(sf, meta_OPUS, offset, num_samples, loop_start, loop_end); fail: return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/opus_ppp.c b/Frameworks/vgmstream/vgmstream/src/meta/opus_ppp.c deleted file mode 100644 index 0ff9a26ae..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/opus_ppp.c +++ /dev/null @@ -1,93 +0,0 @@ -#include "meta.h" -#include "../coding/coding.h" -#include "../layout/layout.h" - -/* Nippon Ichi SPS wrapper (segmented) [Penny-Punching Princess (Switch)] */ -VGMSTREAM * init_vgmstream_opus_sps_n1_segmented(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t segment_offset; - int loop_flag, channel_count; - int i; - - segmented_layout_data *data = NULL; - int segment_count, loop_start_segment = 0, loop_end_segment = 0; - int num_samples = 0, loop_start_sample = 0, loop_end_sample = 0; - - - /* checks */ - if (!check_extensions(streamFile, "at9")) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x09000000) /* file type (see other N1 SPS) */ - goto fail; - if (read_32bitLE(0x04,streamFile) + 0x1c != get_streamfile_size(streamFile)) - goto fail; - /* 0x08(2): sample rate, 0x0a(2): flag?, 0x0c: num_samples (slightly smaller than added samples) */ - - segment_count = 3; /* intro/loop/end */ - loop_start_segment = 1; - loop_end_segment = 1; - loop_flag = (segment_count > 0); - - /* init layout */ - data = init_layout_segmented(segment_count); - if (!data) goto fail; - - /* open each segment subfile */ - segment_offset = 0x1c; - for (i = 0; i < segment_count; i++) { - STREAMFILE* temp_streamFile; - size_t segment_size = read_32bitLE(0x10+0x04*i,streamFile); - - if (!segment_size) - goto fail; - - temp_streamFile = setup_subfile_streamfile(streamFile, segment_offset,segment_size, "opus"); - if (!temp_streamFile) goto fail; - - data->segments[i] = init_vgmstream_opus_std(temp_streamFile); - close_streamfile(temp_streamFile); - if (!data->segments[i]) goto fail; - - segment_offset += segment_size; - - //todo there are some trailing samples that must be removed for smooth loops, start skip seems ok - data->segments[i]->num_samples -= 374; //not correct for all files, no idea how to calculate - - /* get looping and samples */ - if (loop_flag && loop_start_segment == i) - loop_start_sample = num_samples; - - num_samples += data->segments[i]->num_samples; - - if (loop_flag && loop_end_segment == i) - loop_end_sample = num_samples; - } - - /* setup segmented VGMSTREAMs */ - if (!setup_layout_segmented(data)) - goto fail; - - - channel_count = data->segments[0]->channels; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = (uint16_t)read_16bitLE(0x08,streamFile); - vgmstream->num_samples = num_samples; - vgmstream->loop_start_sample = loop_start_sample; - vgmstream->loop_end_sample = loop_end_sample; - - vgmstream->meta_type = meta_OPUS_PPP; - vgmstream->coding_type = data->segments[0]->coding_type; - vgmstream->layout_type = layout_segmented; - vgmstream->layout_data = data; - - return vgmstream; - -fail: - close_vgmstream(vgmstream); - free_layout_segmented(data); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_enth.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_enth.c index c3e6eb19c..4501a1f54 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_enth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_enth.c @@ -1,94 +1,92 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" +#include "ps2_enth_streamfile.h" -/* ENTH (from Enthusia - Professional Racing) */ -VGMSTREAM * init_vgmstream_ps2_enth(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; +/* LP/AP/LEP - from Konami (KCES)'s Enthusia: Professional Racing (PS2) */ +VGMSTREAM* init_vgmstream_ps2_enth(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; off_t start_offset; - int header_check; - int loop_flag; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("enth",filename_extension(filename))) goto fail; - - /* check header and loop_flag */ - header_check = read_32bitBE(0x00,streamFile); - switch (header_check) { - case 0x41502020: /* AP */ - loop_flag = (read_32bitLE(0x14,streamFile)!=0); - break; - case 0x4C455020: /* LEP */ - loop_flag = (read_32bitLE(0x58,streamFile)!=0); - break; - default: - goto fail; - } - - channel_count = 2; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - header_check = read_32bitBE(0x00,streamFile); - - switch (header_check) { - case 0x41502020: /* AP */ - start_offset = read_32bitLE(0x1C,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x08,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (read_32bitLE(0x18,streamFile))*28/16/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = (read_32bitLE(0x14,streamFile))*28/16/channel_count; - vgmstream->loop_end_sample = (read_32bitLE(0x18,streamFile))*28/16/channel_count; - } - vgmstream->interleave_block_size = read_32bitLE(0x0C,streamFile); - break; - case 0x4C455020: /* LEP */ - start_offset = 0x800; - vgmstream->channels = channel_count; - vgmstream->sample_rate = (uint16_t)read_16bitLE(0x12,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (read_32bitLE(0x08,streamFile))*28/16/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = (read_32bitLE(0x58,streamFile))*28/16/channel_count; - vgmstream->loop_end_sample = (read_32bitLE(0x08,streamFile))*28/16/channel_count; - } - vgmstream->interleave_block_size = 0x10; - break; - default: - goto fail; -} + int loop_flag, channels, sample_rate, interleave; + int32_t data_size, loop_start; + uint32_t id; - vgmstream->layout_type = layout_interleave; - vgmstream->meta_type = meta_PS2_ENTH; + /* checks */ + /* .bin/lbin: internal (no names in bigfiles but exes mention "bgm%05d.bin" and "LEP data") + * .lp/lep/ap: header ID */ + if (!check_extensions(sf, "bin,lbin,lp,lep,ap")) + goto fail; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; + id = read_u32be(0x00,sf); + switch (id) { + case 0x41502020: /* "AP " */ + case 0x4C502020: /* "LP " */ + sample_rate = read_u32le(0x08,sf); + interleave = read_u32le(0x0c,sf); + loop_start = read_u32le(0x14,sf); + data_size = read_u32le(0x18,sf); + start_offset = read_u32le(0x1C,sf); + break; - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; + case 0x4C455020: /* "LEP " */ + data_size = read_u32le(0x08,sf); + sample_rate = read_u16le(0x12,sf); + loop_start = read_u32le(0x58,sf); + interleave = 0x10; + start_offset = 0x800; + break; - } + default: + goto fail; } - return vgmstream; + loop_flag = loop_start != 0; + channels = 2; - /* clean up anything we may have opened */ + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_PS2_ENTH; + vgmstream->sample_rate = sample_rate; + + switch (id) { + case 0x4C502020: /* "LP " */ + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = pcm_bytes_to_samples(data_size, channels, 16); + vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, channels, 16); + vgmstream->loop_end_sample = vgmstream->num_samples; + + temp_sf = setup_lp_streamfile(sf, start_offset); /* encrypted/obfuscated PCM */ + if (!temp_sf) goto fail; + break; + + case 0x41502020: /* "AP " */ + case 0x4C455020: /* "LEP " */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = ps_bytes_to_samples(data_size, channels); + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channels); + vgmstream->loop_end_sample = vgmstream->num_samples; + break; + + default: + goto fail; + } + + if (!vgmstream_open_stream(vgmstream, temp_sf ? temp_sf : sf, start_offset)) + goto fail; + close_streamfile(temp_sf); + return vgmstream; fail: - if (vgmstream) close_vgmstream(vgmstream); + close_streamfile(temp_sf); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_enth_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ps2_enth_streamfile.h new file mode 100644 index 000000000..5cac41312 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_enth_streamfile.h @@ -0,0 +1,39 @@ +#ifndef _LP_STREAMFILE_H_ +#define _LP_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + off_t start; +} lp_io_data; + +static size_t lp_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, lp_io_data* data) { + int i; + size_t bytes = read_streamfile(dest, offset, length, sf); + + /* PCM16LE frames are ROL'ed (or lower bit is ignored?) so this only works if reads are aligned + * to sizeof(uint16_t); maybe could be a new decoder but seems like a waste */ + for (i = 0; i < bytes / 2 * 2; i += 2) { + if (offset + i >= data->start) { + uint16_t v = get_u16le(dest + i); + v = (v << 1) | ((v >> 15) & 0x0001); + put_u16le(dest + i, v); + } + } + + return bytes; +} + +/* decrypts Enthusia "LP" PCM streams */ +static STREAMFILE* setup_lp_streamfile(STREAMFILE* sf, off_t start) { + STREAMFILE* new_sf = NULL; + lp_io_data io_data = {0}; + + io_data.start = start; + + new_sf = open_wrap_streamfile(sf); + new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(lp_io_data), lp_io_read, NULL); + return new_sf; +} + +#endif /* _LP_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c index c3eea49d4..5def76f73 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_joe.c @@ -1,11 +1,11 @@ #include "meta.h" #include "../coding/coding.h" -static size_t joe_find_padding(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave); +static size_t joe_find_padding(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave); /* .JOE - from Asobo Studio games [Up (PS2), Wall-E (PS2)] */ -VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_ps2_joe(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; int channel_count, loop_flag, sample_rate; int32_t num_samples; @@ -13,54 +13,59 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) { /* checks */ - if (!check_extensions(streamFile, "joe")) + if (!check_extensions(sf, "joe")) goto fail; - file_size = get_streamfile_size(streamFile); - data_size = read_32bitLE(0x04,streamFile); - unknown1 = read_32bitLE(0x08,streamFile); - unknown2 = read_32bitLE(0x0c,streamFile); + file_size = get_streamfile_size(sf); + data_size = read_u32le(0x04,sf); + unknown1 = read_u32le(0x08,sf); + unknown2 = read_u32le(0x0c,sf); /* detect version */ - if (data_size / 2 == file_size - 0x10 - && unknown1 == 0x0045039A && unknown2 == 0x00108920) { /* Super Farm (PS2) */ + if (data_size == file_size - 0x800 + && unknown1 == 0x00002000 && unknown2 == 0xFFFFFFFF) { /* NYR (PS2) */ + interleave = 0x2000; + start_offset = 0x800; + } + else if (data_size / 2 == file_size - 0x10 + && unknown1 == 0x0045039A && unknown2 == 0x00108920) { /* Super Farm (PS2) */ data_size = data_size / 2; interleave = 0x4000; start_offset = 0x10; - } else if (data_size / 2 == file_size - 0x10 - && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* Sitting Ducks (PS2) */ + } + else if (data_size / 2 == file_size - 0x10 + && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* Sitting Ducks (PS2) */ data_size = data_size / 2; interleave = 0x8000; start_offset = 0x10; - } else if (data_size == file_size - 0x10 - && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* The Mummy: The Animated Series (PS2) */ + } + else if (data_size == file_size - 0x10 + && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* The Mummy: The Animated Series (PS2) */ interleave = 0x8000; start_offset = 0x10; - } else if (data_size == file_size - 0x4020) { /* CT Special Forces (PS2), and all games beyond */ - interleave = unknown1; /* always 0? */ - if (!interleave) - interleave = 0x10; - start_offset = 0x4020; /* header padding contains garbage */ - } else { + } + else if (data_size == file_size - 0x4020) { /* Counter Terrorism Special Forces (PS2), all games beyond */ + /* header can be section(?) table (0x08=entry count) then 0xCCCCCCCC, all 0s, or all 0xCCCCCCCC (no table) */ + interleave = 0x10; + start_offset = 0x4020; + } + else { goto fail; } //start_offset = file_size - data_size; /* also ok */ channel_count = 2; loop_flag = 0; - sample_rate = read_32bitLE(0x00,streamFile); - - /* the file's end is padded with either 0xcdcdcdcd or zeroes */ - padding_size = joe_find_padding(streamFile, start_offset, data_size, channel_count, interleave); - if (padding_size == SIZE_MAX) - goto fail; + sample_rate = read_s32le(0x00,sf); + /* the file's end is padded with either 0xcdcdcdcd or zeroes (but not always, ex. NYR) */ + padding_size = joe_find_padding(sf, start_offset, data_size, channel_count, interleave); data_size -= padding_size; num_samples = ps_bytes_to_samples(data_size, channel_count); /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; @@ -72,7 +77,7 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) { vgmstream->interleave_block_size = interleave; vgmstream->meta_type = meta_PS2_JOE; - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; @@ -81,23 +86,23 @@ fail: return NULL; } -static size_t joe_find_padding(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave) { - uint8_t flag; +static size_t joe_find_padding(STREAMFILE* sf, off_t start_offset, size_t data_size, int channels, size_t interleave) { + uint32_t pad; off_t min_offset, offset; size_t frame_size = 0x10; size_t padding_size = 0; size_t interleave_consumed = 0; if (data_size == 0 || channels == 0 || (channels > 0 && interleave == 0)) - return SIZE_MAX; + return 0; offset = start_offset + data_size - interleave * (channels - 1); min_offset = start_offset; while (offset > min_offset) { offset -= frame_size; - flag = read_8bit(offset + 0x01, streamFile); - if (flag == 0x03) + pad = read_u32be(offset, sf); + if (pad != 0xCDCDCDCD && pad != 0x00000000) break; padding_size += frame_size * channels; @@ -111,7 +116,7 @@ static size_t joe_find_padding(STREAMFILE *streamFile, off_t start_offset, size_ } if (padding_size >= data_size) - return SIZE_MAX; + return 0; return padding_size; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_svag_snk.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_svag_snk.c deleted file mode 100644 index 6bcb220ce..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_svag_snk.c +++ /dev/null @@ -1,80 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* PS2 SVAG (SNK) - * - * Found in SNK's World Heroes Anthology and Fatal Fury Battle Archives 2, maybe others - * No relation with Konami's SVAG. - */ - -VGMSTREAM * init_vgmstream_ps2_svag_snk(STREAMFILE* streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - off_t start_offset = 0x20; - - int loop_flag; - int channel_count; - int loop_start_block; - int loop_end_block; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("svag",filename_extension(filename))) goto fail; - - /* check SNK SVAG Header ("VAGm") */ - if (read_32bitBE(0x00,streamFile) != 0x5641476D) - goto fail; - - - channel_count = read_32bitLE(0x0c,streamFile); - - loop_start_block = read_32bitLE(0x18,streamFile); - loop_end_block = read_32bitLE(0x1c,streamFile); - - loop_flag = loop_end_block > 0; /* loop_start_block can be 0 */ - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* header data */ - vgmstream->coding_type = coding_PSX; - vgmstream->meta_type = meta_PS2_SVAG_SNK; - - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x08,streamFile); - vgmstream->num_samples = read_32bitLE(0x10,streamFile) * 28; /* size in blocks */ - if( vgmstream->loop_flag ) { - vgmstream->loop_start_sample = loop_start_block * 28; - vgmstream->loop_end_sample = loop_end_block * 28; - } - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; - - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset = - vgmstream->ch[i].offset = - start_offset + vgmstream->interleave_block_size*i; - } - } - - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/redspark.c b/Frameworks/vgmstream/vgmstream/src/meta/redspark.c index fe8f0b93c..15c39be8e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/redspark.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/redspark.c @@ -23,8 +23,8 @@ VGMSTREAM * init_vgmstream_redspark(STREAMFILE *streamFile) { uint32_t key; enum {encsize = 0x1000}; uint8_t buf[encsize]; - int32_t(*get_32bit)(uint8_t *p) = NULL; - int16_t(*get_16bit)(uint8_t *p) = NULL; + int32_t(*get_32bit)(const uint8_t *p) = NULL; + int16_t(*get_16bit)(const uint8_t *p) = NULL; get_16bit = get_16bitBE; get_32bit = get_32bitBE; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index 53eba94e2..9b488c2d9 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -9,7 +9,7 @@ /* return milliseconds */ -static long parse_adtl_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,14 +21,14 @@ static long parse_adtl_marker(unsigned char * marker) { } /* loop points have been found hiding here */ -static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *streamFile, long *loop_start, long *loop_end, int *loop_flag) { +static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE* sf, 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+0x04; while (current_chunk < adtl_offset + adtl_length) { - uint32_t chunk_type = read_32bitBE(current_chunk+0x00,streamFile); - off_t chunk_size = read_32bitLE(current_chunk+0x04,streamFile); + uint32_t chunk_type = read_32bitBE(current_chunk+0x00,sf); + off_t chunk_size = read_32bitLE(current_chunk+0x04,sf); if (current_chunk+0x08+chunk_size > adtl_offset+adtl_length) return; @@ -37,12 +37,12 @@ static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *stream case 0x6c61626c: { /* "labl" */ unsigned char *labelcontent = malloc(chunk_size-0x04); if (!labelcontent) return; - if (read_streamfile(labelcontent,current_chunk+0x0c, chunk_size-0x04,streamFile) != chunk_size-0x04) { + if (read_streamfile(labelcontent,current_chunk+0x0c, chunk_size-0x04,sf) != chunk_size-0x04) { free(labelcontent); return; } - switch (read_32bitLE(current_chunk+8,streamFile)) { + switch (read_32bitLE(current_chunk+8,sf)) { case 1: if (!loop_start_found && (*loop_start = parse_adtl_marker(labelcontent)) >= 0) loop_start_found = 1; @@ -95,29 +95,29 @@ typedef struct { int is_at9; } riff_fmt_chunk; -static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk, riff_fmt_chunk * fmt, int mwv) { +static int read_fmt(int big_endian, STREAMFILE* sf, off_t current_chunk, riff_fmt_chunk* fmt, int mwv) { 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+0x04,streamFile); + fmt->size = read_32bit(current_chunk+0x04,sf); /* WAVEFORMAT */ - fmt->codec = (uint16_t)read_16bit(current_chunk+0x08,streamFile); - fmt->channel_count = read_16bit(current_chunk+0x0a,streamFile); - fmt->sample_rate = read_32bit(current_chunk+0x0c,streamFile); - //fmt->avg_bps = read_32bit(current_chunk+0x10,streamFile); - fmt->block_size = read_16bit(current_chunk+0x14,streamFile); - fmt->bps = read_16bit(current_chunk+0x16,streamFile); + fmt->codec = (uint16_t)read_16bit(current_chunk+0x08,sf); + fmt->channel_count = read_16bit(current_chunk+0x0a,sf); + fmt->sample_rate = read_32bit(current_chunk+0x0c,sf); + //fmt->avg_bps = read_32bit(current_chunk+0x10,sf); + fmt->block_size = read_16bit(current_chunk+0x14,sf); + fmt->bps = read_16bit(current_chunk+0x16,sf); /* WAVEFORMATEX */ if (fmt->size >= 0x10) { - fmt->extra_size = read_16bit(current_chunk+0x18,streamFile); + fmt->extra_size = read_16bit(current_chunk+0x18,sf); /* 0x1a+ depends on codec (ex. coef table for MSADPCM, samples_per_frame in MS-IMA, etc) */ } /* WAVEFORMATEXTENSIBLE */ if (fmt->codec == 0xFFFE && fmt->extra_size >= 0x16) { - //fmt->extra_samples = read_16bit(current_chunk+0x1a,streamFile); /* valid_bits_per_sample or samples_per_block */ - fmt->channel_layout = read_32bit(current_chunk+0x1c,streamFile); + //fmt->extra_samples = read_16bit(current_chunk+0x1a,sf); /* valid_bits_per_sample or samples_per_block */ + fmt->channel_layout = read_32bit(current_chunk+0x1c,sf); /* 0x10 guid at 0x20 */ /* happens in various .at3/at9, may be a bug in their encoder b/c MS's defs set mono as FC */ @@ -158,7 +158,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk case 0x02: /* MSADPCM */ if (fmt->bps == 4) { fmt->coding_type = coding_MSADPCM; - if (!msadpcm_check_coefs(streamFile, fmt->offset + 0x08 + 0x14)) + if (!msadpcm_check_coefs(sf, fmt->offset + 0x08 + 0x14)) goto fail; } else if (fmt->bps == 16 && fmt->block_size == 0x02 * fmt->channel_count && fmt->size == 0x14) { @@ -188,7 +188,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk case 0x007A: /* MS IMA ADPCM [LA Rush (PC), Psi Ops (PC)] (unofficial) */ /* 0x007A is apparently "Voxware SC3" but in .MED it's just MS-IMA (0x11) */ - if (!check_extensions(streamFile,"med")) + if (!check_extensions(sf,"med")) goto fail; if (fmt->bps == 4) /* normal MS IMA */ @@ -232,11 +232,11 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk #endif case 0xFFFE: { /* WAVEFORMATEXTENSIBLE (see ksmedia.h for known GUIDs) */ - uint32_t guid1 = (uint32_t)read_32bit (current_chunk+0x20,streamFile); - uint32_t guid2 = ((uint16_t)read_16bit (current_chunk+0x24,streamFile) << 16u) | - ((uint16_t)read_16bit (current_chunk+0x26,streamFile)); - uint32_t guid3 = (uint32_t)read_32bitBE(current_chunk+0x28,streamFile); - uint32_t guid4 = (uint32_t)read_32bitBE(current_chunk+0x2c,streamFile); + uint32_t guid1 = (uint32_t)read_32bit (current_chunk+0x20,sf); + uint32_t guid2 = ((uint16_t)read_16bit (current_chunk+0x24,sf) << 16u) | + ((uint16_t)read_16bit (current_chunk+0x26,sf)); + uint32_t guid3 = (uint32_t)read_32bitBE(current_chunk+0x28,sf); + uint32_t guid4 = (uint32_t)read_32bitBE(current_chunk+0x2c,sf); //;VGM_LOG("RIFF: guid %08x %08x %08x %08x\n", guid1, guid2, guid3, guid4); /* PCM GUID (0x00000001,0000,0010,80,00,00,AA,00,38,9B,71) */ @@ -255,7 +255,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk /* ATRAC3plus GUID (0xE923AABF,CB58,4471,A1,19,FF,FA,01,E4,CE,62) */ if (guid1 == 0xE923AABF && guid2 == 0xCB584471 && guid3 == 0xA119FFFA && guid4 == 0x01E4CE62) { #ifdef VGM_USE_MAIATRAC3PLUS - uint16_t bztmp = read_16bit(current_chunk+0x32,streamFile); + uint16_t bztmp = read_16bit(current_chunk+0x32,sf); bztmp = (bztmp >> 8) | (bztmp << 8); fmt->coding_type = coding_AT3plus; fmt->block_size = (bztmp & 0x3FF) * 8 + 8; /* should match fmt->block_size */ @@ -294,12 +294,12 @@ fail: return 0; } -static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* streamFile, riff_fmt_chunk* fmt, int fact_sample_count, off_t start_offset); -static size_t get_ue4_msadpcm_interleave(STREAMFILE *sf, riff_fmt_chunk *fmt, off_t start, size_t size); +static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* sf, riff_fmt_chunk* fmt, int fact_sample_count, off_t start_offset); +static size_t get_ue4_msadpcm_interleave(STREAMFILE* sf, riff_fmt_chunk* fmt, off_t start, size_t size); -VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; riff_fmt_chunk fmt = {0}; size_t file_size, riff_size, data_size = 0; @@ -317,7 +317,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { int FormatChunkFound = 0, DataChunkFound = 0, JunkFound = 0; - int mwv = 0; /* Level-5 .mwv (Dragon Quest VIII, Rogue Galaxy) */ + int mwv = 0; off_t mwv_pflt_offset = -1; off_t mwv_ctrl_offset = -1; @@ -346,11 +346,12 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { * .aud: EA Replay ATRAC3 * .at9: standard ATRAC9 * .saf: Whacked! (Xbox) + * .mwv: Level-5 games [Dragon Quest VIII (PS2), Rogue Galaxy (PS2)] */ - if ( check_extensions(streamFile, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,saf") ) { + if ( check_extensions(sf, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,saf") ) { ; } - else if ( check_extensions(streamFile, "mwv") ) { + else if ( check_extensions(sf, "mwv") ) { mwv = 1; } else { @@ -358,53 +359,75 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { } /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x52494646) /* "RIFF" */ + if (read_32bitBE(0x00,sf) != 0x52494646) /* "RIFF" */ goto fail; - if (read_32bitBE(0x08,streamFile) != 0x57415645) /* "WAVE" */ + if (read_32bitBE(0x08,sf) != 0x57415645) /* "WAVE" */ goto fail; - riff_size = read_32bitLE(0x04,streamFile); - file_size = get_streamfile_size(streamFile); + riff_size = read_32bitLE(0x04,sf); + file_size = get_streamfile_size(sf); /* some games have wonky sizes, selectively fix to catch bad rips and new mutations */ - { - uint16_t codec = read_16bitLE(0x14,streamFile); - if (riff_size+0x08+0x01 == file_size) - riff_size += 0x01; /* [Shikkoku no Sharnoth (PC)] */ + if (file_size != riff_size + 0x08) { + uint16_t codec = read_16bitLE(0x14,sf); - else if (riff_size == file_size && codec == 0x0069) + if (codec == 0x6771 && riff_size + 0x08 + 0x01 == file_size) + riff_size += 0x01; /* [Shikkoku no Sharnoth (PC)] (Sony Sound Forge?) */ + + else if (codec == 0x0069 && riff_size == file_size) riff_size -= 0x08; /* [Dynasty Warriors 3 (Xbox), BloodRayne (Xbox)] */ - else if (riff_size + 0x04 == file_size && codec == 0x0069) + else if (codec == 0x0069 && riff_size + 0x04 == file_size) riff_size -= 0x04; /* [Halo 2 (PC)] (possibly bad extractor? 'Gravemind Tool') */ - else if (riff_size + 0x04 == file_size && codec == 0x0000) + else if (codec == 0x0000 && riff_size + 0x04 == file_size) riff_size -= 0x04; /* [Headhunter (DC), Bomber hehhe (DC)] */ - else if (riff_size == file_size && codec == 0x0000) + else if (codec == 0x0000 && riff_size == file_size) riff_size -= 0x08; /* [Rayman 2 (DC)] */ - else if (riff_size + 0x02 + 0x08 == file_size && codec == 0x0000) + else if (codec == 0x0000 && riff_size + 0x08 + 0x02 == file_size) riff_size -= 0x02; /* [Rayman 2 (DC)]-dcz */ - else if (riff_size == file_size && codec == 0x0300) + else if (codec == 0x0300 && riff_size == file_size) riff_size -= 0x08; /* [Chrono Ma:gia (Android)] */ - else if (riff_size >= file_size && read_32bitBE(0x24,streamFile) == 0x4E584246) /* "NXBF" */ + else if (codec == 0xFFFE && riff_size + 0x08 + 0x18 == file_size) + riff_size += 0x18; /* [F1 2011 (Vita)] (adds a "pada" chunk but RIFF size wasn't updated) */ + + else if (mwv) { + int channels = read_16bitLE(0x16, sf); /* [Dragon Quest VIII (PS2), Rogue Galaxy (PS2)] */ + size_t file_size_fixed = riff_size + 0x08 + 0x04 * (channels - 1); + + if (file_size_fixed <= file_size && file_size - file_size_fixed < 0x10) + { + /* files inside HD6/DAT are also padded to 0x10 so need to fix file_size */ + file_size = file_size_fixed; + riff_size = file_size - 0x08; + } + } + + else if (riff_size >= file_size && read_32bitBE(0x24,sf) == 0x4E584246) /* "NXBF" */ riff_size = file_size - 0x08; /* [R:Racing Evolution (Xbox)] */ + + else if (codec == 0x0011 && (riff_size / 2 / 2 == read_32bitLE(0x30,sf))) /* riff_size = pcm_size (always stereo, has fact at 0x30) */ + riff_size = file_size - 0x08; /* [Asphalt 6 (iOS)] (sfx/memory wavs have ok sizes?) */ + + else if (codec == 0xFFFE && riff_size + 0x08 + 0x30 == file_size) + riff_size += 0x30; /* [E.X. Troopers (PS3)] (adds "ver /eBIT/tIME/mrkr" empty chunks but RIFF size wasn't updated) */ } /* check for truncated RIFF */ - if (file_size < riff_size+0x08) + if (file_size != riff_size + 0x08) goto fail; /* read through chunks to verify format and find metadata */ { off_t current_chunk = 0x0c; /* start with first chunk */ - while (current_chunk < file_size && current_chunk < riff_size+8) { - uint32_t chunk_id = read_32bitBE(current_chunk + 0x00,streamFile); /* FOURCC */ - size_t chunk_size = read_32bitLE(current_chunk + 0x04,streamFile); + while (current_chunk < file_size) { + uint32_t chunk_id = read_32bitBE(current_chunk + 0x00,sf); /* FOURCC */ + size_t chunk_size = read_32bitLE(current_chunk + 0x04,sf); if (current_chunk + 0x08 + chunk_size > file_size) goto fail; @@ -414,7 +437,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { if (FormatChunkFound) goto fail; /* only one per file */ FormatChunkFound = 1; - if (!read_fmt(0, streamFile, current_chunk, &fmt, mwv)) + if (!read_fmt(0, sf, current_chunk, &fmt, mwv)) goto fail; /* some Dreamcast/Naomi games again [Headhunter (DC), Bomber hehhe (DC), Rayman 2 (DC)] */ @@ -431,11 +454,11 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { break; case 0x4C495354: /* "LIST" */ - switch (read_32bitBE(current_chunk+0x08, streamFile)) { + switch (read_32bitBE(current_chunk+0x08, sf)) { case 0x6164746C: /* "adtl" */ /* yay, atdl is its own little world */ parse_adtl(current_chunk + 8, chunk_size, - streamFile, + sf, &loop_start_ms,&loop_end_ms,&loop_flag); break; default: @@ -447,13 +470,13 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { /* check loop count/loop info (most common) */ /* 0x00: manufacturer id, 0x04: product id, 0x08: sample period, 0x0c: unity node, * 0x10: pitch fraction, 0x14: SMPTE format, 0x18: SMPTE offset, 0x1c: loop count, 0x20: sampler data */ - if (read_32bitLE(current_chunk+0x08+0x1c, streamFile) == 1) { /* handle only one loop (could contain N MIDILoop) */ + if (read_32bitLE(current_chunk+0x08+0x1c, sf) == 1) { /* handle only one loop (could contain N MIDILoop) */ /* 0x24: cue point id, 0x28: type (0=forward, 1=alternating, 2=backward) * 0x2c: start, 0x30: end, 0x34: fraction, 0x38: play count */ - if (read_32bitLE(current_chunk+0x08+0x28, streamFile) == 0) { /* loop forward */ + if (read_32bitLE(current_chunk+0x08+0x28, sf) == 0) { /* loop forward */ loop_flag = 1; - loop_start_smpl = read_32bitLE(current_chunk+0x08+0x2c, streamFile); - loop_end_smpl = read_32bitLE(current_chunk+0x08+0x30, streamFile) + 1; /* must add 1 as per spec (ok for standard WAV/AT3/AT9) */ + loop_start_smpl = read_32bitLE(current_chunk+0x08+0x2c, sf); + loop_end_smpl = read_32bitLE(current_chunk+0x08+0x30, sf) + 1; /* must add 1 as per spec (ok for standard WAV/AT3/AT9) */ } } break; @@ -462,14 +485,14 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { /* check loop count/info (found in some Xbox games: Halo (non-looping), Dynasty Warriors 3, Crimson Sea) */ /* 0x00: size, 0x04: unity note, 0x06: fine tune, 0x08: gain, 0x10: loop count */ if (chunk_size >= 0x24 - && read_32bitLE(current_chunk+0x08+0x00, streamFile) == 0x14 - && read_32bitLE(current_chunk+0x08+0x10, streamFile) > 0 - && read_32bitLE(current_chunk+0x08+0x14, streamFile) == 0x10) { + && read_32bitLE(current_chunk+0x08+0x00, sf) == 0x14 + && read_32bitLE(current_chunk+0x08+0x10, sf) > 0 + && read_32bitLE(current_chunk+0x08+0x14, sf) == 0x10) { /* 0x14: size, 0x18: loop type (0=forward, 1=release), 0x1c: loop start, 0x20: loop length */ - if (read_32bitLE(current_chunk+0x08+0x18, streamFile) == 0) { /* loop forward */ + if (read_32bitLE(current_chunk+0x08+0x18, sf) == 0) { /* loop forward */ loop_flag = 1; - loop_start_wsmp = read_32bitLE(current_chunk+0x08+0x1c, streamFile); - loop_end_wsmp = read_32bitLE(current_chunk+0x08+0x20, streamFile); /* must not add 1 as per spec */ + loop_start_wsmp = read_32bitLE(current_chunk+0x08+0x1c, sf); + loop_end_wsmp = read_32bitLE(current_chunk+0x08+0x20, sf); /* must not add 1 as per spec */ loop_end_wsmp += loop_start_wsmp; } } @@ -477,24 +500,24 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { case 0x66616374: /* "fact" */ if (chunk_size == 0x04) { /* standard (usually for ADPCM, MS recommends to set for non-PCM codecs) */ - fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); + fact_sample_count = read_32bitLE(current_chunk+0x08, sf); } - else if (chunk_size == 0x10 && read_32bitBE(current_chunk+0x08+0x04, streamFile) == 0x4C794E20) { /* "LyN " */ + else if (chunk_size == 0x10 && read_32bitBE(current_chunk+0x08+0x04, sf) == 0x4C794E20) { /* "LyN " */ goto fail; /* parsed elsewhere */ } else if ((fmt.is_at3 || fmt.is_at3p) && chunk_size == 0x08) { /* early AT3 (mainly PSP games) */ - fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); - fact_sample_skip = read_32bitLE(current_chunk+0x0c, streamFile); /* base skip samples */ + fact_sample_count = read_32bitLE(current_chunk+0x08, sf); + fact_sample_skip = read_32bitLE(current_chunk+0x0c, sf); /* base skip samples */ } else if ((fmt.is_at3 || fmt.is_at3p) && chunk_size == 0x0c) { /* late AT3 (mainly PS3 games and few PSP games) */ - fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); + fact_sample_count = read_32bitLE(current_chunk+0x08, sf); /* 0x0c: base skip samples, ignored by decoder */ - fact_sample_skip = read_32bitLE(current_chunk+0x10, streamFile); /* skip samples with extra 184 */ + fact_sample_skip = read_32bitLE(current_chunk+0x10, sf); /* skip samples with extra 184 */ } else if (fmt.is_at9 && chunk_size == 0x0c) { - fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile); + fact_sample_count = read_32bitLE(current_chunk+0x08, sf); /* 0x0c: base skip samples (same as next field) */ - fact_sample_skip = read_32bitLE(current_chunk+0x10, streamFile); + fact_sample_skip = read_32bitLE(current_chunk+0x10, sf); } break; @@ -505,18 +528,18 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { case 0x6374726c: /* "ctrl" (.mwv extension) */ if (!mwv) break; - loop_flag = read_32bitLE(current_chunk+0x08, streamFile); + loop_flag = read_32bitLE(current_chunk+0x08, sf); mwv_ctrl_offset = current_chunk; break; case 0x63756520: /* "cue " (used in Source Engine for storing loop points) */ if (fmt.coding_type == coding_PCM16LE || fmt.coding_type == coding_MSADPCM) { - uint32_t num_cues = read_32bitLE(current_chunk + 0x08, streamFile); + uint32_t num_cues = read_32bitLE(current_chunk + 0x08, sf); if (num_cues > 0) { /* The second cue sets loop end point but it's not actually used by the engine. */ loop_flag = 1; - loop_start_cue = read_32bitLE(current_chunk + 0x20, streamFile); + loop_start_cue = read_32bitLE(current_chunk + 0x20, sf); } } break; @@ -528,7 +551,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { /* 0x08: data size */ /* 0x0c: channels */ /* 0x10: null */ - loop_start_nxbf = read_32bitLE(current_chunk + 0x08 + 0x14, streamFile); + loop_start_nxbf = read_32bitLE(current_chunk + 0x08 + 0x14, sf); /* 0x18: sample rate */ /* 0x1c: volume? (0x3e8 = 1000 = max) */ /* 0x20: type/flags? */ @@ -566,20 +589,20 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { * 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 - && check_extensions(streamFile,"wav,lwav") /* for some .MED IMA */ + && check_extensions(sf,"wav,lwav") /* for some .MED IMA */ && (fmt.coding_type==coding_MSADPCM /*|| fmt.coding_type==coding_MS_IMA*/ || fmt.coding_type==coding_XBOX_IMA)) goto fail; /* ignore Beyond Good & Evil HD PS3 evil reuse of PCM codec */ if (fmt.coding_type == coding_PCM16LE && - read_32bitBE(start_offset+0x00, streamFile) == 0x4D534643 && /* "MSF\43" */ - read_32bitBE(start_offset+0x34, streamFile) == 0xFFFFFFFF && /* always */ - read_32bitBE(start_offset+0x38, streamFile) == 0xFFFFFFFF && - read_32bitBE(start_offset+0x3c, streamFile) == 0xFFFFFFFF) + read_32bitBE(start_offset+0x00, sf) == 0x4D534643 && /* "MSF\43" */ + read_32bitBE(start_offset+0x34, sf) == 0xFFFFFFFF && /* always */ + read_32bitBE(start_offset+0x38, sf) == 0xFFFFFFFF && + read_32bitBE(start_offset+0x3c, sf) == 0xFFFFFFFF) goto fail; /* ignore Gitaroo Man Live! (PSP) multi-RIFF (to allow chunked TXTH) */ - if (fmt.is_at3 && get_streamfile_size(streamFile) > 0x2800 && read_32bitBE(0x2800, streamFile) == 0x52494646) { /* "RIFF" */ + if (fmt.is_at3 && get_streamfile_size(sf) > 0x2800 && read_32bitBE(0x2800, sf) == 0x52494646) { /* "RIFF" */ goto fail; } @@ -644,17 +667,17 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { { int i, ch; const int filter_order = 3; - int filter_count = read_32bitLE(mwv_pflt_offset+0x0c, streamFile); + int filter_count = read_32bitLE(mwv_pflt_offset+0x0c, sf); if (filter_count > 0x20) goto fail; 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) + read_32bitLE(mwv_pflt_offset+0x08, sf) != filter_order || + read_32bitLE(mwv_pflt_offset+0x04, sf) < 8 + filter_count * 4 * filter_order) goto fail; 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); + int coef = read_32bitLE(mwv_pflt_offset+0x10+i*0x04, sf); vgmstream->ch[ch].adpcm_coef_3by32[i] = coef; } } @@ -694,7 +717,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { case coding_FFmpeg: { if (!fmt.is_at3 && !fmt.is_at3p) goto fail; - vgmstream->codec_data = init_ffmpeg_atrac3_riff(streamFile, 0x00, NULL); + vgmstream->codec_data = init_ffmpeg_atrac3_riff(sf, 0x00, NULL); if (!vgmstream->codec_data) goto fail; vgmstream->num_samples = fact_sample_count; @@ -727,7 +750,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { atrac9_config cfg = {0}; cfg.channels = vgmstream->channels; - cfg.config_data = read_32bitBE(fmt.offset+0x08+0x2c,streamFile); + cfg.config_data = read_32bitBE(fmt.offset+0x08+0x2c,sf); cfg.encoder_delay = fact_sample_skip; vgmstream->codec_data = init_atrac9(&cfg); @@ -746,7 +769,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { #ifdef VGM_USE_VORBIS case coding_OGG_VORBIS: { /* special handling of Liar-soft's buggy RIFF+Ogg made with Soundforge [Shikkoku no Sharnoth (PC)] */ - STREAMFILE *temp_sf = setup_riff_ogg_streamfile(streamFile, start_offset, data_size); + STREAMFILE *temp_sf = setup_riff_ogg_streamfile(sf, start_offset, data_size); if (!temp_sf) goto fail; vgmstream->codec_data = init_ogg_vorbis(temp_sf, 0x00, get_streamfile_size(temp_sf), NULL); @@ -763,13 +786,13 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { } /* UE4 uses interleaved mono MSADPCM, try to autodetect without breaking normal MSADPCM */ - if (fmt.coding_type == coding_MSADPCM && is_ue4_msadpcm(vgmstream, streamFile, &fmt, fact_sample_count, start_offset)) { + if (fmt.coding_type == coding_MSADPCM && is_ue4_msadpcm(vgmstream, sf, &fmt, fact_sample_count, start_offset)) { vgmstream->coding_type = coding_MSADPCM_int; vgmstream->frame_size = fmt.block_size; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = get_ue4_msadpcm_interleave(streamFile, &fmt, start_offset, data_size); + vgmstream->interleave_block_size = get_ue4_msadpcm_interleave(sf, &fmt, start_offset, data_size); if (fmt.size == 0x36) - vgmstream->num_samples = read_s32le(fmt.offset+0x08+0x32, streamFile); + vgmstream->num_samples = read_s32le(fmt.offset+0x08+0x32, sf); } /* Dynasty Warriors 5 (Xbox) 6ch interleaves stereo frames, probably not official */ @@ -804,7 +827,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->meta_type = meta_RIFF_WAVE_wsmp; } else if (mwv && mwv_ctrl_offset != -1) { - vgmstream->loop_start_sample = read_32bitLE(mwv_ctrl_offset+12, streamFile); + vgmstream->loop_start_sample = read_32bitLE(mwv_ctrl_offset+12, sf); vgmstream->loop_end_sample = vgmstream->num_samples; } else if (loop_start_cue != -1) { @@ -824,7 +847,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { vgmstream->meta_type = meta_RIFF_WAVE_MWV; } - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; @@ -835,14 +858,14 @@ fail: } /* UE4 MSADPCM is quite normal but has a few minor quirks we can use to detect it */ -static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* streamFile, riff_fmt_chunk* fmt, int fact_sample_count, off_t start) { +static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* sf, riff_fmt_chunk* fmt, int fact_sample_count, off_t start) { /* multichannel ok */ if (fmt->channel_count < 2) goto fail; /* UE4 class is "ADPCM", assume it's the extension too */ - if (!check_extensions(streamFile, "adpcm")) + if (!check_extensions(sf, "adpcm")) goto fail; /* UE4 encoder doesn't add "fact" */ @@ -861,13 +884,13 @@ static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* streamFile, riff_fmt if (fmt->size == 0x32) { off_t offset = start; off_t max_offset = 5 * fmt->block_size; /* try N blocks */ - if (max_offset > get_streamfile_size(streamFile)) - max_offset = get_streamfile_size(streamFile); + if (max_offset > get_streamfile_size(sf)) + max_offset = get_streamfile_size(sf); /* their encoder doesn't calculate optimal coefs and uses fixed values every frame * (could do it for fmt size 0x36 too but maybe they'll fix it in the future) */ while (offset <= max_offset) { - if (read_8bit(offset+0x00, streamFile) != 0 || read_16bitLE(offset+0x01, streamFile) != 0x00E6) + if (read_8bit(offset+0x00, sf) != 0 || read_16bitLE(offset+0x01, sf) != 0x00E6) goto fail; offset += fmt->block_size; } @@ -880,7 +903,7 @@ fail: /* for maximum annoyance later UE4 versions (~v4.2x?) interleave single frames instead of * half interleave, but don't have flags to detect so we need some heuristics */ -static size_t get_ue4_msadpcm_interleave(STREAMFILE *sf, riff_fmt_chunk *fmt, off_t start, size_t size) { +static size_t get_ue4_msadpcm_interleave(STREAMFILE* sf, riff_fmt_chunk* fmt, off_t start, size_t size) { size_t v1_interleave = size / fmt->channel_count; size_t v2_interleave = fmt->block_size; uint8_t nibbles1[0x08] = {0}; @@ -936,9 +959,9 @@ static size_t get_ue4_msadpcm_interleave(STREAMFILE *sf, riff_fmt_chunk *fmt, of return v2_interleave; /* favor newer games */ } -/* same but big endian, seen in the spec and in Kitchenette (PC) */ -VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +/* same but big endian, seen in the spec and in Kitchenette (PC) (possibly from Adobe Director) */ +VGMSTREAM* init_vgmstream_rifx(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; riff_fmt_chunk fmt = {0}; size_t file_size, riff_size, data_size = 0; @@ -952,17 +975,17 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { /* check extension, case insensitive */ - if ( !check_extensions(streamFile, "wav,lwav") ) + if ( !check_extensions(sf, "wav,lwav") ) goto fail; /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x52494658) /* "RIFX" */ + if (read_32bitBE(0x00,sf) != 0x52494658) /* "RIFX" */ goto fail; - if (read_32bitBE(0x08,streamFile) != 0x57415645) /* "WAVE" */ + if (read_32bitBE(0x08,sf) != 0x57415645) /* "WAVE" */ goto fail; - riff_size = read_32bitBE(0x04,streamFile); - file_size = get_streamfile_size(streamFile); + riff_size = read_32bitBE(0x04,sf); + file_size = get_streamfile_size(sf); /* check for truncated RIFF */ if (file_size < riff_size+8) goto fail; @@ -972,8 +995,8 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { off_t current_chunk = 0xc; /* start with first chunk */ while (current_chunk < file_size && current_chunk < riff_size+8) { - uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); - off_t chunk_size = read_32bitBE(current_chunk+4,streamFile); + uint32_t chunk_type = read_32bitBE(current_chunk,sf); + off_t chunk_size = read_32bitBE(current_chunk+4,sf); if (current_chunk+8+chunk_size > file_size) goto fail; @@ -983,7 +1006,7 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { if (FormatChunkFound) goto fail; FormatChunkFound = 1; - if (!read_fmt(1, streamFile, current_chunk, &fmt, 0)) + if (!read_fmt(1, sf, current_chunk, &fmt, 0)) goto fail; break; @@ -997,11 +1020,11 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { break; case 0x736D706C: /* smpl */ /* check loop count and loop info */ - if (read_32bitBE(current_chunk+0x24, streamFile)==1) { - if (read_32bitBE(current_chunk+0x2c+4, streamFile)==0) { + if (read_32bitBE(current_chunk+0x24, sf)==1) { + if (read_32bitBE(current_chunk+0x2c+4, sf)==0) { loop_flag = 1; - loop_start_offset = read_32bitBE(current_chunk+0x2c+8, streamFile); - loop_end_offset = read_32bitBE(current_chunk+0x2c+0xc,streamFile) + 1; + loop_start_offset = read_32bitBE(current_chunk+0x2c+8, sf); + loop_end_offset = read_32bitBE(current_chunk+0x2c+0xc,sf) + 1; } } break; @@ -1059,7 +1082,7 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) { } - if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rsd.c b/Frameworks/vgmstream/vgmstream/src/meta/rsd.c index d1c8502b4..50f7a4c37 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rsd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rsd.c @@ -3,7 +3,7 @@ /* RSD - from Radical Entertainment games */ -VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_rsd(STREAMFILE *sf) { VGMSTREAM * vgmstream = NULL; off_t start_offset, name_offset; size_t data_size; @@ -13,24 +13,24 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { /* checks */ - if (!check_extensions(streamFile,"rsd")) + if (!check_extensions(sf,"rsd,rsp")) goto fail; - if ((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) != 0x52534400) /* "RSD\00" */ + if ((read_32bitBE(0x00,sf) & 0xFFFFFF00) != 0x52534400) /* "RSD\00" */ goto fail; loop_flag = 0; - codec = (uint32_t)read_32bitBE(0x04,streamFile); - channel_count = read_32bitLE(0x08, streamFile); + codec = (uint32_t)read_32bitBE(0x04,sf); + channel_count = read_32bitLE(0x08, sf); /* 0x0c: always 16? */ - sample_rate = read_32bitLE(0x10, streamFile); + sample_rate = read_32bitLE(0x10, sf); - version = read_8bit(0x03, streamFile); + version = read_8bit(0x03, sf); switch(version) { case '2': /* known codecs: VAG/XADP/PCMB [The Simpsons: Road Rage] */ case '3': /* known codecs: VAG/PCM/PCMB/GADP? [Dark Summit] */ - interleave = read_32bitLE(0x14,streamFile); /* VAG only, 0x04 otherwise */ - start_offset = read_32bitLE(0x18,streamFile); + interleave = read_32bitLE(0x14,sf); /* VAG only, 0x04 otherwise */ + start_offset = read_32bitLE(0x18,sf); name_offset = 0; break; @@ -43,7 +43,7 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { /* PCMB/PCM/GADP normally start early but sometimes have padding [The Simpsons: Hit & Run (GC/Xbox)] */ if ((codec == 0x50434D20 || codec == 0x550434D42 || codec == 0x47414450) - && read_32bitLE(0x80,streamFile) != 0x2D2D2D2D) + && read_32bitLE(0x80,sf) != 0x2D2D2D2D) start_offset = 0x80; break; @@ -58,7 +58,7 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { goto fail; } - data_size = get_streamfile_size(streamFile) - start_offset; + data_size = get_streamfile_size(sf) - start_offset; /* build the VGMSTREAM */ @@ -104,8 +104,8 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x08; /* assumed, known files are mono */ - dsp_read_coefs_le(vgmstream,streamFile,0x14,0x2e); /* LE! */ - dsp_read_hist_le (vgmstream,streamFile,0x38,0x2e); + dsp_read_coefs_le(vgmstream,sf,0x14,0x2e); /* LE! */ + dsp_read_hist_le (vgmstream,sf,0x38,0x2e); vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); break; @@ -114,8 +114,8 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { vgmstream->coding_type = coding_NGC_DSP_subint; vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = 0x02; - dsp_read_coefs_be(vgmstream,streamFile,0x1a4,0x28); - dsp_read_hist_be (vgmstream,streamFile,0x1c8,0x28); + dsp_read_coefs_be(vgmstream,sf,0x1a4,0x28); + dsp_read_hist_be (vgmstream,sf,0x1c8,0x28); vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); break; @@ -134,7 +134,7 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { ovmi.meta_type = meta_RSD; close_vgmstream(vgmstream); - vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); + vgmstream = init_vgmstream_ogg_vorbis_callbacks(sf, NULL, start_offset, &ovmi); if (!vgmstream) goto fail; break; } @@ -145,22 +145,22 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { ffmpeg_codec_data *ffmpeg_data = NULL; /* mini header + WMA header at start_offset */ - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset+0x08,data_size); + ffmpeg_data = init_ffmpeg_offset(sf, start_offset+0x08,data_size); if (!ffmpeg_data) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; /* an estimation, sometimes cuts files a bit early */ - //vgmstream->num_samples = read_32bitLE(start_offset + 0x00, streamFile) / channel_count / 2; /* may be PCM data size, but not exact */ - vgmstream->sample_rate = read_32bitLE(start_offset + 0x04, streamFile); + //vgmstream->num_samples = read_32bitLE(start_offset + 0x00, sf) / channel_count / 2; /* may be PCM data size, but not exact */ + vgmstream->sample_rate = read_32bitLE(start_offset + 0x04, sf); break; } case 0x4154332B: { /* "AT3+" [Crash of the Titans (PSP)] */ int fact_samples = 0; - vgmstream->codec_data = init_ffmpeg_atrac3_riff(streamFile, start_offset, &fact_samples); + vgmstream->codec_data = init_ffmpeg_atrac3_riff(sf, start_offset, &fact_samples); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -177,21 +177,21 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { /* skip mini header */ - start_offset = read_32bitBE(0x800, streamFile) + read_32bitBE(0x804, streamFile) + 0xc; /* assumed, seek table always at 0x800 */ - xma_size = read_32bitBE(0x808, streamFile); - xma_version = read_32bitBE(0x80C, streamFile); + start_offset = 0x800 + read_32bitBE(0x800, sf) + read_32bitBE(0x804, sf) + 0xc; /* assumed, seek table always at 0x800 */ + xma_size = read_32bitBE(0x808, sf); + xma_version = read_32bitBE(0x80C, sf); switch (xma_version) { case 0x03010000: - vgmstream->sample_rate = read_32bitBE(0x818, streamFile); - vgmstream->num_samples = read_32bitBE(0x824, streamFile); - block_count = read_32bitBE(0x828, streamFile); + vgmstream->sample_rate = read_32bitBE(0x818, sf); + vgmstream->num_samples = read_32bitBE(0x824, sf); + block_count = read_32bitBE(0x828, sf); block_size = 0x10000; break; case 0x04010000: - vgmstream->num_samples = read_32bitBE(0x814, streamFile); - vgmstream->sample_rate = read_32bitBE(0x818, streamFile); - block_count = read_32bitBE(0x830, streamFile); + vgmstream->num_samples = read_32bitBE(0x814, sf); + vgmstream->sample_rate = read_32bitBE(0x818, sf); + block_count = read_32bitBE(0x830, sf); block_size = 0x10000; break; default: @@ -199,14 +199,14 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { } bytes = ffmpeg_make_riff_xma2(buf,sizeof(buf), vgmstream->num_samples, xma_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf, bytes, start_offset, xma_size); + ffmpeg_data = init_ffmpeg_header_offset(sf, buf, bytes, start_offset, xma_size); if (!ffmpeg_data) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; /* for some reason (dev trickery?) .rsd don't set skip in the bitstream, though they should */ - //xma_fix_raw_samples(vgmstream, streamFile, start_offset,xma_size, 0, 0,0); + //xma_fix_raw_samples(vgmstream, sf, start_offset,xma_size, 0, 0,0); ffmpeg_set_skip_samples(ffmpeg_data, 512+64); break; } @@ -217,9 +217,9 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) { } if (name_offset) - read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile); + read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,sf); - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sab.c b/Frameworks/vgmstream/vgmstream/src/meta/sab.c index 3fce2b609..b7b8fbc2b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sab.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sab.c @@ -1,157 +1,276 @@ -#include "meta.h" -#include "../util.h" -#include "../coding/coding.h" - -static void get_stream_name(char * stream_name, STREAMFILE *streamFile, int target_stream); - -/* SAB - from Worms 4: Mayhem (PC/Xbox/PS2) */ -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, stream_size, loop_start, loop_end; - int total_subsongs, target_subsong = streamFile->stream_index; - - /* .sab: main, .sob: config/names */ - if (!check_extensions(streamFile,"sab")) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x43535732 && /* "CSW2" (Windows) */ - read_32bitBE(0x00,streamFile) != 0x43535032 && /* "CSP2" (PS2) */ - read_32bitBE(0x00,streamFile) != 0x43535832) /* "CSX2" (Xbox) */ - goto fail; - - is_stream = read_32bitLE(0x04,streamFile) & 0x04; /* other flags don't seem to matter */ - 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_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_subsongs; - if (start_offset % align) - start_offset += align - (start_offset % align); - 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 */ - stream_size *= channel_count; - } - - /* 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 = meta_SAB; - - switch(codec) { - case 0x01: /* PC */ - vgmstream->coding_type = coding_PCM16LE; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = is_stream ? align : 0x02; - - 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); - - break; - - case 0x04: /* PS2 */ - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = is_stream ? align : 0x10; - - 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; - - case 0x08: /* Xbox */ - vgmstream->coding_type = is_stream ? coding_XBOX_IMA_int : coding_XBOX_IMA; - vgmstream->layout_type = is_stream ? layout_interleave : layout_none; - vgmstream->interleave_block_size = is_stream ? align : 0x00; - - vgmstream->num_samples = xbox_ima_bytes_to_samples(stream_size, vgmstream->channels); - vgmstream->loop_start_sample = xbox_ima_bytes_to_samples(loop_start, vgmstream->channels); - vgmstream->loop_end_sample = xbox_ima_bytes_to_samples(loop_end, vgmstream->channels); - break; - - default: - VGM_LOG("SAB: unknown codec\n"); - goto fail; - } - - get_stream_name(vgmstream->stream_name, streamFile, target_subsong); - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - -/* multiple streams may share a name, also sometimes won't a the name */ -static void get_stream_name(char * stream_name, STREAMFILE *streamFile, int target_stream) { - STREAMFILE * streamInfo = NULL; - int i, j, total_cues, num_cue = -1; - size_t name_size = 0; - off_t name_offset = 0x10; - - streamInfo = open_streamfile_by_ext(streamFile, "sob"); - if (!streamInfo) goto end; - if (read_32bitBE(0x00,streamInfo) != 0x43544632) /* "CTF2" */ - goto end; - - total_cues = read_32bitLE(0x08,streamInfo); - - for (i = 0; i < total_cues; i++) { - uint32_t flags, num_subsections, subsection_1_size, subsection_2_size; - - flags = (uint32_t)read_32bitLE(name_offset + 0x00,streamInfo); - num_subsections = (uint32_t)read_32bitLE(name_offset + 0x20,streamInfo); - subsection_1_size = (flags & 0x00000001) ? 0x40 : 0x00; - subsection_1_size +=(flags & 0x00000040) ? 0x20 : 0x00; - subsection_2_size = (flags & 0x00000100) ? 0x1c : 0x10; - - for (j = 0; j < num_subsections; j++) { - int num_stream = read_32bitLE(name_offset + 0x2c + subsection_1_size + j*subsection_2_size + 0x08,streamInfo); - if (target_stream-1 == num_stream) - num_cue = i; - } - - name_offset += 0x2c + subsection_1_size + subsection_2_size * num_subsections; - } - if (num_cue < 0) - goto end; - - for (i = 0; i < total_cues; i++) { - /* 0x00: id */ - name_size = read_32bitLE(name_offset + 0x04,streamInfo); /* non null-terminated */ - if (i == num_cue) { - name_offset += 0x08; - break; - } - name_offset += 0x08 + name_size; - } - - if (name_size > STREAM_NAME_SIZE-1) - name_size = STREAM_NAME_SIZE-1; - - read_string(stream_name,name_size+1, name_offset,streamInfo); - -end: - if (streamInfo) - close_streamfile(streamInfo); -} +#include "meta.h" +#include "../coding/coding.h" +#include "../layout/layout.h" +#include "sab_streamfile.h" + +typedef struct { + int total_subsongs; + int target_subsong; + int is_stream; + int is_extra; + + uint32_t flags; + uint32_t sound_count; + uint32_t block_size; + + uint32_t codec; + int loop_flag; + int channel_count; + int sample_rate; + uint32_t stream_size; + int32_t loop_start; + int32_t loop_end; + off_t stream_offset; +} sab_header; + + +static int parse_sab(STREAMFILE* sf, sab_header* sab); +static VGMSTREAM* build_layered_vgmstream(STREAMFILE* sf, sab_header* sab); +static void get_stream_name(char* stream_name, STREAMFILE* sf, int target_stream); + +/* SAB - from Sensaura GameCODA middleware games [Men of Valor (multi), Worms 4: Mayhem (multi), Just Cause (multi)] */ +VGMSTREAM* init_vgmstream_sab(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + sab_header sab = {0}; + + + /* .sab: main, .sob: cue (has config/names) */ + if (!check_extensions(sf,"sab")) + goto fail; + if (read_u32be(0x00,sf) != 0x43535732 && /* "CSW2" (Windows) */ + read_u32be(0x00,sf) != 0x43535032 && /* "CSP2" (PS2) */ + read_u32be(0x00,sf) != 0x43535832) /* "CSX2" (Xbox) */ + goto fail; + + if (!parse_sab(sf, &sab)) + goto fail; + + /* sab can be (both cases handled here): + * - bank: multiple subsongs (each a different header) + * - stream: layers used as different stereo tracks (Men of Valor) or mono tracks to create stereo (Worms 4) + */ + vgmstream = build_layered_vgmstream(sf, &sab); + if (!vgmstream) goto fail; + + vgmstream->num_streams = sab.total_subsongs; + + get_stream_name(vgmstream->stream_name, sf, sab.target_subsong); + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +//todo doesn't seem correct always (also multiple .sob may share .sab) +/* multiple streams may share a name */ +static void get_stream_name(char* stream_name, STREAMFILE* sf, int target_stream) { + STREAMFILE* sf_info = NULL; + int i, j, total_cues, num_cue = -1; + size_t name_size = 0; + off_t name_offset = 0x10; + + sf_info = open_streamfile_by_ext(sf, "sob"); + if (!sf_info) goto end; + if (read_u32be(0x00,sf_info) != 0x43544632) /* "CTF2" */ + goto end; + + total_cues = read_u32le(0x08,sf_info); + if (total_cues > 0x1000) + goto end; + + for (i = 0; i < total_cues; i++) { + uint32_t flags, num_subsections, subsection_1_size, subsection_2_size; + + flags = read_u32le(name_offset + 0x00,sf_info); + num_subsections = read_u32le(name_offset + 0x20,sf_info); + subsection_1_size = (flags & 0x00000001) ? 0x40 : 0x00; + subsection_1_size +=(flags & 0x00000040) ? 0x20 : 0x00; + subsection_2_size = (flags & 0x00000100) ? 0x1c : 0x10; //todo not always correct + + if (num_subsections > 0x1000) + goto end; /* bad read */ + + for (j = 0; j < num_subsections; j++) { + int num_stream = read_u32le(name_offset + 0x2c + subsection_1_size + j*subsection_2_size + 0x08,sf_info); + if (target_stream - 1 == num_stream) + num_cue = i; + } + + name_offset += 0x2c + subsection_1_size + subsection_2_size * num_subsections; + } + if (num_cue < 0) + goto end; + + for (i = 0; i < total_cues; i++) { + /* 0x00: id */ + name_size = read_u32le(name_offset + 0x04,sf_info); /* non null-terminated */ + if (i == num_cue) { + name_offset += 0x08; + break; + } + name_offset += 0x08 + name_size; + } + + if (name_size > STREAM_NAME_SIZE - 1) + name_size = STREAM_NAME_SIZE - 1; + + read_string(stream_name,name_size+1, name_offset,sf_info); + +end: + close_streamfile(sf_info); +} + +static int parse_sab(STREAMFILE* sf, sab_header* sab) { + off_t entry_offset; + + sab->flags = read_u32le(0x04,sf); /* upper byte is always 0x02 (version?) */ + sab->sound_count = read_u32le(0x08,sf); + sab->block_size = read_u32le(0x0c,sf); + /* 0x10: number of blocks */ + /* 0x14: file id? */ + + sab->is_stream = sab->flags & 0x04; /* "not bank" */ + sab->is_extra = sab->flags & 0x10; /* not used in Worms 4 */ + /* flags 1/2 are also common, no flags in banks */ + + sab->total_subsongs = sab->is_stream ? 1 : sab->sound_count; + sab->target_subsong = sf->stream_index; + if (sab->target_subsong == 0) sab->target_subsong = 1; + if (sab->target_subsong < 0 || sab->target_subsong > sab->total_subsongs || sab->total_subsongs < 1) goto fail; + + /* stream config */ + entry_offset = 0x18 + 0x1c * (sab->target_subsong - 1); + sab->codec = read_u32le(entry_offset + 0x00,sf); + sab->channel_count = read_u32le(entry_offset + 0x04,sf); + sab->sample_rate = read_u32le(entry_offset + 0x08,sf); + sab->stream_size = read_u32le(entry_offset + 0x0c,sf); + sab->loop_start = read_u32le(entry_offset + 0x10,sf); + sab->loop_end = read_u32le(entry_offset + 0x14,sf); + sab->loop_flag = (sab->loop_end > 0); + + if (sab->is_stream) { + sab->stream_offset = sab->block_size; + } + else { + sab->stream_offset = 0x18 + 0x1c * sab->total_subsongs; + if (sab->stream_offset % sab->block_size) + sab->stream_offset += sab->block_size - (sab->stream_offset % sab->block_size); + sab->stream_offset += read_u32le(0x18 + 0x1c * (sab->target_subsong - 1) + 0x18,sf); + } + + /* some extra values (counts?) and name at start */ + if (sab->is_extra) + sab->stream_offset += sab->block_size; + + return 1; +fail: + return 0; +} + +static VGMSTREAM* build_vgmstream(STREAMFILE* sf, sab_header* sab) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + + /* in streams streamfile is de-chunked so start becomes 0 */ + start_offset = sab->is_stream ? + 0 : + sab->stream_offset; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(sab->channel_count, sab->loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sab->sample_rate; + vgmstream->stream_size = sab->stream_size; + vgmstream->meta_type = meta_SAB; + + switch(sab->codec) { + case 0x01: /* PC */ + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + + vgmstream->num_samples = pcm_bytes_to_samples(sab->stream_size, vgmstream->channels, 16); + vgmstream->loop_start_sample = pcm_bytes_to_samples(sab->loop_start, vgmstream->channels, 16); + vgmstream->loop_end_sample = pcm_bytes_to_samples(sab->loop_end, vgmstream->channels, 16); + break; + + case 0x04: /* PS2 */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + + vgmstream->num_samples = ps_bytes_to_samples(sab->stream_size, vgmstream->channels); + vgmstream->loop_start_sample = ps_bytes_to_samples(sab->loop_start, vgmstream->channels); + vgmstream->loop_end_sample = ps_bytes_to_samples(sab->loop_end, vgmstream->channels); + break; + + case 0x08: /* Xbox */ + vgmstream->coding_type = coding_XBOX_IMA; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = xbox_ima_bytes_to_samples(sab->stream_size, vgmstream->channels); + vgmstream->loop_start_sample = xbox_ima_bytes_to_samples(sab->loop_start, vgmstream->channels); + vgmstream->loop_end_sample = xbox_ima_bytes_to_samples(sab->loop_end, vgmstream->channels); + break; + + default: + VGM_LOG("SAB: unknown codec\n"); + goto fail; + } + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +static VGMSTREAM* build_layered_vgmstream(STREAMFILE* sf, sab_header* sab) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + layered_layout_data* data = NULL; + int i; + + if (sab->sound_count == 1 || !sab->is_stream) { + return build_vgmstream(sf, sab); + } + + /* init layout */ + data = init_layout_layered(sab->sound_count); + if (!data) goto fail; + + /* de-chunk audio layers */ + for (i = 0; i < sab->sound_count; i++) { + temp_sf = setup_sab_streamfile(sf, sab->stream_offset, sab->sound_count, i, sab->block_size); + if (!temp_sf) goto fail; + + data->layers[i] = build_vgmstream(temp_sf, sab); + if (!data->layers[i]) goto fail; + + close_streamfile(temp_sf); + temp_sf = NULL; + } + + /* setup VGMSTREAMs */ + if (!setup_layout_layered(data)) + goto fail; + + /* build the layout VGMSTREAM */ + vgmstream = allocate_layered_vgmstream(data); + if (!vgmstream) goto fail; + + //sab->stream_size = sab->stream_size * sab->sound_count; /* ? */ + + return vgmstream; +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + if (!vgmstream) + free_layout_layered(data); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sab_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/sab_streamfile.h new file mode 100644 index 000000000..1b76c8019 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/sab_streamfile.h @@ -0,0 +1,20 @@ +#ifndef _SAB_STREAMFILE_H_ +#define _SAB_STREAMFILE_H_ +#include "deblock_streamfile.h" + +static STREAMFILE* setup_sab_streamfile(STREAMFILE* sf, off_t stream_start, int stream_count, int stream_number, size_t interleave) { + STREAMFILE* new_sf = NULL; + deblock_config_t cfg = {0}; + + cfg.stream_start = stream_start; + cfg.chunk_size = interleave; + cfg.step_start = stream_number; + cfg.step_count = stream_count; + + /* setup sf */ + new_sf = open_wrap_streamfile(sf); + new_sf = open_io_deblock_streamfile_f(new_sf, &cfg); + return new_sf; +} + +#endif /* _SAB_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c index b107b22b3..91af9aec7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sgxd.c @@ -3,36 +3,36 @@ /* SGXD - Sony/SCEI's format (SGB+SGH / SGD / SGX) */ -VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamHeader = NULL; +VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* sf_head = NULL; off_t start_offset, data_offset, chunk_offset, name_offset = 0; size_t stream_size; int is_sgx, is_sgb = 0; int loop_flag, channels, codec; int sample_rate, num_samples, loop_start_sample, loop_end_sample; - int total_subsongs, target_subsong = streamFile->stream_index; + int total_subsongs, target_subsong = sf->stream_index; /* check extension, case insensitive */ /* .sgx: header+data (Genji), .sgd: header+data, .sgh/sgd: header/data */ - if (!check_extensions(streamFile,"sgx,sgd,sgb")) + if (!check_extensions(sf,"sgx,sgd,sgb")) goto fail; - is_sgx = check_extensions(streamFile,"sgx"); - is_sgb = check_extensions(streamFile,"sgb"); + is_sgx = check_extensions(sf,"sgx"); + is_sgb = check_extensions(sf,"sgb"); /* SGB+SGH: use SGH as header; otherwise use the current file as header */ if (is_sgb) { - streamHeader = open_streamfile_by_ext(streamFile, "sgh"); - if (!streamHeader) goto fail; + sf_head = open_streamfile_by_ext(sf, "sgh"); + if (!sf_head) goto fail; } else { - streamHeader = streamFile; + sf_head = sf; } /* SGXD base (size 0x10) */ - if (read_32bitBE(0x00,streamHeader) != 0x53475844) /* "SGXD" */ + if (read_32bitBE(0x00,sf_head) != 0x53475844) /* "SGXD" */ goto fail; /* 0x04 SGX: full header_size; SGD/SGH: unknown header_size (counting from 0x0/0x8/0x10, varies) */ /* 0x08 SGX: first chunk offset? (0x10); SGD/SGH: full header_size */ @@ -40,24 +40,24 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { if (is_sgb) { data_offset = 0x00; } else if ( is_sgx ) { - data_offset = read_32bitLE(0x04,streamHeader); + data_offset = read_32bitLE(0x04,sf_head); } else { - data_offset = read_32bitLE(0x08,streamHeader); + data_offset = read_32bitLE(0x08,sf_head); } /* typical chunks: WAVE, RGND, NAME (strings for WAVE or RGND), SEQD (related to SFX), WSUR, WMKR, BUSS */ /* WAVE chunk (size 0x10 + files * 0x38 + optional padding) */ if (is_sgx) { /* position after chunk+size */ - if (read_32bitBE(0x10,streamHeader) != 0x57415645) goto fail; /* "WAVE" */ + if (read_32bitBE(0x10,sf_head) != 0x57415645) goto fail; /* "WAVE" */ chunk_offset = 0x18; } else { - if (!find_chunk_le(streamHeader, 0x57415645,0x10,0, &chunk_offset,NULL)) goto fail; /* "WAVE" */ + if (!find_chunk_le(sf_head, 0x57415645,0x10,0, &chunk_offset,NULL)) goto fail; /* "WAVE" */ } /* 0x04 SGX: unknown; SGD/SGH: chunk length, 0x08 null */ /* check multi-streams (usually only SE containers; Puppeteer) */ - total_subsongs = read_32bitLE(chunk_offset+0x04,streamHeader); + total_subsongs = read_32bitLE(chunk_offset+0x04,sf_head); if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; @@ -68,11 +68,11 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { /* 0x00 ? (00/01/02) */ if (!is_sgx) /* meaning unknown in .sgx; offset 0 = not a stream (a RGND sample) */ - name_offset = read_32bitLE(chunk_offset+0x04,streamHeader); - codec = read_8bit(chunk_offset+0x08,streamHeader); - channels = read_8bit(chunk_offset+0x09,streamHeader); + name_offset = read_32bitLE(chunk_offset+0x04,sf_head); + codec = read_8bit(chunk_offset+0x08,sf_head); + channels = read_8bit(chunk_offset+0x09,sf_head); /* 0x0a null */ - sample_rate = read_32bitLE(chunk_offset+0x0c,streamHeader); + sample_rate = read_32bitLE(chunk_offset+0x0c,sf_head); /* 0x10 info_type: meaning of the next value * (00=null, 30/40=data size without padding (ADPCM, ATRAC3plus), 80/A0=block size (AC3) */ @@ -80,15 +80,15 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { /* 0x18 unknown (ex. 0x0008/0010/3307/CC02/etc)x2 */ /* 0x1c null */ - 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); - stream_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */ + num_samples = read_32bitLE(chunk_offset+0x20,sf_head); + loop_start_sample = read_32bitLE(chunk_offset+0x24,sf_head); + loop_end_sample = read_32bitLE(chunk_offset+0x28,sf_head); + stream_size = read_32bitLE(chunk_offset+0x2c,sf_head); /* stream size (without padding) / interleave (for type3) */ if (is_sgx) { stream_offset = 0x0; } else{ - stream_offset = read_32bitLE(chunk_offset+0x30,streamHeader); + stream_offset = read_32bitLE(chunk_offset+0x30,sf_head); } /* 0x34 SGX: unknown; SGD/SGH: stream size (with padding) / interleave */ @@ -109,7 +109,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { vgmstream->stream_size = stream_size; vgmstream->meta_type = meta_SGXD; if (name_offset) - read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader); + read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,sf_head); switch (codec) { @@ -121,7 +121,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { #ifdef VGM_USE_VORBIS case 0x02: /* Ogg Vorbis [Ni no Kuni: Wrath of the White Witch Remastered (PC)] (codec hijack?) */ - vgmstream->codec_data = init_ogg_vorbis(streamFile, start_offset, stream_size, NULL); + vgmstream->codec_data = init_ogg_vorbis(sf, start_offset, stream_size, NULL); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_OGG_VORBIS; vgmstream->layout_type = layout_none; @@ -143,7 +143,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { #ifdef VGM_USE_FFMPEG case 0x04: { /* ATRAC3plus [Kurohyo 1/2 (PSP), BraveStory (PSP)] */ - vgmstream->codec_data = init_ffmpeg_atrac3_riff(streamFile, start_offset, NULL); + vgmstream->codec_data = init_ffmpeg_atrac3_riff(sf, start_offset, NULL); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -163,20 +163,15 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { #ifdef VGM_USE_FFMPEG case 0x06: { /* AC3 [Tokyo Jungle (PS3), Afrika (PS3)] */ - ffmpeg_codec_data *ffmpeg_data; - - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, stream_size); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_offset(sf, start_offset, stream_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - /* manually set skip_samples if FFmpeg didn't do it */ - if (ffmpeg_data->skipSamples <= 0) { - /* PS3 AC3 consistently has 256 encoder delay samples, and there are ~1000-2000 samples after num_samples. - * Skipping them marginally improves full loops in some Tokyo Jungle tracks (ex. a_1.sgd). */ - ffmpeg_set_skip_samples(ffmpeg_data, 256); - } + /* PS3 AC3 consistently has 256 encoder delay samples, and there are ~1000-2000 samples after num_samples. + * Skipping them marginally improves full loops in some Tokyo Jungle tracks (ex. a_1.sgd). */ + ffmpeg_set_skip_samples(vgmstream->codec_data, 256); + /* SGXD loop/sample values are relative (without skip samples), no need to adjust */ break; @@ -188,15 +183,14 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) { goto fail; } - /* open the file for reading */ - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; - if (is_sgb && streamHeader) close_streamfile(streamHeader); + if (is_sgb && sf_head) close_streamfile(sf_head); return vgmstream; fail: - if (is_sgb && streamHeader) close_streamfile(streamHeader); + if (is_sgb && sf_head) close_streamfile(sf_head); close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/silence.c b/Frameworks/vgmstream/vgmstream/src/meta/silence.c new file mode 100644 index 000000000..28f7d0198 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/silence.c @@ -0,0 +1,29 @@ +#include "meta.h" + +/* silent stream - mainly for engines that need them or dummy subsongs */ +VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_samples) { + VGMSTREAM* vgmstream = NULL; + + if (channels <= 0) + channels = 2; + if (sample_rate <= 0) + sample_rate = 48000; + if (num_samples <= 0) + num_samples = 1.0 * sample_rate; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, 0); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_SILENCE; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + + vgmstream->coding_type = coding_SILENCE; + vgmstream->layout_type = layout_none; + + return vgmstream; +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c b/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c index d75101bac..8e26967cb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c @@ -1,54 +1,179 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* .SPS - Nippon Ichi 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); - subfile_size = read_32bitLE(0x04,streamFile); - sample_rate = (uint16_t)read_16bitLE(0x08,streamFile); - /* 0x0a: flag? */ - //num_samples = read_32bitLE(0x0c,streamFile); - subfile_offset = 0x10; - - /* init the VGMSTREAM */ - switch(type) { - case 1: /* .vag */ - temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset, subfile_size, "vag"); - if (!temp_streamFile) goto fail; - - vgmstream = init_vgmstream_vag(temp_streamFile); - if (!vgmstream) goto fail; - break; - - case 2: /* .at3 */ - temp_streamFile = setup_subfile_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; - } - - 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; -} +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + +/* also see init_vgmstream_dsp_sps_n1 and init_vgmstream_opus_sps_n1 */ + +/* Nippon Ichi SPS wrapper [ClaDun (PSP)] */ +VGMSTREAM* init_vgmstream_sps_n1(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + int type, sample_rate; + off_t subfile_offset; + size_t subfile_size; + + VGMSTREAM* (*init_vgmstream_subfile)(STREAMFILE*) = NULL; + const char* extension; + + + /* checks */ + if (!check_extensions(sf,"sps")) + goto fail; + + type = read_u32le(0x00,sf); + subfile_size = read_u32le(0x04,sf); + sample_rate = read_u16le(0x08,sf); + /* 0x0a: flag? (stereo?) */ + /* 0x0b: flag? */ + /* 0x0c: num_samples */ + + switch(type) { + case 1: + init_vgmstream_subfile = init_vgmstream_vag; + extension = "vag"; + break; + + case 2: + init_vgmstream_subfile = init_vgmstream_riff; + extension = "at3"; + break; + + default: + goto fail; + } + + subfile_offset = 0x10; + if (subfile_size + subfile_offset != get_streamfile_size(sf)) + goto fail; + + /* init the VGMSTREAM */ + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, extension); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_subfile(temp_sf); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; /* .vag header doesn't match */ + + close_streamfile(temp_sf); + return vgmstream; + +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} + +/* Nippon Ichi SPS wrapper (segmented) [Penny-Punching Princess (Switch), Disgaea 4 Complete (PC)] */ +VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t segment_offset; + size_t data_size, max_size; + int loop_flag, type, sample_rate; + int i, segment; + + VGMSTREAM* (*init_vgmstream_subfile)(STREAMFILE*) = NULL; + const char* extension; + segmented_layout_data* data = NULL; + int segment_count, loop_start_segment, loop_end_segment; + + + /* checks */ + /* .at9: Penny-Punching Princess (Switch) + * .nlsd: Disgaea 4 Complete (PC) */ + if (!check_extensions(sf, "at9,nlsd")) + goto fail; + + type = read_u32le(0x00,sf); + data_size = read_u32le(0x04,sf); + sample_rate = read_u16le(0x08,sf); + /* 0x0a: flag? (stereo?) */ + /* 0x0b: flag? */ + /* 0x0c: num_samples (slightly smaller than added samples?) */ + + switch(type) { + #ifdef VGM_USE_VORBIS + case 7: + init_vgmstream_subfile = init_vgmstream_ogg_vorbis; + extension = "ogg"; + break; + #endif + + case 9: + init_vgmstream_subfile = init_vgmstream_opus_std; + extension = "opus"; + break; + + default: + goto fail; + } + + segment_offset = 0x1c; + if (data_size + segment_offset != get_streamfile_size(sf)) + goto fail; + + /* segmented using 3 files (intro/loop/outro). non-segmented wrapper is the same + * but with loop samples instead of sub-sizes */ + max_size = 0; + segment_count = 0; + for (i = 0; i < 3; i++) { + size_t segment_size = read_u32le(0x10 + 0x04*i,sf); + max_size += segment_size; + /* may only set 1 segment (Disgaea4's bgm_185) */ + if (segment_size) + segment_count++; + } + if (data_size != max_size) + goto fail; + + loop_flag = segment_count > 1; /* intro+loop section must exit */ + loop_start_segment = 1; + loop_end_segment = 1; + + /* init layout */ + data = init_layout_segmented(segment_count); + if (!data) goto fail; + + /* open each segment subfile */ + segment = 0; + for (i = 0; i < 3; i++) { + STREAMFILE* temp_sf; + size_t segment_size = read_u32le(0x10 + 0x04*i,sf); + + if (!segment_size) + continue; + + temp_sf = setup_subfile_streamfile(sf, segment_offset,segment_size, extension); + if (!temp_sf) goto fail; + + data->segments[segment] = init_vgmstream_subfile(temp_sf); + close_streamfile(temp_sf); + if (!data->segments[segment]) goto fail; + + segment_offset += segment_size; + segment++; + + if (type == 9) { + //todo there are some trailing samples that must be removed for smooth loops, start skip seems ok + //not correct for all files, no idea how to calculate + data->segments[segment]->num_samples -= 374; + } + } + + /* setup segmented VGMSTREAMs */ + if (!setup_layout_segmented(data)) + goto fail; + + vgmstream = allocate_segmented_vgmstream(data, loop_flag, loop_start_segment, loop_end_segment); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->meta_type = meta_SPS_N1; + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + free_layout_segmented(data); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c index 286a5f9d4..35bb8bc0c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c @@ -241,22 +241,20 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { #ifdef VGM_USE_MPEG case 0x07: { /* MPEG [Final Fantasy XIII (PS3)] */ - mpeg_codec_data *mpeg_data = NULL; mpeg_custom_config cfg = {0}; cfg.interleave = 0x800; /* for multistream [Final Fantasy XIII-2 (PS3)], otherwise ignored */ cfg.data_size = stream_size; - 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->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_SCD, &cfg); + if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; /* some Drakengard 3, Kingdom Hearts HD have adjusted sample rate (47999, 44099), for looping? */ - vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, mpeg_data); - vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, mpeg_data); - vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, mpeg_data); + vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, vgmstream->codec_data); + vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, vgmstream->codec_data); + vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, vgmstream->codec_data); /* somehow loops offsets aren't always frame-aligned, and the code below supposedly helped, * but there isn't much difference since MPEG loops are rough (1152-aligned). Seems it diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c index 09b32b9ae..43de6e8f8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_sead.c @@ -230,7 +230,6 @@ VGMSTREAM* init_vgmstream_sqex_sead(STREAMFILE* sf) { #ifdef VGM_USE_MPEG case 0x06: { /* MSMP3 (MSF subfile) [Dragon Quest Builders (PS3)] */ - mpeg_codec_data *mpeg_data = NULL; mpeg_custom_config cfg = {0}; start_offset = sead.extradata_offset + sead.extradata_size; @@ -238,12 +237,11 @@ VGMSTREAM* init_vgmstream_sqex_sead(STREAMFILE* sf) { /* extradata: */ /* proper MSF header, but sample rate/loops are ignored in favor of SAB's */ - mpeg_data = init_mpeg_custom(sf, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); - if (!mpeg_data) goto fail; - vgmstream->codec_data = mpeg_data; + vgmstream->codec_data = init_mpeg_custom(sf, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); + if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; - vgmstream->num_samples = mpeg_bytes_to_samples(sead.stream_size, mpeg_data); + vgmstream->num_samples = mpeg_bytes_to_samples(sead.stream_size, vgmstream->codec_data); vgmstream->loop_start_sample = sead.loop_start; vgmstream->loop_end_sample = sead.loop_end; break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/strm_abylight.c b/Frameworks/vgmstream/vgmstream/src/meta/strm_abylight.c index c5f884da6..4b619d651 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/strm_abylight.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/strm_abylight.c @@ -1,71 +1,65 @@ -#include "meta.h" -#include "../coding/coding.h" - - -/* .STRM - from Abylight 3DS games [Cursed Castilla (3DS)] */ -VGMSTREAM * init_vgmstream_strm_abylight(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count, sample_rate; - size_t data_size; - - - /* check extension */ - if ( !check_extensions(streamFile,"strm") ) - goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x5354524D) /* "STRM" */ - goto fail; - if (read_32bitLE(0x04,streamFile) != 0x03E8) /* version 1000? */ - goto fail; - - loop_flag = 0; - channel_count = 2; /* there are various possible fields but all files are stereo */ - sample_rate = read_32bitLE(0x08,streamFile); - - start_offset = 0x1e; - data_size = read_32bitLE(0x10,streamFile); - if (data_size != get_streamfile_size(streamFile) - start_offset) - goto fail; - if (data_size != read_32bitLE(0x18,streamFile)) - goto fail; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = aac_get_samples(streamFile, start_offset, data_size); - - vgmstream->meta_type = meta_STRM_ABYLIGHT; - -#ifdef VGM_USE_FFMPEG - { - ffmpeg_codec_data *ffmpeg_data = NULL; - - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,data_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - /* apparently none, or maybe ~600 */ - //if (!ffmpeg_data->skipSamples) - // ffmpeg_set_skip_samples(ffmpeg_data, 1024); - //vgmstream->num_samples -= 1024; - } -#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; -} +#include "meta.h" +#include "../coding/coding.h" + + +/* .STRM - from Abylight 3DS games [Cursed Castilla (3DS)] */ +VGMSTREAM* init_vgmstream_strm_abylight(STREAMFILE* sf) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, sample_rate; + size_t data_size; + + + /* checks */ + if ( !check_extensions(sf,"strm") ) + goto fail; + + if (read_32bitBE(0x00,sf) != 0x5354524D) /* "STRM" */ + goto fail; + if (read_32bitLE(0x04,sf) != 0x03E8) /* version 1000? */ + goto fail; + + loop_flag = 0; + channel_count = 2; /* there are various possible fields but all files are stereo */ + sample_rate = read_32bitLE(0x08,sf); + + start_offset = 0x1e; + data_size = read_32bitLE(0x10,sf); + if (data_size != get_streamfile_size(sf) - start_offset) + goto fail; + if (data_size != read_32bitLE(0x18,sf)) + goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = aac_get_samples(sf, start_offset, data_size); + + vgmstream->meta_type = meta_STRM_ABYLIGHT; + +#ifdef VGM_USE_FFMPEG + { + vgmstream->codec_data = init_ffmpeg_offset(sf, start_offset, data_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + /* apparently none, or maybe ~600 */ + //ffmpeg_set_skip_samples(ffmpeg_data, 1024); + //vgmstream->num_samples -= 1024; + } +#else + goto fail; +#endif + + if ( !vgmstream_open_stream(vgmstream, sf, start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_svag.c b/Frameworks/vgmstream/vgmstream/src/meta/svag_kcet.c similarity index 51% rename from Frameworks/vgmstream/vgmstream/src/meta/ps2_svag.c rename to Frameworks/vgmstream/vgmstream/src/meta/svag_kcet.c index 4b9bc17d2..18e09b93c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_svag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/svag_kcet.c @@ -2,51 +2,54 @@ #include "../coding/coding.h" /* SVAG - from Konami Tokyo games [OZ (PS2), Neo Contra (PS2), Silent Hill 2 (PS2)] */ -VGMSTREAM * init_vgmstream_ps2_svag(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_svag_kcet(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; size_t data_size; int loop_flag, channel_count; /* checks */ - if (!check_extensions(streamFile, "svag")) + if (!check_extensions(sf, "svag")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x53766167) /* "Svag" */ + if (read_32bitBE(0x00,sf) != 0x53766167) /* "Svag" */ goto fail; - channel_count = read_16bitLE(0x0C,streamFile); /* always 2? ("S"tereo vag?) */ - loop_flag = (read_32bitLE(0x14,streamFile)==1); + channel_count = read_16bitLE(0x0C,sf); /* always 2? ("S"tereo vag?) */ + loop_flag = (read_32bitLE(0x14,sf) ==1); - /* header repeated at 0x400 presumably for stereo */ - if (channel_count > 1 && read_32bitBE(0x400,streamFile) != 0x53766167) /* "Svag" */ + /* test padding (a set "KCE-Tokyo ..." phrase) after 0x1c to catch bad rips, + * at 0x400 may be header again (Silent Hill 2) or more padding (Silent Scope 2) */ + if (channel_count > 1 && + read_32bitBE(0x400,sf) != 0x53766167 && /* "Svag" */ + read_32bitBE(0x400,sf) != 0x44657369) /* "Desi" */ goto fail; start_offset = 0x800; - data_size = read_32bitLE(0x04,streamFile); + data_size = read_32bitLE(0x04,sf); /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = read_32bitLE(0x08,streamFile); + vgmstream->sample_rate = read_32bitLE(0x08,sf); - vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x04,streamFile), vgmstream->channels); + vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x04,sf), vgmstream->channels); if(vgmstream->loop_flag) { - vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitLE(0x18,streamFile)*vgmstream->channels, vgmstream->channels); + vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitLE(0x18,sf)*vgmstream->channels, vgmstream->channels); vgmstream->loop_end_sample = vgmstream->num_samples; } - vgmstream->meta_type = meta_PS2_SVAG; + vgmstream->meta_type = meta_SVAG_KCET; vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitLE(0x10,streamFile); + vgmstream->interleave_block_size = read_32bitLE(0x10,sf); if (vgmstream->interleave_block_size) vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels; - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/svag_snk.c b/Frameworks/vgmstream/vgmstream/src/meta/svag_snk.c new file mode 100644 index 000000000..79ca24ecd --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/svag_snk.c @@ -0,0 +1,46 @@ +#include "meta.h" +#include "../util.h" + +/* .SVAG - from SNK games [World Heroes Anthology (PS2), Fatal Fury Battle Archives 2 (PS2)] */ +VGMSTREAM* init_vgmstream_svag_snk(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, loop_start_block, loop_end_block; + + /* checks */ + if (!check_extensions(sf, "svag")) + goto fail; + if (read_32bitBE(0x00,sf) != 0x5641476D) /* "VAGm" */ + goto fail; + + channel_count = read_32bitLE(0x0c,sf); + loop_start_block = read_32bitLE(0x18,sf); + loop_end_block = read_32bitLE(0x1c,sf); + loop_flag = loop_end_block > 0; /* loop_start_block can be 0 */ + start_offset = 0x20; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_SVAG_SNK; + + vgmstream->sample_rate = read_32bitLE(0x08,sf); + vgmstream->num_samples = read_32bitLE(0x10,sf) * 28; /* size in blocks */ + vgmstream->loop_start_sample = loop_start_block * 28; + vgmstream->loop_end_sample = loop_end_block * 28; + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/swav.c b/Frameworks/vgmstream/vgmstream/src/meta/swav.c new file mode 100644 index 000000000..ba1c5eaf0 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/swav.c @@ -0,0 +1,99 @@ +#include "meta.h" +#include "../util.h" + + +/* SWAV - wave files generated by the DS SDK */ +VGMSTREAM* init_vgmstream_swav(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + int channel_count, loop_flag; + off_t start_offset; + int codec_number, bits_per_sample; + coding_t coding_type; + + + /* checks */ + /* .swav: standard + * .adpcm: Merlin - A Servant of Two Masters (DS) */ + if (!check_extensions(sf, "swav,adpcm")) + goto fail; + + if (read_u32be(0x00,sf) != 0x53574156) /* "SWAV" */ + goto fail; + if (read_u32be(0x10,sf) != 0x44415441) /* "DATA" */ + goto fail; + + /* check type details */ + codec_number = read_8bit(0x18,sf); + loop_flag = read_8bit(0x19,sf); + + channel_count = 1; + if (get_streamfile_size(sf) != read_s32le(0x08,sf)) { + if (get_streamfile_size(sf) != (read_s32le(0x08,sf) - 0x24) * 2 + 0x24) + goto fail; + channel_count = 2; + } + + switch (codec_number) { + case 0: + coding_type = coding_PCM8; + bits_per_sample = 8; + break; + case 1: + coding_type = coding_PCM16LE; + bits_per_sample = 16; + break; + case 2: + coding_type = coding_IMA_int; + bits_per_sample = 4; + break; + default: + goto fail; + } + start_offset = 0x24; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->num_samples = (read_s32le(0x14,sf) - 0x14) * 8 / bits_per_sample; + vgmstream->sample_rate = read_u16le(0x1A,sf); + if (loop_flag) { + vgmstream->loop_start_sample = read_u16le(0x1E,sf) * 32 / bits_per_sample; + vgmstream->loop_end_sample = read_s32le(0x20,sf) * 32 / bits_per_sample + vgmstream->loop_start_sample; + } + + if (coding_type == coding_IMA_int) { + /* handle IMA frame header */ + vgmstream->loop_start_sample -= 32 / bits_per_sample; + vgmstream->loop_end_sample -= 32 / bits_per_sample; + vgmstream->num_samples -= 32 / bits_per_sample; + + { + int i; + for (i = 0; i < channel_count; i++) { + vgmstream->ch[i].adpcm_history1_32 = read_s16le(start_offset + 0 + 4*i, sf); + vgmstream->ch[i].adpcm_step_index = read_s16le(start_offset + 2 + 4*i, sf); + } + } + + start_offset += 4 * channel_count; + } + + vgmstream->coding_type = coding_type; + vgmstream->meta_type = meta_SWAV; + if (channel_count == 2) { + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 1; + } else { + vgmstream->layout_type = layout_none; + } + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c b/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c index 05dd29a4f..d1c707d37 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ta_aac.c @@ -1,306 +1,170 @@ #include "meta.h" #include "../coding/coding.h" -/* AAC - tri-Ace (Aska engine) Audio Container */ +typedef struct { + int total_subsongs; + int codec; + int channels; + int sample_rate; -/* Xbox 360 Variants (Star Ocean 4, End of Eternity, Infinite Undiscovery) */ -VGMSTREAM * init_vgmstream_ta_aac_x360(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - size_t sampleRate, numSamples, startSample, dataSize, blockSize, blockCount; // A mess + int block_count; + int block_size; - /* check extension, case insensitive */ - /* .aac: expected, .laac/ace: for players to avoid hijacking MP4/AAC */ - if ( !check_extensions(streamFile,"aac,laac,ace")) + int32_t num_samples; + int32_t loop_start; + int32_t loop_end; + int loop_flag; + + off_t stream_offset; + off_t stream_size; + off_t extra_offset; + + off_t name_offset; +} aac_header; + +static int parse_aac(STREAMFILE* sf, aac_header* aac); + + +/* AAC - tri-Ace (ASKA engine) Audio Container */ +VGMSTREAM* init_vgmstream_ta_aac(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + aac_header aac = {0}; + + + /* checks */ + /* .aac: actual extension, .laac: for players to avoid hijacking MP4/AAC */ + if (!check_extensions(sf, "aac,laac")) + goto fail; + if (read_u32be(0x00, sf) != 0x41414320 && + read_u32le(0x00, sf) != 0x41414320) /* "AAC " */ goto fail; - if (read_32bitBE(0x00,streamFile) != 0x41414320) /* "AAC " */ + if (!parse_aac(sf, &aac)) goto fail; - /* Ok, let's check what's behind door number 1 */ - if (read_32bitBE(0x1000, streamFile) == 0x41534320) /* "ASC " */ - { - loop_flag = read_32bitBE(0x1118, streamFile); - - /*Funky Channel Count Checking */ - if (read_32bitBE(0x1184, streamFile) == 0x7374726D) - channel_count = 6; - else if (read_32bitBE(0x1154, streamFile) == 0x7374726D) - channel_count = 4; - else - channel_count = read_8bit(0x1134, streamFile); - - sampleRate = read_32bitBE(0x10F4, streamFile); - numSamples = read_32bitBE(0x10FC, streamFile); - startSample = read_32bitBE(0x10F8, streamFile); - dataSize = read_32bitBE(0x10F0, streamFile); - blockSize = read_32bitBE(0x1100, streamFile); - blockCount = read_32bitBE(0x110C, streamFile); - } - else if (read_32bitBE(0x1000, streamFile) == 0x57415645) /* "WAVE" */ - { - loop_flag = read_32bitBE(0x1048, streamFile); - - /*Funky Channel Count Checking */ - if (read_32bitBE(0x10B0, streamFile) == 0x7374726D) - channel_count = 6; - else if (read_32bitBE(0x1080, streamFile) == 0x7374726D) - channel_count = 4; - else - channel_count = read_8bit(0x1060, streamFile); - - sampleRate = read_32bitBE(0x1024, streamFile); - numSamples = read_32bitBE(0x102C, streamFile); - startSample = read_32bitBE(0x1028, streamFile); - dataSize = read_32bitBE(0x1020, streamFile); - blockSize = read_32bitBE(0x1030, streamFile); - blockCount = read_32bitBE(0x103C, streamFile); - } - else if (read_32bitBE(0x1000, streamFile) == 0x00000000) /* some like to be special */ - { - loop_flag = read_32bitBE(0x6048, streamFile); - - /*Funky Channel Count Checking */ - if (read_32bitBE(0x60B0, streamFile) == 0x7374726D) - channel_count = 6; - else if (read_32bitBE(0x6080, streamFile) == 0x7374726D) - channel_count = 4; - else - channel_count = read_8bit(0x6060, streamFile); - - sampleRate = read_32bitBE(0x6024, streamFile); - numSamples = read_32bitBE(0x602C, streamFile); - startSample = read_32bitBE(0x6028, streamFile); - dataSize = read_32bitBE(0x6020, streamFile); - blockSize = read_32bitBE(0x6030, streamFile); - blockCount = read_32bitBE(0x603C, streamFile); - } - else - goto fail; //cuz I don't know if there are other variants - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - if (read_32bitBE(0x1000, streamFile) == 0x00000000) - start_offset = 0x7000; - else - start_offset = 0x2000; - - vgmstream->sample_rate = sampleRate; - vgmstream->channels = channel_count; - vgmstream->num_samples = numSamples; - if (loop_flag) { - vgmstream->loop_start_sample = startSample; - vgmstream->loop_end_sample = vgmstream->num_samples; - } - vgmstream->meta_type = meta_TA_AAC_X360; - -#ifdef VGM_USE_FFMPEG - { - ffmpeg_codec_data *ffmpeg_data = NULL; - uint8_t buf[100]; - size_t bytes, datasize, block_size, block_count; - - block_count = blockCount; - block_size = blockSize; - datasize = dataSize; - - bytes = ffmpeg_make_riff_xma2(buf,100, vgmstream->num_samples, datasize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,datasize); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - xma_fix_raw_samples(vgmstream, streamFile, start_offset, datasize, 0, 1,1); - if (loop_flag) { /* reapply adjusted samples */ - vgmstream->loop_end_sample = vgmstream->num_samples; - } - - } -#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; -} - -/* PlayStation 3 Variants (Star Ocean International, Resonance of Fate) */ -VGMSTREAM * init_vgmstream_ta_aac_ps3(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - uint32_t data_size, loop_start, loop_end, codec_id, asc_chunk; - - /* 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_32bitBE(0x00, streamFile) != 0x41414320) /* "AAC " */ - goto fail; - - /* Find the ASC chunk, That's where the goodies are */ - asc_chunk = read_32bitBE(0x40, streamFile); - if (read_32bitBE(asc_chunk, streamFile) != 0x41534320) /* "ASC " */ - goto fail; - - if (read_32bitBE(asc_chunk+0x104, streamFile) != 0xFFFFFFFF) - loop_flag = 1; - else - loop_flag = 0; - - channel_count = read_32bitBE(asc_chunk + 0xF4, streamFile); - codec_id = read_32bitBE(asc_chunk + 0xF0, streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - /* ASC header */ - start_offset = asc_chunk + 0x110; - vgmstream->sample_rate = read_32bitBE(asc_chunk + 0xFC, streamFile); - vgmstream->channels = channel_count; - vgmstream->meta_type = meta_TA_AAC_PS3; - data_size = read_32bitBE(asc_chunk + 0xF8, streamFile); - loop_start = read_32bitBE(asc_chunk + 0x104, streamFile); - loop_end = read_32bitBE(asc_chunk + 0x108, streamFile); - -#ifdef VGM_USE_FFMPEG - { - int block_align, encoder_delay; - - block_align = (codec_id == 4 ? 0x60 : (codec_id == 5 ? 0x98 : 0xC0)) * vgmstream->channels; - encoder_delay = 1024 + 69; /* approximate, gets good loops */ - vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_align) - encoder_delay; - - vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ - vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_align); // - encoder_delay - vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_align) - encoder_delay; - } -#endif - - /* open the file for reading */ - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) - goto fail; - return vgmstream; - -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_vorbis(STREAMFILE *streamFile) { -#ifdef VGM_USE_VORBIS - off_t start_offset; - int8_t codec_id; - - /* 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 */ - { - ogg_vorbis_meta_info_t ovmi = {0}; - VGMSTREAM * result = NULL; - - ovmi.meta_type = meta_TA_AAC_MOBILE; - ovmi.loop_start = read_32bitLE(0x140, streamFile); - ovmi.loop_end = read_32bitLE(0x144, streamFile); - ovmi.loop_flag = ovmi.loop_end > ovmi.loop_start; - ovmi.loop_end_found = ovmi.loop_flag; - - start_offset = read_32bitLE(0x120, streamFile); - result = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi); - - if (result != NULL) { - return result; - } - } - -fail: - /* clean up anything we may have opened */ -#endif - return NULL; -} - -/* Android/iOS Variants, before they switched to Vorbis (Star Ocean Anamnesis (Android), Heaven x Inferno (iOS)) */ -VGMSTREAM * init_vgmstream_ta_aac_mobile(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int channel_count, loop_flag, codec; - size_t data_size; - - - /* check extension, case insensitive */ - /* .aac: expected, .laac: for players to avoid hijacking MP4/AAC */ - if (!check_extensions(streamFile, "aac,laac")) - goto fail; - - if (read_32bitLE(0x00, streamFile) != 0x41414320) /* "AAC " */ - goto fail; - - if (read_32bitLE(0xf0, streamFile) != 0x57415645) /* "WAVE" */ - goto fail; - - codec = read_8bit(0x104, streamFile); - channel_count = read_8bit(0x105, streamFile); - /* 0x106: 0x01?, 0x107: 0x10? */ - data_size = read_32bitLE(0x10c, streamFile); /* usable data only, cuts last frame */ - start_offset = read_32bitLE(0x120, streamFile); - /* 0x124: full data size */ - loop_flag = (read_32bitLE(0x134, streamFile) > 0); - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); + vgmstream = allocate_vgmstream(aac.channels, aac.loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = read_32bitLE(0x108, streamFile); - vgmstream->meta_type = meta_TA_AAC_MOBILE; + vgmstream->meta_type = meta_TA_AAC; + vgmstream->sample_rate = aac.sample_rate; + vgmstream->num_streams = aac.total_subsongs; + vgmstream->stream_size = aac.stream_size; - switch(codec) { - case 0x0d: - if (read_32bitLE(0x144, streamFile) != 0x40) goto fail; /* frame size */ - /* 0x148 or 0x150 (later games): frame samples */ - if (channel_count > 2) goto fail; /* unknown data layout */ + switch(aac.codec) { + +#ifdef VGM_USE_FFMPEG + case 0x0165: { /* Infinite Undiscovery (X360), Star Ocean 4 (X360), Resonance of Fate (X360) */ + uint8_t buf[0x100]; + size_t bytes; + + bytes = ffmpeg_make_riff_xma2(buf, sizeof(buf), aac.num_samples, aac.stream_size, aac.channels, aac.sample_rate, aac.block_count, aac.block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf, bytes, aac.stream_offset, aac.stream_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = aac.num_samples; + vgmstream->loop_start_sample = aac.loop_start; + vgmstream->loop_end_sample = aac.loop_end; + + xma_fix_raw_samples(vgmstream, sf, aac.stream_offset, aac.stream_size, 0, 0,1); + break; + } + + case 0x04: + case 0x05: + case 0x06: { /* Resonance of Fate (PS3), Star Ocean 4 (PS3) */ + int block_align = (aac.codec == 0x04 ? 0x60 : (aac.codec == 0x05 ? 0x98 : 0xC0)) * aac.channels; + int encoder_delay = 1024 + 69; /* AT3 default, gets good loops */ + + vgmstream->num_samples = atrac3_bytes_to_samples(aac.stream_size, block_align) - encoder_delay; + /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ + vgmstream->loop_start_sample = atrac3_bytes_to_samples(aac.loop_start, block_align); // - encoder_delay + vgmstream->loop_end_sample = atrac3_bytes_to_samples(aac.loop_end, block_align) - encoder_delay; + + vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf, aac.stream_offset, aac.stream_size, vgmstream->num_samples, vgmstream->channels, vgmstream->sample_rate, block_align, encoder_delay); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } +#endif +#ifdef VGM_USE_ATRAC9 + case 0x08: { /* Judas Code (Vita) */ + atrac9_config cfg = {0}; + + cfg.channels = vgmstream->channels; + cfg.encoder_delay = read_s32le(aac.extra_offset + 0x04,sf); + cfg.config_data = read_u32be(aac.extra_offset + 0x08,sf); + + 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(aac.stream_size, vgmstream->codec_data); + vgmstream->num_samples -= cfg.encoder_delay; + vgmstream->loop_start_sample = atrac9_bytes_to_samples(aac.loop_start, vgmstream->codec_data); + vgmstream->loop_end_sample = atrac9_bytes_to_samples(aac.loop_end, vgmstream->codec_data); + break; + } +#endif + case 0x0a: /* Star Ocean 4 (PC) */ + if (aac.channels > 2) goto fail; /* unknown data layout */ + /* 0x00: some value * channels? */ + /* 0x04: frame size */ + /* 0x08: frame samples */ + + vgmstream->coding_type = coding_MSADPCM; + vgmstream->layout_type = layout_none; + vgmstream->frame_size = read_u32le(aac.extra_offset + 0x04,sf); + + vgmstream->num_samples = msadpcm_bytes_to_samples(aac.stream_size, vgmstream->frame_size, aac.channels); + vgmstream->loop_start_sample = msadpcm_bytes_to_samples(aac.loop_start, vgmstream->frame_size, aac.channels); + vgmstream->loop_end_sample = msadpcm_bytes_to_samples(aac.loop_end, vgmstream->frame_size, aac.channels); + break; + + case 0x0d: /* Star Ocean Anamnesis (Android), Heaven x Inferno (iOS), Star Ocean 4 (PC), Resonance of Fate (PC) */ + /* 0x00: 0x17700 * channels? */ + /* 0x04: frame size */ + /* 0x08: frame samples (not always?) */ vgmstream->coding_type = coding_ASKA; vgmstream->layout_type = layout_none; + vgmstream->frame_size = read_u32le(aac.extra_offset + 0x04,sf); /* usually 0x40, rarely 0x20/C0 (ex. some ROF PC) */ + /* N-channel frames are allowed (ex. 4/6ch in SO4/ROF PC) */ + if (vgmstream->frame_size > 0xc0) /* known max */ + goto fail; - vgmstream->num_samples = aska_bytes_to_samples(data_size, channel_count); - vgmstream->loop_start_sample = aska_bytes_to_samples(read_32bitLE(0x130, streamFile), channel_count); - vgmstream->loop_end_sample = aska_bytes_to_samples(read_32bitLE(0x134, streamFile), channel_count); + vgmstream->num_samples = aska_bytes_to_samples(aac.stream_size, vgmstream->frame_size, aac.channels); + vgmstream->loop_start_sample = aska_bytes_to_samples(aac.loop_start, vgmstream->frame_size, aac.channels); + vgmstream->loop_end_sample = aska_bytes_to_samples(aac.loop_end, vgmstream->frame_size, aac.channels); break; +#ifdef VGM_USE_VORBIS + case 0x0e: { /* Star Ocean Anamnesis (Android-v1.9.2), Heaven x Inferno (iOS) */ + vgmstream->codec_data = init_ogg_vorbis(sf, aac.stream_offset, aac.stream_size, NULL); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_OGG_VORBIS; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = aac.num_samples; + vgmstream->loop_start_sample = read_s32le(aac.extra_offset + 0x00,sf); + vgmstream->loop_end_sample = read_s32le(aac.extra_offset + 0x04,sf); + /* seek table after loops */ + break; + } +#endif default: goto fail; } - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (aac.name_offset) + read_string(vgmstream->stream_name, STREAM_NAME_SIZE, aac.name_offset, sf); + + if (!vgmstream_open_stream(vgmstream, sf, aac.stream_offset)) goto fail; return vgmstream; @@ -309,65 +173,334 @@ fail: return NULL; } -/* Vita variants [Judas Code (Vita)] */ -VGMSTREAM * init_vgmstream_ta_aac_vita(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int channel_count, loop_flag; +/* DIR/dirn + WAVE chunk [Infinite Undiscovery (X360), Star Ocean 4 (X360)] */ +static int parse_aac_v1(STREAMFILE* sf, aac_header* aac) { + off_t offset, test_offset, wave_offset; + int target_subsong = sf->stream_index; - /* check extension, case insensitive */ - /* .aac: expected, .laac: for players to avoid hijacking MP4/AAC */ - if (!check_extensions(streamFile, "aac,laac")) + /* base header */ + /* 0x00: id */ + /* 0x04: size */ + /* 0x10: subsongs */ + /* 0x14: base size */ + /* 0x14: head size */ + /* 0x18: data size */ + /* 0x20: config? (0x00010003) */ + /* 0x30+ DIR + dirn subsongs */ + + if (read_u32be(0x30, sf) != 0x44495220) /* "DIR " */ goto fail; + aac->total_subsongs = read_u32be(0x40, sf); - if (read_32bitLE(0x00, streamFile) != 0x41414320) /* "AAC " */ - goto fail; - if (read_32bitLE(0x14, streamFile) != 0x56495441) /* "VITA" */ - goto fail; - if (read_32bitLE(0x10d0, streamFile) != 0x57415645) /* "WAVE" */ - goto fail; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > aac->total_subsongs || aac->total_subsongs < 1) goto fail; - /* there is a bunch of chunks but we simplify */ - - /* 0x10E4: codec 0x08? */ - channel_count = read_8bit(0x10E5, streamFile); - start_offset = read_32bitLE(0x1100, streamFile); - loop_flag = (read_32bitLE(0x1114, streamFile) > 0); - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = read_32bitLE(0x10e8, streamFile); - vgmstream->meta_type = meta_TA_AAC_VITA; - -#ifdef VGM_USE_ATRAC9 { - atrac9_config cfg = {0}; + int i; + offset = 0; + test_offset = 0x50; + for (i = 0; i < aac->total_subsongs; i++) { + uint32_t entry_type = read_u32be(test_offset + 0x00, sf); + uint32_t entry_size = read_u32be(test_offset + 0x04, sf); - cfg.channels = vgmstream->channels; - cfg.encoder_delay = read_32bitLE(0x1124,streamFile); - cfg.config_data = read_32bitBE(0x1128,streamFile); + switch(entry_type) { + case 0x6469726E: /* "dirn" */ + if (i + 1 == target_subsong) { + aac->name_offset = test_offset + 0x10; + offset = read_u32be(test_offset + 0x90, sf); /* absolute */ + } + break; - vgmstream->codec_data = init_atrac9(&cfg); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_ATRAC9; - vgmstream->layout_type = layout_none; + default: + goto fail; + } - vgmstream->num_samples = atrac9_bytes_to_samples(read_32bitLE(0x10EC, streamFile), vgmstream->codec_data); - vgmstream->num_samples -= cfg.encoder_delay; - vgmstream->loop_start_sample = atrac9_bytes_to_samples(read_32bitLE(0x1110, streamFile), vgmstream->codec_data); - vgmstream->loop_end_sample = atrac9_bytes_to_samples(read_32bitLE(0x1114, streamFile), vgmstream->codec_data); + test_offset += entry_size; + } } -#endif - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (read_u32be(offset + 0x00, sf) != 0x57415645) /* "WAVE" */ goto fail; - return vgmstream; + wave_offset = offset; + offset += 0x10; + { + /* X360 */ + int i, streams; + off_t strm_offset; + + /* 0x00: 0x0400 + song ID */ + streams = read_u16be(offset + 0x04, sf); + aac->codec = read_u16be(offset + 0x06, sf); + /* 0x08: null */ + /* 0x0c: null */ + aac->stream_size = read_u32be(offset + 0x10, sf); + aac->sample_rate = read_s32be(offset + 0x14, sf); + aac->loop_start = read_u32be(offset + 0x18, sf); + aac->loop_end = read_u32be(offset + 0x1C, sf); /* max samples if not set */ + aac->block_size = read_u32be(offset + 0x20, sf); + /* 0x24: max samples */ + aac->num_samples = read_u32be(offset + 0x28, sf); + aac->block_count = read_u32be(offset + 0x2c, sf); + + /* one UI file has a smaller header, early version? */ + if (read_u32be(offset + 0x30, sf) == 0x7374726D) { + aac->loop_flag = 0; /* ? */ + strm_offset = 0x30; + } + else { + /* 0x30: null */ + /* 0x34: encoder delay? */ + aac->loop_flag = read_u32be(offset + 0x38, sf) != 0; /* loop end block */ + /* 0x3c: size? (loop-related) */ + strm_offset = 0x40; + } + + aac->stream_offset = wave_offset + 0x1000; + + /* channels depends on streams definitions, "strm" chunk (max 2ch per strm) */ + aac->channels = 0; + for (i = 0; i < streams; i++) { + /* format: "strm", size, null, null, channels, ?, sample rate, encoder delay, samples, nulls */ + aac->channels += read_s8(offset + strm_offset + i*0x30 + 0x10, sf); + } + } + + return 1; fail: - close_vgmstream(vgmstream); - return NULL; + return 0; +} + +/* ASC + WAVE chunks [Resonance of Fate (X360/PS3), Star Ocean 4 (PS3)] */ +static int parse_aac_v2(STREAMFILE* sf, aac_header* aac) { + off_t offset, start, size, test_offset, asc_offset; + int target_subsong = sf->stream_index; + + /* base header */ + /* 0x00: id */ + /* 0x04: size */ + /* 0x10: config? (0x00020100/0x00020002/0x00020301/etc) */ + /* 0x14: flag (0x80/01) */ + /* 0x18: align? (PS3=0x30, X360=0xFD0) */ + /* 0x28: platform (PS3=3, X360=2) */ + /* 0x30+ offsets+sizes to ASC or GUIDs */ + + start = read_u32be(0x2c, sf); + + if (target_subsong == 0) target_subsong = 1; + aac->total_subsongs = 0; + + if (read_u32be(start + 0x00, sf) == 0x414D4620) { /* "AMF " */ + /* GUID subsongs */ + if (read_u32be(start + 0x10, sf) != 0x68656164) /* "head" */ + goto fail; + size = read_u32be(start + 0x10 + 0x10, sf); + + offset = 0; + test_offset = start + 0x10; + while (test_offset < start + size) { + uint32_t entry_type = read_u32be(test_offset + 0x00, sf); + uint32_t entry_size = read_u32be(test_offset + 0x04, sf); + + if (entry_type == 0) + break; + + switch(entry_type) { + case 0x61646472: /* "addr" (GUID + config) */ + aac->total_subsongs++; + if (aac->total_subsongs == target_subsong) { + offset = read_u32be(test_offset + 0x2c, sf) + start + size; + } + break; + + default: /* "head", "buff" */ + break; + } + + test_offset += entry_size; + } + } + else if (read_u32be(start + 0x00, sf) == 0x41534320) { /* "ASC " */ + /* regular subsongs */ + offset = 0; + for (test_offset = 0x30; test_offset < start; test_offset += 0x10) { + uint32_t entry_offset = read_u32be(test_offset + 0x00, sf); + /* 0x04: entry size */ + + if (entry_offset) { /* often 0 */ + aac->total_subsongs++; + if (aac->total_subsongs == target_subsong) { + offset = entry_offset; + } + } + } + } + else { + goto fail; + } + + if (target_subsong < 0 || target_subsong > aac->total_subsongs || aac->total_subsongs < 1) goto fail; + + if (read_u32be(offset + 0x00, sf) != 0x41534320) /* "ASC " */ + goto fail; + asc_offset = offset; + + /* ASC section has offsets to "PLBK" chunk (?) and "WAVE" (header), may be followed by "VRC " (?) */ + /* 0x50: PLBK offset */ + offset += read_u32be(offset + 0x54, sf); /* WAVE offset */ + if (read_u32be(offset + 0x00, sf) != 0x57415645) /* "WAVE" */ + goto fail; + offset += 0x10; + + if (read_u16be(offset + 0x00, sf) == 0x0400) { + /* X360 */ + int i, streams; + + /* 0x00: 0x0400 + song ID? (0) */ + streams = read_u16be(offset + 0x04, sf); + aac->codec = read_u16be(offset + 0x06, sf); + /* 0x08: null */ + /* 0x0c: null */ + aac->stream_size = read_u32be(offset + 0x10, sf); + aac->sample_rate = read_s32be(offset + 0x14, sf); + aac->loop_start = read_u32be(offset + 0x18, sf); + aac->loop_end = read_u32be(offset + 0x1C, sf); /* max samples if not set */ + aac->block_size = read_u32be(offset + 0x20, sf); + /* 0x24: max samples */ + aac->num_samples = read_u32be(offset + 0x28, sf); + aac->block_count = read_u32be(offset + 0x2c, sf); + /* 0x30: null */ + /* 0x34: encoder delay? */ + aac->loop_flag = read_u32be(offset + 0x38, sf) != 0; /* loop end block */ + /* 0x3c: size? (loop-related) */ + aac->stream_offset = read_u32be(offset + 0x40, sf) + asc_offset; + + /* channels depends on streams definitions, "strm" chunk (max 2ch per strm) */ + aac->channels = 0; + for (i = 0; i < streams; i++) { + /* format: "strm", size, null, null, channels, ?, sample rate, encoder delay, samples, nulls */ + aac->channels += read_s8(offset + 0x44 + i*0x30 + 0x10, sf); + } + + /* after streams and aligned to 0x10 is "Seek" table */ + } + else { + /* PS3 */ + aac->codec = read_u32be(offset + 0x00, sf); + aac->channels = read_u32be(offset + 0x04, sf); + aac->stream_size = read_u32be(offset + 0x08, sf); /* usable size (without padding) */ + aac->sample_rate = read_s32be(offset + 0x0c, sf); + /* 0x10: 0x51? */ + aac->loop_start = read_u32be(offset + 0x14, sf); + aac->loop_end = read_u32be(offset + 0x18, sf); + /* 0x1c: null */ + + aac->stream_offset = offset + 0x20; + } + + aac->loop_flag = (aac->loop_start != -1); + + return 1; +fail: + return 0; +} + +/* AAOB + WAVE + WAVB chunks [Judas Code (Vita), Star Ocean Anamnesis (Android), Star Ocean 4 (PC)] */ +static int parse_aac_v3(STREAMFILE* sf, aac_header* aac) { + off_t offset, size, test_offset; + int target_subsong = sf->stream_index; + + /* base header */ + /* 0x00: id */ + /* 0x04: size */ + /* 0x10: config? (0x00020100/0x00020002/0x00020301/etc) */ + /* 0x14: platform ("VITA"=Vita, "DRD "=Android, "MSPC"=PC) */ + + /* offsets table: offset + flag? + size + align? */ + offset = read_u32le(0x20, sf); /* "AAOB" table (audio object?) */ + /* 0x30: "VRCB" table (some cue/config? related to subsongs? may be empty) */ + /* 0x40: "WAVB" table (wave body, has offset + size per stream then data, not needed since offsets are elsewhere too) */ + + if (read_u32le(offset + 0x00, sf) != 0x41414F42) /* "AAOB" */ + goto fail; + size = read_u32le(offset + 0x04, sf); + + if (target_subsong == 0) target_subsong = 1; + aac->total_subsongs = 0; + + /* AAOB may point to N AAO (headers) in SFX/voice packs, seems signaled with flag 0x80 at AAOB+0x10 + * but there is no subsong count or even max size (always 0x1000?) */ + { + for (test_offset = offset + 0x20; offset + size; test_offset += 0x10) { + uint32_t entry_offset = read_u32le(test_offset + 0x00, sf); + /* 0x04: entry size */ + + if (entry_offset == 0x41414F20) /* reached first "AAO " */ + break; + + if (entry_offset) { /* often 0 */ + aac->total_subsongs++; + if (aac->total_subsongs == target_subsong) { + offset += entry_offset; + } + } + } + } + + if (target_subsong < 0 || target_subsong > aac->total_subsongs || aac->total_subsongs < 1) goto fail; + + if (read_u32le(offset + 0x00, sf) != 0x41414F20) /* "AAO " */ + goto fail; + + + /* AAO section has offsets to "PLBK" chunk (?) and "WAVE" (header) */ + /* 0x14: PLBK offset */ + offset += read_u32le(offset + 0x18, sf); /* WAVE offset */ + if (read_u32le(offset + 0x00, sf) != 0x57415645) /* "WAVE" */ + goto fail; + offset += 0x10; + + /* 0x00: 0x00/01/01CC0000? */ + aac->codec = read_u8(offset + 0x04, sf); + aac->channels = read_u8(offset + 0x05, sf); + /* 0x06: 0x01? */ + /* 0x07: 0x10? (rarely 0x00) */ + aac->sample_rate = read_s32le(offset + 0x08, sf); + aac->stream_size = read_u32le(offset + 0x0C, sf); /* usable size (without padding) */ + /* 0x10-1c: null */ + aac->stream_offset = read_u32le(offset + 0x20, sf); /* absolute */ + /* 0x24: data size (with padding) */ + /* 0x28: null */ + /* 0x2c: null */ + aac->loop_start = read_u32le(offset + 0x30, sf); /* table positions(?) in OGG */ + aac->loop_end = read_u32le(offset + 0x34, sf); + /* 0x38: ? in OGG */ + aac->num_samples = read_s32le(offset + 0x3c, sf); /* OGG only */ + aac->extra_offset = offset + 0x40; /* codec specific */ + /* may have seek tables or other stuff per codec */ + + aac->loop_flag = (aac->loop_end > 0); + + return 1; +fail: + return 0; +} + +static int parse_aac(STREAMFILE* sf, aac_header* aac) { + int ok = 0; + + /* try variations as format evolved over time + * chunk headers are always: id + size + null + null (ids in machine endianness) */ + + ok = parse_aac_v1(sf, aac); + if (ok) return 1; + + ok = parse_aac_v2(sf, aac); + if (ok) return 1; + + ok = parse_aac_v3(sf, aac); + if (ok) return 1; + + return 0; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index f98e6296f..958db9e4d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -38,8 +38,11 @@ typedef enum { AAC = 28, /* Advanced Audio Coding (raw without .mp4) */ TGC = 29, /* Tiger Game.com 4-bit ADPCM */ ASF = 30, /* Argonaut ASF 4-bit ADPCM */ + EAXA = 31, /* Electronic Arts EA-XA 4-bit ADPCM v1 */ } txth_type; +typedef enum { DEFAULT, NEGATIVE, POSITIVE, INVERTED } txth_loop_t; + typedef struct { txth_type codec; uint32_t codec_mode; @@ -72,7 +75,7 @@ typedef struct { uint32_t skip_samples; uint32_t loop_flag; - uint32_t loop_behavior; + txth_loop_t loop_behavior; int loop_flag_set; int loop_flag_auto; @@ -92,7 +95,7 @@ typedef struct { int target_subsong; uint32_t subsong_count; - uint32_t subsong_offset; + uint32_t subsong_spacing; uint32_t name_offset_set; uint32_t name_offset; @@ -119,61 +122,61 @@ typedef struct { int name_values_count; /* original STREAMFILE and its type (may be an unsupported "base" file or a .txth) */ - STREAMFILE *streamFile; + STREAMFILE* sf; int streamfile_is_txth; /* configurable STREAMFILEs and if we opened it (thus must close it later) */ - STREAMFILE *streamText; - STREAMFILE *streamHead; - STREAMFILE *streamBody; - int streamtext_opened; - int streamhead_opened; - int streambody_opened; + STREAMFILE* sf_text; + STREAMFILE* sf_head; + STREAMFILE* sf_body; + int sf_text_opened; + int sf_head_opened; + int sf_body_opened; } txth_header; -static VGMSTREAM *init_subfile(txth_header * txth); -static STREAMFILE * open_txth(STREAMFILE * streamFile); -static void clean_txth(txth_header * txth); -static int parse_txth(txth_header * txth); +static VGMSTREAM* init_subfile(txth_header* txth); +static STREAMFILE* open_txth(STREAMFILE* sf); +static void clean_txth(txth_header* txth); +static int parse_txth(txth_header* txth); /* TXTH - an artificial "generic" header for headerless streams. * Similar to GENH, but with a single separate .txth file in the dir and text-based. */ -VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; txth_header txth = {0}; coding_t coding; int i, j; /* accept .txth (should set body_file or will fail later) */ - if (check_extensions(streamFile, "txth")) { - txth.streamFile = streamFile; + if (check_extensions(sf, "txth")) { + txth.sf = sf; txth.streamfile_is_txth = 1; - txth.streamText = streamFile; - txth.streamHead = NULL; - txth.streamBody = NULL; - txth.streamtext_opened = 0; - txth.streamhead_opened = 0; - txth.streambody_opened = 0; + txth.sf_text = sf; + txth.sf_head = NULL; + txth.sf_body = NULL; + txth.sf_text_opened = 0; + txth.sf_head_opened = 0; + txth.sf_body_opened = 0; } else { /* accept base file (no need for ID or ext checks --if a companion .TXTH exists all is good). * player still needs to accept the streamfile's ext, so at worst rename to .vgmstream */ - STREAMFILE * streamText = open_txth(streamFile); - if (!streamText) goto fail; + STREAMFILE* sf_text = open_txth(sf); + if (!sf_text) goto fail; - txth.streamFile = streamFile; + txth.sf = sf; txth.streamfile_is_txth = 0; - txth.streamText = streamText; - txth.streamHead = streamFile; - txth.streamBody = streamFile; - txth.streamtext_opened = 1; - txth.streamhead_opened = 0; - txth.streambody_opened = 0; + txth.sf_text = sf_text; + txth.sf_head = sf; + txth.sf_body = sf; + txth.sf_text_opened = 1; + txth.sf_head_opened = 0; + txth.sf_body_opened = 0; } /* process the text file */ @@ -182,7 +185,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { /* special case of parsing subfiles */ if (txth.subfile_set) { - VGMSTREAM *subfile_vgmstream = init_subfile(&txth); + VGMSTREAM* subfile_vgmstream = init_subfile(&txth); clean_txth(&txth); return subfile_vgmstream; } @@ -225,6 +228,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { case OKI16: coding = coding_OKI16; break; case TGC: coding = coding_TGC; break; case ASF: coding = coding_ASF; break; + case EAXA: coding = coding_EA_XA; break; default: goto fail; } @@ -232,7 +236,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { /* try to autodetect PS-ADPCM loop data */ if (txth.loop_flag_auto && coding == coding_PSX) { - txth.loop_flag = ps_find_loop_offsets(txth.streamBody, txth.start_offset, txth.data_size, txth.channels, txth.interleave, + txth.loop_flag = ps_find_loop_offsets(txth.sf_body, txth.start_offset, txth.data_size, txth.channels, txth.interleave, (int32_t*)&txth.loop_start_sample, (int32_t*)&txth.loop_end_sample); } @@ -249,7 +253,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { vgmstream->stream_size = txth.data_size; if (txth.name_offset_set) { size_t name_size = txth.name_size ? txth.name_size + 1 : STREAM_NAME_SIZE; - read_string(vgmstream->stream_name,name_size, txth.name_offset,txth.streamHead); + read_string(vgmstream->stream_name,name_size, txth.name_offset,txth.sf_head); } /* codec specific (taken from GENH with minimal changes) */ @@ -341,6 +345,20 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { vgmstream->interleave_block_size = 0x11; break; + case coding_EA_XA: /* from 'raw' modes in sx.exe [Harry Potter and the Chamber of Secrets (PC)] */ + if (txth.codec_mode == 1) { /* mono interleave */ + coding = coding_EA_XA_int; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = txth.interleave; + vgmstream->interleave_last_block_size = txth.interleave_last; + } else { /* mono/stereo */ + if (vgmstream->channels > 2) + goto fail; /* only 1ch and 2ch are known */ + + vgmstream->layout_type = layout_none; + } + break; + case coding_MS_IMA: if (!txth.interleave) goto fail; /* creates garbage */ @@ -396,8 +414,8 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { /* get coefs */ { - int16_t (*read_16bit)(off_t , STREAMFILE*) = txth.coef_big_endian ? read_16bitBE : read_16bitLE; - int16_t (*get_16bit)(uint8_t * p) = txth.coef_big_endian ? get_16bitBE : get_16bitLE; + int16_t (*read_16bit)(off_t, STREAMFILE*) = txth.coef_big_endian ? read_16bitBE : read_16bitLE; + int16_t (*get_16bit)(const uint8_t* p) = txth.coef_big_endian ? get_16bitBE : get_16bitLE; for (i = 0; i < vgmstream->channels; i++) { if (txth.coef_mode == 0) { /* normal coefs */ @@ -406,7 +424,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { if (txth.coef_table_set) coef = get_16bit(txth.coef_table + i*txth.coef_spacing + j*2); else - coef = read_16bit(txth.coef_offset + i*txth.coef_spacing + j*2, txth.streamHead); + coef = read_16bit(txth.coef_offset + i*txth.coef_spacing + j*2, txth.sf_head); vgmstream->ch[i].adpcm_coef[j] = coef; } } @@ -414,8 +432,8 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { goto fail; //IDK what is this /* for (j = 0; j < 8; j++) { - vgmstream->ch[i].adpcm_coef[j*2] = read_16bit(genh.coef_offset + i*genh.coef_spacing + j*2, txth.streamHead); - vgmstream->ch[i].adpcm_coef[j*2+1] = read_16bit(genh.coef_split_offset + i*genh.coef_split_spacing + j*2, txth.streamHead); + vgmstream->ch[i].adpcm_coef[j*2] = read_16bit(genh.coef_offset + i*genh.coef_spacing + j*2, txth.sf_head); + vgmstream->ch[i].adpcm_coef[j*2+1] = read_16bit(genh.coef_split_offset + i*genh.coef_split_spacing + j*2, txth.sf_head); } */ } @@ -428,8 +446,8 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { for (i = 0; i < vgmstream->channels; i++) { off_t offset = txth.hist_offset + i*txth.hist_spacing; - vgmstream->ch[i].adpcm_history1_16 = read_16bit(offset + 0x00, txth.streamHead); - vgmstream->ch[i].adpcm_history2_16 = read_16bit(offset + 0x02, txth.streamHead); + vgmstream->ch[i].adpcm_history1_16 = read_16bit(offset + 0x00, txth.sf_head); + vgmstream->ch[i].adpcm_history2_16 = read_16bit(offset + 0x02, txth.sf_head); } } @@ -438,7 +456,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(txth.streamBody, txth.start_offset, &coding, vgmstream->channels); + vgmstream->codec_data = init_mpeg(txth.sf_body, txth.start_offset, &coding, vgmstream->channels); if (!vgmstream->codec_data) goto fail; break; @@ -449,7 +467,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { if (txth.codec == FFMPEG || txth.codec == AC3 || txth.codec == AAC) { /* default FFmpeg */ - ffmpeg_data = init_ffmpeg_offset(txth.streamBody, txth.start_offset,txth.data_size); + ffmpeg_data = init_ffmpeg_offset(txth.sf_body, txth.start_offset,txth.data_size); if ( !ffmpeg_data ) goto fail; if (vgmstream->num_samples == 0) @@ -466,21 +484,21 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { block_align = txth.interleave; encoder_delay = txth.skip_samples; - ffmpeg_data = init_ffmpeg_atrac3_raw(txth.streamBody, txth.start_offset,txth.data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + ffmpeg_data = init_ffmpeg_atrac3_raw(txth.sf_body, txth.start_offset,txth.data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); if (!ffmpeg_data) goto fail; } else if (txth.codec == ATRAC3PLUS) { int block_size = txth.interleave; bytes = ffmpeg_make_riff_atrac3plus(buf, sizeof(buf), vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_size, txth.skip_samples); - ffmpeg_data = init_ffmpeg_header_offset(txth.streamBody, buf,bytes, txth.start_offset,txth.data_size); + ffmpeg_data = init_ffmpeg_header_offset(txth.sf_body, buf,bytes, txth.start_offset,txth.data_size); if ( !ffmpeg_data ) goto fail; } else if (txth.codec == XMA1) { int xma_stream_mode = txth.codec_mode == 1 ? 1 : 0; bytes = ffmpeg_make_riff_xma1(buf, sizeof(buf), vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, xma_stream_mode); - ffmpeg_data = init_ffmpeg_header_offset(txth.streamBody, buf,bytes, txth.start_offset,txth.data_size); + ffmpeg_data = init_ffmpeg_header_offset(txth.sf_body, buf,bytes, txth.start_offset,txth.data_size); if ( !ffmpeg_data ) goto fail; } else if (txth.codec == XMA2) { @@ -490,7 +508,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { block_count = txth.data_size / block_size; bytes = ffmpeg_make_riff_xma2(buf, sizeof(buf), vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - ffmpeg_data = init_ffmpeg_header_offset(txth.streamBody, buf,bytes, txth.start_offset,txth.data_size); + ffmpeg_data = init_ffmpeg_header_offset(txth.sf_body, buf,bytes, txth.start_offset,txth.data_size); if ( !ffmpeg_data ) goto fail; } else { @@ -502,7 +520,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; if (txth.codec == XMA1 || txth.codec == XMA2) { - xma_fix_raw_samples(vgmstream, txth.streamBody, txth.start_offset,txth.data_size, 0, 0,0); + xma_fix_raw_samples(vgmstream, txth.sf_body, txth.start_offset,txth.data_size, 0, 0,0); } else if (txth.skip_samples_set && txth.codec != ATRAC3) { /* force encoder delay */ ffmpeg_set_skip_samples(ffmpeg_data, txth.skip_samples); } @@ -531,7 +549,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { msd.loop_end_subframe = txth.loop_adjust >> 4; /* upper 4b: subframe where the loop ends, 0..3 */ } - xma_get_samples(&msd, txth.streamBody); + xma_get_samples(&msd, txth.sf_body); vgmstream->num_samples = msd.num_samples; if (txth.sample_type==1) { @@ -546,7 +564,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { vgmstream->allow_dual_stereo = 1; - if ( !vgmstream_open_stream(vgmstream,txth.streamBody,txth.start_offset) ) + if ( !vgmstream_open_stream(vgmstream,txth.sf_body,txth.start_offset) ) goto fail; clean_txth(&txth); @@ -558,10 +576,10 @@ fail: return NULL; } -static VGMSTREAM *init_subfile(txth_header * txth) { - VGMSTREAM *vgmstream = NULL; +static VGMSTREAM* init_subfile(txth_header* txth) { + VGMSTREAM* vgmstream = NULL; char extension[PATH_LIMIT]; - STREAMFILE * streamSubfile = NULL; + STREAMFILE* sf_sub = NULL; if (txth->subfile_size == 0) { @@ -569,12 +587,12 @@ static VGMSTREAM *init_subfile(txth_header * txth) { txth->subfile_size = txth->data_size; else txth->subfile_size = txth->data_size - txth->subfile_offset; - if (txth->subfile_size + txth->subfile_offset > get_streamfile_size(txth->streamBody)) - txth->subfile_size = get_streamfile_size(txth->streamBody) - txth->subfile_offset; + if (txth->subfile_size + txth->subfile_offset > get_streamfile_size(txth->sf_body)) + txth->subfile_size = get_streamfile_size(txth->sf_body) - txth->subfile_offset; } if (txth->subfile_extension[0] == '\0') - get_streamfile_ext(txth->streamFile,txth->subfile_extension,sizeof(txth->subfile_extension)); + get_streamfile_ext(txth->sf,txth->subfile_extension,sizeof(txth->subfile_extension)); /* must detect a potential infinite loop: * - init_vgmstream enters TXTH and reads .txth @@ -586,10 +604,12 @@ static VGMSTREAM *init_subfile(txth_header * txth) { strcpy(extension, "subfile_txth."); strcat(extension, txth->subfile_extension); - streamSubfile = setup_subfile_streamfile(txth->streamBody, txth->subfile_offset, txth->subfile_size, extension); - if (!streamSubfile) goto fail; + sf_sub = setup_subfile_streamfile(txth->sf_body, txth->subfile_offset, txth->subfile_size, extension); + if (!sf_sub) goto fail; - vgmstream = init_vgmstream_from_STREAMFILE(streamSubfile); + sf_sub->stream_index = txth->sf->stream_index; /* in case of subfiles with subsongs */ + + vgmstream = init_vgmstream_from_STREAMFILE(sf_sub); if (!vgmstream) goto fail; /* apply some fields */ @@ -625,77 +645,77 @@ static VGMSTREAM *init_subfile(txth_header * txth) { // txth->loop_flag = vgmstream->loop_flag; - close_streamfile(streamSubfile); + close_streamfile(sf_sub); return vgmstream; fail: - close_streamfile(streamSubfile); + close_streamfile(sf_sub); close_vgmstream(vgmstream); return NULL; } -static STREAMFILE * open_txth(STREAMFILE * streamFile) { +static STREAMFILE* open_txth(STREAMFILE* sf) { char basename[PATH_LIMIT]; char filename[PATH_LIMIT]; char fileext[PATH_LIMIT]; const char *subext; - STREAMFILE * streamText; + STREAMFILE* sf_text; /* try "(path/)(name.ext).txth" */ - get_streamfile_name(streamFile,filename,PATH_LIMIT); + get_streamfile_name(sf,filename,PATH_LIMIT); if (strstr(filename, "subfile_txth") != NULL) return NULL; /* detect special case of subfile-within-subfile */ strcat(filename, ".txth"); - streamText = open_streamfile(streamFile,filename); - if (streamText) return streamText; + sf_text = open_streamfile(sf,filename); + if (sf_text) return sf_text; /* try "(path/)(.sub.ext).txth" */ - get_streamfile_basename(streamFile,basename,PATH_LIMIT); + get_streamfile_basename(sf,basename,PATH_LIMIT); subext = filename_extension(basename); if (subext != NULL) { - get_streamfile_path(streamFile,filename,PATH_LIMIT); - get_streamfile_ext(streamFile,fileext,PATH_LIMIT); + get_streamfile_path(sf,filename,PATH_LIMIT); + get_streamfile_ext(sf,fileext,PATH_LIMIT); strcat(filename,"."); strcat(filename, subext); strcat(filename,"."); strcat(filename, fileext); strcat(filename, ".txth"); - streamText = open_streamfile(streamFile,filename); - if (streamText) return streamText; + sf_text = open_streamfile(sf,filename); + if (sf_text) return sf_text; } /* try "(path/)(.ext).txth" */ - get_streamfile_path(streamFile,filename,PATH_LIMIT); - get_streamfile_ext(streamFile,fileext,PATH_LIMIT); + get_streamfile_path(sf,filename,PATH_LIMIT); + get_streamfile_ext(sf,fileext,PATH_LIMIT); strcat(filename,"."); strcat(filename, fileext); strcat(filename, ".txth"); - streamText = open_streamfile(streamFile,filename); - if (streamText) return streamText; + sf_text = open_streamfile(sf,filename); + if (sf_text) return sf_text; /* try "(path/).txth" */ - get_streamfile_path(streamFile,filename,PATH_LIMIT); + get_streamfile_path(sf,filename,PATH_LIMIT); strcat(filename, ".txth"); - streamText = open_streamfile(streamFile,filename); - if (streamText) return streamText; + sf_text = open_streamfile(sf,filename); + if (sf_text) return sf_text; /* not found */ return NULL; } -static void clean_txth(txth_header * txth) { +static void clean_txth(txth_header* txth) { /* close stuff manually opened during parse */ - if (txth->streamtext_opened) close_streamfile(txth->streamText); - if (txth->streamhead_opened) close_streamfile(txth->streamHead); - if (txth->streambody_opened) close_streamfile(txth->streamBody); + if (txth->sf_text_opened) close_streamfile(txth->sf_text); + if (txth->sf_head_opened) close_streamfile(txth->sf_head); + if (txth->sf_body_opened) close_streamfile(txth->sf_body); } /* ****************************************************************** */ -static void set_body_chunk(txth_header * txth) { - STREAMFILE *temp_streamFile = NULL; +static void set_body_chunk(txth_header* txth) { + STREAMFILE* temp_sf = NULL; /* sets body "chunk" if all needed values are set * (done inline for padding/get_samples/etc calculators to work) */ @@ -704,7 +724,7 @@ static void set_body_chunk(txth_header * txth) { return; if (txth->chunk_size == 0 || txth->chunk_start > txth->data_size || txth->chunk_count == 0) return; - if (!txth->streamBody) + if (!txth->sf_body) return; /* treat chunks as subsongs */ @@ -725,20 +745,20 @@ static void set_body_chunk(txth_header * txth) { cfg.chunk_count = txth->chunk_count; cfg.chunk_number = txth->chunk_number - 1; /* 1-index to 0-index */ - temp_streamFile = setup_txth_streamfile(txth->streamBody, cfg, txth->streambody_opened); - if (!temp_streamFile) return; + temp_sf = setup_txth_streamfile(txth->sf_body, cfg, txth->sf_body_opened); + if (!temp_sf) return; } - /* closing is handled by temp_streamFile */ - //if (txth->streambody_opened) { - // close_streamfile(txth->streamBody); - // txth->streamBody = NULL; - // txth->streambody_opened = 0; + /* closing is handled by temp_sf */ + //if (txth->sf_body_opened) { + // close_streamfile(txth->sf_body); + // txth->sf_body = NULL; + // txth->sf_body_opened = 0; //} - txth->streamBody = temp_streamFile; - txth->streambody_opened = 1; + txth->sf_body = temp_sf; + txth->sf_body_opened = 1; /* cancel values once set, to avoid weirdness and possibly allow chunks-in-chunks? */ txth->chunk_start_set = 0; @@ -747,38 +767,38 @@ static void set_body_chunk(txth_header * txth) { /* re-apply */ if (!txth->data_size_set) { - txth->data_size = get_streamfile_size(txth->streamBody); + txth->data_size = get_streamfile_size(txth->sf_body); } } -static int parse_keyval(STREAMFILE * streamFile, txth_header * txth, const char * key, char * val); -static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * val, uint32_t * out_value); -static int parse_string(STREAMFILE * streamFile, txth_header * txth, const char * val, char * str); -static int parse_coef_table(STREAMFILE * streamFile, txth_header * txth, const char * val, uint8_t * out_value, size_t out_size); -static int parse_name_table(txth_header * txth, char * val); +static int parse_keyval(STREAMFILE* sf, txth_header* txth, const char * key, char * val); +static int parse_num(STREAMFILE* sf, txth_header* txth, const char * val, uint32_t * out_value); +static int parse_string(STREAMFILE* sf, txth_header* txth, const char * val, char * str); +static int parse_coef_table(STREAMFILE* sf, txth_header* txth, const char * val, uint8_t * out_value, size_t out_size); +static int parse_name_table(txth_header* txth, char * val); static int is_string(const char * val, const char * cmp); -static int get_bytes_to_samples(txth_header * txth, uint32_t bytes); -static int get_padding_size(txth_header * txth, int discard_empty); +static int get_bytes_to_samples(txth_header* txth, uint32_t bytes); +static int get_padding_size(txth_header* txth, int discard_empty); /* Simple text parser of "key = value" lines. * The code is meh and error handling not exactly the best. */ -static int parse_txth(txth_header * txth) { +static int parse_txth(txth_header* txth) { off_t txt_offset = 0x00; - off_t file_size = get_streamfile_size(txth->streamText); + off_t file_size = get_streamfile_size(txth->sf_text); /* setup txth defaults */ - if (txth->streamBody) - txth->data_size = get_streamfile_size(txth->streamBody); - txth->target_subsong = txth->streamFile->stream_index; + if (txth->sf_body) + txth->data_size = get_streamfile_size(txth->sf_body); + txth->target_subsong = txth->sf->stream_index; if (txth->target_subsong == 0) txth->target_subsong = 1; /* skip BOM if needed */ - if ((uint16_t)read_16bitLE(0x00, txth->streamText) == 0xFFFE || - (uint16_t)read_16bitLE(0x00, txth->streamText) == 0xFEFF) { + if ((uint16_t)read_16bitLE(0x00, txth->sf_text) == 0xFFFE || + (uint16_t)read_16bitLE(0x00, txth->sf_text) == 0xFEFF) { txt_offset = 0x02; } - else if (((uint32_t)read_32bitBE(0x00, txth->streamText) & 0xFFFFFF00) == 0xEFBBBF00) { + else if (((uint32_t)read_32bitBE(0x00, txth->sf_text) & 0xFFFFFF00) == 0xEFBBBF00) { txt_offset = 0x03; } @@ -788,36 +808,36 @@ static int parse_txth(txth_header * txth) { char key[TXT_LINE_MAX] = {0}, val[TXT_LINE_MAX] = {0}; /* at least as big as a line to avoid overflows (I hope) */ int ok, bytes_read, line_ok; - bytes_read = read_line(line, sizeof(line), txt_offset, txth->streamText, &line_ok); + bytes_read = read_line(line, sizeof(line), txt_offset, txth->sf_text, &line_ok); if (!line_ok) goto fail; //;VGM_LOG("TXTH: line=%s\n",line); txt_offset += bytes_read; - + /* get key/val (ignores lead spaces, stops at space/comment/separator) */ ok = sscanf(line, " %[^ \t#=] = %[^\t#\r\n] ", key,val); if (ok != 2) /* ignore line if no key=val (comment or garbage) */ continue; - if (!parse_keyval(txth->streamFile, txth, key, val)) /* read key/val */ + if (!parse_keyval(txth->sf, txth, key, val)) /* read key/val */ goto fail; } if (!txth->loop_flag_set) txth->loop_flag = txth->loop_end_sample && txth->loop_end_sample != 0xFFFFFFFF; - if (!txth->streamBody) + if (!txth->sf_body) goto fail; - if (txth->data_size > get_streamfile_size(txth->streamBody) - txth->start_offset || txth->data_size <= 0) - txth->data_size = get_streamfile_size(txth->streamBody) - txth->start_offset; + if (txth->data_size > get_streamfile_size(txth->sf_body) - txth->start_offset || txth->data_size <= 0) + txth->data_size = get_streamfile_size(txth->sf_body) - txth->start_offset; return 1; fail: return 0; } -static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char * key, char * val) { +static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char * key, char * val) { //;VGM_LOG("TXTH: key=%s, val=%s\n", key, val); /* CODEC */ @@ -856,6 +876,7 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char else if (is_string(val,"TGC")) txth->codec = TGC; else if (is_string(val,"GCOM_ADPCM")) txth->codec = TGC; else if (is_string(val,"ASF")) txth->codec = ASF; + else if (is_string(val,"EAXA")) txth->codec = EAXA; else goto fail; /* set common interleaves to simplify usage @@ -874,29 +895,29 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char } } else if (is_string(key,"codec_mode")) { - if (!parse_num(txth->streamHead,txth,val, &txth->codec_mode)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->codec_mode)) goto fail; } /* VALUE MODIFIERS */ else if (is_string(key,"value_mul") || is_string(key,"value_*")) { - if (!parse_num(txth->streamHead,txth,val, &txth->value_mul)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->value_mul)) goto fail; } else if (is_string(key,"value_div") || is_string(key,"value_/")) { - if (!parse_num(txth->streamHead,txth,val, &txth->value_div)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->value_div)) goto fail; } else if (is_string(key,"value_add") || is_string(key,"value_+")) { - if (!parse_num(txth->streamHead,txth,val, &txth->value_add)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->value_add)) goto fail; } else if (is_string(key,"value_sub") || is_string(key,"value_-")) { - if (!parse_num(txth->streamHead,txth,val, &txth->value_sub)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->value_sub)) goto fail; } /* ID VALUES */ else if (is_string(key,"id_value")) { - if (!parse_num(txth->streamHead,txth,val, &txth->id_value)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->id_value)) goto fail; } else if (is_string(key,"id_offset")) { - if (!parse_num(txth->streamHead,txth,val, &txth->id_offset)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->id_offset)) goto fail; if (txth->id_value != txth->id_offset) /* evaluate current ID */ goto fail; } @@ -908,7 +929,7 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char txth->interleave = txth->data_size / txth->channels; } else { - if (!parse_num(txth->streamHead,txth,val, &txth->interleave)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->interleave)) goto fail; } } else if (is_string(key,"interleave_last")) { @@ -917,21 +938,21 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char txth->interleave_last = (txth->data_size % (txth->interleave * txth->channels)) / txth->channels; } else { - if (!parse_num(txth->streamHead,txth,val, &txth->interleave_last)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->interleave_last)) goto fail; } } /* BASE CONFIG */ else if (is_string(key,"channels")) { - if (!parse_num(txth->streamHead,txth,val, &txth->channels)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->channels)) goto fail; } else if (is_string(key,"sample_rate")) { - if (!parse_num(txth->streamHead,txth,val, &txth->sample_rate)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->sample_rate)) goto fail; } /* DATA CONFIG */ else if (is_string(key,"start_offset")) { - if (!parse_num(txth->streamHead,txth,val, &txth->start_offset)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->start_offset)) goto fail; /* apply */ @@ -942,7 +963,7 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char if (txth->subsong_count > 1 && txth->target_subsong < txth->subsong_count) { /* temp move to next start_offset and move back*/ txth->target_subsong++; - parse_num(txth->streamHead,txth,val, &txth->next_offset); + parse_num(txth->sf_head,txth,val, &txth->next_offset); txth->target_subsong--; if (txth->next_offset < txth->start_offset) txth->next_offset = 0; @@ -962,7 +983,7 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char txth->padding_size = get_padding_size(txth, 1); } else { - if (!parse_num(txth->streamHead,txth,val, &txth->padding_size)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->padding_size)) goto fail; } /* apply */ @@ -972,7 +993,7 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char } } else if (is_string(key,"data_size")) { - if (!parse_num(txth->streamHead,txth,val, &txth->data_size)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->data_size)) goto fail; txth->data_size_set = 1; } @@ -989,15 +1010,15 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char txth->num_samples_data_size = 1; } else { - if (!parse_num(txth->streamHead,txth,val, &txth->num_samples)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->num_samples)) goto fail; if (txth->sample_type==1) txth->num_samples = get_bytes_to_samples(txth, txth->num_samples); if (txth->sample_type==2) txth->num_samples = get_bytes_to_samples(txth, txth->num_samples * (txth->interleave*txth->channels)); } } - else if (is_string(key,"loop_start_sample")) { - if (!parse_num(txth->streamHead,txth,val, &txth->loop_start_sample)) goto fail; + else if (is_string(key,"loop_start_sample") || is_string(key,"loop_start")) { + if (!parse_num(txth->sf_head,txth,val, &txth->loop_start_sample)) goto fail; if (txth->sample_type==1) txth->loop_start_sample = get_bytes_to_samples(txth, txth->loop_start_sample); if (txth->sample_type==2) @@ -1005,12 +1026,12 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char if (txth->loop_adjust) txth->loop_start_sample += txth->loop_adjust; } - else if (is_string(key,"loop_end_sample")) { + else if (is_string(key,"loop_end_sample") || is_string(key,"loop_end")) { if (is_string(val,"data_size")) { txth->loop_end_sample = get_bytes_to_samples(txth, txth->data_size); } else { - if (!parse_num(txth->streamHead,txth,val, &txth->loop_end_sample)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->loop_end_sample)) goto fail; if (txth->sample_type==1) txth->loop_end_sample = get_bytes_to_samples(txth, txth->loop_end_sample); if (txth->sample_type==2) @@ -1020,7 +1041,7 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char txth->loop_end_sample += txth->loop_adjust; } else if (is_string(key,"skip_samples")) { - if (!parse_num(txth->streamHead,txth,val, &txth->skip_samples)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->skip_samples)) goto fail; txth->skip_samples_set = 1; if (txth->sample_type==1) txth->skip_samples = get_bytes_to_samples(txth, txth->skip_samples); @@ -1028,7 +1049,7 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char txth->skip_samples = get_bytes_to_samples(txth, txth->skip_samples * (txth->interleave*txth->channels)); } else if (is_string(key,"loop_adjust")) { - if (!parse_num(txth->streamHead,txth,val, &txth->loop_adjust)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->loop_adjust)) goto fail; if (txth->sample_type==1) txth->loop_adjust = get_bytes_to_samples(txth, txth->loop_adjust); if (txth->sample_type==2) @@ -1039,173 +1060,184 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char txth->loop_flag_auto = 1; } else { - if (!parse_num(txth->streamHead,txth,val, &txth->loop_flag)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->loop_flag)) goto fail; txth->loop_flag_set = 1; - if (txth->loop_behavior == 0) { + if (txth->loop_behavior == DEFAULT) { if ((txth->loop_flag == 0xFFFF || txth->loop_flag == 0xFFFFFFFF) ) txth->loop_flag = 0; - } - else if (txth->loop_behavior == 1) { + else if (txth->loop_behavior == NEGATIVE) { if (txth->loop_flag == 0xFF || txth->loop_flag == 0xFFFF || txth->loop_flag == 0xFFFFFFFF) txth->loop_flag = 1; } - else if (txth->loop_behavior == 2) { + else if (txth->loop_behavior == POSITIVE) { if (txth->loop_flag == 0xFF || txth->loop_flag == 0xFFFF || txth->loop_flag == 0xFFFFFFFF) txth->loop_flag = 0; } + else if (txth->loop_behavior == INVERTED) { + txth->loop_flag = (txth->loop_flag == 0); + } } } else if (is_string(key,"loop_behavior")) { if (is_string(val, "default")) - txth->loop_behavior = 0; + txth->loop_behavior = DEFAULT; else if (is_string(val, "negative")) - txth->loop_behavior = 1; + txth->loop_behavior = NEGATIVE; else if (is_string(val, "positive")) - txth->loop_behavior = 2; + txth->loop_behavior = POSITIVE; + else if (is_string(val, "inverted")) + txth->loop_behavior = INVERTED; else goto fail; } /* COEFS */ else if (is_string(key,"coef_offset")) { - if (!parse_num(txth->streamHead,txth,val, &txth->coef_offset)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->coef_offset)) goto fail; /* special adjustments */ txth->coef_offset += txth->base_offset; - if (txth->subsong_offset) - txth->coef_offset += txth->subsong_offset * (txth->target_subsong - 1); + if (txth->subsong_spacing) + txth->coef_offset += txth->subsong_spacing * (txth->target_subsong - 1); } else if (is_string(key,"coef_spacing")) { - if (!parse_num(txth->streamHead,txth,val, &txth->coef_spacing)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->coef_spacing)) goto fail; } else if (is_string(key,"coef_endianness")) { if (is_string(val, "BE")) txth->coef_big_endian = 1; else if (is_string(val, "LE")) txth->coef_big_endian = 0; - else if (!parse_num(txth->streamHead,txth,val, &txth->coef_big_endian)) goto fail; + else if (!parse_num(txth->sf_head,txth,val, &txth->coef_big_endian)) goto fail; } else if (is_string(key,"coef_mode")) { - if (!parse_num(txth->streamHead,txth,val, &txth->coef_mode)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->coef_mode)) goto fail; } else if (is_string(key,"coef_table")) { - if (!parse_coef_table(txth->streamHead,txth,val, txth->coef_table, sizeof(txth->coef_table))) goto fail; + if (!parse_coef_table(txth->sf_head,txth,val, txth->coef_table, sizeof(txth->coef_table))) goto fail; txth->coef_table_set = 1; } /* HIST */ else if (is_string(key,"hist_offset")) { - if (!parse_num(txth->streamHead,txth,val, &txth->hist_offset)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->hist_offset)) goto fail; txth->hist_set = 1; /* special adjustment */ txth->hist_offset += txth->hist_offset; - if (txth->subsong_offset) - txth->hist_offset += txth->subsong_offset * (txth->target_subsong - 1); + if (txth->subsong_spacing) + txth->hist_offset += txth->subsong_spacing * (txth->target_subsong - 1); } else if (is_string(key,"hist_spacing")) { - if (!parse_num(txth->streamHead,txth,val, &txth->hist_spacing)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->hist_spacing)) goto fail; } else if (is_string(key,"hist_endianness")) { if (is_string(val, "BE")) txth->hist_big_endian = 1; else if (is_string(val, "LE")) txth->hist_big_endian = 0; - else if (!parse_num(txth->streamHead,txth,val, &txth->hist_big_endian)) goto fail; + else if (!parse_num(txth->sf_head,txth,val, &txth->hist_big_endian)) goto fail; } /* SUBSONGS */ else if (is_string(key,"subsong_count")) { - if (!parse_num(txth->streamHead,txth,val, &txth->subsong_count)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->subsong_count)) goto fail; } - else if (is_string(key,"subsong_offset")) { - if (!parse_num(txth->streamHead,txth,val, &txth->subsong_offset)) goto fail; + else if (is_string(key,"subsong_spacing") || is_string(key,"subsong_offset")) { + if (!parse_num(txth->sf_head,txth,val, &txth->subsong_spacing)) goto fail; } else if (is_string(key,"name_offset")) { - if (!parse_num(txth->streamHead,txth,val, &txth->name_offset)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->name_offset)) goto fail; txth->name_offset_set = 1; /* special adjustment */ txth->name_offset += txth->base_offset; - if (txth->subsong_offset) - txth->name_offset += txth->subsong_offset * (txth->target_subsong - 1); + if (txth->subsong_spacing) + txth->name_offset += txth->subsong_spacing * (txth->target_subsong - 1); + } + else if (is_string(key,"name_offset_absolute")) { + if (!parse_num(txth->sf_head,txth,val, &txth->name_offset)) goto fail; + txth->name_offset_set = 1; + /* special adjustment */ + txth->name_offset += txth->base_offset; + /* unlike the above this is meant for reads that point to somewhere in the file, regardless subsong number */ } else if (is_string(key,"name_size")) { - if (!parse_num(txth->streamHead,txth,val, &txth->name_size)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->name_size)) goto fail; } /* SUBFILES */ else if (is_string(key,"subfile_offset")) { - if (!parse_num(txth->streamHead,txth,val, &txth->subfile_offset)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->subfile_offset)) goto fail; txth->subfile_set = 1; } else if (is_string(key,"subfile_size")) { - if (!parse_num(txth->streamHead,txth,val, &txth->subfile_size)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->subfile_size)) goto fail; txth->subfile_set = 1; } else if (is_string(key,"subfile_extension")) { - if (!parse_string(txth->streamHead,txth,val, txth->subfile_extension)) goto fail; + if (!parse_string(txth->sf_head,txth,val, txth->subfile_extension)) goto fail; txth->subfile_set = 1; } /* HEADER/BODY CONFIG */ else if (is_string(key,"header_file")) { - if (txth->streamhead_opened) { - close_streamfile(txth->streamHead); - txth->streamHead = NULL; - txth->streamhead_opened = 0; + if (txth->sf_head_opened) { + close_streamfile(txth->sf_head); + txth->sf_head = NULL; + txth->sf_head_opened = 0; } if (is_string(val,"null")) { /* reset */ if (!txth->streamfile_is_txth) { - txth->streamHead = txth->streamFile; + txth->sf_head = txth->sf; } } else if (val[0]=='*' && val[1]=='.') { /* basename + extension */ - txth->streamHead = open_streamfile_by_ext(txth->streamFile, (val+2)); - if (!txth->streamHead) goto fail; - txth->streamhead_opened = 1; + txth->sf_head = open_streamfile_by_ext(txth->sf, (val+2)); + if (!txth->sf_head) goto fail; + txth->sf_head_opened = 1; } else { /* open file */ fix_dir_separators(val); /* clean paths */ - txth->streamHead = open_streamfile_by_filename(txth->streamFile, val); - if (!txth->streamHead) goto fail; - txth->streamhead_opened = 1; + txth->sf_head = open_streamfile_by_filename(txth->sf, val); + if (!txth->sf_head) goto fail; + txth->sf_head_opened = 1; } } else if (is_string(key,"body_file")) { - if (txth->streambody_opened) { - close_streamfile(txth->streamBody); - txth->streamBody = NULL; - txth->streambody_opened = 0; + if (txth->sf_body_opened) { + close_streamfile(txth->sf_body); + txth->sf_body = NULL; + txth->sf_body_opened = 0; } if (is_string(val,"null")) { /* reset */ if (!txth->streamfile_is_txth) { - txth->streamBody = txth->streamFile; + txth->sf_body = txth->sf; } } else if (val[0]=='*' && val[1]=='.') { /* basename + extension */ - txth->streamBody = open_streamfile_by_ext(txth->streamFile, (val+2)); - if (!txth->streamBody) goto fail; - txth->streambody_opened = 1; + txth->sf_body = open_streamfile_by_ext(txth->sf, (val+2)); + if (!txth->sf_body) goto fail; + txth->sf_body_opened = 1; } else { /* open file */ fix_dir_separators(val); /* clean paths */ - txth->streamBody = open_streamfile_by_filename(txth->streamFile, val); - if (!txth->streamBody) goto fail; - txth->streambody_opened = 1; + txth->sf_body = open_streamfile_by_filename(txth->sf, val); + if (!txth->sf_body) goto fail; + txth->sf_body_opened = 1; } /* use body as header when opening a .txth directly to simplify things */ - if (txth->streamfile_is_txth && !txth->streamhead_opened) { - txth->streamHead = txth->streamBody; + if (txth->streamfile_is_txth && !txth->sf_head_opened) { + txth->sf_head = txth->sf_body; } /* re-apply */ if (!txth->data_size_set) { - txth->data_size = get_streamfile_size(txth->streamBody); + txth->data_size = get_streamfile_size(txth->sf_body); /* maybe should be manually set again? */ if (txth->data_size && txth->data_size > txth->next_offset && txth->next_offset) @@ -1219,37 +1251,37 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char /* CHUNKS */ else if (is_string(key,"chunk_number")) { - if (!parse_num(txth->streamHead,txth,val, &txth->chunk_number)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->chunk_number)) goto fail; } else if (is_string(key,"chunk_start")) { - if (!parse_num(txth->streamHead,txth,val, &txth->chunk_start)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->chunk_start)) goto fail; txth->chunk_start_set = 1; set_body_chunk(txth); } else if (is_string(key,"chunk_header_size")) { - if (!parse_num(txth->streamHead,txth,val, &txth->chunk_header_size)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->chunk_header_size)) goto fail; //txth->chunk_header_size_set = 1; //set_body_chunk(txth); /* optional and should go before chunk_size */ } else if (is_string(key,"chunk_data_size")) { - if (!parse_num(txth->streamHead,txth,val, &txth->chunk_data_size)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->chunk_data_size)) goto fail; //txth->chunk_data_size_set = 1; //set_body_chunk(txth); /* optional and should go before chunk_size */ } else if (is_string(key,"chunk_size")) { - if (!parse_num(txth->streamHead,txth,val, &txth->chunk_size)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->chunk_size)) goto fail; txth->chunk_size_set = 1; set_body_chunk(txth); } else if (is_string(key,"chunk_count")) { - if (!parse_num(txth->streamHead,txth,val, &txth->chunk_count)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->chunk_count)) goto fail; txth->chunk_count_set = 1; set_body_chunk(txth); } /* BASE OFFSET */ else if (is_string(key,"base_offset")) { - if (!parse_num(txth->streamHead,txth,val, &txth->base_offset)) goto fail; + if (!parse_num(txth->sf_head,txth,val, &txth->base_offset)) goto fail; } /* NAME TABLE */ @@ -1381,7 +1413,7 @@ static int is_string_match(const char * text, const char * pattern) { /* either all chars consumed/matched and both pos point to null, or one didn't so string didn't match */ return text[t_pos] == '\0' && pattern[p_pos] == '\0'; } -static int parse_string(STREAMFILE * streamFile, txth_header * txth, const char * val, char * str) { +static int parse_string(STREAMFILE* sf, txth_header* txth, const char * val, char * str) { int n = 0; /* read string without trailing spaces */ @@ -1390,7 +1422,7 @@ static int parse_string(STREAMFILE * streamFile, txth_header * txth, const char return n; } -static int parse_coef_table(STREAMFILE * streamFile, txth_header * txth, const char * val, uint8_t * out_value, size_t out_size) { +static int parse_coef_table(STREAMFILE* sf, txth_header* txth, const char * val, uint8_t * out_value, size_t out_size) { uint32_t byte; int done = 0; @@ -1418,15 +1450,15 @@ fail: return 0; } -static int parse_name_table(txth_header * txth, char * name_list) { - STREAMFILE *nameFile = NULL; +static int parse_name_table(txth_header* txth, char * name_list) { + STREAMFILE* nameFile = NULL; off_t txt_offset, file_size; char fullname[PATH_LIMIT]; char filename[PATH_LIMIT]; char basename[PATH_LIMIT]; /* just in case */ - if (!txth->streamText || !txth->streamBody) + if (!txth->sf_text || !txth->sf_body) goto fail; /* trim name_list just in case */ @@ -1443,12 +1475,12 @@ static int parse_name_table(txth_header * txth, char * name_list) { //;VGM_LOG("TXTH: name_list='%s'\n", name_list); /* open companion file near .txth */ - nameFile = open_streamfile_by_filename(txth->streamText, name_list); + nameFile = open_streamfile_by_filename(txth->sf_text, name_list); if (!nameFile) goto fail; - get_streamfile_name(txth->streamBody, fullname, sizeof(filename)); - get_streamfile_filename(txth->streamBody, filename, sizeof(filename)); - get_streamfile_basename(txth->streamBody, basename, sizeof(basename)); + get_streamfile_name(txth->sf_body, fullname, sizeof(filename)); + get_streamfile_filename(txth->sf_body, filename, sizeof(filename)); + get_streamfile_basename(txth->sf_body, basename, sizeof(basename)); //;VGM_LOG("TXTH: names full=%s, file=%s, base=%s\n", fullname, filename, basename); txt_offset = 0x00; @@ -1509,7 +1541,7 @@ static int parse_name_table(txth_header * txth, char * name_list) { if (current[0] == ',') current++; - if (!parse_num(txth->streamHead,txth,subval, &txth->name_values[txth->name_values_count])) goto fail; + if (!parse_num(txth->sf_head,txth,subval, &txth->name_values[txth->name_values_count])) goto fail; txth->name_values_count++; if (txth->name_values_count >= 16) /* surely nobody needs that many */ goto fail; @@ -1530,13 +1562,13 @@ fail: } -static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * val, uint32_t * out_value) { +static int parse_num(STREAMFILE* sf, txth_header* txth, const char * val, uint32_t * out_value) { /* out_value can be these, save before modifying */ uint32_t value_mul = txth->value_mul; uint32_t value_div = txth->value_div; uint32_t value_add = txth->value_add; uint32_t value_sub = txth->value_sub; - uint32_t subsong_offset = txth->subsong_offset; + uint32_t subsong_spacing = txth->subsong_spacing; char op = ' '; int brackets = 0; @@ -1576,7 +1608,7 @@ static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * v int hex = (val[1]=='0' && val[2]=='x'); /* can happen when loading .txth and not setting body/head */ - if (!streamFile) + if (!sf) goto fail; /* read exactly N fields in the expected format */ @@ -1593,7 +1625,7 @@ static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * v /* adjust offset */ offset += txth->base_offset; - if (/*offset < 0 ||*/ offset > get_streamfile_size(streamFile)) + if (/*offset < 0 ||*/ offset > get_streamfile_size(sf)) goto fail; if (ed1 == 'B' && ed2 == 'E') @@ -1601,14 +1633,14 @@ static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * v else if (!(ed1 == 'L' && ed2 == 'E')) goto fail; - if (subsong_offset) - offset = offset + subsong_offset * (txth->target_subsong - 1); + if (subsong_spacing) + offset = offset + subsong_spacing * (txth->target_subsong - 1); switch(size) { - case 1: value = (uint8_t)read_8bit(offset,streamFile); break; - case 2: value = big_endian ? (uint16_t)read_16bitBE(offset,streamFile) : (uint16_t)read_16bitLE(offset,streamFile); break; - case 3: value = (big_endian ? (uint32_t)read_32bitBE(offset,streamFile) : (uint32_t)read_32bitLE(offset,streamFile)) & 0x00FFFFFF; break; - case 4: value = big_endian ? (uint32_t)read_32bitBE(offset,streamFile) : (uint32_t)read_32bitLE(offset,streamFile); break; + case 1: value = (uint8_t)read_8bit(offset,sf); break; + case 2: value = big_endian ? (uint16_t)read_16bitBE(offset,sf) : (uint16_t)read_16bitLE(offset,sf); break; + case 3: value = (big_endian ? (uint32_t)read_32bitBE(offset,sf) : (uint32_t)read_32bitLE(offset,sf)) & 0x00FFFFFF; break; + case 4: value = big_endian ? (uint32_t)read_32bitBE(offset,sf) : (uint32_t)read_32bitLE(offset,sf); break; default: goto fail; } value_read = 1; @@ -1629,9 +1661,12 @@ static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * v else if ((n = is_string_field(val,"data_size"))) value = txth->data_size; else if ((n = is_string_field(val,"num_samples"))) value = txth->num_samples; else if ((n = is_string_field(val,"loop_start_sample"))) value = txth->loop_start_sample; + else if ((n = is_string_field(val,"loop_start"))) value = txth->loop_start_sample; else if ((n = is_string_field(val,"loop_end_sample"))) value = txth->loop_end_sample; + else if ((n = is_string_field(val,"loop_end"))) value = txth->loop_end_sample; else if ((n = is_string_field(val,"subsong_count"))) value = txth->subsong_count; - else if ((n = is_string_field(val,"subsong_offset"))) value = txth->subsong_offset; + else if ((n = is_string_field(val,"subsong_spacing"))) value = txth->subsong_spacing; + else if ((n = is_string_field(val,"subsong_offset"))) value = txth->subsong_spacing; else if ((n = is_string_field(val,"subfile_offset"))) value = txth->subfile_offset; else if ((n = is_string_field(val,"subfile_size"))) value = txth->subfile_size; else if ((n = is_string_field(val,"base_offset"))) value = txth->base_offset; @@ -1702,7 +1737,7 @@ fail: return 0; } -static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) { +static int get_bytes_to_samples(txth_header* txth, uint32_t bytes) { switch(txth->codec) { case MS_IMA: return ms_ima_bytes_to_samples(bytes, txth->interleave, txth->channels); @@ -1731,15 +1766,17 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) { case ATRAC3PLUS: return atrac3plus_bytes_to_samples(bytes, txth->interleave); case AAC: - return aac_get_samples(txth->streamBody, txth->start_offset, bytes); + return aac_get_samples(txth->sf_body, txth->start_offset, bytes); #ifdef VGM_USE_MPEG case MPEG: - return mpeg_get_samples(txth->streamBody, txth->start_offset, bytes); + return mpeg_get_samples(txth->sf_body, txth->start_offset, bytes); #endif case AC3: return ac3_bytes_to_samples(bytes, txth->interleave, txth->channels); case ASF: return asf_bytes_to_samples(bytes, txth->channels); + case EAXA: + return ea_xa_bytes_to_samples(bytes, txth->channels); /* XMA bytes-to-samples is done at the end as the value meanings are a bit different */ case XMA1: @@ -1770,13 +1807,13 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) { } } -static int get_padding_size(txth_header * txth, int discard_empty) { +static int get_padding_size(txth_header* txth, int discard_empty) { if (txth->data_size == 0 || txth->channels == 0) return 0; switch(txth->codec) { case PSX: - return ps_find_padding(txth->streamBody, txth->start_offset, txth->data_size, txth->channels, txth->interleave, discard_empty); + return ps_find_padding(txth->sf_body, txth->start_offset, txth->data_size, txth->channels, txth->interleave, discard_empty); default: return 0; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c index e00a09f90..49a4860d4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c @@ -2,12 +2,15 @@ #include "../coding/coding.h" #include "../layout/layout.h" #include "../mixing.h" +#include "../plugins.h" #define TXTP_LINE_MAX 1024 #define TXTP_MIXING_MAX 512 #define TXTP_GROUP_MODE_SEGMENTED 'S' #define TXTP_GROUP_MODE_LAYERED 'L' +#define TXTP_GROUP_MODE_RANDOM 'R' +#define TXTP_GROUP_RANDOM_ALL '-' #define TXTP_GROUP_REPEAT 'R' #define TXTP_POSITION_LOOPS 'L' @@ -62,25 +65,21 @@ typedef struct { typedef struct { + /* main entry */ char filename[TXTP_LINE_MAX]; + int silent; + /* TXTP settings (applied at the end) */ int range_start; int range_end; int subsong; uint32_t channel_mask; + int mixing_count; txtp_mix_data mixing[TXTP_MIXING_MAX]; - int config_loop_count_set; - double config_loop_count; - int config_fade_time_set; - double config_fade_time; - int config_fade_delay_set; - double config_fade_delay; - int config_ignore_loop; - int config_force_loop; - int config_ignore_fade; + play_config_t config; int sample_rate; @@ -90,6 +89,9 @@ typedef struct { int32_t loop_start_sample; double loop_end_second; int32_t loop_end_sample; + /* flags */ + int loop_anchor_start; + int loop_anchor_end; int trim_set; double trim_second; @@ -103,21 +105,23 @@ typedef struct { char type; int count; char repeat; + int selected; - txtp_entry group_config; + txtp_entry group_settings; } txtp_group; typedef struct { - txtp_entry *entry; + txtp_entry* entry; size_t entry_count; size_t entry_max; - txtp_group *group; + txtp_group* group; size_t group_count; size_t group_max; + int group_pos; /* entry counter for groups */ - VGMSTREAM* *vgmstream; + VGMSTREAM** vgmstream; size_t vgmstream_count; uint32_t loop_start_segment; @@ -133,128 +137,36 @@ typedef struct { int is_single; } txtp_header; -static txtp_header* parse_txtp(STREAMFILE* streamFile); +static txtp_header* parse_txtp(STREAMFILE* sf); +static int parse_entries(txtp_header* txtp, STREAMFILE* sf); +static int parse_groups(txtp_header* txtp); static void clean_txtp(txtp_header* txtp, int fail); -static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current); +static void apply_settings(VGMSTREAM* vgmstream, txtp_entry* current); void add_mixing(txtp_entry* cfg, txtp_mix_data* mix, txtp_mix_t command); -static int make_group_segment(txtp_header* txtp, int from, int count); -static int make_group_layer(txtp_header* txtp, int from, int count); - /* TXTP - an artificial playlist-like format to play files with segments/layers/config */ -VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) { - VGMSTREAM *vgmstream = NULL; +VGMSTREAM* init_vgmstream_txtp(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; txtp_header* txtp = NULL; - int i; + int ok; /* checks */ - if (!check_extensions(streamFile, "txtp")) + if (!check_extensions(sf, "txtp")) goto fail; - /* read .txtp with all files and config */ - txtp = parse_txtp(streamFile); + /* read .txtp with all files and settings */ + txtp = parse_txtp(sf); if (!txtp) goto fail; - /* post-process */ - { - if (txtp->entry_count == 0) - goto fail; + /* process files in the .txtp */ + ok = parse_entries(txtp, sf); + if (!ok) goto fail; - txtp->vgmstream = calloc(txtp->entry_count, sizeof(VGMSTREAM*)); - if (!txtp->vgmstream) goto fail; - - txtp->vgmstream_count = txtp->entry_count; - } - - - /* detect single files before grouping */ - if (txtp->group_count == 0 && txtp->vgmstream_count == 1) { - txtp->is_single = 1; - txtp->is_segmented = 0; - txtp->is_layered = 0; - } - - - /* open all entry files first as they'll be modified by modes */ - for (i = 0; i < txtp->vgmstream_count; i++) { - STREAMFILE* temp_streamFile = open_streamfile_by_filename(streamFile, txtp->entry[i].filename); - if (!temp_streamFile) { - VGM_LOG("TXTP: cannot open streamfile for %s\n", txtp->entry[i].filename); - goto fail; - } - temp_streamFile->stream_index = txtp->entry[i].subsong; - - txtp->vgmstream[i] = init_vgmstream_from_STREAMFILE(temp_streamFile); - close_streamfile(temp_streamFile); - if (!txtp->vgmstream[i]) { - VGM_LOG("TXTP: cannot open vgmstream for %s#%i\n", txtp->entry[i].filename, txtp->entry[i].subsong); - goto fail; - } - - apply_config(txtp->vgmstream[i], &txtp->entry[i]); - } - - - /* group files as needed */ - for (i = 0; i < txtp->group_count; i++) { - txtp_group *grp = &txtp->group[i]; - int pos, groups; - - //;VGM_LOG("TXTP: apply group %i%c%i%c\n",txtp->group[i].position,txtp->group[i].type,txtp->group[i].count,txtp->group[i].repeat); - - /* special meaning of "all files" */ - if (grp->position < 0 || grp->position >= txtp->vgmstream_count) - grp->position = 0; - if (grp->count <= 0) - grp->count = txtp->vgmstream_count - grp->position; - - /* repeats N groups (trailing files are not grouped) */ - if (grp->repeat == TXTP_GROUP_REPEAT) { - groups = ((txtp->vgmstream_count - grp->position) / grp->count); - } - else { - groups = 1; - } - - /* as groups are compacted position goes 1 by 1 */ - for (pos = grp->position; pos < grp->position + groups; pos++) { - //;VGM_LOG("TXTP: group=%i, count=%i, groups=%i\n", pos, grp->count, groups); - switch(grp->type) { - case TXTP_GROUP_MODE_LAYERED: - if (!make_group_layer(txtp, pos, grp->count)) - goto fail; - break; - case TXTP_GROUP_MODE_SEGMENTED: - if (!make_group_segment(txtp, pos, grp->count)) - goto fail; - break; - default: - goto fail; - } - } - - /* group may also have config (like downmixing) */ - apply_config(txtp->vgmstream[grp->position], &grp->group_config); - } - - /* final tweaks (should be integrated with the above?) */ - if (txtp->is_layered) { - if (!make_group_layer(txtp, 0, txtp->vgmstream_count)) - goto fail; - } - if (txtp->is_segmented) { - if (!make_group_segment(txtp, 0, txtp->vgmstream_count)) - goto fail; - } - if (txtp->is_single) { - /* special case of setting start_segment to force/overwrite looping - * (better to use #E but left for compatibility with older TXTPs) */ - if (txtp->loop_start_segment == 1 && !txtp->loop_end_segment) { - vgmstream_force_loop(txtp->vgmstream[0], 1, txtp->vgmstream[0]->loop_start_sample, txtp->vgmstream[0]->num_samples); - } - } + /* group files into layouts */ + ok = parse_groups(txtp); + if (!ok) goto fail; /* may happen if using mixed mode but some files weren't grouped */ @@ -263,14 +175,13 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) { goto fail; } - /* apply default config to the resulting file */ - if (txtp->default_entry_set) { - apply_config(txtp->vgmstream[0], &txtp->default_entry); - } - - + /* should result in a final, single vgmstream possibly containing multiple vgmstreams */ vgmstream = txtp->vgmstream[0]; + /* flags for title config */ + vgmstream->config.is_txtp = 1; + vgmstream->config.is_mini_txtp = (get_streamfile_size(sf) == 0); + clean_txtp(txtp, 0); return vgmstream; @@ -279,6 +190,125 @@ fail: return NULL; } +static void clean_txtp(txtp_header* txtp, int fail) { + int i, start; + + if (!txtp) + return; + + /* returns first vgmstream on success so it's not closed */ + start = fail ? 0 : 1; + + for (i = start; i < txtp->vgmstream_count; i++) { + close_vgmstream(txtp->vgmstream[i]); + } + + free(txtp->vgmstream); + free(txtp->group); + free(txtp->entry); + free(txtp); +} + +//todo fragment parser later + +/*******************************************************************************/ +/* ENTRIES */ +/*******************************************************************************/ + +static int parse_silents(txtp_header* txtp) { + int i; + int channels = 0; + int sample_rate = 0; + int32_t num_samples = 0; + + /* silents use same channels as close files */ + for (i = 0; i < txtp->vgmstream_count; i++) { + if (!txtp->entry[i].silent) { + channels = txtp->vgmstream[i]->channels; + sample_rate = txtp->vgmstream[i]->sample_rate; + break; + } + } + + /* actually open silents */ + for (i = 0; i < txtp->vgmstream_count; i++) { + if (!txtp->entry[i].silent) + continue; + + txtp->vgmstream[i] = init_vgmstream_silence(channels, sample_rate, num_samples); + if (!txtp->vgmstream[i]) goto fail; + + apply_settings(txtp->vgmstream[i], &txtp->entry[i]); + } + + return 1; +fail: + return 0; +} + +static int is_silent(txtp_entry* entry) { + /* should also contain "." in the filename for commands with seconds ("1.0") to work */ + return entry->filename[0] == '?'; +} + +/* open all entries and apply settings to resulting VGMSTREAMs */ +static int parse_entries(txtp_header* txtp, STREAMFILE* sf) { + int i; + int has_silents = 0; + + + if (txtp->entry_count == 0) + goto fail; + + txtp->vgmstream = calloc(txtp->entry_count, sizeof(VGMSTREAM*)); + if (!txtp->vgmstream) goto fail; + + txtp->vgmstream_count = txtp->entry_count; + + + /* open all entry files first as they'll be modified by modes */ + for (i = 0; i < txtp->vgmstream_count; i++) { + STREAMFILE* temp_sf = NULL; + + /* silent entry ignore */ + if (is_silent(&txtp->entry[i])) { + txtp->entry[i].silent = 1; + has_silents = 1; + continue; + } + + temp_sf = open_streamfile_by_filename(sf, txtp->entry[i].filename); + if (!temp_sf) { + VGM_LOG("TXTP: cannot open streamfile for %s\n", txtp->entry[i].filename); + goto fail; + } + temp_sf->stream_index = txtp->entry[i].subsong; + + txtp->vgmstream[i] = init_vgmstream_from_STREAMFILE(temp_sf); + close_streamfile(temp_sf); + if (!txtp->vgmstream[i]) { + VGM_LOG("TXTP: cannot open vgmstream for %s#%i\n", txtp->entry[i].filename, txtp->entry[i].subsong); + goto fail; + } + + apply_settings(txtp->vgmstream[i], &txtp->entry[i]); + } + + if (has_silents) { + if (!parse_silents(txtp)) + goto fail; + } + + return 1; +fail: + return 0; +} + + +/*******************************************************************************/ +/* GROUPS */ +/*******************************************************************************/ + static void update_vgmstream_list(VGMSTREAM* vgmstream, txtp_header* txtp, int position, int count) { int i; @@ -289,6 +319,7 @@ static void update_vgmstream_list(VGMSTREAM* vgmstream, txtp_header* txtp, int p for (i = position + count; i < txtp->vgmstream_count; i++) { //;VGM_LOG("TXTP: copy %i to %i\n", i, i + 1 - count); txtp->vgmstream[i + 1 - count] = txtp->vgmstream[i]; + txtp->entry[i + 1 - count] = txtp->entry[i]; /* memcpy old settings for other groups */ } /* list can only become smaller, no need to alloc/free/etc */ @@ -296,14 +327,43 @@ static void update_vgmstream_list(VGMSTREAM* vgmstream, txtp_header* txtp, int p //;VGM_LOG("TXTP: compact vgmstreams=%i\n", txtp->vgmstream_count); } -static int make_group_segment(txtp_header* txtp, int position, int count) { - VGMSTREAM * vgmstream = NULL; +static int find_loop_anchors(txtp_header* txtp, int position, int count, int* p_loop_start, int* p_loop_end) { + int loop_start = 0, loop_end = 0; + int i, j; + + //;VGM_LOG("TXTP: find loop anchors from %i to %i\n", position, count); + + for (i = position, j = 0; i < position + count; i++, j++) { + if (txtp->entry[i].loop_anchor_start) { + loop_start = j + 1; /* logic elsewhere also uses +1 */ + } + if (txtp->entry[i].loop_anchor_end) { + loop_end = j + 1; + } + } + + if (loop_start) { + if (!loop_end) + loop_end = count; + *p_loop_start = loop_start; + *p_loop_end = loop_end; + //;VGM_LOG("TXTP: loop anchors %i, %i\n", loop_start, loop_end); + return 1; + } + + return 0; +} + +static int make_group_segment(txtp_header* txtp, int is_group, int position, int count) { + VGMSTREAM* vgmstream = NULL; segmented_layout_data *data_s = NULL; int i, loop_flag = 0; + int loop_start = 0, loop_end = 0; - if (count == 1) { /* nothing to do */ - //;VGM_LOG("TXTP: ignored segments of 1\n"); + /* allowed for actual groups (not final "mode"), otherwise skip to optimize */ + if (!is_group && count == 1) { + //;VGM_LOG("TXTP: ignored single group\n"); return 1; } @@ -312,16 +372,25 @@ static int make_group_segment(txtp_header* txtp, int position, int count) { return 1; } - /* loop settings only make sense if this group becomes final vgmstream */ - if (position == 0 && txtp->vgmstream_count == count) { - if (txtp->loop_start_segment && !txtp->loop_end_segment) { - txtp->loop_end_segment = count; + + /* set loops with "anchors" (this allows loop config inside groups, not just in the final group, + * which is sometimes useful when paired with random/selectable groups or loop times) */ + if (find_loop_anchors(txtp, position, count, &loop_start, &loop_end)) { + loop_flag = (loop_start > 0 && loop_start <= count); + } + /* loop segment settings only make sense if this group becomes final vgmstream */ + else if (position == 0 && txtp->vgmstream_count == count) { + loop_start = txtp->loop_start_segment; + loop_end = txtp->loop_end_segment; + + if (loop_start && !loop_end) { + loop_end = count; } else if (txtp->is_loop_auto) { /* auto set to last segment */ - txtp->loop_start_segment = count; - txtp->loop_end_segment = count; + loop_start = count; + loop_end = count; } - loop_flag = (txtp->loop_start_segment > 0 && txtp->loop_start_segment <= count); + loop_flag = (loop_start > 0 && loop_start <= count); } @@ -340,11 +409,11 @@ static int make_group_segment(txtp_header* txtp, int position, int count) { goto fail; /* build the layout VGMSTREAM */ - vgmstream = allocate_segmented_vgmstream(data_s,loop_flag, txtp->loop_start_segment - 1, txtp->loop_end_segment - 1); + vgmstream = allocate_segmented_vgmstream(data_s, loop_flag, loop_start - 1, loop_end - 1); if (!vgmstream) goto fail; /* custom meta name if all parts don't match */ - for (i = 0; i < data_s->segment_count; i++) { + for (i = 0; i < count; i++) { if (vgmstream->meta_type != data_s->segments[i]->meta_type) { vgmstream->meta_type = meta_TXTP; break; @@ -354,14 +423,14 @@ static int make_group_segment(txtp_header* txtp, int position, int count) { /* fix loop keep */ if (loop_flag && txtp->is_loop_keep) { int32_t current_samples = 0; - for (i = 0; i < data_s->segment_count; i++) { - if (txtp->loop_start_segment == i+1 /*&& data_s->segments[i]->loop_start_sample*/) { + for (i = 0; i < count; i++) { + if (loop_start == i+1 /*&& data_s->segments[i]->loop_start_sample*/) { vgmstream->loop_start_sample = current_samples + data_s->segments[i]->loop_start_sample; } current_samples += data_s->segments[i]->num_samples; - if (txtp->loop_end_segment == i+1 && data_s->segments[i]->loop_end_sample) { + if (loop_end == i+1 && data_s->segments[i]->loop_end_sample) { vgmstream->loop_end_sample = current_samples - data_s->segments[i]->num_samples + data_s->segments[i]->loop_end_sample; } } @@ -379,14 +448,15 @@ fail: return 0; } -static int make_group_layer(txtp_header* txtp, int position, int count) { - VGMSTREAM * vgmstream = NULL; - layered_layout_data * data_l = NULL; +static int make_group_layer(txtp_header* txtp, int is_group, int position, int count) { + VGMSTREAM* vgmstream = NULL; + layered_layout_data* data_l = NULL; int i; - if (count == 1) { /* nothing to do */ - //;VGM_LOG("TXTP: ignored layer of 1\n"); + /* allowed for actual groups (not final mode), otherwise skip to optimize */ + if (!is_group && count == 1) { + //;VGM_LOG("TXTP: ignored single group\n"); return 1; } @@ -422,6 +492,12 @@ static int make_group_layer(txtp_header* txtp, int position, int count) { } } + /* loop settings only make sense if this group becomes final vgmstream */ + if (position == 0 && txtp->vgmstream_count == count) { + if (txtp->is_loop_auto && !vgmstream->loop_flag) { + vgmstream_force_loop(vgmstream, 1, 0, vgmstream->num_samples); + } + } /* set new vgmstream and reorder positions */ update_vgmstream_list(vgmstream, txtp, position, count); @@ -434,24 +510,206 @@ fail: return 0; } +static int make_group_random(txtp_header* txtp, int is_group, int position, int count, int selected) { + VGMSTREAM* vgmstream = NULL; + int i; -static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) { + /* allowed for actual groups (not final mode), otherwise skip to optimize */ + if (!is_group && count == 1) { + //;VGM_LOG("TXTP: ignored single group\n"); + return 1; + } - if (current->config_loop_count_set) - vgmstream->config_loop_count = current->config_loop_count; - if (current->config_fade_time_set) - vgmstream->config_fade_time = current->config_fade_time; - if (current->config_fade_delay_set) - vgmstream->config_fade_delay = current->config_fade_delay; - if (current->config_ignore_loop) - vgmstream->config_ignore_loop = current->config_ignore_loop; - if (current->config_force_loop) - vgmstream->config_force_loop = current->config_force_loop; - if (current->config_ignore_fade) - vgmstream->config_ignore_fade = current->config_ignore_fade; + if (position + count > txtp->vgmstream_count || position < 0 || count < 0) { + VGM_LOG("TXTP: ignored random position=%i, count=%i, entries=%i\n", position, count, txtp->vgmstream_count); + return 1; + } - if (current->sample_rate > 0) + /* special case meaning "play all", basically for quick testing */ + if (selected == count) { + return make_group_segment(txtp, is_group, position, count); + } + + /* 0=actually random for fun and testing, but undocumented since random music is kinda weird, may change anytime + * (plus foobar caches song duration unless .txtp is modifies, so it can get strange if randoms are too different) */ + if (selected < 0) { + static int random_seed = 0; + srand((unsigned)txtp + random_seed++); /* whatevs */ + selected = (rand() % count); /* 0..count-1 */ + //;VGM_LOG("TXTP: autoselected random %i\n", selected); + } + + if (selected < 0 || selected >= count) { + goto fail; + } + + /* get selected and remove non-selected */ + vgmstream = txtp->vgmstream[position + selected]; + txtp->vgmstream[position + selected] = NULL; + for (i = 0; i < count; i++) { + close_vgmstream(txtp->vgmstream[i + position]); + } + + /* set new vgmstream and reorder positions */ + update_vgmstream_list(vgmstream, txtp, position, count); + + return 1; +fail: + close_vgmstream(vgmstream); + return 0; +} + +static int parse_groups(txtp_header* txtp) { + int i; + + /* detect single files before grouping */ + if (txtp->group_count == 0 && txtp->vgmstream_count == 1) { + txtp->is_single = 1; + txtp->is_segmented = 0; + txtp->is_layered = 0; + } + + /* group files as needed */ + for (i = 0; i < txtp->group_count; i++) { + txtp_group *grp = &txtp->group[i]; + int pos, groups; + + //;VGM_LOG("TXTP: apply group %i%c%i%c\n",txtp->group[i].position,txtp->group[i].type,txtp->group[i].count,txtp->group[i].repeat); + + /* special meaning of "all files" */ + if (grp->position < 0 || grp->position >= txtp->vgmstream_count) + grp->position = 0; + if (grp->count <= 0) + grp->count = txtp->vgmstream_count - grp->position; + + /* repeats N groups (trailing files are not grouped) */ + if (grp->repeat == TXTP_GROUP_REPEAT) { + groups = ((txtp->vgmstream_count - grp->position) / grp->count); + } + else { + groups = 1; + } + + /* as groups are compacted position goes 1 by 1 */ + for (pos = grp->position; pos < grp->position + groups; pos++) { + //;VGM_LOG("TXTP: group=%i, count=%i, groups=%i\n", pos, grp->count, groups); + switch(grp->type) { + case TXTP_GROUP_MODE_LAYERED: + if (!make_group_layer(txtp, 1, pos, grp->count)) + goto fail; + break; + case TXTP_GROUP_MODE_SEGMENTED: + if (!make_group_segment(txtp, 1, pos, grp->count)) + goto fail; + break; + case TXTP_GROUP_MODE_RANDOM: + if (!make_group_random(txtp, 1, pos, grp->count, grp->selected)) + goto fail; + break; + default: + goto fail; + } + } + + /* group may also have settings (like downmixing) */ + apply_settings(txtp->vgmstream[grp->position], &grp->group_settings); + txtp->entry[grp->position] = grp->group_settings; /* memcpy old settings for subgroups */ + } + + /* final tweaks (should be integrated with the above?) */ + if (txtp->is_layered) { + if (!make_group_layer(txtp, 0, 0, txtp->vgmstream_count)) + goto fail; + } + if (txtp->is_segmented) { + if (!make_group_segment(txtp, 0, 0, txtp->vgmstream_count)) + goto fail; + } + if (txtp->is_single) { + /* special case of setting start_segment to force/overwrite looping + * (better to use #E but left for compatibility with older TXTPs) */ + if (txtp->loop_start_segment == 1 && !txtp->loop_end_segment) { + vgmstream_force_loop(txtp->vgmstream[0], 1, txtp->vgmstream[0]->loop_start_sample, txtp->vgmstream[0]->num_samples); + } + } + + /* apply default settings to the resulting file */ + if (txtp->default_entry_set) { + apply_settings(txtp->vgmstream[0], &txtp->default_entry); + } + + return 1; +fail: + return 0; +} + + +/*******************************************************************************/ +/* CONFIG */ +/*******************************************************************************/ + +static void copy_flag(int* dst_flag, int* src_flag) { + if (!*src_flag) + return; + *dst_flag = 1; +} + +static void copy_secs(int* dst_flag, double* dst_secs, int* src_flag, double* src_secs) { + if (!*src_flag) + return; + *dst_flag = 1; + *dst_secs = *src_secs; +} + +static void copy_time(int* dst_flag, int32_t* dst_time, double* dst_time_s, int* src_flag, int32_t* src_time, double* src_time_s) { + if (!*src_flag) + return; + *dst_flag = 1; + *dst_time = *src_time; + *dst_time_s = *src_time_s; +} + +static void copy_config(play_config_t* dst, play_config_t* src) { + if (!src->config_set) + return; + + dst->config_set = 1; + copy_flag(&dst->play_forever, &src->play_forever); + copy_flag(&dst->ignore_fade, &src->ignore_fade); + copy_flag(&dst->force_loop, &src->force_loop); + copy_flag(&dst->really_force_loop, &src->really_force_loop); + copy_flag(&dst->ignore_loop, &src->ignore_loop); + copy_secs(&dst->loop_count_set, &dst->loop_count, &src->loop_count_set, &src->loop_count); + copy_secs(&dst->fade_time_set, &dst->fade_time, &src->fade_time_set, &src->fade_time); + copy_secs(&dst->fade_delay_set, &dst->fade_delay, &src->fade_delay_set, &src->fade_delay); + copy_time(&dst->pad_begin_set, &dst->pad_begin, &dst->pad_begin_s, &src->pad_begin_set, &src->pad_begin, &src->pad_begin_s); + copy_time(&dst->pad_end_set, &dst->pad_end, &dst->pad_end_s, &src->pad_end_set, &src->pad_end, &src->pad_end_s); + copy_time(&dst->trim_begin_set, &dst->trim_begin, &dst->trim_begin_s, &src->trim_begin_set, &src->trim_begin, &src->trim_begin_s); + copy_time(&dst->trim_end_set, &dst->trim_end, &dst->trim_end_s, &src->trim_end_set, &src->trim_end, &src->trim_end_s); + copy_time(&dst->body_time_set, &dst->body_time, &dst->body_time_s, &src->body_time_set, &src->body_time, &src->body_time_s); +} + +#if 0 +static void init_config(VGMSTREAM* vgmstream) { + play_config_t* cfg = &vgmstream->config; + + //todo only on segmented/layered? + if (cfg->play_forever + cfg->loop_count_set || cfg->fade_time_set || cfg->fade_delay_set || + cfg->pad_begin_set || cfg->pad_end_set || cfg->trim_begin_set || cfg->trim_end_set || + cfg->body_time_set) { + VGM_LOG("setup!\n"); + + } +} +#endif + +static void apply_settings(VGMSTREAM* vgmstream, txtp_entry* current) { + + /* base settings */ + if (current->sample_rate > 0) { vgmstream->sample_rate = current->sample_rate; + } if (current->loop_install_set) { if (current->loop_start_second > 0 || current->loop_end_second > 0) { @@ -471,14 +729,15 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) { if (current->trim_set) { if (current->trim_second != 0.0) { - current->trim_sample = current->trim_second * vgmstream->sample_rate; + /* trim sample can become 0 here when second is too small (rounded) */ + current->trim_sample = (double)current->trim_second * (double)vgmstream->sample_rate; } if (current->trim_sample < 0) { vgmstream->num_samples += current->trim_sample; /* trim from end (add negative) */ } - else if (vgmstream->num_samples > current->trim_sample) { - vgmstream->num_samples = current->trim_sample; /* trim to value */ + else if (current->trim_sample > 0 && vgmstream->num_samples > current->trim_sample) { + vgmstream->num_samples = current->trim_sample; /* trim to value >0 */ } /* readjust after triming if it went over (could check for more edge cases but eh) */ @@ -559,30 +818,19 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) { } } } + + + /* default play config (last after sample rate mods/mixing/etc) */ + copy_config(&vgmstream->config, ¤t->config); + setup_state_vgmstream(vgmstream); + /* config is enabled in layouts or externally (for compatibility, since we don't know yet if this + * VGMSTREAM will part of a layout, or is enabled externally to not mess up plugins's calcs) */ } -/* ********************************** */ - -static void clean_filename(char * filename) { - int i; - size_t len; - - if (filename[0] == '\0') - return; - - /* normalize paths */ - fix_dir_separators(filename); - - /* remove trailing spaces */ - len = strlen(filename); - for (i = len-1; i > 0; i--) { - if (filename[i] != ' ') - break; - filename[i] = '\0'; - } - -} +/*******************************************************************************/ +/* PARSER - HELPERS */ +/*******************************************************************************/ /* sscanf 101: "matches = sscanf(string-from, string-commands, parameters...)" * - reads linearly and matches "%" commands to input parameters as found @@ -598,13 +846,13 @@ static void clean_filename(char * filename) { * - %n: special match (not counted in return value), chars consumed until that point (can appear and be set multiple times) */ -static int get_double(const char * config, double *value, int *is_set) { +static int get_double(const char* params, double *value, int *is_set) { int n, m; double temp; if (is_set) *is_set = 0; - m = sscanf(config, " %lf%n", &temp,&n); + m = sscanf(params, " %lf%n", &temp,&n); if (m != 1 || temp < 0) return 0; @@ -613,11 +861,11 @@ static int get_double(const char * config, double *value, int *is_set) { return n; } -static int get_int(const char * config, int *value) { +static int get_int(const char* params, int *value) { int n,m; int temp; - m = sscanf(config, " %d%n", &temp,&n); + m = sscanf(params, " %d%n", &temp,&n); if (m != 1 || temp < 0) return 0; @@ -625,13 +873,13 @@ static int get_int(const char * config, int *value) { return n; } -static int get_position(const char * config, double *value_f, char *value_type) { +static int get_position(const char* params, double* value_f, char* value_type) { int n,m; double temp_f; char temp_c; /* test if format is position: N.n(type) */ - m = sscanf(config, " %lf%c%n", &temp_f,&temp_c,&n); + m = sscanf(params, " %lf%c%n", &temp_f,&temp_c,&n); if (m != 2 || temp_f < 0.0) return 0; /* test accepted chars as it will capture anything */ @@ -643,17 +891,55 @@ static int get_position(const char * config, double *value_f, char *value_type) return n; } +static int get_volume(const char* params, double *value, int *is_set) { + int n, m; + double temp_f; + char temp_c1, temp_c2; -static int get_time(const char * config, double *value_f, int32_t *value_i) { + if (is_set) *is_set = 0; + + /* test if format is NdB (decibels) */ + m = sscanf(params, " %lf%c%c%n", &temp_f, &temp_c1, &temp_c2, &n); + if (m == 3 && temp_c1 == 'd' && (temp_c2 == 'B' || temp_c2 == 'b')) { + /* dB 101: + * - logaritmic scale + * - dB = 20 * log(percent / 100) + * - percent = pow(10, dB / 20)) * 100 + * - for audio: 100% = 0dB (base max volume of current file = reference dB) + * - negative dB decreases volume, positive dB increases + * ex. + * 200% = 20 * log(200 / 100) = +6.02059991328 dB + * 50% = 20 * log( 50 / 100) = -6.02059991328 dB + * 6dB = pow(10, 6 / 20) * 100 = +195.26231497 % + * -6dB = pow(10, -6 / 20) * 100 = +50.50118723362 % + */ + + if (is_set) *is_set = 1; + *value = pow(10, temp_f / 20.0); /* dB to % where 1.0 = max */ + return n; + } + + /* test if format is N.N (percent) */ + m = sscanf(params, " %lf%n", &temp_f, &n); + if (m == 1) { + if (is_set) *is_set = 1; + *value = temp_f; + return n; + } + + return 0; +} + +static int get_time(const char* params, double* value_f, int32_t* value_i) { int n,m; int temp_i1, temp_i2; double temp_f1, temp_f2; char temp_c; /* test if format is hour: N:N(.n) or N_N(.n) */ - m = sscanf(config, " %d%c%d%n", &temp_i1,&temp_c,&temp_i2,&n); + m = sscanf(params, " %d%c%d%n", &temp_i1,&temp_c,&temp_i2,&n); if (m == 3 && (temp_c == ':' || temp_c == '_')) { - m = sscanf(config, " %lf%c%lf%n", &temp_f1,&temp_c,&temp_f2,&n); + m = sscanf(params, " %lf%c%lf%n", &temp_f1,&temp_c,&temp_f2,&n); if (m != 3 || /*temp_f1 < 0.0 ||*/ temp_f1 >= 60.0 || temp_f2 < 0.0 || temp_f2 >= 60.0) return 0; @@ -662,9 +948,9 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) { } /* test if format is seconds: N.n */ - m = sscanf(config, " %d.%d%n", &temp_i1,&temp_i2,&n); + m = sscanf(params, " %d.%d%n", &temp_i1,&temp_i2,&n); if (m == 2) { - m = sscanf(config, " %lf%n", &temp_f1,&n); + m = sscanf(params, " %lf%n", &temp_f1,&n); if (m != 1 /*|| temp_f1 < 0.0*/) return 0; *value_f = temp_f1; @@ -672,7 +958,7 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) { } /* test is format is hex samples: 0xN */ - m = sscanf(config, " 0x%x%n", &temp_i1,&n); + m = sscanf(params, " 0x%x%n", &temp_i1,&n); if (m == 1) { /* allow negative samples for special meanings */ //if (temp_i1 < 0) @@ -683,7 +969,7 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) { } /* assume format is samples: N */ - m = sscanf(config, " %d%n", &temp_i1,&n); + m = sscanf(params, " %d%n", &temp_i1,&n); if (m == 1) { /* allow negative samples for special meanings */ //if (temp_i1 < 0) @@ -696,12 +982,19 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) { return 0; } -static int get_bool(const char * config, int *value) { +static int get_time_f(const char* params, double* value_f, int32_t* value_i, int* flag) { + int n = get_time(params, value_f, value_i); + if (n > 0) + *flag = 1; + return n; +} + +static int get_bool(const char* params, int* value) { int n,m; char temp; n = 0; /* init as it's not matched if c isn't */ - m = sscanf(config, " %c%n", &temp, &n); + m = sscanf(params, " %c%n", &temp, &n); if (m >= 1 && !(temp == '#' || temp == '\r' || temp == '\n')) return 0; /* ignore if anything non-space/comment matched */ @@ -711,21 +1004,21 @@ static int get_bool(const char * config, int *value) { return n; } -static int get_mask(const char * config, uint32_t *value) { +static int get_mask(const char* params, uint32_t* value) { int n, m, total_n = 0; int temp1,temp2, r1, r2; int i; char cmd; uint32_t mask = *value; - while (config[0] != '\0') { - m = sscanf(config, " %c%n", &cmd,&n); /* consume comma */ + while (params[0] != '\0') { + m = sscanf(params, " %c%n", &cmd,&n); /* consume comma */ if (m == 1 && (cmd == ',' || cmd == '-')) { /* '-' is alt separator (space is ok too, implicitly) */ - config += n; + params += n; continue; } - m = sscanf(config, " %d%n ~ %d%n", &temp1,&n, &temp2,&n); + m = sscanf(params, " %d%n ~ %d%n", &temp1,&n, &temp2,&n); if (m == 1) { /* single values */ r1 = temp1 - 1; r2 = temp1 - 1; @@ -745,11 +1038,11 @@ static int get_mask(const char * config, uint32_t *value) { mask |= (1 << i); } - config += n; + params += n; total_n += n; - if (config[0]== ',' || config[0]== '-') - config++; + if (params[0]== ',' || params[0]== '-') + params++; } *value = mask; @@ -757,55 +1050,55 @@ static int get_mask(const char * config, uint32_t *value) { } -static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) { +static int get_fade(const char* params, txtp_mix_data* mix, int* p_n) { int n, m, tn = 0; char type, separator; - m = sscanf(config, " %d %c%n", &mix->ch_dst, &type, &n); + m = sscanf(params, " %d %c%n", &mix->ch_dst, &type, &n); if (m != 2 || n == 0) goto fail; - config += n; + params += n; tn += n; if (type == '^') { /* full definition */ - m = sscanf(config, " %lf ~ %lf = %c @%n", &mix->vol_start, &mix->vol_end, &mix->shape, &n); + m = sscanf(params, " %lf ~ %lf = %c @%n", &mix->vol_start, &mix->vol_end, &mix->shape, &n); if (m != 3 || n == 0) goto fail; - config += n; + params += n; tn += n; - n = get_time(config, &mix->time_pre, &mix->sample_pre); + n = get_time(params, &mix->time_pre, &mix->sample_pre); if (n == 0) goto fail; - config += n; + params += n; tn += n; - m = sscanf(config, " %c%n", &separator, &n); + m = sscanf(params, " %c%n", &separator, &n); if ( m != 1 || n == 0 || separator != '~') goto fail; - config += n; + params += n; tn += n; - n = get_time(config, &mix->time_start, &mix->sample_start); + n = get_time(params, &mix->time_start, &mix->sample_start); if (n == 0) goto fail; - config += n; + params += n; tn += n; - m = sscanf(config, " %c%n", &separator, &n); + m = sscanf(params, " %c%n", &separator, &n); if (m != 1 || n == 0 || separator != '+') goto fail; - config += n; + params += n; tn += n; - n = get_time(config, &mix->time_end, &mix->sample_end); + n = get_time(params, &mix->time_end, &mix->sample_end); if (n == 0) goto fail; - config += n; + params += n; tn += n; - m = sscanf(config, " %c%n", &separator, &n); + m = sscanf(params, " %c%n", &separator, &n); if (m != 1 || n == 0 || separator != '~') goto fail; - config += n; + params += n; tn += n; - n = get_time(config, &mix->time_post, &mix->sample_post); + n = get_time(params, &mix->time_post, &mix->sample_post); if (n == 0) goto fail; - config += n; + params += n; tn += n; } else { @@ -827,24 +1120,24 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) { mix->time_pre = -1.0; mix->sample_pre = -1; - n = get_position(config, &mix->position, &mix->position_type); + n = get_position(params, &mix->position, &mix->position_type); //if (n == 0) goto fail; /* optional */ - config += n; + params += n; tn += n; - n = get_time(config, &mix->time_start, &mix->sample_start); + n = get_time(params, &mix->time_start, &mix->sample_start); if (n == 0) goto fail; - config += n; + params += n; tn += n; - m = sscanf(config, " %c%n", &separator, &n); + m = sscanf(params, " %c%n", &separator, &n); if (m != 1 || n == 0 || separator != '+') goto fail; - config += n; + params += n; tn += n; - n = get_time(config, &mix->time_end, &mix->sample_end); + n = get_time(params, &mix->time_end, &mix->sample_end); if (n == 0) goto fail; - config += n; + params += n; tn += n; mix->time_post = -1.0; @@ -853,14 +1146,18 @@ static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) { mix->time_end = mix->time_start + mix->time_end; /* defined as length */ - *out_n = tn; + *p_n = tn; return 1; fail: return 0; } -void add_mixing(txtp_entry* cfg, txtp_mix_data* mix, txtp_mix_t command) { - if (cfg->mixing_count + 1 > TXTP_MIXING_MAX) { +/*******************************************************************************/ +/* PARSER - MAIN */ +/*******************************************************************************/ + +void add_mixing(txtp_entry* entry, txtp_mix_data* mix, txtp_mix_t command) { + if (entry->mixing_count + 1 > TXTP_MIXING_MAX) { VGM_LOG("TXTP: too many mixes\n"); return; } @@ -871,303 +1168,341 @@ void add_mixing(txtp_entry* cfg, txtp_mix_data* mix, txtp_mix_t command) { mix->ch_src--; mix->command = command; - cfg->mixing[cfg->mixing_count] = *mix; /* memcpy'ed */ - cfg->mixing_count++; + entry->mixing[entry->mixing_count] = *mix; /* memcpy'ed */ + entry->mixing_count++; } +static void add_settings(txtp_entry* current, txtp_entry* entry, const char* filename) { -static void add_config(txtp_entry* current, txtp_entry* cfg, const char* filename) { - - /* don't memcopy to allow list additions and ignore values not set, - * as current can be "default" config */ + /* don't memcopy to allow list additions and ignore values not set, as current can be "default" settings */ //*current = *cfg; if (filename) strcpy(current->filename, filename); - if (cfg->subsong) - current->subsong = cfg->subsong; - if (cfg->channel_mask) - current->channel_mask = cfg->channel_mask; + /* play config */ + copy_config(¤t->config, &entry->config); - if (cfg->mixing_count > 0) { + /* file settings */ + if (entry->subsong) + current->subsong = entry->subsong; + + if (entry->sample_rate > 0) + current->sample_rate = entry->sample_rate; + + if (entry->channel_mask) + current->channel_mask = entry->channel_mask; + + if (entry->loop_install_set) { + current->loop_install_set = entry->loop_install_set; + current->loop_end_max = entry->loop_end_max; + current->loop_start_sample = entry->loop_start_sample; + current->loop_start_second = entry->loop_start_second; + current->loop_end_sample = entry->loop_end_sample; + current->loop_end_second = entry->loop_end_second; + } + + if (entry->trim_set) { + current->trim_set = entry->trim_set; + current->trim_second = entry->trim_second; + current->trim_sample = entry->trim_sample; + } + + if (entry->mixing_count > 0) { int i; - for (i = 0; i < cfg->mixing_count; i++) { - current->mixing[current->mixing_count] = cfg->mixing[i]; + for (i = 0; i < entry->mixing_count; i++) { + current->mixing[current->mixing_count] = entry->mixing[i]; current->mixing_count++; } } - if (cfg->config_loop_count_set) { - current->config_loop_count_set = cfg->config_loop_count_set; - current->config_loop_count = cfg->config_loop_count; - } - if (cfg->config_fade_time_set) { - current->config_fade_time_set = cfg->config_fade_time_set; - current->config_fade_time = cfg->config_fade_time; - } - if (cfg->config_fade_delay_set) { - current->config_fade_delay_set = cfg->config_fade_delay_set; - current->config_fade_delay = cfg->config_fade_delay; - } - if (cfg->config_ignore_loop) { - current->config_ignore_loop = cfg->config_ignore_loop; - } - if (cfg->config_force_loop) { - current->config_force_loop = cfg->config_force_loop; - } - if (cfg->config_ignore_fade) { - current->config_ignore_fade = cfg->config_ignore_fade; - } - - if (cfg->sample_rate > 0) { - current->sample_rate = cfg->sample_rate; - } - - if (cfg->loop_install_set) { - current->loop_install_set = cfg->loop_install_set; - current->loop_end_max = cfg->loop_end_max; - current->loop_start_sample = cfg->loop_start_sample; - current->loop_start_second = cfg->loop_start_second; - current->loop_end_sample = cfg->loop_end_sample; - current->loop_end_second = cfg->loop_end_second; - } - - if (cfg->trim_set) { - current->trim_set = cfg->trim_set; - current->trim_second = cfg->trim_second; - current->trim_sample = cfg->trim_sample; - } + current->loop_anchor_start = entry->loop_anchor_start; + current->loop_anchor_end = entry->loop_anchor_end; } -static void parse_config(txtp_entry *cfg, char *config) { - /* parse config: #(commands) */ +//TODO use +static inline int is_match(const char* str1, const char* str2) { + return strcmp(str1, str2) == 0; +} + +static void parse_params(txtp_entry* entry, char* params) { + /* parse params: #(commands) */ int n, nc, nm, mc; char command[TXTP_LINE_MAX] = {0}; + play_config_t* tcfg = &entry->config; - cfg->range_start = 0; - cfg->range_end = 1; + entry->range_start = 0; + entry->range_end = 1; - while (config != NULL) { + while (params != NULL) { /* position in next #(command) */ - config = strchr(config, '#'); - if (!config) break; - //;VGM_LOG("TXTP: config='%s'\n", config); + params = strchr(params, '#'); + if (!params) break; + //;VGM_LOG("TXTP: params='%s'\n", params); /* get command until next space/number/comment/end */ command[0] = '\0'; - mc = sscanf(config, "#%n%[^ #0-9\r\n]%n", &nc, command, &nc); + mc = sscanf(params, "#%n%[^ #0-9\r\n]%n", &nc, command, &nc); //;VGM_LOG("TXTP: command='%s', nc=%i, mc=%i\n", command, nc, mc); if (mc <= 0 && nc == 0) break; - config[0] = '\0'; //todo don't modify input string and properly calculate filename end + params[0] = '\0'; //todo don't modify input string and properly calculate filename end - config += nc; /* skip '#' and command */ + params += nc; /* skip '#' and command */ /* check command string (though at the moment we only use single letters) */ if (strcmp(command,"c") == 0) { /* channel mask: file.ext#c1,2 = play channels 1,2 and mutes rest */ - config += get_mask(config, &cfg->channel_mask); - //;VGM_LOG("TXTP: channel_mask ");{int i; for (i=0;i<16;i++)VGM_LOG("%i ",(cfg->channel_mask>>i)&1);}VGM_LOG("\n"); + params += get_mask(params, &entry->channel_mask); + //;VGM_LOG("TXTP: channel_mask ");{int i; for (i=0;i<16;i++)VGM_LOG("%i ",(entry->channel_mask>>i)&1);}VGM_LOG("\n"); } else if (strcmp(command,"m") == 0) { /* channel mixing: file.ext#m(sub-command),(sub-command),etc */ char cmd; - while (config[0] != '\0') { + while (params[0] != '\0') { txtp_mix_data mix = {0}; - //;VGM_LOG("TXTP: subcommand='%s'\n", config); + //;VGM_LOG("TXTP: subcommand='%s'\n", params); //todo use strchr instead? - if (sscanf(config, " %c%n", &cmd, &n) == 1 && n != 0 && cmd == ',') { - config += n; + if (sscanf(params, " %c%n", &cmd, &n) == 1 && n != 0 && cmd == ',') { + params += n; continue; } - if (sscanf(config, " %d - %d%n", &mix.ch_dst, &mix.ch_src, &n) == 2 && n != 0) { + if (sscanf(params, " %d - %d%n", &mix.ch_dst, &mix.ch_src, &n) == 2 && n != 0) { //;VGM_LOG("TXTP: mix %i-%i\n", mix.ch_dst, mix.ch_src); - add_mixing(cfg, &mix, MIX_SWAP); /* N-M: swaps M with N */ - config += n; + add_mixing(entry, &mix, MIX_SWAP); /* N-M: swaps M with N */ + params += n; continue; } - if ((sscanf(config, " %d + %d * %lf%n", &mix.ch_dst, &mix.ch_src, &mix.vol, &n) == 3 && n != 0) || - (sscanf(config, " %d + %d x %lf%n", &mix.ch_dst, &mix.ch_src, &mix.vol, &n) == 3 && n != 0)) { + if ((sscanf(params, " %d + %d * %lf%n", &mix.ch_dst, &mix.ch_src, &mix.vol, &n) == 3 && n != 0) || + (sscanf(params, " %d + %d x %lf%n", &mix.ch_dst, &mix.ch_src, &mix.vol, &n) == 3 && n != 0)) { //;VGM_LOG("TXTP: mix %i+%i*%f\n", mix.ch_dst, mix.ch_src, mix.vol); - add_mixing(cfg, &mix, MIX_ADD_VOLUME); /* N+M*V: mixes M*volume to N */ - config += n; + add_mixing(entry, &mix, MIX_ADD_VOLUME); /* N+M*V: mixes M*volume to N */ + params += n; continue; } - if (sscanf(config, " %d + %d%n", &mix.ch_dst, &mix.ch_src, &n) == 2 && n != 0) { + if (sscanf(params, " %d + %d%n", &mix.ch_dst, &mix.ch_src, &n) == 2 && n != 0) { //;VGM_LOG("TXTP: mix %i+%i\n", mix.ch_dst, mix.ch_src); - add_mixing(cfg, &mix, MIX_ADD); /* N+M: mixes M to N */ - config += n; + add_mixing(entry, &mix, MIX_ADD); /* N+M: mixes M to N */ + params += n; continue; } - if ((sscanf(config, " %d * %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0) || - (sscanf(config, " %d x %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0)) { + if ((sscanf(params, " %d * %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0) || + (sscanf(params, " %d x %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0)) { //;VGM_LOG("TXTP: mix %i*%f\n", mix.ch_dst, mix.vol); - add_mixing(cfg, &mix, MIX_VOLUME); /* N*V: changes volume of N */ - config += n; + add_mixing(entry, &mix, MIX_VOLUME); /* N*V: changes volume of N */ + params += n; continue; } - if ((sscanf(config, " %d = %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0)) { + if ((sscanf(params, " %d = %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0)) { //;VGM_LOG("TXTP: mix %i=%f\n", mix.ch_dst, mix.vol); - add_mixing(cfg, &mix, MIX_LIMIT); /* N=V: limits volume of N */ - config += n; + add_mixing(entry, &mix, MIX_LIMIT); /* N=V: limits volume of N */ + params += n; continue; } - if (sscanf(config, " %d%c%n", &mix.ch_dst, &cmd, &n) == 2 && n != 0 && cmd == 'D') { + if (sscanf(params, " %d%c%n", &mix.ch_dst, &cmd, &n) == 2 && n != 0 && cmd == 'D') { //;VGM_LOG("TXTP: mix %iD\n", mix.ch_dst); - add_mixing(cfg, &mix, MIX_KILLMIX); /* ND: downmix N and all following channels */ - config += n; + add_mixing(entry, &mix, MIX_KILLMIX); /* ND: downmix N and all following channels */ + params += n; continue; } - if (sscanf(config, " %d%c%n", &mix.ch_dst, &cmd, &n) == 2 && n != 0 && cmd == 'd') { + if (sscanf(params, " %d%c%n", &mix.ch_dst, &cmd, &n) == 2 && n != 0 && cmd == 'd') { //;VGM_LOG("TXTP: mix %id\n", mix.ch_dst); - add_mixing(cfg, &mix, MIX_DOWNMIX);/* Nd: downmix N only */ - config += n; + add_mixing(entry, &mix, MIX_DOWNMIX);/* Nd: downmix N only */ + params += n; continue; } - if (sscanf(config, " %d%c%n", &mix.ch_dst, &cmd, &n) == 2 && n != 0 && cmd == 'u') { + if (sscanf(params, " %d%c%n", &mix.ch_dst, &cmd, &n) == 2 && n != 0 && cmd == 'u') { //;VGM_LOG("TXTP: mix %iu\n", mix.ch_dst); - add_mixing(cfg, &mix, MIX_UPMIX); /* Nu: upmix N */ - config += n; + add_mixing(entry, &mix, MIX_UPMIX); /* Nu: upmix N */ + params += n; continue; } - if (get_fade(config, &mix, &n) != 0) { + if (get_fade(params, &mix, &n) != 0) { //;VGM_LOG("TXTP: fade %d^%f~%f=%c@%f~%f+%f~%f\n", // mix.ch_dst, mix.vol_start, mix.vol_end, mix.shape, // mix.time_pre, mix.time_start, mix.time_end, mix.time_post); - add_mixing(cfg, &mix, MIX_FADE); /* N^V1~V2@T1~T2+T3~T4: fades volumes between positions */ - config += n; + add_mixing(entry, &mix, MIX_FADE); /* N^V1~V2@T1~T2+T3~T4: fades volumes between positions */ + params += n; continue; } break; /* unknown mix/new command/end */ } } - else if (strcmp(command,"s") == 0 || (nc == 1 && config[0] >= '0' && config[0] <= '9')) { + else if (strcmp(command,"s") == 0 || (nc == 1 && params[0] >= '0' && params[0] <= '9')) { /* subsongs: file.ext#s2 = play subsong 2, file.ext#2~10 = play subsong range */ int subsong_start = 0, subsong_end = 0; - //todo also advance config? - if (sscanf(config, " %d ~ %d", &subsong_start, &subsong_end) == 2) { + //todo also advance params? + if (sscanf(params, " %d ~ %d", &subsong_start, &subsong_end) == 2) { if (subsong_start > 0 && subsong_end > 0) { - cfg->range_start = subsong_start-1; - cfg->range_end = subsong_end; + entry->range_start = subsong_start-1; + entry->range_end = subsong_end; } //;VGM_LOG("TXTP: subsong range %i~%i\n", range_start, range_end); } - else if (sscanf(config, " %d", &subsong_start) == 1) { + else if (sscanf(params, " %d", &subsong_start) == 1) { if (subsong_start > 0) { - cfg->range_start = subsong_start-1; - cfg->range_end = subsong_start; + entry->range_start = subsong_start-1; + entry->range_end = subsong_start; } //;VGM_LOG("TXTP: subsong single %i-%i\n", range_start, range_end); } - else { /* wrong config, ignore */ + else { /* wrong setting, ignore */ //;VGM_LOG("TXTP: subsong none\n"); } } + + /* play config */ else if (strcmp(command,"i") == 0) { - config += get_bool(config, &cfg->config_ignore_loop); - //;VGM_LOG("TXTP: ignore_loop=%i\n", cfg->config_ignore_loop); + params += get_bool(params, &tcfg->ignore_loop); + tcfg->config_set = 1; + } + else if (strcmp(command,"e") == 0) { + params += get_bool(params, &tcfg->force_loop); + tcfg->config_set = 1; } else if (strcmp(command,"E") == 0) { - config += get_bool(config, &cfg->config_force_loop); - //;VGM_LOG("TXTP: force_loop=%i\n", cfg->config_force_loop); + params += get_bool(params, &tcfg->really_force_loop); + tcfg->config_set = 1; } else if (strcmp(command,"F") == 0) { - config += get_bool(config, &cfg->config_ignore_fade); - //;VGM_LOG("TXTP: ignore_fade=%i\n", cfg->config_ignore_fade); + params += get_bool(params, &tcfg->ignore_fade); + tcfg->config_set = 1; + } + else if (strcmp(command,"L") == 0) { + params += get_bool(params, &tcfg->play_forever); + tcfg->config_set = 1; } else if (strcmp(command,"l") == 0) { - config += get_double(config, &cfg->config_loop_count, &cfg->config_loop_count_set); - //;VGM_LOG("TXTP: loop_count=%f\n", cfg->config_loop_count); + params += get_double(params, &tcfg->loop_count, &tcfg->loop_count_set); + if (tcfg->loop_count < 0) + tcfg->loop_count_set = 0; + tcfg->config_set = 1; } else if (strcmp(command,"f") == 0) { - config += get_double(config, &cfg->config_fade_time, &cfg->config_fade_time_set); - //;VGM_LOG("TXTP: fade_time=%f\n", cfg->config_fade_time); + params += get_double(params, &tcfg->fade_time, &tcfg->fade_time_set); + if (tcfg->fade_time < 0) + tcfg->fade_time_set = 0; + tcfg->config_set = 1; } else if (strcmp(command,"d") == 0) { - config += get_double(config, &cfg->config_fade_delay, &cfg->config_fade_delay_set); - //;VGM_LOG("TXTP: fade_delay %f\n", cfg->config_fade_delay); + params += get_double(params, &tcfg->fade_delay, &tcfg->fade_delay_set); + if (tcfg->fade_delay < 0) + tcfg->fade_delay_set = 0; + tcfg->config_set = 1; } + else if (strcmp(command,"p") == 0) { + params += get_time_f(params, &tcfg->pad_begin_s, &tcfg->pad_begin, &tcfg->pad_begin_set); + tcfg->config_set = 1; + } + else if (strcmp(command,"P") == 0) { + params += get_time_f(params, &tcfg->pad_end_s, &tcfg->pad_end, &tcfg->pad_end_set); + tcfg->config_set = 1; + } + else if (strcmp(command,"r") == 0) { + params += get_time_f(params, &tcfg->trim_begin_s, &tcfg->trim_begin, &tcfg->trim_begin_set); + tcfg->config_set = 1; + } + else if (strcmp(command,"R") == 0) { + params += get_time_f(params, &tcfg->trim_end_s, &tcfg->trim_end, &tcfg->trim_end_set); + tcfg->config_set = 1; + } + else if (strcmp(command,"b") == 0) { + params += get_time_f(params, &tcfg->body_time_s, &tcfg->body_time, &tcfg->body_time_set); + tcfg->config_set = 1; + } + + /* other settings */ else if (strcmp(command,"h") == 0) { - config += get_int(config, &cfg->sample_rate); + params += get_int(params, &entry->sample_rate); //;VGM_LOG("TXTP: sample_rate %i\n", cfg->sample_rate); } else if (strcmp(command,"I") == 0) { - n = get_time(config, &cfg->loop_start_second, &cfg->loop_start_sample); + n = get_time(params, &entry->loop_start_second, &entry->loop_start_sample); if (n > 0) { /* first value must exist */ - config += n; + params += n; - n = get_time(config, &cfg->loop_end_second, &cfg->loop_end_sample); + n = get_time(params, &entry->loop_end_second, &entry->loop_end_sample); if (n == 0) { /* second value is optional */ - cfg->loop_end_max = 1; + entry->loop_end_max = 1; } - config += n; - cfg->loop_install_set = 1; + params += n; + entry->loop_install_set = 1; } - //;VGM_LOG("TXTP: loop_install %i (max=%i): %i %i / %f %f\n", cfg->loop_install, cfg->loop_end_max, - // cfg->loop_start_sample, cfg->loop_end_sample, cfg->loop_start_second, cfg->loop_end_second); + //;VGM_LOG("TXTP: loop_install %i (max=%i): %i %i / %f %f\n", entry->loop_install, entry->loop_end_max, + // entry->loop_start_sample, entry->loop_end_sample, entry->loop_start_second, entry->loop_end_second); } else if (strcmp(command,"t") == 0) { - n = get_time(config, &cfg->trim_second, &cfg->trim_sample); - cfg->trim_set = (n > 0); - //;VGM_LOG("TXTP: trim %i - %f / %i\n", cfg->trim_set, cfg->trim_second, cfg->trim_sample); + entry->trim_set = get_time(params, &entry->trim_second, &entry->trim_sample); + //;VGM_LOG("TXTP: trim %i - %f / %i\n", entry->trim_set, entry->trim_second, entry->trim_sample); } + + else if (is_match(command,"a") || is_match(command,"@loop")) { + entry->loop_anchor_start = 1; + //;VGM_LOG("TXTP: anchor start set\n"); + } + else if (is_match(command,"A") || is_match(command,"@LOOP")) { + entry->loop_anchor_end = 1; + //;VGM_LOG("TXTP: anchor end set\n"); + } + //todo cleanup - else if (strcmp(command,"@volume") == 0) { + /* macros */ + else if (is_match(command,"v") || is_match(command,"@volume")) { txtp_mix_data mix = {0}; - nm = get_double(config, &mix.vol, NULL); - config += nm; + nm = get_volume(params, &mix.vol, NULL); + params += nm; if (nm == 0) continue; - nm = get_mask(config, &mix.mask); - config += nm; + nm = get_mask(params, &mix.mask); + params += nm; - add_mixing(cfg, &mix, MACRO_VOLUME); + add_mixing(entry, &mix, MACRO_VOLUME); } else if (strcmp(command,"@track") == 0 || strcmp(command,"C") == 0 ) { txtp_mix_data mix = {0}; - nm = get_mask(config, &mix.mask); - config += nm; + nm = get_mask(params, &mix.mask); + params += nm; if (nm == 0) continue; - add_mixing(cfg, &mix, MACRO_TRACK); + add_mixing(entry, &mix, MACRO_TRACK); } else if (strcmp(command,"@layer-v") == 0 || strcmp(command,"@layer-b") == 0 || strcmp(command,"@layer-e") == 0) { txtp_mix_data mix = {0}; - nm = get_int(config, &mix.max); - config += nm; - if (nm == 0) continue; + nm = get_int(params, &mix.max); + params += nm; - nm = get_mask(config, &mix.mask); - config += nm; + if (nm > 0) { /* max is optional (auto-detects and uses max channels) */ + nm = get_mask(params, &mix.mask); + params += nm; + } mix.mode = command[7]; /* pass letter */ - add_mixing(cfg, &mix, MACRO_LAYER); + add_mixing(entry, &mix, MACRO_LAYER); } else if (strcmp(command,"@crosslayer-v") == 0 || strcmp(command,"@crosslayer-b") == 0 || @@ -1183,23 +1518,23 @@ static void parse_config(txtp_entry *cfg, char *config) { mix.mode = command[12]; /* pass letter */ } - nm = get_int(config, &mix.max); - config += nm; + nm = get_int(params, &mix.max); + params += nm; if (nm == 0) continue; - add_mixing(cfg, &mix, type); + add_mixing(entry, &mix, type); } else if (strcmp(command,"@downmix") == 0) { txtp_mix_data mix = {0}; mix.max = 2; /* stereo only for now */ - //nm = get_int(config, &mix.max); - //config += nm; + //nm = get_int(params, &mix.max); + //params += nm; //if (nm == 0) continue; - add_mixing(cfg, &mix, MACRO_DOWNMIX); + add_mixing(entry, &mix, MACRO_DOWNMIX); } - else if (config[nc] == ' ') { + else if (params[nc] == ' ') { //;VGM_LOG("TXTP: comment\n"); break; /* comment, ignore rest */ } @@ -1213,14 +1548,21 @@ static void parse_config(txtp_entry *cfg, char *config) { } - -static int add_group(txtp_header * txtp, char *line) { +static int add_group(txtp_header* txtp, char* line) { int n, m; txtp_group cfg = {0}; + int auto_pos = 0; + char c; /* parse group: (position)(type)(count)(repeat) #(commands) */ //;VGM_LOG("TXTP: parse group '%s'\n", line); + m = sscanf(line, " %c%n", &c, &n); + if (m == 1 && c == '-') { + auto_pos = 1; + line += n; + } + m = sscanf(line, " %d%n", &cfg.position, &n); if (m == 1) { cfg.position--; /* externally 1=first but internally 0=first */ @@ -1239,13 +1581,48 @@ static int add_group(txtp_header * txtp, char *line) { m = sscanf(line, " %c%n", &cfg.repeat, &n); if (m == 1 && cfg.repeat == TXTP_GROUP_REPEAT) { + auto_pos = 0; line += n; } + m = sscanf(line, " >%c%n", &c, &n); + if (m == 1 && c == TXTP_GROUP_RANDOM_ALL) { + cfg.selected = cfg.count; /* special meaning */ + line += n; + } + else { + m = sscanf(line, " >%d%n", &cfg.selected, &n); + if (m == 1) { + cfg.selected--; /* externally 1=first but internally 0=first */ + line += n; + } + } - parse_config(&cfg.group_config, line); + parse_params(&cfg.group_settings, line); - //;VGM_LOG("TXTP: parsed group %i%c%i%c\n",cfg.position+1,cfg.type,cfg.count,cfg.repeat); + /* Groups can use "auto" position of last N files, so we need a counter that changes like this: + * #layer of 2 (pos = 0) + * #sequence of 2 + * bgm pos +1 > 1 + * bgm pos +1 > 2 + * group = -S2 pos -2 +1 > 1 (group is at 1 now since it "collapses" wems but becomes a position) + * #sequence of 3 + * bgm pos +1 > 2 + * bgm pos +1 > 3 + * #sequence of 2 + * bgm pos +1 > 4 + * bgm pos +1 > 5 + * group = -S2 pos -2 +1 > 4 (groups is at 4 now since are uncollapsed wems at 2/3) + * group = -S3 pos -3 +1 > 2 + * group = -L2 pos -2 +1 > 1 + */ + txtp->group_pos++; + txtp->group_pos -= cfg.count; + if (auto_pos) { + cfg.position = txtp->group_pos - 1; /* internally 1 = first */ + } + + //;VGM_LOG("TXTP: parsed group %i%c%i%c, auto=%i\n",cfg.position+1,cfg.type,cfg.count,cfg.repeat, auto_pos); /* add final group */ { @@ -1271,52 +1648,73 @@ fail: } -static int add_entry(txtp_header * txtp, char *filename, int is_default) { +static void clean_filename(char* filename) { int i; - txtp_entry cfg = {0}; + size_t len; + + if (filename[0] == '\0') + return; + + /* normalize paths */ + fix_dir_separators(filename); + + /* remove trailing spaces */ + len = strlen(filename); + for (i = len-1; i > 0; i--) { + if (filename[i] != ' ') + break; + filename[i] = '\0'; + } + +} + +//TODO see if entry can be set to &default/&entry[entry_count] to avoid add_settings +static int add_entry(txtp_header* txtp, char* filename, int is_default) { + int i; + txtp_entry entry = {0}; //;VGM_LOG("TXTP: filename=%s\n", filename); /* parse filename: file.ext#(commands) */ { - char *config; + char* params; if (is_default) { - config = filename; /* multiple commands without filename */ + params = filename; /* multiple commands without filename */ } else { - /* find config start (filenames and config can contain multiple dots and #, - * so this may be fooled by certain patterns of . and #) */ - config = strchr(filename, '.'); /* first dot (may be a false positive) */ - if (!config) /* extensionless */ - config = filename; - config = strchr(config, '#'); /* next should be config */ - if (!config) /* no config */ - config = NULL; + /* find settings start after filenames (filenames can also contain dots and #, + * so this may be fooled by certain patterns) */ + params = strchr(filename, '.'); /* first dot (may be a false positive) */ + if (!params) /* extensionless */ + params = filename; + params = strchr(params, '#'); /* next should be actual settings */ + if (!params) + params = NULL; } - parse_config(&cfg, config); + parse_params(&entry, params); } clean_filename(filename); //;VGM_LOG("TXTP: clean filename='%s'\n", filename); - /* config that applies to all files */ + /* settings that applies to final vgmstream */ if (is_default) { txtp->default_entry_set = 1; - add_config(&txtp->default_entry, &cfg, NULL); + add_settings(&txtp->default_entry, &entry, NULL); return 1; } /* add final entry */ - for (i = cfg.range_start; i < cfg.range_end; i++){ - txtp_entry *current; + for (i = entry.range_start; i < entry.range_end; i++){ + txtp_entry* current; /* resize in steps if not enough */ if (txtp->entry_count+1 > txtp->entry_max) { - txtp_entry *temp_entry; + txtp_entry* temp_entry; txtp->entry_max += 5; temp_entry = realloc(txtp->entry, sizeof(txtp_entry) * txtp->entry_max); @@ -1327,11 +1725,12 @@ static int add_entry(txtp_header * txtp, char *filename, int is_default) { /* new entry */ current = &txtp->entry[txtp->entry_count]; memset(current,0, sizeof(txtp_entry)); - cfg.subsong = (i+1); + entry.subsong = (i+1); - add_config(current, &cfg, filename); + add_settings(current, &entry, filename); txtp->entry_count++; + txtp->group_pos++; } return 1; @@ -1339,9 +1738,12 @@ fail: return 0; } -/* ************************************************************************ */ -static int is_substring(const char * val, const char * cmp) { +/*******************************************************************************/ +/* PARSER - BASE */ +/*******************************************************************************/ + +static int is_substring(const char* val, const char* cmp) { int n; char subval[TXTP_LINE_MAX] = {0}; @@ -1354,7 +1756,7 @@ static int is_substring(const char * val, const char * cmp) { return n; } -static int parse_num(const char * val, uint32_t * out_value) { +static int parse_num(const char* val, uint32_t* out_value) { int hex = (val[0]=='0' && val[1]=='x'); if (sscanf(val, hex ? "%x" : "%u", out_value) != 1) goto fail; @@ -1364,7 +1766,7 @@ fail: return 0; } -static int parse_keyval(txtp_header * txtp, const char * key, const char * val) { +static int parse_keyval(txtp_header* txtp, const char* key, const char* val) { //;VGM_LOG("TXTP: key=val '%s'='%s'\n", key,val); @@ -1423,10 +1825,10 @@ fail: return 0; } -static txtp_header* parse_txtp(STREAMFILE* streamFile) { +static txtp_header* parse_txtp(STREAMFILE* sf) { txtp_header* txtp = NULL; off_t txt_offset = 0x00; - off_t file_size = get_streamfile_size(streamFile); + off_t file_size = get_streamfile_size(sf); txtp = calloc(1,sizeof(txtp_header)); @@ -1438,7 +1840,7 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile) { /* skip BOM if needed */ if (file_size > 0 && - ((uint16_t)read_16bitLE(0x00, streamFile) == 0xFFFE || (uint16_t)read_16bitLE(0x00, streamFile) == 0xFEFF)) + ((uint16_t)read_16bitLE(0x00, sf) == 0xFFFE || (uint16_t)read_16bitLE(0x00, sf) == 0xFEFF)) txt_offset = 0x02; /* read and parse lines */ @@ -1448,7 +1850,7 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile) { char filename[TXTP_LINE_MAX] = {0}; int ok, bytes_read, line_ok; - bytes_read = read_line(line, sizeof(line), txt_offset, streamFile, &line_ok); + bytes_read = read_line(line, sizeof(line), txt_offset, sf, &line_ok); if (!line_ok) goto fail; txt_offset += bytes_read; @@ -1468,17 +1870,17 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile) { if (filename[0] == '#') continue; /* simple comment */ - /* filename with config */ + /* filename with settings */ if (!add_entry(txtp, filename, 0)) goto fail; } /* mini-txth: if no entries are set try with filename, ex. from "song.ext#3.txtp" use "song.ext#3" - * (it's possible to have default "commands" inside the .txtp plus filename+config) */ + * (it's possible to have default "commands" inside the .txtp plus filename+settings) */ if (txtp->entry_count == 0) { char filename[PATH_LIMIT] = {0}; - get_streamfile_basename(streamFile, filename, sizeof(filename)); + get_streamfile_basename(sf, filename, sizeof(filename)); add_entry(txtp, filename, 0); } @@ -1489,22 +1891,3 @@ fail: clean_txtp(txtp, 1); return NULL; } - -static void clean_txtp(txtp_header* txtp, int fail) { - int i, start; - - if (!txtp) - return; - - /* returns first vgmstream on success so it's not closed */ - start = fail ? 0 : 1; - - for (i = start; i < txtp->vgmstream_count; i++) { - close_vgmstream(txtp->vgmstream[i]); - } - - free(txtp->vgmstream); - free(txtp->group); - free(txtp->entry); - free(txtp); -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c index ac6855285..d21102be4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c @@ -124,47 +124,47 @@ typedef struct { int allowed_types[16]; } ubi_bao_header; -static int parse_header(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset); -static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset, int target_subsong); -static int parse_pk(ubi_bao_header * bao, STREAMFILE *streamFile); -static VGMSTREAM * init_vgmstream_ubi_bao_header(ubi_bao_header * bao, STREAMFILE *streamFile); -static STREAMFILE * setup_bao_streamfile(ubi_bao_header *bao, STREAMFILE *streamFile); -static STREAMFILE * open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int is_stream, STREAMFILE *streamFile); -static int find_package_bao(uint32_t bao_id, STREAMFILE *streamFile, off_t *out_offset, size_t *out_size); +static int parse_header(ubi_bao_header* bao, STREAMFILE* sf, off_t offset); +static int parse_bao(ubi_bao_header* bao, STREAMFILE* sf, off_t offset, int target_subsong); +static int parse_pk(ubi_bao_header* bao, STREAMFILE* sf); +static VGMSTREAM* init_vgmstream_ubi_bao_header(ubi_bao_header* bao, STREAMFILE* sf); +static STREAMFILE* setup_bao_streamfile(ubi_bao_header* bao, STREAMFILE* sf); +static STREAMFILE* open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int is_stream, STREAMFILE* sf); +static int find_package_bao(uint32_t target_id, STREAMFILE* sf, off_t* p_offset, size_t* p_size); -static int config_bao_version(ubi_bao_header * bao, STREAMFILE *streamFile); -static void config_bao_endian(ubi_bao_header * bao, off_t offset, STREAMFILE *streamFile); -static void build_readable_name(char * buf, size_t buf_size, ubi_bao_header * bao); +static int config_bao_version(ubi_bao_header* bao, STREAMFILE* sf); +static void config_bao_endian(ubi_bao_header* bao, off_t offset, STREAMFILE* sf); +static void build_readable_name(char* buf, size_t buf_size, ubi_bao_header* bao); /* .PK - packages with BAOs from Ubisoft's sound engine ("DARE") games in 2008+ */ -VGMSTREAM * init_vgmstream_ubi_bao_pk(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ubi_bao_pk(STREAMFILE* sf) { ubi_bao_header bao = { 0 }; /* checks */ - if (!check_extensions(streamFile, "pk,lpk,cpk")) + if (!check_extensions(sf, "pk,lpk,cpk")) goto fail; /* package .pk+spk (or .lpk+lspk for localized) database-like format, evolved from Ubi sbN/smN. * .pk has an index pointing to memory BAOs and tables with external stream BAOs in .spk. */ /* main parse */ - if (!parse_pk(&bao, streamFile)) + if (!parse_pk(&bao, sf)) goto fail; build_readable_name(bao.readable_name, sizeof(bao.readable_name), &bao); - return init_vgmstream_ubi_bao_header(&bao, streamFile); + return init_vgmstream_ubi_bao_header(&bao, sf); fail: return NULL; } /* .BAO - single BAO files from Ubisoft's sound engine ("DARE") games in 2008+ */ -VGMSTREAM * init_vgmstream_ubi_bao_atomic(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ubi_bao_atomic(STREAMFILE* sf) { ubi_bao_header bao = { 0 }; - STREAMFILE * streamData = NULL; + STREAMFILE* streamData = NULL; /* checks */ - if (!check_extensions(streamFile, "bao,")) + if (!check_extensions(sf, "bao,")) goto fail; /* atomic .bao+bao/sbao found in .forge and similar bigfiles. The bigfile acts as index, but @@ -172,21 +172,21 @@ VGMSTREAM * init_vgmstream_ubi_bao_atomic(STREAMFILE *streamFile) { * be other) we can simulate it. Extension is .bao/sbao or extensionaless in some games. */ /* format: 0x01=AC1, 0x02=POP2008 */ - if (read_8bit(0x00, streamFile) != 0x01 && read_8bit(0x00, streamFile) != 0x02) + if (read_8bit(0x00, sf) != 0x01 && read_8bit(0x00, sf) != 0x02) goto fail; bao.is_atomic = 1; - bao.version = read_32bitBE(0x00, streamFile) & 0x00FFFFFF; - if (!config_bao_version(&bao, streamFile)) + bao.version = read_32bitBE(0x00, sf) & 0x00FFFFFF; + if (!config_bao_version(&bao, sf)) goto fail; /* main parse */ - if (!parse_bao(&bao, streamFile, 0x00, 1)) + if (!parse_bao(&bao, sf, 0x00, 1)) goto fail; build_readable_name(bao.readable_name, sizeof(bao.readable_name), &bao); - return init_vgmstream_ubi_bao_header(&bao, streamFile); + return init_vgmstream_ubi_bao_header(&bao, sf); fail: close_streamfile(streamData); return NULL; @@ -194,11 +194,11 @@ fail: #if 0 /* .SPK - special mini package with BAOs [Avatar (PS3)] */ -VGMSTREAM * init_vgmstream_ubi_bao_spk(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ubi_bao_spk(STREAMFILE* sf) { ubi_bao_header bao = { 0 }; /* checks */ - if (!check_extensions(streamFile, "spk")) + if (!check_extensions(sf, "spk")) goto fail; /* Variation of .pk: @@ -221,8 +221,8 @@ VGMSTREAM * init_vgmstream_ubi_bao_spk(STREAMFILE *streamFile) { /* ************************************************************************* */ -static VGMSTREAM * init_vgmstream_ubi_bao_base(ubi_bao_header * bao, STREAMFILE *streamHead, STREAMFILE * streamData) { - VGMSTREAM * vgmstream = NULL; +static VGMSTREAM* init_vgmstream_ubi_bao_base(ubi_bao_header* bao, STREAMFILE* streamHead, STREAMFILE* streamData) { + VGMSTREAM* vgmstream = NULL; off_t start_offset = 0x00; @@ -271,14 +271,14 @@ static VGMSTREAM * init_vgmstream_ubi_bao_base(ubi_bao_header * bao, STREAMFILE break; #ifdef VGM_USE_FFMPEG - //todo: some XMA1 decode a bit strangely at certain positions (FFmpeg bug?) + //TODO: Ubi XMA1 (raw or fmt) is a bit strange, FFmpeg decodes some frames slightly wrong (see Ubi SB) case RAW_XMA1: case RAW_XMA2_OLD: case RAW_XMA2_NEW: { uint8_t buf[0x100]; size_t bytes, chunk_size, data_size; off_t chunk_offset; - STREAMFILE *streamXMA; + STREAMFILE* streamXMA; switch(bao->codec) { case RAW_XMA1: chunk_size = 0x20; break; @@ -407,14 +407,14 @@ fail: return NULL; } -static VGMSTREAM * init_vgmstream_ubi_bao_audio(ubi_bao_header * bao, STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamData = NULL; +static VGMSTREAM* init_vgmstream_ubi_bao_audio(ubi_bao_header* bao, STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* streamData = NULL; - streamData = setup_bao_streamfile(bao, streamFile); + streamData = setup_bao_streamfile(bao, sf); if (!streamData) goto fail; - vgmstream = init_vgmstream_ubi_bao_base(bao, streamFile, streamData); + vgmstream = init_vgmstream_ubi_bao_base(bao, sf, streamData); if (!vgmstream) goto fail; close_streamfile(streamData); @@ -428,15 +428,15 @@ fail: return NULL; } -static VGMSTREAM * init_vgmstream_ubi_bao_layer(ubi_bao_header *bao, STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +static VGMSTREAM* init_vgmstream_ubi_bao_layer(ubi_bao_header* bao, STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; layered_layout_data* data = NULL; - STREAMFILE* temp_streamFile = NULL; - STREAMFILE * streamData = NULL; + STREAMFILE* temp_sf = NULL; + STREAMFILE* streamData = NULL; size_t full_stream_size = bao->stream_size; int i, total_channels = 0; - streamData = setup_bao_streamfile(bao, streamFile); + streamData = setup_bao_streamfile(bao, sf); if (!streamData) goto fail; /* init layout */ @@ -447,19 +447,19 @@ static VGMSTREAM * init_vgmstream_ubi_bao_layer(ubi_bao_header *bao, STREAMFILE for (i = 0; i < bao->layer_count; i++) { /* prepare streamfile from a single layer section */ - temp_streamFile = setup_ubi_bao_streamfile(streamData, 0x00, full_stream_size, i, bao->layer_count, bao->big_endian); - if (!temp_streamFile) goto fail; + temp_sf = setup_ubi_bao_streamfile(streamData, 0x00, full_stream_size, i, bao->layer_count, bao->big_endian); + if (!temp_sf) goto fail; - bao->stream_size = get_streamfile_size(temp_streamFile); + bao->stream_size = get_streamfile_size(temp_sf); bao->channels = bao->layer_channels[i]; total_channels += bao->layer_channels[i]; /* build the layer VGMSTREAM (standard sb with custom streamfile) */ - data->layers[i] = init_vgmstream_ubi_bao_base(bao, streamFile, temp_streamFile); + data->layers[i] = init_vgmstream_ubi_bao_base(bao, sf, temp_sf); if (!data->layers[i]) goto fail; - close_streamfile(temp_streamFile); - temp_streamFile = NULL; + close_streamfile(temp_sf); + temp_sf = NULL; } if (!setup_layout_layered(data)) @@ -486,7 +486,7 @@ static VGMSTREAM * init_vgmstream_ubi_bao_layer(ubi_bao_header *bao, STREAMFILE return vgmstream; fail: - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); close_streamfile(streamData); if (vgmstream) close_vgmstream(vgmstream); @@ -497,8 +497,8 @@ fail: return NULL; } -static VGMSTREAM * init_vgmstream_ubi_bao_sequence(ubi_bao_header *bao, STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +static VGMSTREAM* init_vgmstream_ubi_bao_sequence(ubi_bao_header* bao, STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; STREAMFILE* streamChain = NULL; segmented_layout_data* data = NULL; int i; @@ -518,7 +518,7 @@ static VGMSTREAM * init_vgmstream_ubi_bao_sequence(ubi_bao_header *bao, STREAMFI if (bao->is_atomic) { /* open memory audio BAO */ - streamChain = open_atomic_bao(bao->cfg.file_type, entry_id, 0, streamFile); + streamChain = open_atomic_bao(bao->cfg.file_type, entry_id, 0, sf); if (!streamChain) { VGM_LOG("UBI BAO: chain BAO %08x not found\n", entry_id); goto fail; @@ -535,13 +535,13 @@ static VGMSTREAM * init_vgmstream_ubi_bao_sequence(ubi_bao_header *bao, STREAMFI else { /* find memory audio BAO */ off_t entry_offset; - if (!find_package_bao(entry_id, streamFile, &entry_offset, NULL)) { + if (!find_package_bao(entry_id, sf, &entry_offset, NULL)) { VGM_LOG("UBI BAO: expected chain id %08x not found\n", entry_id); goto fail; } /* parse BAO */ - if (!parse_header(&temp_bao, streamFile, entry_offset)) + if (!parse_header(&temp_bao, sf, entry_offset)) goto fail; } @@ -551,7 +551,7 @@ static VGMSTREAM * init_vgmstream_ubi_bao_sequence(ubi_bao_header *bao, STREAMFI } /* build the layer VGMSTREAM (current sb entry config) */ - data->segments[i] = init_vgmstream_ubi_bao_header(&temp_bao, streamFile); + data->segments[i] = init_vgmstream_ubi_bao_header(&temp_bao, sf); if (!data->segments[i]) goto fail; if (i == bao->sequence_loop) @@ -599,38 +599,38 @@ fail: } -static size_t silence_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, void* data) { +static size_t silence_io_read(STREAMFILE* streamfile, uint8_t *dest, off_t offset, size_t length, void* data) { int i; for (i = 0; i < length; i++) { dest[i] = 0; } return length; /* pretend we read zeroes */ } -static size_t silence_io_size(STREAMFILE *streamfile, void* data) { +static size_t silence_io_size(STREAMFILE* streamfile, void* data) { return 0x7FFFFFF; /* whatevs */ } -static STREAMFILE* setup_silence_streamfile(STREAMFILE *streamFile) { - STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; +static STREAMFILE* setup_silence_streamfile(STREAMFILE* sf) { + STREAMFILE* temp_sf = NULL, *new_sf = NULL; /* setup custom streamfile */ - new_streamFile = open_wrap_streamfile(streamFile); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + new_sf = open_wrap_streamfile(sf); + if (!new_sf) goto fail; + temp_sf = new_sf; - new_streamFile = open_io_streamfile(temp_streamFile, NULL,0, silence_io_read,silence_io_size); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + new_sf = open_io_streamfile(temp_sf, NULL,0, silence_io_read,silence_io_size); + if (!new_sf) goto fail; + temp_sf = new_sf; - return temp_streamFile; + return temp_sf; fail: - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); return NULL; } -static VGMSTREAM * init_vgmstream_ubi_bao_silence(ubi_bao_header *bao, STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE *temp_streamFile = NULL; +static VGMSTREAM* init_vgmstream_ubi_bao_silence(ubi_bao_header* bao, STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; int channel_count, sample_rate; channel_count = bao->channels; @@ -658,22 +658,22 @@ static VGMSTREAM * init_vgmstream_ubi_bao_silence(ubi_bao_header *bao, STREAMFIL vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x02; - temp_streamFile = setup_silence_streamfile(streamFile); - if ( !vgmstream_open_stream(vgmstream, temp_streamFile, 0x00) ) + temp_sf = setup_silence_streamfile(sf); + if ( !vgmstream_open_stream(vgmstream, temp_sf, 0x00) ) goto fail; - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); return vgmstream; fail: close_vgmstream(vgmstream); - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); return vgmstream; } -static VGMSTREAM * init_vgmstream_ubi_bao_header(ubi_bao_header * bao, STREAMFILE * streamFile) { - VGMSTREAM * vgmstream = NULL; +static VGMSTREAM* init_vgmstream_ubi_bao_header(ubi_bao_header* bao, STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; if (bao->total_subsongs <= 0) { VGM_LOG("UBI BAO: no subsongs\n"); @@ -691,19 +691,19 @@ static VGMSTREAM * init_vgmstream_ubi_bao_header(ubi_bao_header * bao, STREAMFIL switch(bao->type) { case UBI_AUDIO: - vgmstream = init_vgmstream_ubi_bao_audio(bao, streamFile); + vgmstream = init_vgmstream_ubi_bao_audio(bao, sf); break; case UBI_LAYER: - vgmstream = init_vgmstream_ubi_bao_layer(bao, streamFile); + vgmstream = init_vgmstream_ubi_bao_layer(bao, sf); break; case UBI_SEQUENCE: - vgmstream = init_vgmstream_ubi_bao_sequence(bao, streamFile); + vgmstream = init_vgmstream_ubi_bao_sequence(bao, sf); break; case UBI_SILENCE: - vgmstream = init_vgmstream_ubi_bao_silence(bao, streamFile); + vgmstream = init_vgmstream_ubi_bao_silence(bao, sf); break; default: @@ -727,24 +727,24 @@ fail: * BAOs pointing to internal/external stream BAOs (.spk is the same, with stream BAOs only). * A fun feature of .pk is that different BAOs in a .pk can point to different .spk BAOs * that actually hold the same data, with different GUID too, somehow. */ -static int parse_pk(ubi_bao_header * bao, STREAMFILE *streamFile) { +static int parse_pk(ubi_bao_header* bao, STREAMFILE* sf) { int i; int index_entries; size_t index_size, index_header_size; off_t bao_offset; - int target_subsong = streamFile->stream_index; - STREAMFILE *streamIndex = NULL; - STREAMFILE *streamTest = NULL; + int target_subsong = sf->stream_index; + STREAMFILE* streamIndex = NULL; + STREAMFILE* streamTest = NULL; /* format: 0x01=package index, 0x02=package BAO */ - if (read_8bit(0x00, streamFile) != 0x01) + if (read_8bit(0x00, sf) != 0x01) goto fail; /* index and resources are always LE */ if (target_subsong <= 0) target_subsong = 1; - bao->version = read_32bitBE(0x00, streamFile) & 0x00FFFFFF; - index_size = read_32bitLE(0x04, streamFile); /* can be 0, not including */ + bao->version = read_32bitBE(0x00, sf) & 0x00FFFFFF; + index_size = read_32bitLE(0x04, sf); /* can be 0, not including */ /* 0x08: resource table offset, always found even if not used */ /* 0x0c: always 0? */ /* 0x10: unknown, null if no entries */ @@ -755,7 +755,7 @@ static int parse_pk(ubi_bao_header * bao, STREAMFILE *streamFile) { /* 0x30(10): parent GUID? may be same as 0x18, may be shared with other files */ /* (the above values seem ignored by games, probably just info for their tools) */ - if (!config_bao_version(bao, streamFile)) + if (!config_bao_version(bao, sf)) goto fail; @@ -769,10 +769,10 @@ static int parse_pk(ubi_bao_header * bao, STREAMFILE *streamFile) { } /* use smaller I/O buffers for performance, as this read lots of small headers all over the place */ - streamIndex = reopen_streamfile(streamFile, index_size); + streamIndex = reopen_streamfile(sf, index_size); if (!streamIndex) goto fail; - streamTest = reopen_streamfile(streamFile, 0x100); + streamTest = reopen_streamfile(sf, 0x100); if (!streamTest) goto fail; /* parse index to get target subsong N = Nth valid header BAO */ @@ -804,7 +804,7 @@ fail: /* ************************************************************************* */ -static void build_readable_name(char * buf, size_t buf_size, ubi_bao_header * bao) { +static void build_readable_name(char* buf, size_t buf_size, ubi_bao_header* bao) { const char *grp_name; const char *pft_name; const char *typ_name; @@ -858,42 +858,42 @@ static void build_readable_name(char * buf, size_t buf_size, ubi_bao_header * ba } } -static int parse_type_audio(ubi_bao_header * bao, off_t offset, STREAMFILE* streamFile) { +static int parse_type_audio(ubi_bao_header* bao, off_t offset, STREAMFILE* sf) { int32_t (*read_32bit)(off_t,STREAMFILE*) = bao->big_endian ? read_32bitBE : read_32bitLE; off_t h_offset = offset + bao->header_skip; /* audio header */ bao->type = UBI_AUDIO; - bao->stream_size = read_32bit(h_offset + bao->cfg.audio_stream_size, streamFile); - bao->stream_id = read_32bit(h_offset + bao->cfg.audio_stream_id, streamFile); - bao->is_external = read_32bit(h_offset + bao->cfg.audio_external_flag, streamFile) & bao->cfg.audio_external_and; - bao->loop_flag = read_32bit(h_offset + bao->cfg.audio_loop_flag, streamFile) & bao->cfg.audio_loop_and; - bao->channels = read_32bit(h_offset + bao->cfg.audio_channels, streamFile); - bao->sample_rate = read_32bit(h_offset + bao->cfg.audio_sample_rate, streamFile); + bao->stream_size = read_32bit(h_offset + bao->cfg.audio_stream_size, sf); + bao->stream_id = read_32bit(h_offset + bao->cfg.audio_stream_id, sf); + bao->is_external = read_32bit(h_offset + bao->cfg.audio_external_flag, sf) & bao->cfg.audio_external_and; + bao->loop_flag = read_32bit(h_offset + bao->cfg.audio_loop_flag, sf) & bao->cfg.audio_loop_and; + bao->channels = read_32bit(h_offset + bao->cfg.audio_channels, sf); + bao->sample_rate = read_32bit(h_offset + bao->cfg.audio_sample_rate, sf); /* prefetch data is in another internal BAO right after the base header */ if (bao->cfg.audio_prefetch_size) { - bao->prefetch_size = read_32bit(h_offset + bao->cfg.audio_prefetch_size, streamFile); + bao->prefetch_size = read_32bit(h_offset + bao->cfg.audio_prefetch_size, sf); bao->is_prefetched = (bao->prefetch_size > 0); } if (bao->loop_flag) { - bao->loop_start = read_32bit(h_offset + bao->cfg.audio_num_samples, streamFile); - bao->num_samples = read_32bit(h_offset + bao->cfg.audio_num_samples2, streamFile) + bao->loop_start; + bao->loop_start = read_32bit(h_offset + bao->cfg.audio_num_samples, sf); + bao->num_samples = read_32bit(h_offset + bao->cfg.audio_num_samples2, sf) + bao->loop_start; } else { - bao->num_samples = read_32bit(h_offset + bao->cfg.audio_num_samples, streamFile); + bao->num_samples = read_32bit(h_offset + bao->cfg.audio_num_samples, sf); } - bao->stream_type = read_32bit(h_offset + bao->cfg.audio_stream_type, streamFile); + bao->stream_type = read_32bit(h_offset + bao->cfg.audio_stream_type, sf); return 1; //fail: // return 0; } -static int parse_type_sequence(ubi_bao_header * bao, off_t offset, STREAMFILE* streamFile) { +static int parse_type_sequence(ubi_bao_header* bao, off_t offset, STREAMFILE* sf) { int32_t (*read_32bit)(off_t,STREAMFILE*) = bao->big_endian ? read_32bitBE : read_32bitLE; off_t h_offset = offset + bao->header_skip; off_t table_offset; @@ -906,9 +906,9 @@ static int parse_type_sequence(ubi_bao_header * bao, off_t offset, STREAMFILE* s goto fail; } - bao->sequence_loop = read_32bit(h_offset + bao->cfg.sequence_sequence_loop, streamFile); - bao->sequence_single = read_32bit(h_offset + bao->cfg.sequence_sequence_single, streamFile); - bao->sequence_count = read_32bit(h_offset + bao->cfg.sequence_sequence_count, streamFile); + bao->sequence_loop = read_32bit(h_offset + bao->cfg.sequence_sequence_loop, sf); + bao->sequence_single = read_32bit(h_offset + bao->cfg.sequence_sequence_single, sf); + bao->sequence_count = read_32bit(h_offset + bao->cfg.sequence_sequence_count, sf); if (bao->sequence_count > BAO_MAX_CHAIN_COUNT) { VGM_LOG("UBI BAO: incorrect sequence count\n"); goto fail; @@ -917,7 +917,7 @@ static int parse_type_sequence(ubi_bao_header * bao, off_t offset, STREAMFILE* s /* get chain in extra table */ table_offset = offset + bao->header_size; for (i = 0; i < bao->sequence_count; i++) { - uint32_t entry_id = (uint32_t)read_32bit(table_offset + bao->cfg.sequence_entry_number, streamFile); + uint32_t entry_id = (uint32_t)read_32bit(table_offset + bao->cfg.sequence_entry_number, sf); bao->sequence_chain[i] = entry_id; @@ -930,7 +930,7 @@ fail: } -static int parse_type_layer(ubi_bao_header * bao, off_t offset, STREAMFILE* streamFile) { +static int parse_type_layer(ubi_bao_header* bao, off_t offset, STREAMFILE* sf) { int32_t (*read_32bit)(off_t,STREAMFILE*) = bao->big_endian ? read_32bitBE : read_32bitLE; off_t h_offset = offset + bao->header_skip; off_t table_offset; @@ -944,31 +944,31 @@ static int parse_type_layer(ubi_bao_header * bao, off_t offset, STREAMFILE* stre goto fail; } - bao->layer_count = read_32bit(h_offset + bao->cfg.layer_layer_count, streamFile); + bao->layer_count = read_32bit(h_offset + bao->cfg.layer_layer_count, sf); if (bao->layer_count > BAO_MAX_LAYER_COUNT) { VGM_LOG("UBI BAO: incorrect layer count\n"); goto fail; } - bao->is_external = read_32bit(h_offset + bao->cfg.layer_external_flag, streamFile) & bao->cfg.layer_external_and; - bao->stream_size = read_32bit(h_offset + bao->cfg.layer_stream_size, streamFile); - bao->stream_id = read_32bit(h_offset + bao->cfg.layer_stream_id, streamFile); + bao->is_external = read_32bit(h_offset + bao->cfg.layer_external_flag, sf) & bao->cfg.layer_external_and; + bao->stream_size = read_32bit(h_offset + bao->cfg.layer_stream_size, sf); + bao->stream_id = read_32bit(h_offset + bao->cfg.layer_stream_id, sf); if (bao->cfg.layer_prefetch_size) { - bao->prefetch_size = read_32bit(h_offset + bao->cfg.layer_prefetch_size, streamFile); + bao->prefetch_size = read_32bit(h_offset + bao->cfg.layer_prefetch_size, sf); bao->is_prefetched = (bao->prefetch_size > 0); } /* extra cue table (rare, has N variable-sized labels + cue table pointing to them) */ if (bao->cfg.layer_cue_labels) { - cues_size += read_32bit(h_offset + bao->cfg.layer_cue_labels, streamFile); + cues_size += read_32bit(h_offset + bao->cfg.layer_cue_labels, sf); } if (bao->cfg.layer_cue_count) { - cues_size += read_32bit(h_offset + bao->cfg.layer_cue_count, streamFile) * 0x08; + cues_size += read_32bit(h_offset + bao->cfg.layer_cue_count, sf) * 0x08; } if (bao->cfg.layer_extra_size) { - bao->extra_size = read_32bit(h_offset + bao->cfg.layer_extra_size, streamFile); + bao->extra_size = read_32bit(h_offset + bao->cfg.layer_extra_size, sf); } else { bao->extra_size = cues_size + bao->layer_count * bao->cfg.layer_entry_size + cues_size; @@ -976,24 +976,22 @@ static int parse_type_layer(ubi_bao_header * bao, off_t offset, STREAMFILE* stre /* get 1st layer header in extra table and validate all headers match */ table_offset = offset + bao->header_size + cues_size; - //bao->channels = read_32bit(table_offset + bao->cfg.layer_channels, streamFile); - bao->sample_rate = read_32bit(table_offset + bao->cfg.layer_sample_rate, streamFile); - bao->stream_type = read_32bit(table_offset + bao->cfg.layer_stream_type, streamFile); - bao->num_samples = read_32bit(table_offset + bao->cfg.layer_num_samples, streamFile); + //bao->channels = read_32bit(table_offset + bao->cfg.layer_channels, sf); + bao->sample_rate = read_32bit(table_offset + bao->cfg.layer_sample_rate, sf); + bao->stream_type = read_32bit(table_offset + bao->cfg.layer_stream_type, sf); + bao->num_samples = read_32bit(table_offset + bao->cfg.layer_num_samples, sf); for (i = 0; i < bao->layer_count; i++) { - int channels = read_32bit(table_offset + bao->cfg.layer_channels, streamFile); - int sample_rate = read_32bit(table_offset + bao->cfg.layer_sample_rate, streamFile); - int stream_type = read_32bit(table_offset + bao->cfg.layer_stream_type, streamFile); - int num_samples = read_32bit(table_offset + bao->cfg.layer_num_samples, streamFile); + int channels = read_32bit(table_offset + bao->cfg.layer_channels, sf); + int sample_rate = read_32bit(table_offset + bao->cfg.layer_sample_rate, sf); + int stream_type = read_32bit(table_offset + bao->cfg.layer_stream_type, sf); + int num_samples = read_32bit(table_offset + bao->cfg.layer_num_samples, sf); if (bao->sample_rate != sample_rate || bao->stream_type != stream_type) { VGM_LOG("UBI BAO: layer headers don't match at %x\n", (uint32_t)table_offset); - if (bao->cfg.layer_ignore_error) { - continue; + if (!bao->cfg.layer_ignore_error) { + goto fail; } - - goto fail; } /* uncommonly channels may vary per layer [Rayman Raving Rabbids: TV Party (Wii) ex. 0x22000cbc.pk] */ @@ -1012,7 +1010,7 @@ fail: return 0; } -static int parse_type_silence(ubi_bao_header * bao, off_t offset, STREAMFILE* streamFile) { +static int parse_type_silence(ubi_bao_header* bao, off_t offset, STREAMFILE* sf) { float (*read_f32)(off_t,STREAMFILE*) = bao->big_endian ? read_f32be : read_f32le; off_t h_offset = offset + bao->header_skip; @@ -1023,7 +1021,7 @@ static int parse_type_silence(ubi_bao_header * bao, off_t offset, STREAMFILE* st goto fail; } - bao->duration = read_f32(h_offset + bao->cfg.silence_duration_float, streamFile); + bao->duration = read_f32(h_offset + bao->cfg.silence_duration_float, sf); if (bao->duration <= 0.0f) { VGM_LOG("UBI BAO: bad duration %f at %x\n", bao->duration, (uint32_t)offset); goto fail; @@ -1035,7 +1033,7 @@ fail: } /* adjust some common values */ -static int parse_values(ubi_bao_header * bao, STREAMFILE *streamFile) { +static int parse_values(ubi_bao_header* bao, STREAMFILE* sf) { if (bao->type == UBI_SEQUENCE || bao->type == UBI_SILENCE) return 1; @@ -1093,7 +1091,7 @@ fail: /* set actual offsets in various places */ -static int parse_offsets(ubi_bao_header * bao, STREAMFILE *streamFile) { +static int parse_offsets(ubi_bao_header* bao, STREAMFILE* sf) { off_t bao_offset; size_t bao_size; @@ -1128,7 +1126,7 @@ static int parse_offsets(ubi_bao_header * bao, STREAMFILE *streamFile) { } else { if (bao->is_prefetched) { - if (!find_package_bao(bao->prefetch_id, streamFile, &bao_offset, &bao_size)) { + if (!find_package_bao(bao->prefetch_id, sf, &bao_offset, &bao_size)) { VGM_LOG("UBI BAO: expected prefetch id %08x not found\n", bao->prefetch_id); goto fail; } @@ -1143,22 +1141,22 @@ static int parse_offsets(ubi_bao_header * bao, STREAMFILE *streamFile) { if (bao->is_external) { int i; off_t offset; - off_t resources_offset = read_32bitLE(0x08, streamFile); - int resources_count = read_32bitLE(resources_offset+0x00, streamFile); - size_t strings_size = read_32bitLE(resources_offset+0x04, streamFile); + off_t resources_offset = read_32bitLE(0x08, sf); + int resources_count = read_32bitLE(resources_offset+0x00, sf); + size_t strings_size = read_32bitLE(resources_offset+0x04, sf); /* parse resource table to external stream (may be empty, or exist even with nothing in the file) */ offset = resources_offset + 0x04+0x04 + strings_size; for (i = 0; i < resources_count; i++) { - uint32_t resource_id = read_32bitLE(offset+0x10*i+0x00, streamFile); - off_t name_offset = read_32bitLE(offset+0x10*i+0x04, streamFile); - off_t resource_offset = read_32bitLE(offset+0x10*i+0x08, streamFile); - size_t resource_size = read_32bitLE(offset+0x10*i+0x0c, streamFile); + uint32_t resource_id = read_32bitLE(offset+0x10*i+0x00, sf); + off_t name_offset = read_32bitLE(offset+0x10*i+0x04, sf); + off_t resource_offset = read_32bitLE(offset+0x10*i+0x08, sf); + size_t resource_size = read_32bitLE(offset+0x10*i+0x0c, sf); if (resource_id == bao->stream_id) { bao->stream_offset = resource_offset + bao->stream_skip; - read_string(bao->resource_name,255, resources_offset + 0x04+0x04 + name_offset, streamFile); + read_string(bao->resource_name,255, resources_offset + 0x04+0x04 + name_offset, sf); if (bao->stream_size != resource_size - bao->stream_skip + bao->prefetch_size) { VGM_ASSERT(bao->stream_size != resource_size - bao->stream_skip + bao->prefetch_size, @@ -1175,7 +1173,7 @@ static int parse_offsets(ubi_bao_header * bao, STREAMFILE *streamFile) { } } else { - if (!find_package_bao(bao->stream_id, streamFile, &bao_offset, &bao_size)) { + if (!find_package_bao(bao->stream_id, sf, &bao_offset, &bao_size)) { VGM_LOG("UBI BAO: expected internal id %08x not found\n", bao->stream_id); goto fail; } @@ -1204,13 +1202,13 @@ fail: } /* parse a single known header resource at offset (see config_bao for info) */ -static int parse_header(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) { +static int parse_header(ubi_bao_header* bao, STREAMFILE* sf, off_t offset) { int32_t (*read_32bit)(off_t,STREAMFILE*) = bao->big_endian ? read_32bitBE : read_32bitLE; bao->header_offset = offset; - bao->header_format = read_8bit (offset + 0x00, streamFile); /* 0x01: atomic, 0x02: package */ - bao->header_version = read_32bitBE(offset + 0x00, streamFile) & 0x00FFFFFF; + bao->header_format = read_8bit (offset + 0x00, sf); /* 0x01: atomic, 0x02: package */ + bao->header_version = read_32bitBE(offset + 0x00, sf) & 0x00FFFFFF; if (bao->version != bao->header_version) { VGM_LOG("UBI BAO: mismatched header version at %x\n", (uint32_t)offset); goto fail; @@ -1231,8 +1229,8 @@ static int parse_header(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offs * 0x1c: fixed value? */ bao->header_skip = bao->cfg.header_skip; - bao->header_id = read_32bit(offset + bao->header_skip + 0x00, streamFile); - bao->header_type = read_32bit(offset + bao->header_skip + 0x04, streamFile); + bao->header_id = read_32bit(offset + bao->header_skip + 0x00, sf); + bao->header_type = read_32bit(offset + bao->header_skip + 0x04, sf); bao->header_size = bao->cfg.header_base_size; @@ -1243,9 +1241,9 @@ static int parse_header(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offs } /* detect extra unused field in PC/Wii * (could be improved but no apparent flags or anything useful) */ - else if (get_streamfile_size(streamFile) > offset + bao->header_size) { + else if (get_streamfile_size(sf) > offset + bao->header_size) { /* may read next BAO version, layer header, cues, resource table size, etc, always > 1 */ - int32_t end_field = read_32bit(offset + bao->header_size, streamFile); + int32_t end_field = read_32bit(offset + bao->header_size, sf); if (end_field == -1 || end_field == 0 || end_field == 1) /* some count? */ bao->header_size += 0x04; @@ -1253,19 +1251,19 @@ static int parse_header(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offs switch(bao->header_type) { case 0x01: - if (!parse_type_audio(bao, offset, streamFile)) + if (!parse_type_audio(bao, offset, sf)) goto fail; break; case 0x05: - if (!parse_type_sequence(bao, offset, streamFile)) + if (!parse_type_sequence(bao, offset, sf)) goto fail; break; case 0x06: - if (!parse_type_layer(bao, offset, streamFile)) + if (!parse_type_layer(bao, offset, sf)) goto fail; break; case 0x08: - if (!parse_type_silence(bao, offset, streamFile)) + if (!parse_type_silence(bao, offset, sf)) goto fail; break; default: @@ -1273,10 +1271,10 @@ static int parse_header(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offs goto fail; } - if (!parse_values(bao, streamFile)) + if (!parse_values(bao, sf)) goto fail; - if (!parse_offsets(bao, streamFile)) + if (!parse_offsets(bao, sf)) goto fail; return 1; @@ -1284,16 +1282,16 @@ fail: return 0; } -static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset, int target_subsong) { +static int parse_bao(ubi_bao_header* bao, STREAMFILE* sf, off_t offset, int target_subsong) { int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; uint32_t bao_class, header_type; - /*bao_version =*/ read_32bitBE(offset+0x00, streamFile); /* force buffer read */ + /*bao_version =*/ read_32bitBE(offset+0x00, sf); /* force buffer read */ - config_bao_endian(bao, offset, streamFile); + config_bao_endian(bao, offset, sf); read_32bit = bao->big_endian ? read_32bitBE : read_32bitLE; - bao_class = read_32bit(offset+bao->cfg.bao_class, streamFile); + bao_class = read_32bit(offset+bao->cfg.bao_class, sf); if (bao_class & 0x0FFFFFFF) { VGM_LOG("UBI BAO: unknown class %x at %x\n", bao_class, (uint32_t)offset); goto fail; @@ -1303,7 +1301,7 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset, if (bao_class != 0x20000000) /* ignore non-header classes */ return 1; - header_type = read_32bit(offset + bao->cfg.header_skip + 0x04, streamFile); + header_type = read_32bit(offset + bao->cfg.header_skip + 0x04, sf); if (header_type > 9) { VGM_LOG("UBI BAO: unknown type %x at %x\n", header_type, (uint32_t)offset); goto fail; @@ -1319,7 +1317,7 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset, if (target_subsong != bao->total_subsongs) return 1; - if (!parse_header(bao, streamFile, offset)) + if (!parse_header(bao, sf, offset)) goto fail; return 1; @@ -1330,8 +1328,8 @@ fail: /* ************************************************************************* */ /* opens a file BAO's companion BAO (memory or stream) */ -static STREAMFILE * open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int is_stream, STREAMFILE *streamFile) { - STREAMFILE *streamBAO = NULL; +static STREAMFILE* open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int is_stream, STREAMFILE* sf) { + STREAMFILE* sf_bao = NULL; char buf[255]; size_t buf_size = 255; @@ -1344,63 +1342,63 @@ static STREAMFILE * open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, in * .forge data can be uncompressed (stream BAOs) and compressed (subfiles per area with memory BAOs). */ if (is_stream) { snprintf(buf,buf_size, "Common_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; strcat(buf,".sbao"); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "English_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "French_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "Spanish_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "German_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "Italian_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "Japanese_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "Korean_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "Russian_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "Czech_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; snprintf(buf,buf_size, "Polish_BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; /* these are all of the languages that were referenced in Assassin's Creed exe (out of each platform), there may be more */ } else { snprintf(buf,buf_size, "BAO_0x%08x", file_id); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; strcat(buf,".bao"); - streamBAO = open_streamfile_by_filename(streamFile, buf); - if (streamBAO) return streamBAO; + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; } goto fail; @@ -1409,33 +1407,33 @@ static STREAMFILE * open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, in goto fail; } - return streamBAO; /* may be NULL */ + return sf_bao; /* may be NULL */ fail: - close_streamfile(streamBAO); + close_streamfile(sf_bao); VGM_LOG("UBI BAO: failed opening atomic BAO id %08x\n", file_id); return NULL; } -static int find_package_bao(uint32_t target_id, STREAMFILE *streamFile, off_t *out_offset, size_t *out_size) { +static int find_package_bao(uint32_t target_id, STREAMFILE* sf, off_t* p_offset, size_t* p_size) { int i; int index_entries; off_t bao_offset; size_t index_size, index_header_size; - index_size = read_32bitLE(0x04, streamFile); + index_size = read_32bitLE(0x04, sf); index_entries = index_size / 0x08; index_header_size = 0x40; /* parse index to get target BAO */ bao_offset = index_header_size + index_size; for (i = 0; i < index_entries; i++) { - uint32_t bao_id = read_32bitLE(index_header_size + 0x08*i + 0x00, streamFile); - size_t bao_size = read_32bitLE(index_header_size + 0x08*i + 0x04, streamFile); + uint32_t bao_id = read_32bitLE(index_header_size + 0x08*i + 0x00, sf); + size_t bao_size = read_32bitLE(index_header_size + 0x08*i + 0x04, sf); if (bao_id == target_id) { - if (out_offset) *out_offset = bao_offset; - if (out_size) *out_size = bao_size; + if (p_offset) *p_offset = bao_offset; + if (p_size) *p_size = bao_size; return 1; } bao_offset += bao_size; @@ -1446,10 +1444,10 @@ static int find_package_bao(uint32_t target_id, STREAMFILE *streamFile, off_t *o /* create a usable streamfile */ -static STREAMFILE * setup_bao_streamfile(ubi_bao_header *bao, STREAMFILE *streamFile) { - STREAMFILE *new_streamFile = NULL; - STREAMFILE *temp_streamFile = NULL; - STREAMFILE *stream_segments[2] = { 0 }; +static STREAMFILE* setup_bao_streamfile(ubi_bao_header* bao, STREAMFILE* sf) { + STREAMFILE* new_sf = NULL; + STREAMFILE* temp_sf = NULL; + STREAMFILE* stream_segments[2] = { 0 }; /* Audio comes in "memory" and "streaming" BAOs. When "prefetched" flag is * on we need to join memory and streamed part as they're stored separately. @@ -1465,104 +1463,104 @@ static STREAMFILE * setup_bao_streamfile(ubi_bao_header *bao, STREAMFILE *stream if (bao->is_atomic) { /* file BAOs re-open new STREAMFILEs so no need to wrap them */ if (bao->is_prefetched) { - new_streamFile = open_atomic_bao(bao->cfg.file_type, bao->prefetch_id, 0, streamFile); - if (!new_streamFile) goto fail; - stream_segments[0] = new_streamFile; + new_sf = open_atomic_bao(bao->cfg.file_type, bao->prefetch_id, 0, sf); + if (!new_sf) goto fail; + stream_segments[0] = new_sf; - new_streamFile = open_clamp_streamfile(stream_segments[0], bao->prefetch_offset, bao->prefetch_size); - if (!new_streamFile) goto fail; - stream_segments[0] = new_streamFile; + new_sf = open_clamp_streamfile(stream_segments[0], bao->prefetch_offset, bao->prefetch_size); + if (!new_sf) goto fail; + stream_segments[0] = new_sf; if (bao->stream_size - bao->prefetch_size != 0) { - new_streamFile = open_atomic_bao(bao->cfg.file_type, bao->stream_id, 1, streamFile); - if (!new_streamFile) goto fail; - stream_segments[1] = new_streamFile; + new_sf = open_atomic_bao(bao->cfg.file_type, bao->stream_id, 1, sf); + if (!new_sf) goto fail; + stream_segments[1] = new_sf; - new_streamFile = open_clamp_streamfile(stream_segments[1], bao->stream_offset, (bao->stream_size - bao->prefetch_size)); - if (!new_streamFile) goto fail; - stream_segments[1] = new_streamFile; + new_sf = open_clamp_streamfile(stream_segments[1], bao->stream_offset, (bao->stream_size - bao->prefetch_size)); + if (!new_sf) goto fail; + stream_segments[1] = new_sf; - new_streamFile = open_multifile_streamfile(stream_segments, 2); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + new_sf = open_multifile_streamfile(stream_segments, 2); + if (!new_sf) goto fail; + temp_sf = new_sf; stream_segments[0] = NULL; stream_segments[1] = NULL; } else { /* weird but happens, streamed chunk is empty in this case */ - temp_streamFile = new_streamFile; + temp_sf = new_sf; stream_segments[0] = NULL; } } else { - new_streamFile = open_atomic_bao(bao->cfg.file_type, bao->stream_id, bao->is_external, streamFile); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + new_sf = open_atomic_bao(bao->cfg.file_type, bao->stream_id, bao->is_external, sf); + if (!new_sf) goto fail; + temp_sf = new_sf; - new_streamFile = open_clamp_streamfile(temp_streamFile, bao->stream_offset, bao->stream_size); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + new_sf = open_clamp_streamfile(temp_sf, bao->stream_offset, bao->stream_size); + if (!new_sf) goto fail; + temp_sf = new_sf; } } else { if (bao->is_prefetched) { - new_streamFile = open_wrap_streamfile(streamFile); - if (!new_streamFile) goto fail; - stream_segments[0] = new_streamFile; + new_sf = open_wrap_streamfile(sf); + if (!new_sf) goto fail; + stream_segments[0] = new_sf; - new_streamFile = open_clamp_streamfile(stream_segments[0], bao->prefetch_offset, bao->prefetch_size); - if (!new_streamFile) goto fail; - stream_segments[0] = new_streamFile; + new_sf = open_clamp_streamfile(stream_segments[0], bao->prefetch_offset, bao->prefetch_size); + if (!new_sf) goto fail; + stream_segments[0] = new_sf; if (bao->stream_size - bao->prefetch_size != 0) { - new_streamFile = open_streamfile_by_filename(streamFile, bao->resource_name); - if (!new_streamFile) { VGM_LOG("UBI BAO: external stream '%s' not found\n", bao->resource_name); goto fail; } - stream_segments[1] = new_streamFile; + new_sf = open_streamfile_by_filename(sf, bao->resource_name); + if (!new_sf) { VGM_LOG("UBI BAO: external stream '%s' not found\n", bao->resource_name); goto fail; } + stream_segments[1] = new_sf; - new_streamFile = open_clamp_streamfile(stream_segments[1], bao->stream_offset, (bao->stream_size - bao->prefetch_size)); - if (!new_streamFile) goto fail; - stream_segments[1] = new_streamFile; - temp_streamFile = NULL; + new_sf = open_clamp_streamfile(stream_segments[1], bao->stream_offset, (bao->stream_size - bao->prefetch_size)); + if (!new_sf) goto fail; + stream_segments[1] = new_sf; + temp_sf = NULL; - new_streamFile = open_multifile_streamfile(stream_segments, 2); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + new_sf = open_multifile_streamfile(stream_segments, 2); + if (!new_sf) goto fail; + temp_sf = new_sf; stream_segments[0] = NULL; stream_segments[1] = NULL; } else { /* weird but happens, streamed chunk is empty in this case */ - temp_streamFile = new_streamFile; + temp_sf = new_sf; stream_segments[0] = NULL; } } else if (bao->is_external) { - new_streamFile = open_streamfile_by_filename(streamFile, bao->resource_name); - if (!new_streamFile) { VGM_LOG("UBI BAO: external stream '%s' not found\n", bao->resource_name); goto fail; } - temp_streamFile = new_streamFile; + new_sf = open_streamfile_by_filename(sf, bao->resource_name); + if (!new_sf) { VGM_LOG("UBI BAO: external stream '%s' not found\n", bao->resource_name); goto fail; } + temp_sf = new_sf; - new_streamFile = open_clamp_streamfile(temp_streamFile, bao->stream_offset, bao->stream_size); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + new_sf = open_clamp_streamfile(temp_sf, bao->stream_offset, bao->stream_size); + if (!new_sf) goto fail; + temp_sf = new_sf; } else { - new_streamFile = open_wrap_streamfile(streamFile); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + new_sf = open_wrap_streamfile(sf); + if (!new_sf) goto fail; + temp_sf = new_sf; - new_streamFile = open_clamp_streamfile(temp_streamFile, bao->stream_offset, bao->stream_size); - if (!new_streamFile) goto fail; - temp_streamFile = new_streamFile; + new_sf = open_clamp_streamfile(temp_sf, bao->stream_offset, bao->stream_size); + if (!new_sf) goto fail; + temp_sf = new_sf; } } - return temp_streamFile; + return temp_sf; fail: close_streamfile(stream_segments[0]); close_streamfile(stream_segments[1]); - close_streamfile(temp_streamFile); + close_streamfile(temp_sf); VGM_LOG("UBI BAO: failed streamfile setup\n"); return NULL; @@ -1570,23 +1568,23 @@ fail: /* ************************************************************************* */ -static void config_bao_endian(ubi_bao_header * bao, off_t offset, STREAMFILE *streamFile) { +static void config_bao_endian(ubi_bao_header* bao, off_t offset, STREAMFILE* sf) { /* Detect endianness using the 'class' field (the 'header skip' field is LE in early * versions, and was removed in later versions). * This could be done once as all BAOs share endianness */ /* negate as fields looks like LE (0xN0000000) */ - bao->big_endian = !guess_endianness32bit(offset+bao->cfg.bao_class, streamFile); + bao->big_endian = !guess_endianness32bit(offset+bao->cfg.bao_class, sf); } -static void config_bao_entry(ubi_bao_header * bao, size_t header_base_size, size_t header_skip) { +static void config_bao_entry(ubi_bao_header* bao, size_t header_base_size, size_t header_skip) { bao->cfg.header_base_size = header_base_size; bao->cfg.header_skip = header_skip; } -static void config_bao_audio_b(ubi_bao_header * bao, off_t stream_size, off_t stream_id, off_t external_flag, off_t loop_flag, int external_and, int loop_and) { +static void config_bao_audio_b(ubi_bao_header* bao, off_t stream_size, off_t stream_id, off_t external_flag, off_t loop_flag, int external_and, int loop_and) { /* audio header base */ bao->cfg.audio_stream_size = stream_size; bao->cfg.audio_stream_id = stream_id; @@ -1595,7 +1593,7 @@ static void config_bao_audio_b(ubi_bao_header * bao, off_t stream_size, off_t st bao->cfg.audio_external_and = external_and; bao->cfg.audio_loop_and = loop_and; } -static void config_bao_audio_m(ubi_bao_header * bao, off_t channels, off_t sample_rate, off_t num_samples, off_t num_samples2, off_t stream_type, off_t prefetch_size) { +static void config_bao_audio_m(ubi_bao_header* bao, off_t channels, off_t sample_rate, off_t num_samples, off_t num_samples2, off_t stream_type, off_t prefetch_size) { /* audio header main */ bao->cfg.audio_channels = channels; bao->cfg.audio_sample_rate = sample_rate; @@ -1607,7 +1605,7 @@ static void config_bao_audio_m(ubi_bao_header * bao, off_t channels, off_t sampl bao->cfg.audio_prefetch_size = prefetch_size; } -static void config_bao_sequence(ubi_bao_header * bao, off_t sequence_count, off_t sequence_single, off_t sequence_loop, off_t entry_size) { +static void config_bao_sequence(ubi_bao_header* bao, off_t sequence_count, off_t sequence_single, off_t sequence_loop, off_t entry_size) { /* sequence header and chain table */ bao->cfg.sequence_sequence_count = sequence_count; bao->cfg.sequence_sequence_single = sequence_single; @@ -1616,7 +1614,7 @@ static void config_bao_sequence(ubi_bao_header * bao, off_t sequence_count, off_ bao->cfg.sequence_entry_number = 0x00; } -static void config_bao_layer_m(ubi_bao_header * bao, off_t stream_id, off_t layer_count, off_t external_flag, off_t stream_size, off_t extra_size, off_t prefetch_size, off_t cue_count, off_t cue_labels, int external_and) { +static void config_bao_layer_m(ubi_bao_header* bao, off_t stream_id, off_t layer_count, off_t external_flag, off_t stream_size, off_t extra_size, off_t prefetch_size, off_t cue_count, off_t cue_labels, int external_and) { /* layer header in the main part */ bao->cfg.layer_stream_id = stream_id; bao->cfg.layer_layer_count = layer_count; @@ -1628,7 +1626,7 @@ static void config_bao_layer_m(ubi_bao_header * bao, off_t stream_id, off_t laye bao->cfg.layer_cue_labels = cue_labels; bao->cfg.layer_external_and = external_and; } -static void config_bao_layer_e(ubi_bao_header * bao, off_t entry_size, off_t sample_rate, off_t channels, off_t stream_type, off_t num_samples) { +static void config_bao_layer_e(ubi_bao_header* bao, off_t entry_size, off_t sample_rate, off_t channels, off_t stream_type, off_t num_samples) { /* layer sub-headers in extra table */ bao->cfg.layer_entry_size = entry_size; bao->cfg.layer_sample_rate = sample_rate; @@ -1637,13 +1635,14 @@ static void config_bao_layer_e(ubi_bao_header * bao, off_t entry_size, off_t sam bao->cfg.layer_num_samples = num_samples; } -static void config_bao_silence_f(ubi_bao_header * bao, off_t duration) { +static void config_bao_silence_f(ubi_bao_header* bao, off_t duration) { /* silence headers in float value */ bao->cfg.silence_duration_float = duration; } -static int config_bao_version(ubi_bao_header * bao, STREAMFILE *streamFile) { +static int config_bao_version(ubi_bao_header* bao, STREAMFILE* sf) { + uint32_t version; /* Ubi BAO evolved from Ubi SB and are conceptually quite similar, see that first. * @@ -1661,7 +1660,7 @@ static int config_bao_version(ubi_bao_header * bao, STREAMFILE *streamFile) { * - 0x60000000: unused? * - 0x70000000: info? has a count+table of id-things * - 0x80000000: unknown (some floats?) - * - 0x90000000: unknown (some kind of command config?), rare [Ghost Recon Future Soldier (PC)] + * - 0x90000000: unknown (some kind of command config?), rare [Ghost Recon Future Soldier (PC), Drawsome (Wii)] * Class 1/2/3 are roughly equivalent to Ubi SB's section1/2/3, and class 4 is * basically .spN project files. * @@ -1706,10 +1705,26 @@ static int config_bao_version(ubi_bao_header * bao, STREAMFILE *streamFile) { bao->cfg.header_id = 0x00; bao->cfg.header_type = 0x04; + version = bao->version; + + /* 2 configs with same ID, autodetect */ + if (version == 0x00220015) { + off_t header_size = 0x40 + read_32bitLE(0x04, sf); /* first is always LE */ + + /* next BAO uses machine endianness, entry should always exist + * (maybe should use project BAO to detect?) */ + if (guess_endianness32bit(header_size + 0x04, sf)) { + version |= 0xFF00; /* signal Wii=BE, but don't modify original */ + } + } + + /* config per version*/ - switch(bao->version) { + switch(version) { case 0x001B0100: /* Assassin's Creed (PS3/X360/PC)-atomic-forge */ + //case 0x001B0100: /* My Fitness Coach (Wii)-atomic-forge */ + //case 0x001B0100: /* Your Shape featuring Jenny McCarthy (Wii)-atomic-forge */ config_bao_entry(bao, 0xA4, 0x28); /* PC: 0xA8, PS3/X360: 0xA4 */ config_bao_audio_b(bao, 0x08, 0x1c, 0x28, 0x34, 1, 1); /* 0x2c: prefetch flag? */ @@ -1737,6 +1752,7 @@ static int config_bao_version(ubi_bao_header * bao, STREAMFILE *streamFile) { case 0x001F0011: /* Naruto: The Broken Bond (X360)-package */ case 0x0021000C: /* Splinter Cell: Conviction (E3 2009 Demo)(X360)-package */ case 0x0022000D: /* Just Dance (Wii)-package */ + case 0x0022FF15: /* James Cameron's Avatar: The Game (Wii)-package */ case 0x0022001B: /* Prince of Persia: The Forgotten Sands (Wii)-package */ config_bao_entry(bao, 0xA4, 0x28); /* PC/Wii: 0xA8 */ @@ -1828,7 +1844,19 @@ static int config_bao_version(ubi_bao_header * bao, STREAMFILE *streamFile) { bao->cfg.file_type = UBI_FORGE_b; return 1; - case 0x00280303: /* Tom Clancy's Ghost Recon Future Soldier (PC/PS3)-pk */ + case 0x00270102: /* Drawsome (Wii)-package */ + config_bao_entry(bao, 0xAC, 0x28); + + config_bao_audio_b(bao, 0x08, 0x28, 0x2c, 0x38, 1, 1); + config_bao_audio_m(bao, 0x44, 0x4c, 0x54, 0x5c, 0x64, 0x70); + + config_bao_sequence(bao, 0x38, 0x2c, 0x28, 0x14); + + bao->cfg.codec_map[0x02] = UBI_IMA; + + return 1; + + case 0x00280303: /* Tom Clancy's Ghost Recon Future Soldier (PC/PS3)-package */ config_bao_entry(bao, 0xBC, 0x28); /* PC/PS3: 0xBC */ config_bao_audio_b(bao, 0x08, 0x38, 0x3c, 0x48, 1, 1); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c index 4e8200b84..ceafaa04b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_lyn.c @@ -154,7 +154,6 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) { #ifdef VGM_USE_MPEG case 0x5051: { /* MPEG (PS3/PC), interleaved 1ch */ - mpeg_codec_data *mpeg_data = NULL; mpeg_custom_config cfg = {0}; int i; @@ -175,9 +174,8 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) { cfg.data_size = data_size; //todo data parsing looks correct but some files decode a bit wrong at the end (ex. Tintin: Music~Boss~Allan~Victory~02) - mpeg_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_LYN, &cfg); - if (!mpeg_data) goto fail; - vgmstream->codec_data = mpeg_data; + vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_LYN, &cfg); + if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; break; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c index cef0109ea..92e96985e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c @@ -120,6 +120,7 @@ typedef struct { int is_dat; int is_ps2_bnm; int is_blk; + int has_numbered_banks; STREAMFILE* sf_header; uint32_t version; /* 16b+16b major+minor version */ uint32_t version_empty; /* map sbX versions are empty */ @@ -631,24 +632,20 @@ static VGMSTREAM *init_vgmstream_ubi_dat_main(ubi_sb_header *sb, STREAMFILE *sf_ STREAMFILE *sf_data = NULL; if (sb->is_external) { - if (strcmp(sb->resource_name, "silence.wav") == 0) { - /* some Rayman 2 banks reference non-existent silence.wav, looks like some kind of hack? */ - sb->duration = (float)(sb->stream_size / sb->channels / 2) / (float)sb->sample_rate; - return init_vgmstream_ubi_sb_silence(sb, sf_index, sf); - } - sf_data = open_streamfile_by_filename(sf, sb->resource_name); if (!sf_data) { - VGM_LOG("UBI DAT: no matching KAT found\n"); - goto fail; + /* play silence if external file is not found since Rayman 2 seems to rely on this behavior */ + VGM_LOG("UBI DAT: external stream '%s' not found\n", sb->resource_name); + strncat(sb->readable_name, " (missing)", sizeof(sb->readable_name)); + sb->duration = (float)pcm_bytes_to_samples(sb->stream_size, sb->channels, 16) / (float)sb->sample_rate; + return init_vgmstream_ubi_sb_silence(sb, sf_index, sf); } } /* DAT banks don't work with raw audio data, they open full external files and rely almost entirely * on their metadata, that's why we're handling this here, separately from other types */ switch (sb->stream_type) { - case 0x01: - { + case 0x01: { if (!sb->is_external) { /* Dreamcast bank */ if (sb->version == 0x00000000) { uint32_t entry_offset, start_offset, num_samples, codec; @@ -709,17 +706,14 @@ static VGMSTREAM *init_vgmstream_ubi_dat_main(ubi_sb_header *sb, STREAMFILE *sf_ goto fail; } } else { /* raw PCM */ - uint32_t data_size; - vgmstream = allocate_vgmstream(sb->channels, sb->loop_flag); if (!vgmstream) goto fail; - data_size = get_streamfile_size(sf_data) - sb->stream_offset; - + /* TODO: some WAVs pop at the end because of LIST chunk, doesn't happen in-game [Donald Duck (DC)] */ vgmstream->coding_type = coding_PCM16LE; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x02; - vgmstream->num_samples = pcm_bytes_to_samples(data_size, sb->channels, 16); + vgmstream->num_samples = pcm_bytes_to_samples(sb->stream_size, sb->channels, 16); vgmstream->loop_start_sample = sb->loop_start; vgmstream->loop_end_sample = vgmstream->num_samples; @@ -728,7 +722,7 @@ static VGMSTREAM *init_vgmstream_ubi_dat_main(ubi_sb_header *sb, STREAMFILE *sf_ } break; } - case 0x04:{ /* standard WAV */ + case 0x04: { /* standard WAV */ if (!sb->is_external) { VGM_LOG("Ubi DAT: Found RAM stream_type 0x04\n"); goto fail; @@ -1025,7 +1019,6 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h vgmstream->interleave_block_size = 0x02; if (vgmstream->num_samples == 0) { /* happens in .bnm */ - //todo with external wav streams stream_size may be off? vgmstream->num_samples = pcm_bytes_to_samples(sb->stream_size, sb->channels, 16); vgmstream->loop_end_sample = vgmstream->num_samples; } @@ -1108,7 +1101,7 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h block_align = 0x98 * sb->channels; encoder_delay = 1024 + 69*2; /* approximate */ - vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf_data, start_offset,sb->stream_size, sb->num_samples,sb->channels,sb->sample_rate, block_align, encoder_delay); + vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf_data, start_offset, sb->stream_size, sb->num_samples, sb->channels, sb->sample_rate, block_align, encoder_delay); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -1118,8 +1111,8 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h //TODO: Ubi XMA1 (raw or fmt) is a bit strange, FFmpeg decodes some frames slightly wrong // XMA1 normally has a frame counter in the first nibble but Ubi's is always set to 0. // Probably a beta/custom encoder that creates some buggy frames, that a real X360 handles ok, but trips FFmpeg + // xmaencode decodes correctly if counters are fixed (otherwise has clicks on every frame). case FMT_XMA1: { - ffmpeg_codec_data *ffmpeg_data; uint8_t buf[0x100]; uint32_t sec1_num, sec2_num, sec3_num, bits_per_frame; uint8_t flag; @@ -1150,9 +1143,8 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, 0x100, header_offset, chunk_size, data_size, sf_data, 1); - ffmpeg_data = init_ffmpeg_header_offset(sf_data, buf, bytes, start_offset, data_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_header_offset(sf_data, buf, bytes, start_offset, data_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -1161,7 +1153,6 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h } case RAW_XMA1: { - ffmpeg_codec_data *ffmpeg_data; uint8_t buf[0x100]; size_t bytes, chunk_size; off_t header_offset; @@ -1176,9 +1167,8 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, 0x100, header_offset, chunk_size, sb->stream_size, sf_head, 1); - ffmpeg_data = init_ffmpeg_header_offset(sf_data, buf, bytes, start_offset, sb->stream_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_header_offset(sf_data, buf, bytes, start_offset, sb->stream_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -1417,7 +1407,7 @@ static VGMSTREAM* init_vgmstream_ubi_sb_sequence(ubi_sb_header* sb, STREAMFILE* /* bnm sequences may use to entries from other banks, do some voodoo */ - if (sb->is_bnm || sb->is_dat || sb->is_ps2_bnm) { + if (sb->has_numbered_banks) { /* see if *current* bank has changed (may use a different bank N times) */ if (is_other_bank(sb, sf_bank, sb->sequence_banks[i])) { char bank_name[255]; @@ -1954,7 +1944,7 @@ static int parse_type_sequence(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) uint32_t entry_number = (uint32_t)read_32bit(table_offset+sb->cfg.sequence_entry_number, sf); /* bnm sequences may refer to entries from different banks, whee */ - if (sb->is_bnm || sb->is_dat || sb->is_ps2_bnm) { + if (sb->has_numbered_banks) { int16_t bank_number = (entry_number >> 16) & 0xFFFF; entry_number = (entry_number >> 00) & 0xFFFF; @@ -2120,7 +2110,7 @@ static int parse_type_random(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) { uint32_t entry_number = (uint32_t)read_32bit(table_offset+0x00, sf); //uint32_t entry_chance = (uint32_t)read_32bit(table_offset+0x04, sf); - if (sb->is_bnm) { + if (sb->has_numbered_banks) { int16_t bank_number = (entry_number >> 16) & 0xFFFF; entry_number = (entry_number >> 00) & 0xFFFF; @@ -2742,13 +2732,32 @@ static void config_sb_silence_f(ubi_sb_header* sb, off_t duration) { /* silence headers in float value */ sb->cfg.silence_duration_float = duration; } - static void config_sb_random_old(ubi_sb_header* sb, off_t sequence_count, off_t entry_size) { sb->cfg.random_sequence_count = sequence_count; sb->cfg.random_entry_size = entry_size; sb->cfg.random_percent_int = 1; } +static int check_project_file(STREAMFILE *sf_header, const char *name, int has_localized_banks) { + STREAMFILE *sf_test = open_streamfile_by_filename(sf_header, name); + if (sf_test) { + close_streamfile(sf_test); + return 1; + } + + if (has_localized_banks) { /* try again for localized subfolders */ + char buf[PATH_LIMIT]; + snprintf(buf, PATH_LIMIT, "../%s", name); + sf_test = open_streamfile_by_filename(sf_header, buf); + if (sf_test) { + close_streamfile(sf_test); + return 1; + } + } + + return 0; +} + static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { int is_ttse_pc = 0; int is_bia_ps2 = 0, is_biadd_psp = 0; @@ -2854,6 +2863,10 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { /* games <= 0x00100000 seem to use old types, rest new types */ + if (sb->is_bnm || sb->is_dat || sb->is_ps2_bnm) { + /* these all have names in BNK_%num% format and can reference each other by index */ + sb->has_numbered_banks = 1; + } /* maybe 0x20/0x24 for some but ok enough (null terminated) */ sb->cfg.resource_name_size = 0x28; /* min for Brother in Arms 2 (PS2) */ @@ -2943,10 +2956,8 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { /* two configs with same id; use SND file as identifier */ if (sb->version == 0x00000000 && sb->platform == UBI_PC) { - STREAMFILE* test_sf = open_streamfile_by_filename(sf, "Dino.lcb"); - if (test_sf) { + if (check_project_file(sf, "Dino.lcb", 0)) { sb->version = 0x00000200; /* some files in Dinosaur use this, probably final version */ - close_streamfile(test_sf); } } @@ -2959,11 +2970,9 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { /* Tonic Touble beta has garbage instead of version */ if (sb->is_bnm && sb->version > 0x00000000 && sb->platform == UBI_PC) { - STREAMFILE* test_sf = open_streamfile_by_filename(sf, "ED_MAIN.LCB"); - if (test_sf) { + if (check_project_file(sf, "ED_MAIN.LCB", 0)) { is_ttse_pc = 1; sb->version = 0x00000000; - close_streamfile(test_sf); } } @@ -3297,12 +3306,8 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { /* two configs with same id; use project file as identifier */ if (sb->version == 0x000A0007 && sb->platform == UBI_PS2) { - STREAMFILE* test_sf = open_streamfile_by_filename(sf, "BIAAUDIO.SP1"); - if (!test_sf) /* try again for localized subfolders */ - test_sf = open_streamfile_by_filename(sf, "../BIAAUDIO.SP1"); - if (test_sf) { + if (check_project_file(sf, "BIAAUDIO.SP1", 1)) { is_bia_ps2 = 1; - close_streamfile(test_sf); } } @@ -3476,10 +3481,8 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { /* two configs with same id and both sb4/sm4; use project file as identifier */ if (sb->version == 0x0012000C && sb->platform == UBI_PSP) { - STREAMFILE* test_sf = open_streamfile_by_filename(sf, "BIAAUDIO.SP4"); - if (test_sf) { + if (check_project_file(sf, "BIAAUDIO.SP4", 1)) { is_biadd_psp = 1; - close_streamfile(test_sf); } } @@ -3743,10 +3746,8 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { /* two configs with same id; use project file as identifier */ if (sb->version == 0x00180006 && sb->platform == UBI_PC) { - STREAMFILE* test_sf = open_streamfile_by_filename(sf, "Sc4_online_SoundProject.SP0"); - if (test_sf) { + if (check_project_file(sf, "Sc4_online_SoundProject.SP0", 1)) { is_sc4_pc_online = 1; - close_streamfile(test_sf); } } @@ -3757,6 +3758,8 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { config_sb_audio_fs(sb, 0x2c, 0x34, 0x30); config_sb_audio_he(sb, 0x5c, 0x54, 0x40, 0x48, 0x64, 0x60); + config_sb_sequence(sb, 0x2c, 0x14); + config_sb_layer_he(sb, 0x20, 0x38, 0x3c, 0x44); config_sb_layer_sh(sb, 0x34, 0x00, 0x08, 0x0c, 0x14); return 1; @@ -3769,6 +3772,8 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { config_sb_audio_fs(sb, 0x2c, 0x34, 0x30); config_sb_audio_he(sb, 0x5c, 0x54, 0x40, 0x48, 0x64, 0x60); + config_sb_sequence(sb, 0x2c, 0x14); + config_sb_layer_he(sb, 0x20, 0x38, 0x3c, 0x44); config_sb_layer_sh(sb, 0x34, 0x00, 0x08, 0x0c, 0x14); return 1; @@ -3782,6 +3787,8 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { config_sb_audio_he(sb, 0x5c, 0x54, 0x40, 0x48, 0x64, 0x60); sb->cfg.audio_xma_offset = 0x70; + config_sb_sequence(sb, 0x2c, 0x14); + config_sb_layer_he(sb, 0x20, 0x38, 0x3c, 0x44); config_sb_layer_sh(sb, 0x34, 0x00, 0x08, 0x0c, 0x14); return 1; @@ -3834,22 +3841,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { return 1; } - /* TMNT (2007)(GC)-bank */ - if (sb->version == 0x00190002 && sb->platform == UBI_GC) { - config_sb_entry(sb, 0x68, 0x6c); - - config_sb_audio_fs(sb, 0x28, 0x2c, 0x30); /* assumed groud_id */ - config_sb_audio_he(sb, 0x3c, 0x40, 0x48, 0x50, 0x58, 0x5c); - - config_sb_sequence(sb, 0x2c, 0x14); - - config_sb_layer_he(sb, 0x20, 0x34, 0x38, 0x40); - config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10); - - config_sb_silence_f(sb, 0x1c); - return 1; - } - /* TMNT (2007)(PS2)-bank */ if (sb->version == 0x00190002 && sb->platform == UBI_PS2) { config_sb_entry(sb, 0x48, 0x5c); @@ -3866,6 +3857,24 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { return 1; } + /* TMNT (2007)(GC)-bank */ + /* Surf's Up (2007)(GC)-bank 0x00190005 */ + if ((sb->version == 0x00190002 && sb->platform == UBI_GC) || + (sb->version == 0x00190005 && sb->platform == UBI_GC)) { + config_sb_entry(sb, 0x68, 0x6c); + + config_sb_audio_fs(sb, 0x28, 0x2c, 0x30); /* assumed groud_id */ + config_sb_audio_he(sb, 0x3c, 0x40, 0x48, 0x50, 0x58, 0x5c); + + config_sb_sequence(sb, 0x2c, 0x14); + + config_sb_layer_he(sb, 0x20, 0x34, 0x38, 0x40); + config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10); + + config_sb_silence_f(sb, 0x1c); + return 1; + } + /* TMNT (2007)(X360)-bank 0x00190002 */ /* My Word Coach (2007)(Wii)-bank 0x00190002 */ /* Prince of Persia: Rival Swords (2007)(Wii)-bank 0x00190003 */ @@ -3964,8 +3973,13 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) { return 1; } + /* Petz Sports: Dog Playground (2008)(Wii)-bank */ + /* Cloudy with a Chance of Meatballs (2009)(Wii)-bank */ + /* Grey's Anatomy: The Video Game (2009)(Wii)-bank */ + /* NCIS: Based on the TV Series (2011)(Wii)-bank */ /* Splinter Cell Classic Trilogy HD (2011)(PS3)-map */ - if (sb->version == 0x001D0000 && sb->platform == UBI_PS3) { + if ((sb->version == 0x001D0000 && sb->platform == UBI_PS3) || + (sb->version == 0x001D0000 && sb->platform == UBI_WII)) { config_sb_entry(sb, 0x5c, 0x80); sb->cfg.audio_interleave = 0x10; sb->cfg.audio_fix_psx_samples = 1; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vag.c b/Frameworks/vgmstream/vgmstream/src/meta/vag.c index eba4bdb55..f248b1538 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/vag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/vag.c @@ -20,9 +20,8 @@ VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) { * .str: Ben10 Galactic Racing * .vig: MX vs. ATV Untamed (PS2) * .l/r: Crash Nitro Kart (PS2), Gradius V (PS2) - * .vas: Kingdom Hearts II (PS2) - * .khv: fake for .vas */ - if ( !check_extensions(streamFile,"vag,swag,str,vig,l,r,vas,khv") ) + * .vas: Kingdom Hearts II (PS2) */ + if ( !check_extensions(streamFile,"vag,swag,str,vig,l,r,vas") ) goto fail; /* check VAG Header */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/vawx.c b/Frameworks/vgmstream/vgmstream/src/meta/vawx.c deleted file mode 100644 index fceddac19..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/vawx.c +++ /dev/null @@ -1,107 +0,0 @@ -#include "meta.h" -#include "../layout/layout.h" -#include "../coding/coding.h" - - -/* VAWX - found in feelplus games [No More Heroes: Heroes Paradise (PS3/X360), Moon Diver (PS3/X360)] */ -VGMSTREAM * init_vgmstream_vawx(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset, data_size; - int loop_flag = 0, channel_count, codec; - - - /* checks */ - /* .xwv: actual extension [Moon Diver (PS3/X360)] - * .vawx: header id */ - if ( !check_extensions(streamFile, "xwv,vawx") ) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x56415758) /* "VAWX" */ - goto fail; - - loop_flag = read_8bit(0x37,streamFile); - channel_count = read_8bit(0x39,streamFile); - start_offset = 0x800; /* ? read_32bitLE(0x0c,streamFile); */ - codec = read_8bit(0x36,streamFile); /* could be at 0x38 too */ - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* 0x04: filesize */ - /* 0x16: file id */ - vgmstream->num_samples = read_32bitBE(0x3c,streamFile); - vgmstream->sample_rate = read_32bitBE(0x40,streamFile); - - vgmstream->meta_type = meta_VAWX; - - switch(codec) { - case 2: /* PS-ADPCM */ - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = channel_count == 6 ? layout_blocked_vawx : layout_interleave; - vgmstream->interleave_block_size = 0x10; - - vgmstream->loop_start_sample = read_32bitBE(0x44,streamFile); - vgmstream->loop_end_sample = read_32bitBE(0x48,streamFile); - - break; - -#ifdef VGM_USE_FFMPEG - case 1: { /* XMA2 */ - ffmpeg_codec_data *ffmpeg_data = NULL; - uint8_t buf[0x100]; - int32_t bytes, block_size, block_count; - - data_size = get_streamfile_size(streamFile)-start_offset; - block_size = 0x10000; /* VAWX default */ - block_count = (uint16_t)read_16bitBE(0x3A, streamFile); /* also at 0x56 */ - - bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - vgmstream->loop_start_sample = read_32bitBE(0x44,streamFile); - vgmstream->loop_end_sample = read_32bitBE(0x48,streamFile); - - //todo fix loops/samples vs ATRAC3 - /* may be only applying end_skip to num_samples? */ - xma_fix_raw_samples(vgmstream, streamFile, start_offset,data_size, 0, 0,0); - break; - } - - case 7: { /* ATRAC3 */ - int block_align, encoder_delay; - - data_size = read_32bitBE(0x54,streamFile); - block_align = 0x98 * vgmstream->channels; - encoder_delay = 1024 + 69*2; /* observed default, matches XMA (needed as many files start with garbage) */ - vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_align) - encoder_delay; /* original samples break looping in some files otherwise */ - - vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ - vgmstream->loop_start_sample = atrac3_bytes_to_samples(read_32bitBE(0x44,streamFile), block_align); //- encoder_delay - vgmstream->loop_end_sample = atrac3_bytes_to_samples(read_32bitBE(0x48,streamFile), block_align) - encoder_delay; - break; - } -#endif - default: - 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/wady.c b/Frameworks/vgmstream/vgmstream/src/meta/wady.c new file mode 100644 index 000000000..575d2faae --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/wady.c @@ -0,0 +1,62 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* WADY - from Marble engine games [Eien no Owari ni (PC), Elf no Futagohime (PC)] */ +VGMSTREAM* init_vgmstream_wady(STREAMFILE *sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channels, sample_rate, scale, num_samples; + size_t codec_size; + + + /* checks */ + /* .way/extensionless: found in some bigfiles */ + if (!check_extensions(sf, "way,")) + goto fail; + if (read_u32be(0x00,sf) != 0x57414459) /* "WADY" */ + goto fail; + + /* 0x04: none */ + scale = read_u8(0x05,sf); + channels = read_u16le(0x06,sf); + sample_rate = read_s32le(0x08,sf); + codec_size = read_u32le(0x0c,sf); + num_samples = read_s32le(0x10,sf); + /* 0x14: PCM size? */ + /* 0x18/1c: null */ + /* 0x20: fmt-like subheader (codec/channels/srate/bitrate/bps/spf) */ + + start_offset = 0x30; + loop_flag = 0; + + //todo implement + /* codec variation used in SFX */ + if (codec_size + start_offset != get_streamfile_size(sf)) + goto fail; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_WADY; + vgmstream->sample_rate = sample_rate; + vgmstream->coding_type = coding_WADY; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x01; + vgmstream->num_samples = num_samples; + + { + int i; + for (i = 0; i < channels; i++) { + vgmstream->ch[i].adpcm_scale = scale; + } + } + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c index 983e922e5..15a9307d8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -7,19 +7,30 @@ * There is some repetition from other metas, but not enough to bother me. * * Some info: https://www.audiokinetic.com/en/library/edge/ + * .bnk (dynamic music/loop) info: https://github.com/bnnm/wwiser */ -typedef enum { PCM, IMA, VORBIS, DSP, XMA2, XWMA, AAC, HEVAG, ATRAC9, OPUSNX, OPUS, PTADPCM } wwise_codec; +typedef enum { PCM, IMA, VORBIS, DSP, XMA2, XWMA, AAC, HEVAG, ATRAC9, OPUSNX, OPUS, OPUSWW, PTADPCM } wwise_codec; typedef struct { int big_endian; size_t file_size; int truncated; /* chunks references */ - off_t fmt_offset; + off_t fmt_offset; size_t fmt_size; - off_t data_offset; + off_t data_offset; size_t data_size; - off_t chunk_offset; + off_t xma2_offset; + size_t xma2_size; + off_t vorb_offset; + size_t vorb_size; + off_t wiih_offset; + size_t wiih_size; + off_t smpl_offset; + size_t smpl_size; + off_t seek_offset; + size_t seek_size; + /* standard fmt stuff */ wwise_codec codec; @@ -38,16 +49,19 @@ typedef struct { int32_t loop_end_sample; } wwise_header; +static int parse_wwise(STREAMFILE* sf, wwise_header* ww); static int is_dsp_full_interleave(STREAMFILE* sf, wwise_header* ww, off_t coef_offset); /* Wwise - Audiokinetic Wwise (Wave Works Interactive Sound Engine) middleware */ -VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; wwise_header ww = {0}; - off_t start_offset, first_offset = 0xc; - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + off_t start_offset; + uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; + int32_t (*read_s32)(off_t,STREAMFILE*) = NULL; + uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL; + /* checks */ /* .wem: newer "Wwise Encoded Media" used after the 2011.2 SDK (~july 2011) @@ -58,193 +72,24 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { if (!check_extensions(sf,"wem,wav,lwav,ogg,logg,xma,bnk")) goto fail; - if (read_32bitBE(0x00,sf) != 0x52494646 && /* "RIFF" (LE) */ - read_32bitBE(0x00,sf) != 0x52494658) /* "RIFX" (BE) */ - goto fail; - if (read_32bitBE(0x08,sf) != 0x57415645 && /* "WAVE" */ - read_32bitBE(0x08,sf) != 0x58574D41) /* "XWMA" */ + if (!parse_wwise(sf, &ww)) goto fail; - ww.big_endian = read_32bitBE(0x00,sf) == 0x52494658; /* RIFX */ - if (ww.big_endian) { /* Wwise honors machine's endianness (PC=RIFF, X360=RIFX --unlike XMA) */ - read_32bit = read_32bitBE; - read_16bit = read_16bitBE; - } else { - read_32bit = read_32bitLE; - read_16bit = read_16bitLE; - } - - ww.file_size = sf->get_size(sf); - -#if 0 - /* Wwise's RIFF size is often wonky, seemingly depending on codec: - * - PCM, IMA/PTADPCM, VORBIS, AAC, OPUSNX/OPUS: correct - * - DSP, XWMA, ATRAC9: almost always slightly smaller (around 0x50) - * - HEVAG: very off - * - XMA2: exact file size - * - some RIFX have LE size - * (later we'll validate "data" which fortunately is correct) - */ - if (read_32bit(0x04,sf)+0x04+0x04 != ww.file_size) { - VGM_LOG("WWISE: bad riff size (real=0x%x vs riff=0x%x)\n", read_32bit(0x04,sf)+0x04+0x04, ww.file_size); - goto fail; - } -#endif - - /* ignore LyN RIFF */ - { - off_t fact_offset; - size_t fact_size; - - if (find_chunk(sf, 0x66616374,first_offset,0, &fact_offset,&fact_size, 0, 0)) { /* "fact" */ - if (fact_size == 0x10 && read_32bitBE(fact_offset+0x04, sf) == 0x4C794E20) /* "LyN " */ - goto fail; /* parsed elsewhere */ - } - /* Wwise doesn't use "fact", though */ - } - - /* parse format (roughly spec-compliant but some massaging is needed) */ - { - off_t loop_offset; - size_t loop_size; - - /* find basic chunks */ - if (read_32bitBE(0x0c, sf) == 0x584D4132) { /* "XMA2" with no "fmt" [Too Human (X360)] */ - ww.format = 0x0165; /* signal for below */ - } - else { - if (!find_chunk(sf, 0x666d7420,first_offset,0, &ww.fmt_offset,&ww.fmt_size, ww.big_endian, 0)) /* "fmt " */ - goto fail; - if (ww.fmt_size < 0x12) - goto fail; - ww.format = (uint16_t)read_16bit(ww.fmt_offset+0x00,sf); - } - - - if (ww.format == 0x0165) { - /* pseudo-XMA2WAVEFORMAT ("fmt"+"XMA2" or just "XMA2) */ - if (!find_chunk(sf, 0x584D4132,first_offset,0, &ww.chunk_offset,NULL, ww.big_endian, 0)) /* "XMA2" */ - goto fail; - xma2_parse_xma2_chunk(sf, ww.chunk_offset,&ww.channels,&ww.sample_rate, &ww.loop_flag, &ww.num_samples, &ww.loop_start_sample, &ww.loop_end_sample); - } - else { - /* pseudo-WAVEFORMATEX */ - ww.channels = read_16bit(ww.fmt_offset+0x02,sf); - ww.sample_rate = read_32bit(ww.fmt_offset+0x04,sf); - ww.average_bps = read_32bit(ww.fmt_offset+0x08,sf);/* bytes per sec */ - ww.block_align = (uint16_t)read_16bit(ww.fmt_offset+0x0c,sf); - ww.bits_per_sample = (uint16_t)read_16bit(ww.fmt_offset+0x0e,sf); - if (ww.fmt_size > 0x10 && ww.format != 0x0165 && ww.format != 0x0166) /* ignore XMAWAVEFORMAT */ - ww.extra_size = (uint16_t)read_16bit(ww.fmt_offset+0x10,sf); - if (ww.extra_size >= 0x06) { /* always present (actual RIFFs only have it in WAVEFORMATEXTENSIBLE) */ - /* mostly WAVEFORMATEXTENSIBLE's bitmask (see AkSpeakerConfig.h) */ - ww.channel_layout = read_32bit(ww.fmt_offset+0x14,sf); - /* later games (+2018?) have a pseudo-format instead to handle more cases: - * - 8b: uNumChannels - * - 4b: eConfigType (0=none, 1=standard, 2=ambisonic) - * - 19b: uChannelMask */ - if ((ww.channel_layout & 0xFF) == ww.channels) { - ww.channel_layout = (ww.channel_layout >> 12); - } - } - } - - /* find loop info ("XMA2" chunks already read them) */ - if (ww.format == 0x0166) { /* XMA2WAVEFORMATEX in fmt */ - ww.chunk_offset = ww.fmt_offset; - xma2_parse_fmt_chunk_extra(sf, ww.chunk_offset, &ww.loop_flag, &ww.num_samples, &ww.loop_start_sample, &ww.loop_end_sample, ww.big_endian); - } - else if (find_chunk(sf, 0x736D706C,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /* "smpl", common */ - if (loop_size >= 0x34 - && read_32bit(loop_offset+0x1c, sf)==1 /* loop count */ - && read_32bit(loop_offset+0x24+4, sf)==0) { - ww.loop_flag = 1; - ww.loop_start_sample = read_32bit(loop_offset+0x24+0x8, sf); - ww.loop_end_sample = read_32bit(loop_offset+0x24+0xc, sf) + 1; /* like standard RIFF */ - } - } - //else if (find_chunk(sf, 0x4C495354,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /*"LIST", common */ - // /* usually contains "cue"s with sample positions for events (ex. Platinum Games) but no real looping info */ - //} - - /* other Wwise specific chunks: - * "JUNK": optional padding for aligment (0-size JUNK exists too) - * "akd ": seem to store extra info for Wwise editor (wave peaks/loudness/HDR envelope?) - */ - - if (!find_chunk(sf, 0x64617461,first_offset,0, &ww.data_offset,&ww.data_size, ww.big_endian, 0)) /* "data" */ - goto fail; - } - - /* format to codec */ - switch(ww.format) { - case 0x0001: ww.codec = PCM; break; /* older Wwise */ - case 0x0002: ww.codec = IMA; break; /* newer Wwise (conflicts with MSADPCM, probably means "platform's ADPCM") */ - case 0x0069: ww.codec = IMA; break; /* older Wwise [Spiderman Web of Shadows (X360), LotR Conquest (PC)] */ - case 0x0161: ww.codec = XWMA; break; /* WMAv2 */ - case 0x0162: ww.codec = XWMA; break; /* WMAPro */ - case 0x0165: ww.codec = XMA2; break; /* XMA2-chunk XMA (Wwise doesn't use XMA1) */ - case 0x0166: ww.codec = XMA2; break; /* fmt-chunk XMA */ - case 0xAAC0: ww.codec = AAC; break; - case 0xFFF0: ww.codec = DSP; break; - case 0xFFFB: ww.codec = HEVAG; break; - case 0xFFFC: ww.codec = ATRAC9; break; - case 0xFFFE: ww.codec = PCM; break; /* "PCM for Wwise Authoring" */ - case 0xFFFF: ww.codec = VORBIS; break; - case 0x3039: ww.codec = OPUSNX; break; /* later renamed from "OPUS" */ - case 0x3040: ww.codec = OPUS; break; - case 0x8311: ww.codec = PTADPCM; break; /* newer, rare [Genshin Impact (PC)] */ - default: - goto fail; - } - - /* identify system's ADPCM */ - if (ww.format == 0x0002) { - if (ww.extra_size == 0x0c + ww.channels * 0x2e) { - /* newer Wwise DSP with coefs [Epic Mickey 2 (Wii), Batman Arkham Origins Blackgate (3DS)] */ - ww.codec = DSP; - } else if (ww.extra_size == 0x0a && find_chunk(sf, 0x57696948, first_offset,0, NULL,NULL, ww.big_endian, 0)) { /* WiiH */ - /* few older Wwise DSP with num_samples in extra_size [Tony Hawk: Shred (Wii)] */ - ww.codec = DSP; - } else if (ww.block_align == 0x104 * ww.channels) { - ww.codec = PTADPCM; /* Bayonetta 2 (Switch) */ - } - } - - - /* Some Wwise .bnk (RAM) files have truncated, prefetch mirrors of another file, that - * play while the rest of the real stream loads. We'll add basic support to avoid - * complaints of this or that .wem not playing */ - if (ww.data_offset + ww.data_size > ww.file_size) { - //VGM_LOG("WWISE: truncated data size (prefetch): (real=0x%x > riff=0x%x)\n", ww.data_size, ww.file_size); - - /* catch wrong rips as truncated tracks' file_size should be much smaller than data_size, - * but it's possible to pre-fetch small files too [Punch Out!! (Wii)] */ - if (ww.data_offset + ww.data_size - ww.file_size < 0x5000 && ww.file_size > 0x10000) { - VGM_LOG("WWISE: wrong expected data_size\n"); - goto fail; - } - - if (ww.codec == PCM || ww.codec == IMA || ww.codec == DSP || ww.codec == VORBIS || ww.codec == XMA2 || ww.codec == OPUSNX || ww.codec == OPUS) { - ww.truncated = 1; /* only seen those, probably all exist */ - } else { - VGM_LOG("WWISE: wrong size, maybe truncated\n"); - goto fail; - } - } - + read_u32 = ww.big_endian ? read_u32be : read_u32le; + read_s32 = ww.big_endian ? read_s32be : read_s32le; + read_u16 = ww.big_endian ? read_u16be : read_u16le; start_offset = ww.data_offset; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(ww.channels,ww.loop_flag); + vgmstream = allocate_vgmstream(ww.channels, ww.loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_WWISE_RIFF; vgmstream->sample_rate = ww.sample_rate; vgmstream->loop_start_sample = ww.loop_start_sample; vgmstream->loop_end_sample = ww.loop_end_sample; vgmstream->channel_layout = ww.channel_layout; - vgmstream->meta_type = meta_WWISE_RIFF; switch(ww.codec) { case PCM: /* common */ @@ -264,10 +109,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { break; case IMA: /* common */ - /* slightly modified XBOX-IMA */ + /* slightly modified and mono-interleaved XBOX-IMA */ /* Wwise reuses common codec ids (ex. 0x0002 MSADPCM) for IMA so this parser should go AFTER riff.c avoid misdetection */ - if (ww.fmt_size != 0x28 && ww.fmt_size != 0x18) goto fail; /* old, new */ + if (ww.fmt_size != 0x14 && ww.fmt_size != 0x28 && ww.fmt_size != 0x18) goto fail; /* oldest, old, new */ if (ww.bits_per_sample != 4) goto fail; if (ww.block_align != 0x24 * ww.channels) goto fail; @@ -276,6 +121,15 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { vgmstream->interleave_block_size = ww.block_align / ww.channels; vgmstream->codec_endian = ww.big_endian; + /* oldest version uses regular XBOX IMA with stereo mode [Shadowrun (PC)] */ + if (ww.fmt_size == 0x14 && ww.format == 0x0069) { + if (ww.channels > 2) goto fail; /* unlikely but just in case */ + if (ww.big_endian) goto fail; /* unsure */ + vgmstream->coding_type = coding_XBOX_IMA; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = 0; + } + if (ww.truncated) { ww.data_size = ww.file_size - ww.data_offset; } @@ -284,10 +138,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { break; #ifdef VGM_USE_VORBIS - case VORBIS: { /* common */ + case VORBIS: { /* common */ /* Wwise uses custom Vorbis, which changed over time (config must be detected to pass to the decoder). */ - off_t vorb_offset, data_offsets, block_offsets; - size_t vorb_size, setup_offset, audio_offset; + off_t data_offsets, block_offsets; + size_t setup_offset, audio_offset; vorbis_custom_config cfg = {0}; cfg.channels = ww.channels; @@ -297,10 +151,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail; /* always 0 for Worbis */ /* autodetect format (fields are mostly common, see the end of the file) */ - if (find_chunk(sf, 0x766F7262,first_offset,0, &vorb_offset,&vorb_size, ww.big_endian, 0)) { /* "vorb" */ + if (ww.vorb_offset) { /* older Wwise (~<2012) */ - switch(vorb_size) { + switch(ww.vorb_size) { case 0x2C: /* earliest (~2009) [The Lord of the Rings: Conquest (PC)] */ case 0x28: /* early (~2009) [UFC Undisputed 2009 (PS3), some EVE Online Apocrypha (PC)] */ data_offsets = 0x18; @@ -328,22 +182,22 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { break; default: - VGM_LOG("WWISE: unknown vorb size 0x%x\n", vorb_size); + VGM_LOG("WWISE: unknown vorb size 0x%x\n", ww.vorb_size); goto fail; } - vgmstream->num_samples = read_32bit(vorb_offset + 0x00, sf); - setup_offset = read_32bit(vorb_offset + data_offsets + 0x00, sf); /* within data (0 = no seek table) */ - audio_offset = read_32bit(vorb_offset + data_offsets + 0x04, sf); /* within data */ + vgmstream->num_samples = read_s32(ww.vorb_offset + 0x00, sf); + setup_offset = read_u32(ww.vorb_offset + data_offsets + 0x00, sf); /* within data (0 = no seek table) */ + audio_offset = read_u32(ww.vorb_offset + data_offsets + 0x04, sf); /* within data */ if (block_offsets) { - cfg.blocksize_1_exp = read_8bit(vorb_offset + block_offsets + 0x00, sf); /* small */ - cfg.blocksize_0_exp = read_8bit(vorb_offset + block_offsets + 0x01, sf); /* big */ + cfg.blocksize_1_exp = read_u8(ww.vorb_offset + block_offsets + 0x00, sf); /* small */ + cfg.blocksize_0_exp = read_u8(ww.vorb_offset + block_offsets + 0x01, sf); /* big */ } ww.data_size -= audio_offset; /* detect normal packets */ - if (vorb_size == 0x2a) { + if (ww.vorb_size == 0x2a) { /* almost all blocksizes are 0x08+0x0B except a few with 0x0a+0x0a [Captain America: Super Soldier (X360) voices/sfx] */ if (cfg.blocksize_0_exp == cfg.blocksize_1_exp) cfg.packet_type = WWV_STANDARD; @@ -353,12 +207,12 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { * - full inline: ~2009, ex. The King of Fighters XII (X360), The Saboteur (PC) * - trimmed inline: ~2010, ex. Army of Two: 40 days (X360) some multiplayer files * - external: ~2010, ex. Assassin's Creed Brotherhood (X360), Dead Nation (X360) */ - if (vorb_size == 0x34) { - size_t setup_size = (uint16_t)read_16bit(start_offset + setup_offset, sf); - uint32_t id = (uint32_t)read_32bitBE(start_offset + setup_offset + 0x06, sf); + if (ww.vorb_size == 0x34) { + size_t setup_size = read_u16 (start_offset + setup_offset + 0x00, sf); + uint32_t setup_id = read_u32be(start_offset + setup_offset + 0x06, sf); /* if the setup after header starts with "(data)BCV" it's an inline codebook) */ - if ((id & 0x00FFFFFF) == 0x00424356) { /* 0"BCV" */ + if ((setup_id & 0x00FFFFFF) == 0x00424356) { /* 0"BCV" */ cfg.setup_type = WWV_FULL_SETUP; } /* if the setup is suspiciously big it's probably trimmed inline codebooks */ @@ -373,7 +227,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { else { /* newer Wwise (>2012) */ off_t extra_offset = ww.fmt_offset + 0x18; /* after flag + channels */ - int is_wem = check_extensions(sf,"wem"); + int is_wem = check_extensions(sf,"wem,bnk"); /* use extension as a guide for faster vorbis inits */ switch(ww.extra_size) { case 0x30: @@ -382,22 +236,22 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { 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 + /* setup not detectable by header, so we'll try both; libvorbis should 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 ? WWV_AOTUV603_CODEBOOKS : WWV_EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */ break; default: - VGM_LOG("WWISE: unknown extra size 0x%x\n", vorb_size); + VGM_LOG("WWISE: unknown extra size 0x%x\n", ww.vorb_size); goto fail; } - vgmstream->num_samples = read_32bit(extra_offset + 0x00, sf); - setup_offset = read_32bit(extra_offset + data_offsets + 0x00, sf); /* within data */ - audio_offset = read_32bit(extra_offset + data_offsets + 0x04, sf); /* within data */ - cfg.blocksize_1_exp = read_8bit(extra_offset + block_offsets + 0x00, sf); /* small */ - cfg.blocksize_0_exp = read_8bit(extra_offset + block_offsets + 0x01, sf); /* big */ + vgmstream->num_samples = read_s32(extra_offset + 0x00, sf); + setup_offset = read_u32(extra_offset + data_offsets + 0x00, sf); /* within data */ + audio_offset = read_u32(extra_offset + data_offsets + 0x04, sf); /* within data */ + cfg.blocksize_1_exp = read_u8(extra_offset + block_offsets + 0x00, sf); /* small */ + cfg.blocksize_0_exp = read_u8(extra_offset + block_offsets + 0x01, sf); /* big */ ww.data_size -= audio_offset; /* detect normal packets */ @@ -432,10 +286,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { } #endif - case DSP: { /* Wii/3DS/WiiU */ - off_t wiih_offset; - size_t wiih_size; - + case DSP: { /* Wii/3DS/WiiU */ //if (ww.fmt_size != 0x28 && ww.fmt_size != ?) goto fail; /* old, new */ if (ww.bits_per_sample != 4) goto fail; @@ -444,17 +295,17 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { vgmstream->interleave_block_size = 0x08; /* ww.block_align = 0x8 in older Wwise, samples per block in newer Wwise */ /* find coef position */ - if (find_chunk(sf, 0x57696948,first_offset,0, &wiih_offset,&wiih_size, ww.big_endian, 0)) { /*"WiiH", older Wwise */ + if (ww.wiih_offset) { /* older */ vgmstream->num_samples = dsp_bytes_to_samples(ww.data_size, ww.channels); - if (wiih_size != 0x2e * ww.channels) goto fail; + if (ww.wiih_size != 0x2e * ww.channels) goto fail; - if (is_dsp_full_interleave(sf, &ww, wiih_offset)) + if (is_dsp_full_interleave(sf, &ww, ww.wiih_offset)) vgmstream->interleave_block_size = ww.data_size / 2; } - else if (ww.extra_size == 0x0c + ww.channels * 0x2e) { /* newer Wwise */ - vgmstream->num_samples = read_32bit(ww.fmt_offset + 0x18, sf); - wiih_offset = ww.fmt_offset + 0x1c; - wiih_size = 0x2e * ww.channels; + else if (ww.extra_size == 0x0c + ww.channels * 0x2e) { /* newer */ + vgmstream->num_samples = read_s32(ww.fmt_offset + 0x18, sf); + ww.wiih_offset = ww.fmt_offset + 0x1c; + ww.wiih_size = 0x2e * ww.channels; } else { goto fail; @@ -472,27 +323,29 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { vgmstream->loop_flag = 0; } - dsp_read_coefs(vgmstream,sf,wiih_offset + 0x00, 0x2e, ww.big_endian); - dsp_read_hist (vgmstream,sf,wiih_offset + 0x24, 0x2e, ww.big_endian); + dsp_read_coefs(vgmstream, sf, ww.wiih_offset + 0x00, 0x2e, ww.big_endian); + dsp_read_hist (vgmstream, sf, ww.wiih_offset + 0x24, 0x2e, ww.big_endian); break; } #ifdef VGM_USE_FFMPEG - case XMA2: { /* X360/XBone */ + case XMA2: { /* X360/XBone */ uint8_t buf[0x100]; int bytes; - off_t xma2_offset; - size_t xma2_size; /* endian check should be enough */ //if (ww.fmt_size != ...) goto fail; /* XMA1 0x20, XMA2old: 0x34, XMA2new: 0x40, XMA2 Guitar Hero Live/padded: 0x64, etc */ - if (!ww.big_endian) goto fail; /* must be Wwise (real XMA are LE and parsed elsewhere) */ - if (find_chunk(sf, 0x584D4132,first_offset,0, &xma2_offset,&xma2_size, ww.big_endian, 0)) { /*"XMA2"*/ /* older Wwise */ - bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,0x100, xma2_offset, xma2_size, ww.data_size, sf); - } else { /* newer Wwise */ - bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, ww.fmt_offset, ww.fmt_size, ww.data_size, sf, ww.big_endian); + /* only Wwise XMA: X360=BE, or XBone=LE+wem (real X360 XMA are LE and parsed elsewhere) */ + if (!(ww.big_endian || (!ww.big_endian && check_extensions(sf,"wem,bnk")))) + goto fail; + + if (ww.xma2_offset) { /* older */ + bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf, sizeof(buf), ww.xma2_offset, ww.xma2_size, ww.data_size, sf); + } + else { /* newer */ + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf, sizeof(buf), ww.fmt_offset, ww.fmt_size, ww.data_size, sf, ww.big_endian); } vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, ww.data_offset,ww.data_size); @@ -503,23 +356,19 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { vgmstream->num_samples = ww.num_samples; /* set while parsing XMAWAVEFORMATs */ /* Wwise loops are always pre-adjusted (old or new) and only num_samples is off */ - xma_fix_raw_samples(vgmstream, sf, ww.data_offset,ww.data_size, ww.chunk_offset, 1,0); - - /* "XMAc": rare Wwise extension, XMA2 physical loop regions (loop_start_b, loop_end_b, loop_subframe_data) - * Can appear even in the file doesn't loop, maybe it's meant to be the playable physical region */ - //VGM_ASSERT(find_chunk(sf, 0x584D4163,first_offset,0, NULL,NULL, ww.big_endian, 0), "WWISE: XMAc chunk found\n"); - /* other chunks: "seek", regular XMA2 seek table */ + xma_fix_raw_samples(vgmstream, sf, ww.data_offset, ww.data_size, ww.xma2_offset ? ww.xma2_offset : ww.fmt_offset, 1,0); /* XMA is VBR so this is very approximate percent, meh */ if (ww.truncated) { vgmstream->num_samples = (int32_t)(vgmstream->num_samples * (double)(ww.file_size - start_offset) / (double)ww.data_size); + //todo data size, call function } break; } - case XWMA: { /* X360 */ + case XWMA: { /* X360 */ ffmpeg_codec_data *ffmpeg_data = NULL; uint8_t buf[0x100]; int bytes; @@ -527,8 +376,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { if (ww.fmt_size != 0x18) goto fail; if (!ww.big_endian) goto fail; /* must be from Wwise X360 (PC LE XWMA is parsed elsewhere) */ - bytes = ffmpeg_make_riff_xwma(buf,0x100, ww.format, ww.data_size, vgmstream->channels, vgmstream->sample_rate, ww.average_bps, ww.block_align); - ffmpeg_data = init_ffmpeg_header_offset(sf, buf,bytes, ww.data_offset,ww.data_size); + bytes = ffmpeg_make_riff_xwma(buf, sizeof(buf), ww.format, ww.data_size, ww.channels, ww.sample_rate, ww.average_bps, ww.block_align); + ffmpeg_data = init_ffmpeg_header_offset(sf, buf,bytes, ww.data_offset, ww.data_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; @@ -544,9 +393,9 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { msd.data_size = ww.data_size; if (ww.format == 0x0162) - wmapro_get_samples(&msd, sf, ww.block_align, ww.sample_rate,0x00E0); + wmapro_get_samples(&msd, sf, ww.block_align, ww.sample_rate, 0x00E0); else - wma_get_samples(&msd, sf, ww.block_align, ww.sample_rate,0x001F); + wma_get_samples(&msd, sf, ww.block_align, ww.sample_rate, 0x001F); vgmstream->num_samples = msd.num_samples; if (!vgmstream->num_samples) @@ -577,24 +426,28 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { case OPUSNX: { /* Switch */ size_t skip; + size_t seek_size; + if (ww.fmt_size != 0x28) goto fail; /* values up to 0x14 seem fixed and similar to HEVAG's (block_align 0x02/04, bits_per_sample 0x10) */ - if (ww.fmt_size == 0x28) { - size_t seek_size; - vgmstream->num_samples += read_32bit(ww.fmt_offset + 0x18, sf); - /* 0x1c: null? 0x20: data_size without seek_size */ - seek_size = read_32bit(ww.fmt_offset + 0x24, sf); + vgmstream->num_samples = read_s32(ww.fmt_offset + 0x18, sf); + /* 0x1c: null? + * 0x20: data_size without seek_size */ + seek_size = read_u32(ww.fmt_offset + 0x24, sf); - start_offset += seek_size; - ww.data_size -= seek_size; - } - else { - goto fail; - } + start_offset += seek_size; + ww.data_size -= seek_size; skip = switch_opus_get_encoder_delay(start_offset, sf); /* should be 120 */ + /* some voices have original sample rate but OPUS can only do 48000 (ex. Mario Kart Home Circuit 24khz) */ + if (vgmstream->sample_rate != 48000) { + vgmstream->sample_rate = 48000; + vgmstream->num_samples = switch_opus_get_samples(start_offset,ww.data_size, sf); /* also original's */ + vgmstream->num_samples -= skip; + } + /* OPUS is VBR so this is very approximate percent, meh */ if (ww.truncated) { vgmstream->num_samples = (int32_t)(vgmstream->num_samples * @@ -602,18 +455,18 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { ww.data_size = ww.file_size - start_offset; } - vgmstream->codec_data = init_ffmpeg_switch_opus(sf, start_offset,ww.data_size, vgmstream->channels, skip, vgmstream->sample_rate); + vgmstream->codec_data = init_ffmpeg_switch_opus(sf, start_offset,ww.data_size, ww.channels, skip, vgmstream->sample_rate); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; } - case OPUS: { /* PC/mobile/etc, rare (most games still use Vorbis) [Girl Cafe Gun (Mobile)] */ + case OPUS: { /* alt to Vorbis [Girl Cafe Gun (Mobile)] */ if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail; /* extra: size 0x12 */ - vgmstream->num_samples = read_32bit(ww.fmt_offset + 0x18, sf); + vgmstream->num_samples = read_s32(ww.fmt_offset + 0x18, sf); /* 0x1c: stream size without OggS? */ /* 0x20: full samples (without encoder delay) */ @@ -624,15 +477,45 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { ww.data_size = ww.file_size - start_offset; } - vgmstream->codec_data = init_ffmpeg_offset(sf, ww.data_offset,ww.data_size); + vgmstream->codec_data = init_ffmpeg_offset(sf, ww.data_offset, ww.data_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; break; } +#if 0 // disabled until more files/tests + case OPUSWW: { /* updated Opus [Assassin's Creed Valhalla (PC)] */ + int skip, table_count; + + if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail; + if (!ww.seek_offset)) goto fail; + + /* extra: size 0x10 */ + /* 0x12: samples per frame */ + vgmstream->num_samples = read_s32(ww.fmt_offset + 0x18, sf); + table_count = read_u32(ww.fmt_offset + 0x1c, sf); /* same as seek size / 2 */ + skip = read_u16(ww.fmt_offset + 0x20, sf); + /* 0x22: 1? (though extra size is declared as 0x10 so this is outsize, AK plz */ + + /* OPUS is VBR so this is very approximate percent, meh */ + if (ww.truncated) { + vgmstream->num_samples = (int32_t)(vgmstream->num_samples * + (double)(ww.file_size - start_offset) / (double)ww.data_size); + ww.data_size = ww.file_size - start_offset; + } + + /* Wwise Opus saves all frame sizes in the seek table */ + vgmstream->codec_data = init_ffmpeg_wwise_opus(sf, ww.seek_offset, table_count, ww.data_offset, ww.data_size, ww.channels, skip); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } #endif - case HEVAG: /* PSV */ + +#endif + case HEVAG: /* PSV */ /* changed values, another bizarre Wwise quirk */ //ww.block_align /* unknown (1ch=2, 2ch=4) */ //ww.bits_per_sample; /* unknown (0x10) */ @@ -641,7 +524,9 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { if (ww.fmt_size != 0x18) goto fail; if (ww.big_endian) goto fail; - /* extra_data: size 0x06, @0x00: samples per block (0x1c), @0x04: channel config */ + /* extra_data (size 0x06) + * 0x00: samples per block (0x1c) + * 0x04: channel config (again?) */ vgmstream->coding_type = coding_HEVAG; vgmstream->layout_type = layout_interleave; @@ -657,20 +542,20 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { if (ww.fmt_size != 0x24) goto fail; if (ww.extra_size != 0x12) goto fail; - cfg.channels = vgmstream->channels; - cfg.config_data = read_32bitBE(ww.fmt_offset+0x18,sf); - cfg.encoder_delay = read_32bit(ww.fmt_offset+0x20,sf); + cfg.channels = ww.channels; + cfg.config_data = read_u32be(ww.fmt_offset + 0x18,sf); + cfg.encoder_delay = read_u32(ww.fmt_offset + 0x20,sf); 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,sf); + vgmstream->num_samples = read_s32(ww.fmt_offset + 0x1c, sf); break; } #endif - case PTADPCM: /* substitutes IMA as default ADPCM codec */ + case PTADPCM: /* newer ADPCM [Bayonetta 2 (Switch), Genshin Impact (PC)] */ if (ww.bits_per_sample != 4) goto fail; if (ww.block_align != 0x24 * ww.channels && ww.block_align != 0x104 * ww.channels) goto fail; @@ -679,6 +564,10 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { vgmstream->interleave_block_size = ww.block_align / ww.channels; //vgmstream->codec_endian = ww.big_endian; //? + if (ww.truncated) { + ww.data_size = ww.file_size - ww.data_offset; + } + vgmstream->num_samples = ptadpcm_bytes_to_samples(ww.data_size, ww.channels, vgmstream->interleave_block_size); break; @@ -687,8 +576,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE* sf) { } - - if ( !vgmstream_open_stream(vgmstream,sf,start_offset) ) + if (!vgmstream_open_stream(vgmstream, sf, start_offset) ) goto fail; return vgmstream; @@ -733,6 +621,219 @@ static int is_dsp_full_interleave(STREAMFILE* sf, wwise_header* ww, off_t coef_o } +static int parse_wwise(STREAMFILE* sf, wwise_header* ww) { + uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; + uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL; + + if (read_u32be(0x00,sf) != 0x52494646 && /* "RIFF" (LE) */ + read_u32be(0x00,sf) != 0x52494658) /* "RIFX" (BE) */ + goto fail; + if (read_u32be(0x08,sf) != 0x57415645 && /* "WAVE" */ + read_u32be(0x08,sf) != 0x58574D41) /* "XWMA" */ + goto fail; + + ww->big_endian = read_u32be(0x00,sf) == 0x52494658; /* RIFX */ + if (ww->big_endian) { /* Wwise honors machine's endianness (PC=RIFF, X360=RIFX --unlike XMA) */ + read_u32 = read_u32be; + read_u16 = read_u16be; + } else { + read_u32 = read_u32le; + read_u16 = read_u16le; + } + + ww->file_size = get_streamfile_size(sf); + +#if 0 + /* Wwise's RIFF size is often wonky, seemingly depending on codec: + * - PCM, IMA/PTADPCM, VORBIS, AAC, OPUSNX/OPUS: correct + * - DSP, XWMA, ATRAC9: almost always slightly smaller (around 0x50) + * - HEVAG: very off + * - XMA2: exact file size + * - some RIFX have LE size + * (later we'll validate "data" which fortunately is correct) + */ + if (read_u32(0x04,sf) + 0x04 + 0x04 != ww->file_size) { + VGM_LOG("WWISE: bad riff size\n"); + goto fail; + } +#endif + + /* parse chunks (reads once linearly) */ + { + off_t offset = 0x0c; + while (offset < ww->file_size) { + uint32_t type = read_u32be(offset + 0x00,sf); + uint32_t size = read_u32 (offset + 0x04,sf); + offset += 0x08; + + switch(type) { + case 0x666d7420: /* "fmt " */ + ww->fmt_offset = offset; + ww->fmt_size = size; + break; + case 0x584D4132: /* "XMA2" */ + ww->xma2_offset = offset; + ww->xma2_size = size; + break; + case 0x64617461: /* "data" */ + ww->data_offset = offset; + ww->data_size = size; + break; + case 0x766F7262: /* "vorb" */ + ww->vorb_offset = offset; + ww->vorb_size = size; + break; + case 0x57696948: /* "WiiH" */ + ww->wiih_offset = offset; + ww->wiih_size = size; + break; + case 0x7365656B: /* "seek" */ + ww->seek_offset = offset; + ww->seek_size = size; + break; + case 0x736D706C: /* "smpl" */ + ww->smpl_offset = offset; + ww->smpl_size = size; + break; + + case 0x66616374: /* "fact" */ + /* Wwise shouldn't use fact, but if somehow some file does uncomment the following: */ + //if (size == 0x10 && read_u32be(offset + 0x04, sf) == 0x4C794E20) /* "LyN " */ + // goto fail; /* ignore LyN RIFF */ + goto fail; + + /* "XMAc": rare XMA2 physical loop regions (loop_start_b, loop_end_b, loop_subframe_data) + * Can appear even in the file doesn't loop, maybe it's meant to be the playable physical region */ + /* "LIST": leftover 'cue' info from OG .wavs (ex. loop starts in Platinum Games) */ + /* "JUNK": optional padding for aligment (0-size JUNK exists too) */ + /* "akd ": extra info for Wwise? (wave peaks/loudness/HDR envelope?) */ + default: + break; + } + + /* chunks are even-aligned and don't need to add padding byte, unlike real RIFFs */ + offset += size; + } + } + + + /* parse format (roughly spec-compliant but some massaging is needed) */ + if (ww->xma2_offset) { + /* pseudo-XMA2WAVEFORMAT, "fmt"+"XMA2" (common) or only "XMA2" [Too Human (X360)] */ + ww->format = 0x0165; /* signal for below */ + xma2_parse_xma2_chunk(sf, + ww->xma2_offset, &ww->channels, &ww->sample_rate, &ww->loop_flag, + &ww->num_samples, &ww->loop_start_sample, &ww->loop_end_sample); + } + else { + /* pseudo-WAVEFORMATEX */ + if (ww->fmt_size < 0x10) + goto fail; + ww->format = read_u16(ww->fmt_offset + 0x00,sf); + ww->channels = read_u16(ww->fmt_offset + 0x02,sf); + ww->sample_rate = read_u32(ww->fmt_offset + 0x04,sf); + ww->average_bps = read_u32(ww->fmt_offset + 0x08,sf); + ww->block_align = read_u16(ww->fmt_offset + 0x0c,sf); + ww->bits_per_sample = read_u16(ww->fmt_offset + 0x0e,sf); + if (ww->fmt_size > 0x10 && ww->format != 0x0165 && ww->format != 0x0166) /* ignore XMAWAVEFORMAT */ + ww->extra_size = read_u16(ww->fmt_offset + 0x10,sf); + if (ww->extra_size >= 0x06) { /* always present (actual RIFFs only have it in WAVEFORMATEXTENSIBLE) */ + /* mostly WAVEFORMATEXTENSIBLE's bitmask (see AkSpeakerConfig.h) */ + ww->channel_layout = read_u32(ww->fmt_offset + 0x14,sf); + /* later games (+2018?) have a pseudo-format instead to handle more cases: + * - 8b: uNumChannels + * - 4b: eConfigType (0=none, 1=standard, 2=ambisonic) + * - 19b: uChannelMask */ + if ((ww->channel_layout & 0xFF) == ww->channels) { + ww->channel_layout = (ww->channel_layout >> 12); + } + } + + if (ww->format == 0x0166) { /* XMA2WAVEFORMATEX in fmt */ + xma2_parse_fmt_chunk_extra(sf, ww->fmt_offset, &ww->loop_flag, + &ww->num_samples, &ww->loop_start_sample, &ww->loop_end_sample, ww->big_endian); + } + } + + /* common loops ("XMA2" chunks already read them) */ + if (ww->smpl_offset) { + if (ww->smpl_size >= 0x34 + && read_u32(ww->smpl_offset + 0x1c, sf) == 1 /* loop count */ + && read_u32(ww->smpl_offset + 0x24 + 0x04, sf) == 0) { /* loop type */ + ww->loop_flag = 1; + ww->loop_start_sample = read_u32(ww->smpl_offset + 0x24 + 0x8, sf); + ww->loop_end_sample = read_u32(ww->smpl_offset + 0x24 + 0xc, sf) + 1; /* +1 like standard RIFF */ + } + } + + if (!ww->data_offset) + goto fail; + + + /* format to codec */ + switch(ww->format) { + case 0x0001: ww->codec = PCM; break; /* older Wwise */ + case 0x0002: ww->codec = IMA; break; /* newer Wwise (conflicts with MSADPCM, probably means "platform's ADPCM") */ + case 0x0069: ww->codec = IMA; break; /* older Wwise [Spiderman Web of Shadows (X360), LotR Conquest (PC)] */ + case 0x0161: ww->codec = XWMA; break; /* WMAv2 */ + case 0x0162: ww->codec = XWMA; break; /* WMAPro */ + case 0x0165: ww->codec = XMA2; break; /* XMA2-chunk XMA (Wwise doesn't use XMA1) */ + case 0x0166: ww->codec = XMA2; break; /* fmt-chunk XMA */ + case 0xAAC0: ww->codec = AAC; break; + case 0xFFF0: ww->codec = DSP; break; + case 0xFFFB: ww->codec = HEVAG; break; + case 0xFFFC: ww->codec = ATRAC9; break; + case 0xFFFE: ww->codec = PCM; break; /* "PCM for Wwise Authoring" */ + case 0xFFFF: ww->codec = VORBIS; break; + case 0x3039: ww->codec = OPUSNX; break; /* renamed from "OPUS" on Wwise 2018.1 */ + case 0x3040: ww->codec = OPUS; break; + case 0x3041: ww->codec = OPUSWW; break; /* added on Wwise 2019.2.3, presumably replaces OPUS */ + case 0x8311: ww->codec = PTADPCM; break; /* added on Wwise 2019.1, replaces IMA */ + default: + goto fail; + } + + /* identify system's ADPCM */ + if (ww->format == 0x0002) { + if (ww->extra_size == 0x0c + ww->channels * 0x2e) { + /* newer Wwise DSP with coefs [Epic Mickey 2 (Wii), Batman Arkham Origins Blackgate (3DS)] */ + ww->codec = DSP; + } else if (ww->extra_size == 0x0a && ww->wiih_offset) { /* WiiH */ + /* few older Wwise DSP with num_samples in extra_size [Tony Hawk: Shred (Wii)] */ + ww->codec = DSP; + } else if (ww->block_align == 0x104 * ww->channels) { + ww->codec = PTADPCM; /* Bayonetta 2 (Switch) */ + } + } + + + /* Some Wwise .bnk (RAM) files have truncated, prefetch mirrors of another file, that + * play while the rest of the real stream loads. We'll add basic support to avoid + * complaints of this or that .wem not playing */ + if (ww->data_offset + ww->data_size > ww->file_size) { + //;VGM_LOG("WWISE: truncated data size (prefetch): (real=0x%x > riff=0x%x)\n", ww->data_size, ww->file_size); + + /* catch wrong rips as truncated tracks' file_size should be much smaller than data_size, + * but it's possible to pre-fetch small files too [Punch Out!! (Wii)] */ + if (ww->data_offset + ww->data_size - ww->file_size < 0x5000 && ww->file_size > 0x10000) { + VGM_LOG("WWISE: wrong expected data_size\n"); + goto fail; + } + + if (ww->codec == PCM || ww->codec == IMA || ww->codec == VORBIS || ww->codec == DSP || ww->codec == XMA2 || + ww->codec == OPUSNX || ww->codec == OPUS || ww->codec == OPUSWW || ww->codec == PTADPCM) { + ww->truncated = 1; /* only seen those, probably all exist (XWMA, AAC, HEVAG, ATRAC9?) */ + } else { + VGM_LOG("WWISE: wrong size, maybe truncated\n"); + goto fail; + } + } + + return 1; +fail: + return 0; +} + /* VORBIS FORMAT RESEARCH */ /* diff --git a/Frameworks/vgmstream/vgmstream/src/meta/x360_cxs.c b/Frameworks/vgmstream/vgmstream/src/meta/x360_cxs.c index 5e5df7280..eb080a4a3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/x360_cxs.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/x360_cxs.c @@ -1,66 +1,63 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* CXS - found in Eternal Sonata (X360) */ -VGMSTREAM * init_vgmstream_x360_cxs(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - - /* checks */ - if ( !check_extensions(streamFile,"cxs")) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x43585320) /* "CXS " */ - goto fail; - - loop_flag = read_32bitBE(0x18,streamFile) > 0; - channel_count = read_32bitBE(0x0c,streamFile); - start_offset = read_32bitBE(0x04,streamFile) + read_32bitBE(0x28,streamFile); /* assumed, seek table always at 0x800 */ - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* 0x04: data start? */ - vgmstream->sample_rate = read_32bitBE(0x08,streamFile); - vgmstream->num_samples = read_32bitBE(0x10,streamFile); - vgmstream->loop_start_sample = read_32bitBE(0x14,streamFile); - vgmstream->loop_end_sample = read_32bitBE(0x18,streamFile); - /* 0x1c: below */ - - vgmstream->meta_type = meta_X360_CXS; - -#ifdef VGM_USE_FFMPEG - { - ffmpeg_codec_data *ffmpeg_data = NULL; - uint8_t buf[100]; - size_t bytes, datasize, block_size, block_count; - - block_count = read_32bitBE(0x1c,streamFile); - block_size = read_32bitBE(0x20,streamFile); - datasize = read_32bitBE(0x24,streamFile); - - bytes = ffmpeg_make_riff_xma2(buf,100, vgmstream->num_samples, datasize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - if (bytes <= 0) goto fail; - - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,datasize); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - xma_fix_raw_samples(vgmstream, streamFile, start_offset,datasize, 0, 0,1); /* num samples are ok */ - } -#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; -} +#include "meta.h" +#include "../coding/coding.h" + +/* CXS - found in Eternal Sonata (X360) */ +VGMSTREAM* init_vgmstream_x360_cxs(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + /* checks */ + if ( !check_extensions(sf,"cxs")) + goto fail; + if (read_32bitBE(0x00,sf) != 0x43585320) /* "CXS " */ + goto fail; + + loop_flag = read_32bitBE(0x18,sf) > 0; + channel_count = read_32bitBE(0x0c,sf); + start_offset = read_32bitBE(0x04,sf) + read_32bitBE(0x28,sf); /* assumed, seek table always at 0x800 */ + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + /* 0x04: data start? */ + vgmstream->sample_rate = read_32bitBE(0x08,sf); + vgmstream->num_samples = read_32bitBE(0x10,sf); + vgmstream->loop_start_sample = read_32bitBE(0x14,sf); + vgmstream->loop_end_sample = read_32bitBE(0x18,sf); + /* 0x1c: below */ + + vgmstream->meta_type = meta_X360_CXS; + +#ifdef VGM_USE_FFMPEG + { + uint8_t buf[0x100]; + size_t bytes, datasize, block_size, block_count; + + block_count = read_32bitBE(0x1c,sf); + block_size = read_32bitBE(0x20,sf); + datasize = read_32bitBE(0x24,sf); + + bytes = ffmpeg_make_riff_xma2(buf,100, vgmstream->num_samples, datasize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + if (bytes <= 0) goto fail; + + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,datasize); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + xma_fix_raw_samples(vgmstream, sf, start_offset,datasize, 0, 0,1); /* num samples are ok */ + } +#else + goto fail; +#endif + + if ( !vgmstream_open_stream(vgmstream, sf, start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xma.c b/Frameworks/vgmstream/vgmstream/src/meta/xma.c index e61b42566..98b414610 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xma.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xma.c @@ -14,9 +14,10 @@ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) { /* checks */ /* .xma: standard * .xma2: Skullgirls (X360) + * .wav: Super Meat Boy (X360) * .nps: Beautiful Katamari (X360) * .str: Sonic & Sega All Stars Racing (X360) */ - if ( !check_extensions(streamFile, "xma,xma2,nps,str") ) + if ( !check_extensions(streamFile, "xma,xma2,wav,nps,str") ) goto fail; /* check header */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xmv_valve.c b/Frameworks/vgmstream/vgmstream/src/meta/xmv_valve.c index 9573b6128..cf37a03ba 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xmv_valve.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xmv_valve.c @@ -142,7 +142,6 @@ VGMSTREAM *init_vgmstream_xmv_valve(STREAMFILE *streamFile) { break; #ifdef VGM_USE_FFMPEG case 0x01: { /* XMA */ - ffmpeg_codec_data *ffmpeg_data; uint8_t buf[0x100]; int block_count, block_size; size_t bytes; @@ -152,10 +151,8 @@ VGMSTREAM *init_vgmstream_xmv_valve(STREAMFILE *streamFile) { bytes = ffmpeg_make_riff_xma2(buf, 0x100, num_samples, data_size, channels, sample_rate, block_count, block_size); - ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf, bytes, start_offset, data_size); - if (!ffmpeg_data) goto fail; - - vgmstream->codec_data = ffmpeg_data; + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf, bytes, start_offset, data_size); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; vgmstream->loop_end_sample -= loop_end_skip; @@ -166,16 +163,13 @@ VGMSTREAM *init_vgmstream_xmv_valve(STREAMFILE *streamFile) { #endif #ifdef VGM_USE_MPEG case 0x03: { /* MP3 */ - mpeg_codec_data *mpeg_data; coding_t mpeg_coding; if (loop_flag) /* should never happen, Source cannot loop MP3 */ goto fail; - mpeg_data = init_mpeg(streamFile, start_offset, &mpeg_coding, channels); - if (!mpeg_data) goto fail; - - vgmstream->codec_data = mpeg_data; + vgmstream->codec_data = init_mpeg(streamFile, start_offset, &mpeg_coding, channels); + if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = mpeg_coding; vgmstream->layout_type = layout_none; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xopus.c b/Frameworks/vgmstream/vgmstream/src/meta/xopus.c index 8ef98813a..9782b92d8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xopus.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xopus.c @@ -1,63 +1,64 @@ -#include "meta.h" -#include "../coding/coding.h" - - -/* XOPUS - from Exient games [Angry Birds: Transformers (Android), Angry Birds: Go (Android)] */ -VGMSTREAM * init_vgmstream_xopus(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag = 0, channel_count, sample_rate, num_samples, skip; - size_t data_size; - int entries; - - - /* checks*/ - if (!check_extensions(streamFile, "xopus")) - goto fail; - if (read_32bitBE(0x00, streamFile) != 0x584F7075) /* "XOpu" */ - goto fail; - - /* 0x04: always 0x01? */ - channel_count = read_8bit(0x05, streamFile); - /* 0x06: always 0x30? */ - /* 0x08: always 0xc8? max allowed packet size? */ - num_samples = read_32bitLE(0x0c, streamFile); - skip = read_32bitLE(0x10, streamFile); - entries = read_32bitLE(0x14, streamFile); - data_size = read_32bitLE(0x18, streamFile); - /* 0x1c: unused */ - /* 0x20+: packet sizes table */ - - sample_rate = 48000; - loop_flag = 0; - - start_offset = 0x20 + 0x02*entries; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_XOPUS; - vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = num_samples; - -#ifdef VGM_USE_FFMPEG - { - vgmstream->codec_data = init_ffmpeg_x_opus(streamFile, start_offset,data_size, vgmstream->channels, skip, vgmstream->sample_rate); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - } -#else - goto fail; -#endif - - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + + +/* XOPUS - from Exient games [Angry Birds: Transformers (Android), Angry Birds: Go (Android)] */ +VGMSTREAM* init_vgmstream_xopus(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset, table_offset; + int loop_flag = 0, channels, sample_rate, num_samples, skip; + size_t data_size; + int entries; + + + /* checks*/ + if (!check_extensions(sf, "xopus")) + goto fail; + if (read_u32be(0x00, sf) != 0x584F7075) /* "XOpu" */ + goto fail; + + /* 0x04: always 0x01? */ + channels = read_u8(0x05, sf); + /* 0x06: always 0x30? */ + /* 0x08: always 0xc8? max allowed packet size? */ + num_samples = read_s32le(0x0c, sf); + skip = read_s32le(0x10, sf); + entries = read_u32le(0x14, sf); + data_size = read_u32le(0x18, sf); + /* 0x1c: unused */ + /* 0x20+: packet sizes table */ + + sample_rate = 48000; + loop_flag = 0; + + table_offset = 0x20; + start_offset = table_offset + 0x02*entries; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_XOPUS; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + +#ifdef VGM_USE_FFMPEG + { + vgmstream->codec_data = init_ffmpeg_x_opus(sf, table_offset, entries, start_offset, data_size, vgmstream->channels, skip); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + } +#else + goto fail; +#endif + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xse.c b/Frameworks/vgmstream/vgmstream/src/meta/xse.c new file mode 100644 index 000000000..1b5a46968 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/xse.c @@ -0,0 +1,301 @@ +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + + +/* SDRH - banks for newer feelplus-related games [Mindjack (PS3/X360)] */ +VGMSTREAM* init_vgmstream_xse_new(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset, data_size, stream_size; + int loop_flag = 0, channels, codec, sample_rate, seek_count; + int32_t num_samples, loop_start, loop_end; + off_t offset; + int total_subsongs, target_subsong = sf->stream_index; + + + /* checks */ + if (!check_extensions(sf, "xse")) + goto fail; + if (read_u32be(0x00,sf) != 0x48524453) /* "HRDS" */ + goto fail; + + /* similar to older version but BE and a bit less complex */ + /* 0x04: version/config? + * 0x08: data size + * 0x30: file name in some strange encoding/compression? + * others: ? (change in old/new) + */ + + /* parse section */ + { + int i; + int tables = read_u16be(0x1C,sf); + off_t base_size, stream_offset; + int entries; + + offset = 0; + /* read sections (FE=cues?, WV=mini-headers?, XW=waves) */ + for (i = 0; i < tables; i++) { + uint16_t id = read_u16be(0x40 + 0x10 * i + 0x00,sf); + /* 0x02: offset in 0x40s */ + /* 0x04: section size */ + /* 0x08: always 1 */ + /* 0x0c: null */ + if (id == 0x5857) { /* "XW" */ + offset += read_u16be(0x40 + 0x10 * i + 0x02,sf) * 0x40; + break; + } + } + + /* section header (other sections have a similar header) */ + /* 0x00: section size */ + base_size = read_u16be(offset + 0x04,sf); + entries = read_u16be(offset + 0x06,sf); + /* 0x08: null */ + start_offset = read_u32be(offset + 0x0c,sf) + offset; /* size including padding up to start */ + + offset += base_size; + + total_subsongs = entries; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + /* find stream header (entries can be variable-sized) */ + for (i = 0; i < entries; i++) { + size_t seek_size = read_u16be(offset + 0x0a,sf) * 0x04; + size_t entry_size = align_size_to_block(0x30 + seek_size, 0x10); + + if (i + 1 == target_subsong) + break; + offset += entry_size; + } + + /* parse target header (similar to xwav) */ + stream_size = read_u32be(offset + 0x00,sf); + /* 0x04: codec? (16=PS3, 03=X360) */ + codec = read_u8(offset + 0x06,sf); /* assumed */ + loop_flag = read_u8(offset + 0x07,sf); /* assumed */ + /* 0x08: bps? */ + channels = read_u8(offset + 0x09,sf); + seek_count = read_u16be(offset + 0x0a,sf); + num_samples = read_u32be(offset + 0x0c,sf); + sample_rate = read_u32be(offset + 0x10,sf); + loop_start = read_u32be(offset + 0x14,sf); + loop_end = read_u32be(offset + 0x18,sf); + /* 0x1c: ? */ + stream_offset = read_u32be(offset + 0x20,sf); /* within data */ + /* 0x24: ? */ + /* 0x26 seek entries */ + /* 0x28: ? */ + /* 0x2c: null? */ + + start_offset += stream_offset; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_SDRH; + vgmstream->num_samples = num_samples; + vgmstream->sample_rate = sample_rate; + + vgmstream->stream_size = stream_size; + vgmstream->num_streams = total_subsongs; + + switch(codec) { + case 2: /* Mindjack (PS3) */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + break; + +#ifdef VGM_USE_FFMPEG + case 1: { /* Mindjack (X360) */ + uint8_t buf[0x100]; + int32_t bytes, block_size, block_count; + + data_size = get_streamfile_size(sf) - start_offset; + block_size = 0x10000; /* XWAV new default */ + block_count = seek_count; + + bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + //todo fix loops/samples vs ATRAC3 + /* may be only applying end_skip to num_samples? */ + xma_fix_raw_samples(vgmstream, sf, start_offset,data_size, 0, 0,0); + break; + } +#endif + + default: + goto fail; + } + + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + +/* SDRH - banks for older feelplus-related games [Lost Odyssey (X360)] */ +VGMSTREAM* init_vgmstream_xse_old(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset, data_size, stream_size; + int loop_flag = 0, channels, codec, sample_rate, seek_count; + int32_t num_samples, loop_start, loop_end; + off_t offset; + int total_subsongs, target_subsong = sf->stream_index; + + + /* checks */ + /* .xse: assumed */ + if (!check_extensions(sf, "xse")) + goto fail; + if (read_u32be(0x00,sf) != 0x53445248) /* "SDRH" */ + goto fail; + + /* similar to older version but LE and a bit more complex */ + /* 0x04: version/config? + * 0x08: data size + * 0x30: file name in some strange encoding/compression? + * others: ? (change in old/new) + */ + + /* parse section */ + { + int i; + int tables = read_u8(0x15,sf); + off_t base_size, stream_offset; + int entries; + + offset = 0x40; + /* read sections (FE=cues?, WV=mini-headers?, FT=?, FQ=?, XW=waves) */ + for (i = 0; i < tables; i++) { + uint16_t id = read_u16be(0x40 + 0x08 * i + 0x00,sf); + /* 0x02: null? */ + /* 0x04: offset from table start */ + if (id == 0x5857) { /* "XW" */ + offset += read_u32le(0x40 + 0x08 * i + 0x04,sf); + break; + } + } + + /* section header (other sections have a similar header) */ + /* 0x00: section size */ + base_size = read_u16le(offset + 0x04,sf); + /* 0x06: ? */ + entries = read_u16le(offset + 0x08,sf); + start_offset = read_u32le(offset + 0x0c,sf) + offset; /* size including padding up to start */ + + offset += base_size; + + total_subsongs = entries; + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + + /* find stream header */ + stream_offset = 0; + for (i = 0; i < entries; i++) { + size_t data_size = read_u32le(offset + 0x00,sf) - 0x30; + size_t seek_size = 0; //read_u16be(offset + 0x0a,sf) * 0x04; /* not seen */ + size_t entry_size = align_size_to_block(0x30 + seek_size, 0x10); + + if (i + 1 == target_subsong) + break; + offset += entry_size; + stream_offset += data_size; /* no offset? */ + } + + /* parse target header (similar to xwav) */ + stream_size = read_u32le(offset + 0x00,sf) - 0x30; /* adds entry size */ + /* 0x04: codec? (16=PS3, 03=X360) */ + codec = read_u8(offset + 0x06,sf); /* assumed */ + /* 0x07: flag? */ + /* 0x08: bps? */ + /* 0x09: codec? */ + /* 0x0a: null */ + num_samples = read_u32le(offset + 0x0c,sf); + seek_count = read_u16le(offset + 0x10,sf); + sample_rate = read_u32le(offset + 0x14,sf); + loop_start = 0; //read_u32le(offset + 0x18,sf); /* ? */ + loop_end = 0; //read_u32le(offset + 0x1c,sf); /* ? */ + /* 0x20: null */ + /* 0x24: ? */ + /* 0x26: channel layout */ + channels = read_u8(offset + 0x27,sf); + /* 0x28: ? */ + /* 0x2c: null? */ + + loop_flag = loop_end > 0; + + start_offset += stream_offset; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_SDRH; + vgmstream->num_samples = num_samples; + vgmstream->sample_rate = sample_rate; + + vgmstream->stream_size = stream_size; + vgmstream->num_streams = total_subsongs; + + switch(codec) { + +#ifdef VGM_USE_FFMPEG + case 4: { /* Lost Odyssey (X360) */ + uint8_t buf[0x100]; + int32_t bytes, block_size, block_count; + + data_size = get_streamfile_size(sf) - start_offset; + block_size = 0x8000; /* XWAV old default */ + block_count = seek_count; + + bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + xma_fix_raw_samples(vgmstream, sf, start_offset, data_size, 0, 0, 1); + break; + } +#endif + + default: + goto fail; + } + + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwav.c b/Frameworks/vgmstream/vgmstream/src/meta/xwav.c new file mode 100644 index 000000000..d3a0c8e4f --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwav.c @@ -0,0 +1,260 @@ +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + + +/* XWAV - streams for newer feelplus-related games [No More Heroes: Heroes Paradise (PS3/X360), Moon Diver (PS3/X360)] */ +VGMSTREAM* init_vgmstream_xwav_new(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset, data_size; + int loop_flag = 0, channels, codec, sample_rate; + int32_t num_samples, loop_start, loop_end; + + + /* checks */ + /* .xwv: actual extension [Moon Diver (PS3/X360)] + * .vawx: header id */ + if (!check_extensions(sf, "xwv,vawx")) + goto fail; + if (read_u32be(0x00,sf) != 0x56415758) /* "VAWX" */ + goto fail; + + /* similar to older version but BE and a bit less complex */ + /* 0x04: data size + * 0x08: version (always 3) + * 0x0a: sub-version (0 in NMH/NNN2, 5 in MD) + * 0x0c: ? (0080 + some value) + * 0x10: ? (00402000) + * 0x14: ? (3380) + * 0x16: file number + * 0x18: null + * 0x1c: null + * 0x20: file name in some strange encoding/compression? + */ + start_offset = 0x800; + + /* parse header */ + { + /* 0x00: stream size */ + /* 0x04: ? */ + codec = read_u8(0x30 + 0x06,sf); + loop_flag = read_u8(0x30 + 0x07,sf); + /* 0x08: ? */ + channels = read_u8(0x30 + 0x09,sf); + /* 0x0a: seek entries */ + num_samples = read_u32be(0x30 + 0x0c,sf); + sample_rate = read_u32be(0x30 + 0x10,sf); + loop_start = read_u32be(0x30 + 0x14,sf); + loop_end = read_u32be(0x30 + 0x18,sf); + /* rest: ? (also see xse) */ + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_XWAV; + vgmstream->num_samples = num_samples; + vgmstream->sample_rate = sample_rate; + + switch(codec) { + case 2: /* No Nore Heroes (PS3) */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = channels == 6 ? layout_blocked_xwav : layout_interleave; + vgmstream->interleave_block_size = 0x10; + + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + break; + +#ifdef VGM_USE_FFMPEG + case 1: { /* No Nore Heroes (X360), Moon Diver (X360), Ninety-Nine Nights 2 (X360) */ + uint8_t buf[0x100]; + int32_t bytes, block_size, block_count; + + data_size = get_streamfile_size(sf) - start_offset; + block_size = 0x10000; /* XWAV new default */ + block_count = read_u16be(0x30 + 0x0A, sf); /* also at 0x56 */ + + bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + //todo fix loops/samples vs ATRAC3 + /* may be only applying end_skip to num_samples? */ + xma_fix_raw_samples(vgmstream, sf, start_offset,data_size, 0, 0,0); + break; + } + + case 7: { /* Moon Diver (PS3) */ + int block_align, encoder_delay; + + data_size = read_u32be(0x54,sf); + block_align = 0x98 * vgmstream->channels; + encoder_delay = 1024 + 69*2; /* observed default, matches XMA (needed as many files start with garbage) */ + vgmstream->num_samples = atrac3_bytes_to_samples(data_size, block_align) - encoder_delay; /* original samples break looping in some files otherwise */ + + vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf, start_offset,data_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + /* set offset samples (offset 0 jumps to sample 0 > pre-applied delay, and offset end loops after sample end > adjusted delay) */ + vgmstream->loop_start_sample = atrac3_bytes_to_samples(loop_start, block_align); //- encoder_delay + vgmstream->loop_end_sample = atrac3_bytes_to_samples(loop_end, block_align) - encoder_delay; + break; + } +#endif + + default: + goto fail; + } + + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* XWAV - streams for older feelplus-related games [Bullet Witch (X360), Lost Odyssey (X360)] */ +VGMSTREAM* init_vgmstream_xwav_old(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset, data_size; + int loop_flag = 0, channels, codec, tracks, sample_rate; + int32_t num_samples, loop_start, loop_end; + + + /* checks */ + /* .xwv: actual extension [Bullet Witch (X360)] */ + if (!check_extensions(sf, "xwv")) + goto fail; + if (read_u32be(0x00,sf) != 0x58574156) /* "XWAV" */ + goto fail; + + /* similar to newer version but LE and a bit more complex */ + /* 0x04: data size + * 0x08: version (always 2) + * 0x0a: sub-version? (0x100/200/300 in LO, 0x200 in BW) + * 0x0c: ? + * 0x10: start offset (in 0x10s) + * 0x12: ? (low number) + * 0x20: stream size + * 0x24: ? + * 0x26: codec? + * 0x27: tracks + * rest varies depending on codec + */ + start_offset = read_u16le(0x10,sf) * 0x10; + + codec = read_u8(0x26,sf); + tracks = read_u8(0x27,sf); + + switch(codec) { + case 2: /* PSX */ + /* 0x2c: null? */ + num_samples = read_u32le(0x30,sf); + sample_rate = read_u16le(0x34,sf); + channels = read_u8(0x37,sf); + loop_start = read_u32le(0x38,sf); + loop_end = read_u32le(0x3c,sf); + if (tracks > 1) + goto fail; + break; + + case 4: /* XMA */ + num_samples = read_u32le(0x2c,sf); + /* 0x30: xma blocks of 0x8000 */ + sample_rate = read_u16le(0x34,sf); + /* 0x38: ? (0x10/20) */ + /* 0x3c: null */ + loop_start = read_u32le(0x48,sf); /* per stream, but all should match */ + loop_end = read_u32le(0x4C,sf); + + /* listed as XMA streams like XMA1, but XMA2 shouldn't need this (uses proper Nch XMA2) */ + { + channels = 0; + for (int i = 0; i < tracks; i++) { + /* 0x00: null */ + /* 0x04: null */ + /* 0x06: channel layout null */ + channels += read_u8(0x40 + 0x10 * i + 0x07,sf); + /* 0x08: loop start */ + /* 0x0c: loop end */ + } + } + + /* next is a seek table, padded to 0x10 */ + break; + + default: + goto fail; + } + + loop_flag = loop_end > 0; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_XWAV; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + + switch(codec) { + case 2: /* Bullet Witch (X360) (seems unused as there are .xwb) */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10; + + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channels); + vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channels); + break; + +#ifdef VGM_USE_FFMPEG + case 4: { /* Lost Odyssey (X360) */ + uint8_t buf[0x100]; + int32_t bytes, block_size, block_count; + + data_size = get_streamfile_size(sf) - start_offset; + block_size = 0x8000; /* XWAV old default */ + block_count = read_u16be(0x30, sf); + + bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + xma_fix_raw_samples(vgmstream, sf, start_offset, data_size, 0, 0, 1); + break; + } +#endif + + default: + goto fail; + } + + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c index 5fe169e31..7d951aafb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c @@ -75,40 +75,43 @@ typedef struct { int fix_xma_loop_samples; } xwb_header; -static void get_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamFile); +static void get_name(char* buf, size_t maxsize, int target_subsong, xwb_header* xwb, STREAMFILE* sf); /* XWB - XACT Wave Bank (Microsoft SDK format for XBOX/XBOX360/Windows) */ -VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_xwb(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset, offset, suboffset; xwb_header xwb = {0}; - int target_subsong = streamFile->stream_index; - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int target_subsong = sf->stream_index; + uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; + int32_t (*read_s32)(off_t,STREAMFILE*) = NULL; /* checks */ /* .xwb: standard * .xna: Touhou Makukasai ~ Fantasy Danmaku Festival (PC) * (extensionless): Ikaruga (X360/PC), Grabbed by the Ghoulies (Xbox) */ - if (!check_extensions(streamFile,"xwb,xna,")) + if (!check_extensions(sf,"xwb,xna,")) goto fail; - if ((read_32bitBE(0x00,streamFile) != 0x57424E44) && /* "WBND" (LE) */ - (read_32bitBE(0x00,streamFile) != 0x444E4257)) /* "DNBW" (BE) */ + if ((read_u32be(0x00,sf) != 0x57424E44) && /* "WBND" (LE) */ + (read_u32be(0x00,sf) != 0x444E4257)) /* "DNBW" (BE) */ goto fail; - xwb.little_endian = read_32bitBE(0x00,streamFile) == 0x57424E44; /* WBND */ + xwb.little_endian = read_u32be(0x00,sf) == 0x57424E44; /* WBND */ if (xwb.little_endian) { - read_32bit = read_32bitLE; + read_u32 = read_u32le; + read_s32 = read_s32le; } else { - read_32bit = read_32bitBE; + read_u32 = read_u32be; + read_s32 = read_s32be; } /* read main header (WAVEBANKHEADER) */ - xwb.version = read_32bit(0x04, streamFile); /* XACT3: 0x04=tool version, 0x08=header version */ + xwb.version = read_u32(0x04, sf); /* XACT3: 0x04=tool version, 0x08=header version */ - /* Crackdown 1 (X360), essentially XACT2 but may have split header in some cases */ + /* Crackdown 1 (X360), essentially XACT2 but may have split header in some cases, compact entries change */ if (xwb.version == XACT_CRACKDOWN) { xwb.version = XACT2_2_MAX; xwb.is_crackdown = 1; @@ -116,15 +119,15 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { /* read segment offsets (SEGIDX) */ if (xwb.version <= XACT1_0_MAX) { - xwb.total_subsongs = read_32bit(0x0c, streamFile); - read_string(xwb.wavebank_name,0x10+1, 0x10, streamFile); /* null-terminated */ + xwb.total_subsongs = read_s32(0x0c, sf); + read_string(xwb.wavebank_name,0x10+1, 0x10, sf); /* null-terminated */ xwb.base_offset = 0; xwb.base_size = 0; xwb.entry_offset = 0x50; xwb.entry_elem_size = 0x14; xwb.entry_size = xwb.entry_elem_size * xwb.total_subsongs; xwb.data_offset = xwb.entry_offset + xwb.entry_size; - xwb.data_size = get_streamfile_size(streamFile) - xwb.data_offset; + xwb.data_size = get_streamfile_size(sf) - xwb.data_offset; xwb.names_offset = 0; xwb.names_size = 0; @@ -134,52 +137,52 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { } else { offset = xwb.version <= XACT2_2_MAX ? 0x08 : 0x0c; - xwb.base_offset = read_32bit(offset+0x00, streamFile);//BANKDATA - xwb.base_size = read_32bit(offset+0x04, streamFile); - xwb.entry_offset= read_32bit(offset+0x08, streamFile);//ENTRYMETADATA - xwb.entry_size = read_32bit(offset+0x0c, streamFile); + xwb.base_offset = read_s32(offset+0x00, sf);//BANKDATA + xwb.base_size = read_s32(offset+0x04, sf); + xwb.entry_offset= read_s32(offset+0x08, sf);//ENTRYMETADATA + xwb.entry_size = read_s32(offset+0x0c, sf); /* read extra segments (values can be 0 == no segment) */ if (xwb.version <= XACT1_1_MAX) { - xwb.names_offset = read_32bit(offset+0x10, streamFile);//ENTRYNAMES - xwb.names_size = read_32bit(offset+0x14, streamFile); + xwb.names_offset = read_s32(offset+0x10, sf);//ENTRYNAMES + xwb.names_size = read_s32(offset+0x14, sf); xwb.names_entry_size= 0x40; xwb.extra_offset = 0; xwb.extra_size = 0; suboffset = 0x04*2; } else if (xwb.version <= XACT2_1_MAX) { - xwb.names_offset = read_32bit(offset+0x10, streamFile);//ENTRYNAMES - xwb.names_size = read_32bit(offset+0x14, streamFile); + xwb.names_offset = read_s32(offset+0x10, sf);//ENTRYNAMES + xwb.names_size = read_s32(offset+0x14, sf); xwb.names_entry_size= 0x40; - xwb.extra_offset = read_32bit(offset+0x18, streamFile);//EXTRA - xwb.extra_size = read_32bit(offset+0x1c, streamFile); + xwb.extra_offset = read_s32(offset+0x18, sf);//EXTRA + xwb.extra_size = read_s32(offset+0x1c, sf); suboffset = 0x04*2 + 0x04*2; } else { - xwb.extra_offset = read_32bit(offset+0x10, streamFile);//SEEKTABLES - xwb.extra_size = read_32bit(offset+0x14, streamFile); - xwb.names_offset = read_32bit(offset+0x18, streamFile);//ENTRYNAMES - xwb.names_size = read_32bit(offset+0x1c, streamFile); + xwb.extra_offset = read_s32(offset+0x10, sf);//SEEKTABLES + xwb.extra_size = read_s32(offset+0x14, sf); + xwb.names_offset = read_s32(offset+0x18, sf);//ENTRYNAMES + xwb.names_size = read_s32(offset+0x1c, sf); xwb.names_entry_size= 0x40; suboffset = 0x04*2 + 0x04*2; } - xwb.data_offset = read_32bit(offset+0x10+suboffset, streamFile);//ENTRYWAVEDATA - xwb.data_size = read_32bit(offset+0x14+suboffset, streamFile); + xwb.data_offset = read_s32(offset+0x10+suboffset, sf);//ENTRYWAVEDATA + xwb.data_size = read_s32(offset+0x14+suboffset, sf); /* for Techland's XWB with no data */ if (xwb.base_offset == 0) goto fail; /* read base entry (WAVEBANKDATA) */ offset = xwb.base_offset; - xwb.base_flags = (uint32_t)read_32bit(offset+0x00, streamFile); - xwb.total_subsongs = read_32bit(offset+0x04, streamFile); - read_string(xwb.wavebank_name,0x40+1, offset+0x08, streamFile); /* null-terminated */ + xwb.base_flags = read_u32(offset+0x00, sf); + xwb.total_subsongs = read_s32(offset+0x04, sf); + read_string(xwb.wavebank_name,0x40+1, offset+0x08, sf); /* null-terminated */ suboffset = 0x08 + (xwb.version <= XACT1_1_MAX ? 0x10 : 0x40); - xwb.entry_elem_size = read_32bit(offset+suboffset+0x00, streamFile); + xwb.entry_elem_size = read_s32(offset+suboffset+0x00, sf); /* suboff+0x04: meta name entry size */ - xwb.entry_alignment = read_32bit(offset+suboffset+0x08, streamFile); /* usually 1 dvd sector */ - xwb.format = read_32bit(offset+suboffset+0x0c, streamFile); /* compact mode only */ + xwb.entry_alignment = read_s32(offset+suboffset+0x08, sf); /* usually 1 dvd sector */ + xwb.format = read_s32(offset+suboffset+0x0c, sf); /* compact mode only */ /* suboff+0x10: build time 64b (XACT2/3) */ } @@ -192,20 +195,34 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { /* read stream entry (WAVEBANKENTRY) */ offset = xwb.entry_offset + (target_subsong-1) * xwb.entry_elem_size; - if (xwb.base_flags & WAVEBANK_FLAGS_COMPACT) { /* compact entry [NFL Fever 2004 demo from Amped 2 (Xbox)] */ + + if ((xwb.base_flags & WAVEBANK_FLAGS_COMPACT) && xwb.is_crackdown) { + /* mutant compact (w/ entry_elem_size=0x08) [Crackdown (X360)] */ + uint32_t entry, size_sectors, sector_offset; + + entry = read_u32(offset+0x00, sf); + size_sectors = ((entry >> 19) & 0x1FFF); /* 13b, exact size in sectors */ + sector_offset = (entry & 0x7FFFF); /* 19b, offset within data in sectors */ + xwb.stream_size = size_sectors * xwb.entry_alignment; + xwb.num_samples = read_u32(offset+0x04, sf); + + xwb.stream_offset = xwb.data_offset + sector_offset * xwb.entry_alignment; + } + else if (xwb.base_flags & WAVEBANK_FLAGS_COMPACT) { + /* compact entry [NFL Fever 2004 demo from Amped 2 (Xbox)] */ uint32_t entry, size_deviation, sector_offset; off_t next_stream_offset; - entry = (uint32_t)read_32bit(offset+0x00, streamFile); + entry = read_u32(offset+0x00, sf); size_deviation = ((entry >> 21) & 0x7FF); /* 11b, padding data for sector alignment in bytes*/ sector_offset = (entry & 0x1FFFFF); /* 21b, offset within data in sectors */ - xwb.stream_offset = xwb.data_offset + sector_offset*xwb.entry_alignment; + xwb.stream_offset = xwb.data_offset + sector_offset * xwb.entry_alignment; /* find size using next offset */ if (target_subsong < xwb.total_subsongs) { - uint32_t next_entry = (uint32_t)read_32bit(offset+0x04, streamFile); - next_stream_offset = xwb.data_offset + (next_entry & 0x1FFFFF)*xwb.entry_alignment; + uint32_t next_entry = read_u32(offset + xwb.entry_elem_size, sf); + next_stream_offset = xwb.data_offset + (next_entry & 0x1FFFFF) * xwb.entry_alignment; } else { /* for last entry (or first, when subsongs = 1) */ next_stream_offset = xwb.data_offset + xwb.data_size; @@ -213,31 +230,31 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { xwb.stream_size = next_stream_offset - xwb.stream_offset - size_deviation; } else if (xwb.version <= XACT1_0_MAX) { - xwb.format = (uint32_t)read_32bit(offset+0x00, streamFile); - xwb.stream_offset = xwb.data_offset + (uint32_t)read_32bit(offset+0x04, streamFile); - xwb.stream_size = (uint32_t)read_32bit(offset+0x08, streamFile); + xwb.format = read_u32(offset+0x00, sf); + xwb.stream_offset = xwb.data_offset + read_u32(offset+0x04, sf); + xwb.stream_size = read_u32(offset+0x08, sf); - xwb.loop_start = (uint32_t)read_32bit(offset+0x0c, streamFile); - xwb.loop_end = (uint32_t)read_32bit(offset+0x10, streamFile);//length + xwb.loop_start = read_u32(offset+0x0c, sf); + xwb.loop_end = read_u32(offset+0x10, sf);//length } else { - uint32_t entry_info = (uint32_t)read_32bit(offset+0x00, streamFile); + uint32_t entry_info = read_u32(offset+0x00, sf); if (xwb.version <= XACT1_1_MAX) { xwb.entry_flags = entry_info; } else { xwb.entry_flags = (entry_info) & 0xF; /*4b*/ xwb.num_samples = (entry_info >> 4) & 0x0FFFFFFF; /*28b*/ } - xwb.format = (uint32_t)read_32bit(offset+0x04, streamFile); - xwb.stream_offset = xwb.data_offset + (uint32_t)read_32bit(offset+0x08, streamFile); - xwb.stream_size = (uint32_t)read_32bit(offset+0x0c, streamFile); + xwb.format = read_u32(offset+0x04, sf); + xwb.stream_offset = xwb.data_offset + read_u32(offset+0x08, sf); + xwb.stream_size = read_u32(offset+0x0c, sf); if (xwb.version <= XACT2_1_MAX) { /* LoopRegion (bytes) */ - xwb.loop_start = (uint32_t)read_32bit(offset+0x10, streamFile); - xwb.loop_end = (uint32_t)read_32bit(offset+0x14, streamFile);//length (LoopRegion) or offset (XMALoopRegion in late XACT2) + xwb.loop_start = read_u32(offset+0x10, sf); + xwb.loop_end = read_u32(offset+0x14, sf);//length (LoopRegion) or offset (XMALoopRegion in late XACT2) } else { /* LoopRegion (samples) */ - xwb.loop_start_sample = (uint32_t)read_32bit(offset+0x10, streamFile); - xwb.loop_end_sample = (uint32_t)read_32bit(offset+0x14, streamFile) + xwb.loop_start_sample; + xwb.loop_start_sample = read_u32(offset+0x10, sf); + xwb.loop_end_sample = read_u32(offset+0x14, sf) + xwb.loop_start_sample; } } @@ -325,10 +342,11 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { xwb.loop_end = 0; } else if (xwb.version == XACT3_0_MAX && xwb.codec == XMA2 - && xwb.bits_per_sample == 0x01 && xwb.block_align == 0x04 - && read_32bitLE(xwb.stream_offset + 0x08, streamFile) == xwb.sample_rate /* DSP header */ - && read_16bitLE(xwb.stream_offset + 0x0e, streamFile) == 0 - && read_32bitLE(xwb.stream_offset + 0x18, streamFile) == 2 + && (xwb.bits_per_sample == 0x00 || xwb.bits_per_sample == 0x01) /* bps=0+ba=2 in mono? (Blossom Tales) */ + && (xwb.block_align == 0x02 || xwb.block_align == 0x04) + && read_u32le(xwb.stream_offset + 0x08, sf) == xwb.sample_rate /* DSP header */ + && read_u16le(xwb.stream_offset + 0x0e, sf) == 0 + && read_u32le(xwb.stream_offset + 0x18, sf) == 2 /*&& xwb.data_size == 0x55951c1c*/) { /* some kind of id in Stardew Valley? */ /* Stardew Valley (Switch), Skulls of the Shogun (Switch): full interleaved DSPs (including headers) */ xwb.codec = DSP; @@ -348,9 +366,9 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { /* Oddworld OGG the data_size value is size of uncompressed bytes instead; DSP uses some id/config as value */ if (xwb.codec != OGG && xwb.codec != DSP && xwb.codec != ATRAC9_RIFF) { /* some low-q rips don't remove padding, relax validation a bit */ - if (xwb.data_offset + xwb.stream_size > get_streamfile_size(streamFile)) + if (xwb.data_offset + xwb.stream_size > get_streamfile_size(sf)) goto fail; - //if (xwb.data_offset + xwb.data_size > get_streamfile_size(streamFile)) /* badly split */ + //if (xwb.data_offset + xwb.data_size > get_streamfile_size(sf)) /* badly split */ // goto fail; } @@ -394,7 +412,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { msd.loop_end_subframe = ((xwb.loop_end >> 2) & 0x3) + 1; /* 2b */ msd.loop_start_subframe = ((xwb.loop_end >> 0) & 0x3) + 1; /* 2b */ - xma_get_samples(&msd, streamFile); + xma_get_samples(&msd, sf); xwb.loop_start_sample = msd.loop_start_sample; xwb.loop_end_sample = msd.loop_end_sample; @@ -404,7 +422,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { /* for XWB v22 (and below?) this seems normal [Project Gotham Racing (X360)] */ if (xwb.num_samples == 0) { - xwb.num_samples = msd.num_samples; + xwb.num_samples = msd.num_samples; xwb.fix_xma_num_samples = 1; } } @@ -431,7 +449,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { vgmstream->num_streams = xwb.total_subsongs; vgmstream->stream_size = xwb.stream_size; vgmstream->meta_type = meta_XWB; - get_name(vgmstream->stream_name,STREAM_NAME_SIZE, target_subsong, &xwb, streamFile); + get_name(vgmstream->stream_name,STREAM_NAME_SIZE, target_subsong, &xwb, sf); switch(xwb.codec) { case PCM: /* Unreal Championship (Xbox)[PCM8], KOF2003 (Xbox)[PCM16LE], Otomedius (X360)[PCM16BE] */ @@ -458,12 +476,12 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { int bytes; bytes = ffmpeg_make_riff_xma1(buf,0x100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, 0); - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, xwb.stream_offset,xwb.stream_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - xma_fix_raw_samples(vgmstream, streamFile, xwb.stream_offset,xwb.stream_size, 0, xwb.fix_xma_num_samples,xwb.fix_xma_loop_samples); + xma_fix_raw_samples(vgmstream, sf, xwb.stream_offset,xwb.stream_size, 0, xwb.fix_xma_num_samples,xwb.fix_xma_loop_samples); /* this fixes some XMA1, perhaps the above isn't reading end_skip correctly (doesn't happen for all files though) */ if (vgmstream->loop_flag && @@ -482,19 +500,19 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { block_count = xwb.stream_size / block_size + (xwb.stream_size % block_size ? 1 : 0); bytes = ffmpeg_make_riff_xma2(buf,0x100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, xwb.stream_offset,xwb.stream_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - xma_fix_raw_samples(vgmstream, streamFile, xwb.stream_offset,xwb.stream_size, 0, xwb.fix_xma_num_samples,xwb.fix_xma_loop_samples); + xma_fix_raw_samples(vgmstream, sf, xwb.stream_offset,xwb.stream_size, 0, xwb.fix_xma_num_samples,xwb.fix_xma_loop_samples); break; } case WMA: { /* WMAudio1 (WMA v2): Prince of Persia 2 port (Xbox) */ ffmpeg_codec_data *ffmpeg_data = NULL; - ffmpeg_data = init_ffmpeg_offset(streamFile, xwb.stream_offset,xwb.stream_size); + ffmpeg_data = init_ffmpeg_offset(sf, xwb.stream_offset,xwb.stream_size); if ( !ffmpeg_data ) goto fail; vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; @@ -520,7 +538,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { wma_codec = xwb.bits_per_sample ? 0x162 : 0x161; /* 0=WMAudio2, 1=WMAudio3 */ bytes = ffmpeg_make_riff_xwma(buf,0x100, wma_codec, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, avg_bps, block_align); - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, xwb.stream_offset,xwb.stream_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -534,7 +552,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { encoder_delay = 1024; /* assumed */ vgmstream->num_samples -= encoder_delay; - vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, xwb.stream_offset,xwb.stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf, xwb.stream_offset,xwb.stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -543,7 +561,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { #endif #ifdef VGM_USE_VORBIS case OGG: { /* Oddworld: Strangers Wrath (iOS/Android) extension */ - vgmstream->codec_data = init_ogg_vorbis(streamFile, xwb.stream_offset, xwb.stream_size, NULL); + vgmstream->codec_data = init_ogg_vorbis(sf, xwb.stream_offset, xwb.stream_size, NULL); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_OGG_VORBIS; vgmstream->layout_type = layout_none; @@ -556,8 +574,8 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = xwb.stream_size / xwb.channels; - dsp_read_coefs(vgmstream,streamFile,xwb.stream_offset + 0x1c,vgmstream->interleave_block_size,!xwb.little_endian); - dsp_read_hist (vgmstream,streamFile,xwb.stream_offset + 0x3c,vgmstream->interleave_block_size,!xwb.little_endian); + dsp_read_coefs(vgmstream,sf,xwb.stream_offset + 0x1c,vgmstream->interleave_block_size,!xwb.little_endian); + dsp_read_hist (vgmstream,sf,xwb.stream_offset + 0x3c,vgmstream->interleave_block_size,!xwb.little_endian); xwb.stream_offset += 0x60; /* skip DSP header */ break; } @@ -565,16 +583,16 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { #ifdef VGM_USE_ATRAC9 case ATRAC9_RIFF: { /* Stardew Valley (Vita) extension */ VGMSTREAM *temp_vgmstream = NULL; - STREAMFILE *temp_streamFile = NULL; + STREAMFILE* temp_sf = NULL; /* standard RIFF, use subfile (seems doesn't use xwb loops) */ VGM_ASSERT(xwb.loop_flag, "XWB: RIFF ATRAC9 loop flag found\n"); - temp_streamFile = setup_subfile_streamfile(streamFile, xwb.stream_offset,xwb.stream_size, "at9"); - if (!temp_streamFile) goto fail; + temp_sf = setup_subfile_streamfile(sf, xwb.stream_offset,xwb.stream_size, "at9"); + if (!temp_sf) goto fail; - temp_vgmstream = init_vgmstream_riff(temp_streamFile); - close_streamfile(temp_streamFile); + temp_vgmstream = init_vgmstream_riff(temp_sf); + close_streamfile(temp_sf); if (!temp_vgmstream) goto fail; temp_vgmstream->num_streams = vgmstream->num_streams; @@ -594,7 +612,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { start_offset = xwb.stream_offset; - if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) + if ( !vgmstream_open_stream(vgmstream,sf,start_offset) ) goto fail; return vgmstream; @@ -605,13 +623,13 @@ fail: /* ****************************************************************************** */ -static int get_xwb_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamFile) { +static int get_xwb_name(char* buf, size_t maxsize, int target_subsong, xwb_header* xwb, STREAMFILE* sf) { size_t read; if (!xwb->names_offset || !xwb->names_size || xwb->names_entry_size > maxsize) goto fail; - read = read_string(buf,xwb->names_entry_size, xwb->names_offset + xwb->names_entry_size*(target_subsong-1),streamFile); + read = read_string(buf,xwb->names_entry_size, xwb->names_offset + xwb->names_entry_size*(target_subsong-1),sf); if (read == 0) goto fail; return 1; @@ -620,11 +638,11 @@ fail: return 0; } -static int get_xsb_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamFile) { +static int get_xsb_name(char* buf, size_t maxsize, int target_subsong, xwb_header* xwb, STREAMFILE* sf) { xsb_header xsb = {0}; xsb.selected_stream = target_subsong - 1; - if (!parse_xsb(&xsb, streamFile, xwb->wavebank_name)) + if (!parse_xsb(&xsb, sf, xwb->wavebank_name)) goto fail; if ((xwb->version <= XACT1_1_MAX && xsb.version > XSB_XACT1_2_MAX) || @@ -649,12 +667,12 @@ static int get_wbh_name(char* buf, size_t maxsize, int target_subsong, xwb_heade int version, name_count; off_t offset, name_number; - if (read_32bitBE(0x00, sf) != 0x57424844) /* "WBHD" */ + if (read_u32be(0x00, sf) != 0x57424844) /* "WBHD" */ goto fail; - version = read_32bitLE(0x04, sf); + version = read_u32le(0x04, sf); if (version != 1) goto fail; - name_count = read_32bitLE(0x08, sf); + name_count = read_u32le(0x08, sf); if (selected_stream > name_count) goto fail; @@ -682,19 +700,19 @@ fail: return 0; } -static void get_name(char * buf, size_t maxsize, int target_subsong, xwb_header * xwb, STREAMFILE *streamXwb) { - STREAMFILE *sf_name = NULL; +static void get_name(char* buf, size_t maxsize, int target_subsong, xwb_header* xwb, STREAMFILE* sf_xwb) { + STREAMFILE* sf_name = NULL; int name_found; /* try to get the stream name in the .xwb, though they are very rarely included */ - name_found = get_xwb_name(buf, maxsize, target_subsong, xwb, streamXwb); + name_found = get_xwb_name(buf, maxsize, target_subsong, xwb, sf_xwb); if (name_found) return; /* try again in a companion files */ if (xwb->version == 1) { /* .wbh, a simple name container */ - sf_name = open_streamfile_by_ext(streamXwb, "wbh"); + sf_name = open_streamfile_by_ext(sf_xwb, "wbh"); if (!sf_name) return; /* rarely found [Pac-Man World 2 (Xbox)] */ name_found = get_wbh_name(buf, maxsize, target_subsong, xwb, sf_name); @@ -702,7 +720,7 @@ static void get_name(char * buf, size_t maxsize, int target_subsong, xwb_header } else { /* .xsb, a comically complex cue format */ - sf_name = open_xsb_filename_pair(streamXwb); + sf_name = open_xsb_filename_pair(sf_xwb); if (!sf_name) return; /* not all xwb have xsb though */ name_found = get_xsb_name(buf, maxsize, target_subsong, xwb, sf_name); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h b/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h index c4f56cd1d..b68e30597 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h @@ -905,6 +905,10 @@ static STREAMFILE * open_xsb_filename_pair(STREAMFILE *streamXwb) { char temp_filename[PATH_LIMIT]; int target_len; + /* try parsing TXTM if present */ + streamXsb = read_filemap_file(streamXwb, 0); + if (streamXsb) return streamXsb; + /* try names in external .xsb, using a bunch of possible name pairs */ get_streamfile_filename(streamXwb,target_filename,PATH_LIMIT); target_len = strlen(target_filename); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwc.c b/Frameworks/vgmstream/vgmstream/src/meta/xwc.c index 3bd30ac25..659cf4ee5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwc.c @@ -67,7 +67,7 @@ VGMSTREAM * init_vgmstream_xwc(STREAMFILE *streamFile) { if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; - vgmstream->sample_rate = ((mpeg_codec_data*)vgmstream->codec_data)->sample_rate_per_frame; + vgmstream->sample_rate = mpeg_get_sample_rate(vgmstream->codec_data); break; } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/zwv.c b/Frameworks/vgmstream/vgmstream/src/meta/zwv.c new file mode 100644 index 000000000..70f436cc9 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/zwv.c @@ -0,0 +1,52 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .zwv - from Namco games [THE iDOLM@STER Shiny TV (PS3)] */ +VGMSTREAM* init_vgmstream_zwv(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + off_t subfile_offset; + size_t subfile_size; + + + /* checks */ + if (!check_extensions(sf,"zwv")) + goto fail; + if (read_u32be(0x00,sf) != 0x77617665) /* "wave" */ + goto fail; + + /* has a mini header then a proper MSF: + * 0x04: null + * 0x08: null + * 0x0c: version/config? (0x06040000) + * 0x10: version/config? (0x00030210) + * 0x14: sample rate + * 0x18: ? (related to sample rate) + * 0x1c: null + * 0x20: data offset + * 0x24: data size + * 0x28: loop flag (0x30+ is removed if no loop) + * 0x2c: ? (related to loop, or null) + * 0x30: null + * 0x30: loop start offset (same as MSF) + * 0x30: loop end offset (same as MSF start+length) + * 0x3c: null + */ + + subfile_offset = read_u32be(0x20, sf) - 0x40; + subfile_size = read_u32be(0x24, sf) + 0x40; + + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "msf"); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_msf(temp_sf); + if (!vgmstream) goto fail; + + close_streamfile(temp_sf); + return vgmstream; + +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/mixing.c b/Frameworks/vgmstream/vgmstream/src/mixing.c index 5dd900b44..43957e8f5 100644 --- a/Frameworks/vgmstream/vgmstream/src/mixing.c +++ b/Frameworks/vgmstream/vgmstream/src/mixing.c @@ -119,6 +119,10 @@ static int is_active(mixing_data *data, int32_t current_start, int32_t current_e static int32_t get_current_pos(VGMSTREAM* vgmstream, int32_t sample_count) { int32_t current_pos; + if (vgmstream->config_enabled) { + return vgmstream->pstate.play_position; + } + if (vgmstream->loop_flag && vgmstream->loop_count > 0) { int loop_pre = vgmstream->loop_start_sample; /* samples before looping */ int loop_into = (vgmstream->current_sample - vgmstream->loop_start_sample); /* samples after loop */ @@ -713,12 +717,40 @@ void mixing_macro_track(VGMSTREAM* vgmstream, uint32_t mask) { } } + +/* get highest channel count */ +static int get_layered_max_channels(VGMSTREAM* vgmstream) { + int i, max; + layered_layout_data* data; + + if (vgmstream->layout_type != layout_layered) + return 0; + + data = vgmstream->layout_data; + + max = 0; + for (i = 0; i < data->layer_count; i++) { + int output_channels = 0; + + mixing_info(data->layers[i], NULL, &output_channels); + + if (max < output_channels) + max = output_channels; + } + + return max; +} + void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, char mode) { mixing_data *data = vgmstream->mixing_data; int current, ch, output_channels, selected_channels; if (!data) return; + + if (max == 0) /* auto calculate */ + max = get_layered_max_channels(vgmstream); + if (max <= 0 || data->output_channels <= max) return; @@ -807,8 +839,11 @@ void mixing_macro_crosstrack(VGMSTREAM* vgmstream, int max) { /* set loops to hear all track changes */ track_num = output_channels / max; - if (vgmstream->config_loop_count < track_num) - vgmstream->config_loop_count = track_num; + if (vgmstream->config.loop_count < track_num) { + vgmstream->config.loop_count = track_num; + vgmstream->config.loop_count_set = 1; + vgmstream->config.config_set = 1; + } ch = 0; for (track = 0; track < track_num; track++) { @@ -868,8 +903,11 @@ void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max, char mode) { /* set loops to hear all track changes */ layer_num = output_channels / max; - if (vgmstream->config_loop_count < layer_num) - vgmstream->config_loop_count = layer_num; + if (vgmstream->config.loop_count < layer_num) { + vgmstream->config.loop_count = layer_num; + vgmstream->config.loop_count_set = 1; + vgmstream->config.config_set = 1; + } /* mode 'v': constant volume * mode 'e': sets fades to successively lower/equalize volume per loop for each layer @@ -1062,12 +1100,6 @@ void mixing_setup(VGMSTREAM * vgmstream, int32_t max_sample_count) { if (!data) goto fail; - /* a bit wonky but eh... */ - if (vgmstream->channel_layout && vgmstream->channels != data->output_channels) { - vgmstream->channel_layout = 0; - ((VGMSTREAM*)vgmstream->start_vgmstream)->channel_layout = 0; - } - /* special value to not actually enable anything (used to query values) */ if (max_sample_count <= 0) goto fail; @@ -1079,6 +1111,12 @@ void mixing_setup(VGMSTREAM * vgmstream, int32_t max_sample_count) { data->mixbuf = mixbuf_re; data->mixing_on = 1; + /* a bit wonky but eh... */ + if (vgmstream->channel_layout && vgmstream->channels != data->output_channels) { + vgmstream->channel_layout = 0; + ((VGMSTREAM*)vgmstream->start_vgmstream)->channel_layout = 0; + } + /* since data exists on its own memory and pointer is already set * there is no need to propagate to start_vgmstream */ @@ -1089,7 +1127,7 @@ fail: return; } -void mixing_info(VGMSTREAM * vgmstream, int *out_input_channels, int *out_output_channels) { +void mixing_info(VGMSTREAM* vgmstream, int* p_input_channels, int* p_output_channels) { mixing_data *data = vgmstream->mixing_data; int input_channels, output_channels; @@ -1101,11 +1139,13 @@ void mixing_info(VGMSTREAM * vgmstream, int *out_input_channels, int *out_output else input_channels = vgmstream->channels; - if (out_input_channels) *out_input_channels = input_channels; - if (out_output_channels) *out_output_channels = output_channels; + if (p_input_channels) *p_input_channels = input_channels; + if (p_output_channels) *p_output_channels = output_channels; //;VGM_LOG("MIX: channels %i, in=%i, out=%i, mix=%i\n", vgmstream->channels, input_channels, output_channels, data->mixing_channels); return; fail: + if (p_input_channels) *p_input_channels = vgmstream->channels; + if (p_output_channels) *p_output_channels = vgmstream->channels; return; } diff --git a/Frameworks/vgmstream/vgmstream/src/plugins.c b/Frameworks/vgmstream/vgmstream/src/plugins.c index 1e982bb69..f9199a3ce 100644 --- a/Frameworks/vgmstream/vgmstream/src/plugins.c +++ b/Frameworks/vgmstream/vgmstream/src/plugins.c @@ -63,6 +63,174 @@ int vgmstream_ctx_is_valid(const char* filename, vgmstream_ctx_valid_cfg *cfg) { return 0; } +void vgmstream_get_title(char* buf, int buf_len, const char* filename, VGMSTREAM* vgmstream, vgmstream_title_t* cfg) { + const char *pos; + char* pos2; + char temp[1024]; + + buf[0] = '\0'; + + /* name without path */ + pos = strrchr(filename, '\\'); + if (!pos) + pos = strrchr(filename, '/'); + if (!pos) + pos = filename; + else + pos++; + strncpy(buf, pos, buf_len); + + /* name without extension */ + if (cfg->remove_extension) { + pos2 = strrchr(buf, '.'); + if (pos2 && strlen(pos2) < 15) /* too big extension = file name probably has a dot in the middle */ + pos2[0] = '\0'; + } + + { + const char* stream_name = vgmstream->stream_name; + int total_subsongs = vgmstream->num_streams; + int target_subsong = vgmstream->stream_index; + //int is_first = vgmstream->stream_index == 0; + int show_name; + + /* special considerations for TXTP: + * - full txtp: don't show subsong number, nor name (assumes one names .txtp as wanted) + * - mini txtp: don't show subsong number, but show name (assumes one choses song #n in filename, but wants title) + */ + int full_txtp = vgmstream->config.is_txtp && !vgmstream->config.is_mini_txtp; + int mini_txtp = vgmstream->config.is_mini_txtp; + + if (target_subsong == 0) + target_subsong = 1; + + /* show number if file has more than 1 subsong */ + if (total_subsongs > 1 && !(full_txtp || mini_txtp)) { + if (cfg && cfg->subsong_range) + snprintf(temp, sizeof(temp), "%s#1~%i", buf, total_subsongs); + else + snprintf(temp, sizeof(temp), "%s#%i", buf, target_subsong); + strncpy(buf, temp, buf_len); + } + + /* show name for some cases */ + show_name = (total_subsongs > 0) && (!cfg || !cfg->subsong_range); + if (full_txtp) + show_name = 0; + if (cfg && cfg->force_title) + show_name = 1; + + if (stream_name[0] != '\0' && show_name) { + snprintf(temp, sizeof(temp), "%s (%s)", buf, stream_name); + strncpy(buf, temp, buf_len); + } + } + + buf[buf_len - 1] = '\0'; +} + + +static void copy_time(int* dst_flag, int32_t* dst_time, double* dst_time_s, int* src_flag, int32_t* src_time, double* src_time_s) { + if (!*src_flag) + return; + *dst_flag = 1; + *dst_time = *src_time; + *dst_time_s = *src_time_s; +} + +//todo reuse in txtp? +static void load_default_config(play_config_t* def, play_config_t* tcfg) { + + /* loop limit: txtp #L > txtp #l > player #L > player #l */ + if (tcfg->play_forever) { + def->play_forever = 1; + def->ignore_loop = 0; + } + if (tcfg->loop_count_set) { + def->loop_count = tcfg->loop_count; + def->loop_count_set = 1; + def->ignore_loop = 0; + if (!tcfg->play_forever) + def->play_forever = 0; + } + + /* fade priority: #F > #f, #d */ + if (tcfg->ignore_fade) { + def->ignore_fade = 1; + } + if (tcfg->fade_delay_set) { + def->fade_delay = tcfg->fade_delay; + def->fade_delay_set = 1; + } + if (tcfg->fade_time_set) { + def->fade_time = tcfg->fade_time; + def->fade_time_set = 1; + } + + /* loop priority: #i > #e > #E */ + if (tcfg->really_force_loop) { + def->ignore_loop = 0; + def->force_loop = 0; + def->really_force_loop = 1; + } + if (tcfg->force_loop) { + def->ignore_loop = 0; + def->force_loop = 1; + def->really_force_loop = 0; + } + if (tcfg->ignore_loop) { + def->ignore_loop = 1; + def->force_loop = 0; + def->really_force_loop = 0; + } + + copy_time(&def->pad_begin_set, &def->pad_begin, &def->pad_begin_s, &tcfg->pad_begin_set, &tcfg->pad_begin, &tcfg->pad_begin_s); + copy_time(&def->pad_end_set, &def->pad_end, &def->pad_end_s, &tcfg->pad_end_set, &tcfg->pad_end, &tcfg->pad_end_s); + copy_time(&def->trim_begin_set, &def->trim_begin, &def->trim_begin_s, &tcfg->trim_begin_set, &tcfg->trim_begin, &tcfg->trim_begin_s); + copy_time(&def->trim_end_set, &def->trim_end, &def->trim_end_s, &tcfg->trim_end_set, &tcfg->trim_end, &tcfg->trim_end_s); + copy_time(&def->body_time_set, &def->body_time, &def->body_time_s, &tcfg->body_time_set, &tcfg->body_time, &tcfg->body_time_s); + + def->is_mini_txtp = tcfg->is_mini_txtp; + def->is_txtp = tcfg->is_txtp; +} + +static void load_player_config(play_config_t* def, vgmstream_cfg_t* vcfg) { + def->play_forever = vcfg->play_forever; + def->ignore_loop = vcfg->ignore_loop; + def->force_loop = vcfg->force_loop; + def->really_force_loop = vcfg->really_force_loop; + def->ignore_fade = vcfg->ignore_fade; + + def->loop_count = vcfg->loop_count; + def->loop_count_set = 1; + def->fade_delay = vcfg->fade_delay; + def->fade_delay_set = 1; + def->fade_time = vcfg->fade_time; + def->fade_time_set = 1; +} + +void vgmstream_apply_config(VGMSTREAM* vgmstream, vgmstream_cfg_t* vcfg) { + play_config_t defs = {0}; + play_config_t* def = &defs; /* for convenience... */ + play_config_t* tcfg = &vgmstream->config; + + + load_player_config(def, vcfg); + def->config_set = 1; + + if (!vcfg->disable_config_override) + load_default_config(def, tcfg); + + if (!vcfg->allow_play_forever) + def->play_forever = 0; + + /* copy final config back */ + *tcfg = *def; + + vgmstream->config_enabled = def->config_set; + setup_state_vgmstream(vgmstream); +} + /* ****************************************** */ /* TAGS: loads key=val tags from a file */ /* ****************************************** */ @@ -91,6 +259,7 @@ struct VGMSTREAM_TAGS { int autotrack_on; int autotrack_written; int track_count; + int exact_match; int autoalbum_on; int autoalbum_written; @@ -213,22 +382,27 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) { if (line[0] == '#') { /* find possible global command */ - ok = sscanf(line, "# $%[^ \t] %[^\r\n]", tags->key,tags->val); + ok = sscanf(line, "# $%n%[^ \t]%n %[^\r\n]", &n1, tags->key, &n2, tags->val); if (ok == 1 || ok == 2) { - if (strcasecmp(tags->key,"AUTOTRACK") == 0) { + int key_len = n2 - n1; + if (strncasecmp(tags->key, "AUTOTRACK", key_len) == 0) { tags->autotrack_on = 1; } - else if (strcasecmp(tags->key,"AUTOALBUM") == 0) { + else if (strncasecmp(tags->key, "AUTOALBUM", key_len) == 0) { tags->autoalbum_on = 1; } + else if (strncasecmp(tags->key, "EXACTMATCH", key_len) == 0) { + tags->exact_match = 1; + VGM_LOG("exact\n"); + } continue; /* not an actual tag */ } /* find possible global tag */ - ok = sscanf(line, "# @%[^@]@ %[^\r\n]", tags->key,tags->val); /* key with spaces */ + ok = sscanf(line, "# @%[^@]@ %[^\r\n]", tags->key, tags->val); /* key with spaces */ if (ok != 2) - ok = sscanf(line, "# @%[^ \t] %[^\r\n]", tags->key,tags->val); /* key without */ + ok = sscanf(line, "# @%[^ \t] %[^\r\n]", tags->key, tags->val); /* key without */ if (ok == 2) { tags_clean(tags); return 1; @@ -244,20 +418,28 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) { int currentname_len = n2 - n1; int filename_found = 0; - /* we want to find file with the same name (case insensitive), OR a virtual .txtp with - * the filename inside (so 'file.adx' gets tags from 'file.adx#i.txtp', reading - * tags even if we don't open !tags.m3u with virtual .txtp directly) */ + /* we want to match file with the same name (case insensitive), OR a virtual .txtp with + * the filename inside to ease creation of tag files with config, also check end char to + * tell apart the unlikely case of having both 'bgm01.ad.txtp' and 'bgm01.adp.txtp' */ - /* strcasecmp works ok even for UTF-8 */ - if (currentname_len >= tags->targetname_len && /* starts with targetname */ - strncasecmp(currentname, tags->targetname, tags->targetname_len) == 0) { - - if (currentname_len == tags->targetname_len) { /* exact match */ - filename_found = 1; + /* try exact match (strcasecmp works ok even for UTF-8) */ + if (currentname_len == tags->targetname_len && + strncasecmp(currentname, tags->targetname, currentname_len) == 0) { + filename_found = 1; + } + else if (!tags->exact_match) { + /* try tagfile is "bgm.adx" + target is "bgm.adx #(cfg) .txtp" */ + if (currentname_len < tags->targetname_len && + strncasecmp(currentname, tags->targetname, currentname_len) == 0 && + vgmstream_is_virtual_filename(tags->targetname)) { + char c = tags->targetname[currentname_len]; + filename_found = (c==' ' || c == '.' || c == '#'); } - else if (vgmstream_is_virtual_filename(currentname)) { /* ends with .txth */ + /* tagfile has "bgm.adx (...) .txtp" + target has "bgm.adx" */ + else if (tags->targetname_len < currentname_len && + strncasecmp(tags->targetname, currentname, tags->targetname_len) == 0 && + vgmstream_is_virtual_filename(currentname)) { char c = currentname[tags->targetname_len]; - /* tell apart the unlikely case of having both 'bgm01.ad.txtp' and 'bgm01.adp.txtp' */ filename_found = (c==' ' || c == '.' || c == '#'); } } @@ -329,6 +511,10 @@ void vgmstream_tags_reset(VGMSTREAM_TAGS* tags, const char* target_filename) { void vgmstream_mixing_enable(VGMSTREAM* vgmstream, int32_t max_sample_count, int *input_channels, int *output_channels) { mixing_setup(vgmstream, max_sample_count); mixing_info(vgmstream, input_channels, output_channels); + + /* update internals */ + mixing_info(vgmstream, &vgmstream->pstate.input_channels, &vgmstream->pstate.output_channels); + setup_vgmstream(vgmstream); } void vgmstream_mixing_autodownmix(VGMSTREAM *vgmstream, int max_channels) { diff --git a/Frameworks/vgmstream/vgmstream/src/plugins.h b/Frameworks/vgmstream/vgmstream/src/plugins.h index 9133eae84..b31b4862d 100644 --- a/Frameworks/vgmstream/vgmstream/src/plugins.h +++ b/Frameworks/vgmstream/vgmstream/src/plugins.h @@ -5,6 +5,25 @@ #define _PLUGINS_H_ #include "streamfile.h" +//todo rename to api.h once public enough + + +#if 0 +/* define standard C param call and name mangling (to avoid __stdcall / .defs) */ +//#define VGMSTREAM_CALL __cdecl //needed? + +/* define external function types (during compilation) */ +#if defined(VGMSTREAM_EXPORT) + #define VGMSTREAM_API __declspec(dllexport) /* when exporting/creating vgmstream DLL */ +#elif defined(VGMSTREAM_IMPORT) + #define VGMSTREAM_API __declspec(dllimport) /* when importing/linking vgmstream DLL */ +#else + #define VGMSTREAM_API /* nothing, internal/default */ +#endif + +//VGMSTREAM_API void VGMSTREAM_CALL vgmstream_function(void); +#endif + /* ****************************************** */ /* CONTEXT: simplifies plugin code */ @@ -21,42 +40,140 @@ typedef struct { /* returns if vgmstream can parse file by extension */ int vgmstream_ctx_is_valid(const char* filename, vgmstream_ctx_valid_cfg *cfg); -#if 0 - -/* opaque player state */ -typedef struct VGMSTREAM_CTX VGMSTREAM_CTX; typedef struct { - //... -} VGMSTREAM_CTX_INFO; + int allow_play_forever; + int disable_config_override; -VGMSTREAM_CTX* vgmstream_ctx_init(...); + /* song mofidiers */ + int play_forever; /* keeps looping forever (needs loop points) */ + int ignore_loop; /* ignores loops points */ + int force_loop; /* enables full loops (0..samples) if file doesn't have loop points */ + int really_force_loop; /* forces full loops even if file has loop points */ + int ignore_fade; /* don't fade after N loops */ -VGMSTREAM_CTX* vgmstream_ctx_format_check(...); -VGMSTREAM_CTX* vgmstream_ctx_set_format_whilelist(...); -VGMSTREAM_CTX* vgmstream_ctx_set_format_blacklist(...); + /* song processing */ + double loop_count; /* target loops */ + double fade_delay; /* fade delay after target loops */ + double fade_time; /* fade period after target loops */ -VGMSTREAM_CTX* vgmstream_ctx_set_file(...); + //int downmix; /* max number of channels allowed (0=disable downmix) */ -VGMSTREAM_CTX* vgmstream_ctx_get_config(...); +} vgmstream_cfg_t; -VGMSTREAM_CTX* vgmstream_ctx_set_config(...); +// WARNING: these are not stable and may change anytime without notice +void vgmstream_apply_config(VGMSTREAM* vgmstream, vgmstream_cfg_t* pcfg); +int32_t vgmstream_get_samples(VGMSTREAM* vgmstream); +int vgmstream_get_play_forever(VGMSTREAM* vgmstream); +void vgmstream_set_play_forever(VGMSTREAM* vgmstream, int enabled); -VGMSTREAM_CTX* vgmstream_ctx_get_buffer(...); -VGMSTREAM_CTX* vgmstream_ctx_get_info(...); +typedef struct { + int force_title; + int subsong_range; + int remove_extension; +} vgmstream_title_t; -VGMSTREAM_CTX* vgmstream_ctx_describe(...); +/* get a simple title for plugins */ +void vgmstream_get_title(char* buf, int buf_len, const char* filename, VGMSTREAM* vgmstream, vgmstream_title_t* cfg); -VGMSTREAM_CTX* vgmstream_ctx_get_title(...); -VGMSTREAM_CTX* vgmstream_ctx_get_tagfile(...); -VGMSTREAM_CTX* vgmstream_ctx_play(...); +#if 0 +//possible future public/opaque API -VGMSTREAM_CTX* vgmstream_ctx_seek(...); +/* opaque player state */ +//#define VGMSTREAM_CTX_VERSION 1 +typedef struct VGMSTREAM_CTX VGMSTREAM_CTX; -VGMSTREAM_CTX* vgmstream_ctx_close(...); + +/* Setups base vgmstream player context. */ +VGMSTREAM_CTX* vgmstream_init_ctx(void); + + +/* Sets default config, that will be applied to song on open (some formats like TXTP may override + * these settings). + * May only be called without song loaded (before _open or after _close), otherwise ignored. */ +void vgmstream_set_config(VGMSTREAM_CTX* vctx, VGMSTREAM_CFG* vcfg); + +void vgmstream_set_buffer(VGMSTREAM_CTX* vctx, int samples, int max_samples); + +/* Opens a new STREAMFILE to play. Returns < 0 on error when the file isn't recogniced. + * If file has subsongs, first open usually loads first subsong. get_info then can be used to check + * whether file has more subsongs (total_subsongs > 1), and call others. + * */ +int vgmstream_open(STREAMFILE* sf); +int vgmstream_open_subsong(STREAMFILE* sf, int subsong); + +typedef struct { + const int channels; + const int sample_rate; + + const int sample_count; /* file's samples (not final duration) */ + const int loop_start_sample; + const int loop_end_sample; + const int loop_flag; + + const int current_subsong; /* 0=not set, N=loaded subsong N */ + const int total_subsongs; /* 0=format has no subsongs, N=has N subsongs */ + const int file_bitrate; /* file's average bitrate */ + //const int codec_bitrate; /* codec's average bitrate */ + + /* descriptions */ + //const char* codec; + //const char* layout; + //const char* metadata; + + //int type; /* 0=pcm16, 1=float32, always interleaved: [0]=ch0, [1]=ch1 ... */ +} VGMSTREAM_INFO; + +/* Get info from current song. */ +void vgmstream_ctx_get_info(VGMSTREAM_CTX* vctx, VGMSTREAM_INFO* vinfo); + + +/* Gets final time based on config and current song. If config is set to "play forever" + * this still returns final time based on config as a reference. Returns > 0 on success. */ +int32_t vgmstream_get_total_time(VGMSTREAM_CTX* vctx); +double vgmstream_get_total_samples(VGMSTREAM_CTX* vctx); + + +/* Gets current position within song. When "play forever" is set, it'll clamp results to total_time. */ +int32_t vgmstream_get_current_time(VGMSTREAM_CTX* vctx); +double vgmstream_get_current_samples(VGMSTREAM_CTX* vctx); + + +/* Seeks to position */ +VGMSTREAM_CTX* vgmstream_seek_absolute_sample(VGMSTREAM_CTX* vctx, int32_t sample); +VGMSTREAM_CTX* vgmstream_seek_absolute_time(VGMSTREAM_CTX* vctx, double time); +VGMSTREAM_CTX* vgmstream_seek_current_sample(VGMSTREAM_CTX* vctx, int32_t sample); +VGMSTREAM_CTX* vgmstream_seek_current_time(VGMSTREAM_CTX* vctx, double time); + + +/* Closes current song. */ +void vgmstream_close(VGMSTREAM_CTX* vctx); + +/* Frees vgmstream context. */ +void vgmstream_free_ctx(VGMSTREAM_CTX* vctx); + + +/* Converts samples. returns number of rendered samples, or <=0 if no more + * samples left (will fill buffer with silence) */ +int vgmstream_play(VGMSTREAM_CTX* vctx); + + +#if 0 +void vgmstream_get_buffer(...); + +void vgmstream_format_check(...); +void vgmstream_set_format_whilelist(...); +void vgmstream_set_format_blacklist(...); + +const char* vgmstream_describe(...); + +const char* vgmstream_get_title(...); + +VGMSTREAM_TAGS* vgmstream_get_tagfile(...); +#endif #endif diff --git a/Frameworks/vgmstream/vgmstream/src/render.c b/Frameworks/vgmstream/vgmstream/src/render.c new file mode 100644 index 000000000..308f32f4e --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/render.c @@ -0,0 +1,724 @@ +#include "vgmstream.h" +#include "layout/layout.h" +#include "render.h" +#include "decode.h" +#include "mixing.h" +#include "plugins.h" + + +/* VGMSTREAM RENDERING + * Caller asks for N samples in buf. vgmstream then calls layouts, that call decoders, and some optional pre/post-processing. + * Processing must be enabled externally: padding/trimming (config), mixing (output changes), resampling, etc. + * + * - MIXING + * After decoding sometimes we need to change number of channels, volume, etc. This is applied in order as + * a mixing chain, and modifies the final buffer (see mixing.c). + * + * - CONFIG + * A VGMSTREAM can work in 2 modes, defaults to simple mode: + * - simple mode (lib-like): decodes/loops forever and results are controlled externally (fades/max time/etc). + * - config mode (player-like): everything is internally controlled (pads/trims/time/fades/etc may be applied). + * + * It's done this way mainly for compatibility and to enable complex TXTP for layers/segments in selected cases + * (Wwise emulation). Could apply always some config like begin trim/padding + modify get_vgmstream_samples, but + * external caller may read loops/samples manually or apply its own config/fade, and changed output would mess it up. + * + * To enable config mode it needs 2 steps: + * - add some internal config settings (via TXTP, or passed by plugin). + * - enable flag with function (to signal "really delegate all decoding to vgmstream"). + * Once done, plugin should simply decode until max samples (calculated by vgmstream). + * + * For complex layouts, behavior of "internal" (single segment/layer) and "external" (main) VGMSTREAMs is + * a bit complex. Internals' enable flag if play config exists (via TXTP), and this allows each part to be + * padded/trimmed/set time/loop/etc individually. + * + * Config mode in the external VGMSTREAM is mostly straighforward with segments: + * - each internal is always decoded separatedly (in simple or config mode) and results in N samples + * - segments may even loop "internally" before moving to next segment (by default they don't) + * - external's samples is the sum of all segments' N samples + * - looping, fades, etc then can be applied in the external part normally. + * + * With layers it's a bit more complex: + * - external's samples is the max of all layers + * - in simple mode external uses internal's looping to loop (for performance) + * - if layers' config mode is set, external can't rely on internal looping, so it uses it's own + * + * Layouts can contain layouts in cascade, so behavior can be a bit hard to understand at times. + * This mainly applies to TXTP, segments/layers in metas usually don't need to trigger config mode. + */ + + +int vgmstream_get_play_forever(VGMSTREAM* vgmstream) { + return vgmstream->config.play_forever; +} + +void vgmstream_set_play_forever(VGMSTREAM* vgmstream, int enabled) { + /* sometimes we need to enable/disable right before playback + * (play config is left untouched, should mix ok as this flag is only used during + * render, while config is always prepared as if play forever wasn't enabled) */ + vgmstream->config.play_forever = enabled; + setup_vgmstream(vgmstream); /* update config */ +} + +int32_t vgmstream_get_samples(VGMSTREAM* vgmstream) { + if (!vgmstream->config_enabled || !vgmstream->config.config_set) + return vgmstream->num_samples; + return vgmstream->pstate.play_duration; +} + +/* calculate samples based on player's config */ +int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM* vgmstream) { + if (vgmstream->loop_flag) { + if (vgmstream->loop_target == (int)looptimes) { /* set externally, as this function is info-only */ + /* Continue playing the file normally after looping, instead of fading. + * Most files cut abruply after the loop, but some do have proper endings. + * With looptimes = 1 this option should give the same output vs loop disabled */ + int loop_count = (int)looptimes; /* no half loops allowed */ + return vgmstream->loop_start_sample + + (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * loop_count + + (vgmstream->num_samples - vgmstream->loop_end_sample); + } + else { + return vgmstream->loop_start_sample + + (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * looptimes + + (fadedelayseconds + fadeseconds) * vgmstream->sample_rate; + } + } + else { + return vgmstream->num_samples; + } +} + +/*****************************************************************************/ + +static void setup_state_modifiers(VGMSTREAM* vgmstream) { + play_config_t* pc = &vgmstream->config; + + /* apply final config */ + if (pc->really_force_loop) { + vgmstream_force_loop(vgmstream, 1, 0,vgmstream->num_samples); + } + if (pc->force_loop && !vgmstream->loop_flag) { + vgmstream_force_loop(vgmstream, 1, 0,vgmstream->num_samples); + } + if (pc->ignore_loop) { + vgmstream_force_loop(vgmstream, 0, 0,0); + } + + if (!vgmstream->loop_flag) { + pc->play_forever = 0; + } + if (pc->play_forever) { + pc->ignore_fade = 0; + } + + + /* loop N times, but also play stream end instead of fading out */ + if (pc->ignore_fade) { + vgmstream_set_loop_target(vgmstream, (int)pc->loop_count); + pc->fade_time = 0; + pc->fade_delay = 0; + } +} + +static void setup_state_processing(VGMSTREAM* vgmstream) { + play_state_t* ps = &vgmstream->pstate; + play_config_t* pc = &vgmstream->config; + double sample_rate = vgmstream->sample_rate; + + /* time to samples */ + if (pc->pad_begin_s) + pc->pad_begin = pc->pad_begin_s * sample_rate; + if (pc->pad_end_s) + pc->pad_end = pc->pad_end_s * sample_rate; + if (pc->trim_begin_s) + pc->trim_begin = pc->trim_begin_s * sample_rate; + if (pc->trim_end_s) + pc->trim_end = pc->trim_end_s * sample_rate; + if (pc->body_time_s) + pc->body_time = pc->body_time_s * sample_rate; + //todo fade time also set to samples + + /* samples before all decode */ + ps->pad_begin_duration = pc->pad_begin; + + /* removed samples from first decode */ + ps->trim_begin_duration = pc->trim_begin; + + /* main samples part */ + ps->body_duration = 0; + if (pc->body_time) { + ps->body_duration += pc->body_time; /* whether it loops or not */ + } + else if (vgmstream->loop_flag) { + double loop_count = 1.0; + if (pc->loop_count_set) /* may set 0.0 on purpose I guess */ + loop_count = pc->loop_count; + + ps->body_duration += vgmstream->loop_start_sample; + if (pc->ignore_fade) { + ps->body_duration += (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * (int)loop_count; + ps->body_duration += (vgmstream->num_samples - vgmstream->loop_end_sample); + } + else { + ps->body_duration += (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * loop_count; + } + } + else { + ps->body_duration += vgmstream->num_samples; + } + + /* samples from some modify body */ + if (pc->trim_begin) + ps->body_duration -= pc->trim_begin; + if (pc->trim_end) + ps->body_duration -= pc->trim_end; + if (pc->fade_delay && vgmstream->loop_flag) + ps->body_duration += pc->fade_delay * vgmstream->sample_rate; + + /* samples from fade part */ + if (pc->fade_time && vgmstream->loop_flag) + ps->fade_duration = pc->fade_time * vgmstream->sample_rate; + + /* samples from last part (anything beyond this is empty, unless play forever is set) */ + ps->pad_end_duration = pc->pad_end; + + /* final count */ + ps->play_duration = ps->pad_begin_duration + ps->body_duration + ps->fade_duration + ps->pad_end_duration; + ps->play_position = 0; + + /* values too big can overflow, just ignore */ + if (ps->pad_begin_duration < 0) + ps->pad_begin_duration = 0; + if (ps->body_duration < 0) + ps->body_duration = 0; + if (ps->fade_duration < 0) + ps->fade_duration = 0; + if (ps->pad_end_duration < 0) + ps->pad_end_duration = 0; + if (ps->play_duration < 0) + ps->play_duration = 0; + + ps->pad_begin_left = ps->pad_begin_duration; + ps->trim_begin_left = ps->trim_begin_duration; + ps->fade_left = ps->fade_duration; + ps->fade_start = ps->pad_begin_duration + ps->body_duration; + //ps->pad_end_left = ps->pad_end_duration; + ps->pad_end_start = ps->fade_start + ps->fade_duration; + + /* other info (updated once mixing is enabled) */ + ps->input_channels = vgmstream->channels; + ps->output_channels = vgmstream->channels; +} + +void setup_state_vgmstream(VGMSTREAM* vgmstream) { + if (!vgmstream->config.config_set) + return; + + setup_state_modifiers(vgmstream); + setup_state_processing(vgmstream); + setup_vgmstream(vgmstream); /* save current config for reset */ +} + +/*****************************************************************************/ + +void free_layout(VGMSTREAM* vgmstream) { + + if (vgmstream->layout_type == layout_segmented) { + free_layout_segmented(vgmstream->layout_data); + } + + if (vgmstream->layout_type == layout_layered) { + free_layout_layered(vgmstream->layout_data); + } +} + +void reset_layout(VGMSTREAM* vgmstream) { + + if (vgmstream->layout_type == layout_segmented) { + reset_layout_segmented(vgmstream->layout_data); + } + + if (vgmstream->layout_type == layout_layered) { + reset_layout_layered(vgmstream->layout_data); + } +} + +static int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) { + + /* current_sample goes between loop points (if looped) or up to max samples, + * must detect beyond that decoders would encounter garbage data */ + + /* not ">=" to allow layouts to loop in some cases when == happens */ + if (vgmstream->current_sample > vgmstream->num_samples) { + int channels = vgmstream->channels; + + memset(buf, 0, sample_count * sizeof(sample_t) * channels); + return sample_count; + } + + switch (vgmstream->layout_type) { + case layout_interleave: + render_vgmstream_interleave(buf, sample_count, vgmstream); + break; + case layout_none: + render_vgmstream_flat(buf, sample_count, vgmstream); + break; + case layout_blocked_mxch: + case layout_blocked_ast: + case layout_blocked_halpst: + case layout_blocked_xa: + case layout_blocked_ea_schl: + case layout_blocked_ea_1snh: + case layout_blocked_caf: + case layout_blocked_wsi: + case layout_blocked_str_snds: + case layout_blocked_ws_aud: + case layout_blocked_matx: + case layout_blocked_dec: + case layout_blocked_vs: + case layout_blocked_mul: + case layout_blocked_gsb: + case layout_blocked_xvas: + case layout_blocked_thp: + case layout_blocked_filp: + case layout_blocked_ivaud: + case layout_blocked_ea_swvr: + case layout_blocked_adm: + case layout_blocked_bdsp: + case layout_blocked_tra: + case layout_blocked_ps2_iab: + case layout_blocked_vs_str: + case layout_blocked_rws: + case layout_blocked_hwas: + case layout_blocked_ea_sns: + case layout_blocked_awc: + case layout_blocked_vgs: + case layout_blocked_xwav: + case layout_blocked_xvag_subsong: + case layout_blocked_ea_wve_au00: + case layout_blocked_ea_wve_ad10: + case layout_blocked_sthd: + case layout_blocked_h4m: + case layout_blocked_xa_aiff: + case layout_blocked_vs_square: + case layout_blocked_vid1: + case layout_blocked_ubi_sce: + render_vgmstream_blocked(buf, sample_count, vgmstream); + break; + case layout_segmented: + render_vgmstream_segmented(buf, sample_count,vgmstream); + break; + case layout_layered: + render_vgmstream_layered(buf, sample_count, vgmstream); + break; + default: + break; + } + + if (vgmstream->current_sample > vgmstream->num_samples) { + int channels = vgmstream->channels; + int32_t excess, decoded; + + excess = (vgmstream->current_sample - vgmstream->num_samples); + if (excess > sample_count) + excess = sample_count; + decoded = sample_count - excess; + + memset(buf + decoded * channels, 0, excess * sizeof(sample_t) * channels); + return sample_count; + } + + return sample_count; +} + + +static void render_trim(VGMSTREAM* vgmstream) { + sample_t* tmpbuf = vgmstream->tmpbuf; + size_t tmpbuf_size = vgmstream->tmpbuf_size; + int32_t buf_samples = tmpbuf_size / vgmstream->channels; /* base channels, no need to apply mixing */ + + while (vgmstream->pstate.trim_begin_left) { + int to_do = vgmstream->pstate.trim_begin_left; + if (to_do > buf_samples) + to_do = buf_samples; + + render_layout(tmpbuf, to_do, vgmstream); + /* no mixing */ + vgmstream->pstate.trim_begin_left -= to_do; + } +} + +static int render_pad_begin(VGMSTREAM* vgmstream, sample_t* buf, int samples_to_do) { + int channels = vgmstream->pstate.output_channels; + int to_do = vgmstream->pstate.pad_begin_left; + if (to_do > samples_to_do) + to_do = samples_to_do; + + memset(buf, 0, to_do * sizeof(sample_t) * channels); + vgmstream->pstate.pad_begin_left -= to_do; + + return to_do; +} + +static int render_fade(VGMSTREAM* vgmstream, sample_t* buf, int samples_done) { + play_state_t* ps = &vgmstream->pstate; + //play_config_t* pc = &vgmstream->config; + + //if (!ps->fade_left || pc->play_forever) + // return; + //if (ps->play_position + samples_done < ps->fade_start) + // return; + + { + int s, ch, start, fade_pos; + int channels = ps->output_channels; + int32_t to_do = ps->fade_left; + + if (ps->play_position < ps->fade_start) { + start = samples_done - (ps->play_position + samples_done - ps->fade_start); + fade_pos = 0; + } + else { + start = 0; + fade_pos = ps->play_position - ps->fade_start; + } + + if (to_do > samples_done - start) + to_do = samples_done - start; + + //TODO: use delta fadedness to improve performance? + for (s = start; s < start + to_do; s++, fade_pos++) { + double fadedness = (double)(ps->fade_duration - fade_pos) / ps->fade_duration; + for (ch = 0; ch < channels; ch++) { + buf[s*channels + ch] = (sample_t)buf[s*channels + ch] * fadedness; + } + } + + ps->fade_left -= to_do; + + /* next samples after fade end would be pad end/silence, so we can just memset */ + memset(buf + (start + to_do) * channels, 0, (samples_done - to_do - start) * sizeof(sample_t) * channels); + + return samples_done; + } +} + +static int render_pad_end(VGMSTREAM* vgmstream, sample_t* buf, int samples_done) { + play_state_t* ps = &vgmstream->pstate; + int channels = vgmstream->pstate.output_channels; + int start = 0; + + /* since anything beyond pad end is silence no need to check end */ + if (ps->play_position < ps->pad_end_start) { + start = samples_done - (ps->play_position + samples_done - ps->pad_end_start); + } + else { + start = 0; + } + + memset(buf + (start * channels), 0, (samples_done - start) * channels * sizeof(sample_t)); + return samples_done; +} + + +/* Decode data into sample buffer. Controls the "external" part of the decoding, + * while layout/decode control the "internal" part. */ +int render_vgmstream(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) { + play_state_t* ps = &vgmstream->pstate; + int samples_to_do = sample_count; + int samples_done = 0; + int done; + sample_t* tmpbuf = buf; + + + /* simple mode with no settings (just skip everything below) */ + if (!vgmstream->config_enabled) { + render_layout(buf, samples_to_do, vgmstream); + mix_vgmstream(buf, samples_to_do, vgmstream); + return samples_to_do; + } + + + /* trim may go first since it doesn't need output nor changes totals */ + if (ps->trim_begin_left) { + render_trim(vgmstream); + } + + /* adds empty samples to buf */ + if (ps->pad_begin_left) { + done = render_pad_begin(vgmstream, tmpbuf, samples_to_do); + samples_done += done; + samples_to_do -= done; + tmpbuf += done * vgmstream->pstate.output_channels; /* as if mixed */ + } + + /* end padding (done before to avoid decoding if possible, samples_to_do becomes 0) */ + if (!vgmstream->config.play_forever /* && ps->pad_end_left */ + && ps->play_position + samples_done >= ps->pad_end_start + && samples_to_do) { + done = render_pad_end(vgmstream, tmpbuf, samples_to_do); + samples_done += done; + samples_to_do -= done; + tmpbuf += done * vgmstream->pstate.output_channels; /* as if mixed */ + } + + /* main decode */ + { //if (samples_to_do) /* 0 ok, less likely */ + done = render_layout(tmpbuf, samples_to_do, vgmstream); + + mix_vgmstream(tmpbuf, done, vgmstream); + + samples_done += done; + + if (!vgmstream->config.play_forever) { + /* simple fadeout */ + if (ps->fade_left && ps->play_position + done >= ps->fade_start) { + render_fade(vgmstream, tmpbuf, done); + } + + /* silence leftover buf samples (rarely used when no fade is set) */ + if (ps->play_position + done >= ps->pad_end_start) { + render_pad_end(vgmstream, tmpbuf, done); + } + } + + tmpbuf += done * vgmstream->pstate.output_channels; + } + + + vgmstream->pstate.play_position += samples_done; + + /* signal end */ + if (!vgmstream->config.play_forever + && ps->play_position > ps->play_duration) { + int excess = ps->play_position - ps->play_duration; + if (excess > sample_count) + excess = sample_count; + + samples_done = (sample_count - excess); + + ps->play_position = ps->play_duration; + } + + return samples_done; +} + +/*****************************************************************************/ + +static void seek_force_loop(VGMSTREAM* vgmstream, int loop_count) { + /* only called after hit loop */ + if (!vgmstream->hit_loop) + return; + + /* pretend decoder reached loop end so state is set to loop start */ + vgmstream->loop_count = loop_count - 1; /* seeking to first loop musy become ++ > 0 */ + vgmstream->current_sample = vgmstream->loop_end_sample; + vgmstream_do_loop(vgmstream); +} + +static void seek_force_decode(VGMSTREAM* vgmstream, int samples) { + sample_t* tmpbuf = vgmstream->tmpbuf; + size_t tmpbuf_size = vgmstream->tmpbuf_size; + int32_t buf_samples = tmpbuf_size / vgmstream->channels; /* base channels, no need to apply mixing */ + + while (samples) { + int to_do = samples; + if (to_do > buf_samples) + to_do = buf_samples; + + render_layout(tmpbuf, to_do, vgmstream); + /* no mixing */ + samples -= to_do; + } +} + +void seek_vgmstream(VGMSTREAM* vgmstream, int32_t seek_sample) { + play_state_t* ps = &vgmstream->pstate; + int play_forever = vgmstream->config.play_forever; + + int32_t decode_samples = 0; + int loop_count = -1; + int is_looped = vgmstream->loop_flag || vgmstream->loop_target > 0; /* loop target disabled loop flag during decode */ + + + /* will decode and loop until seek sample, but slower */ + //todo apply same loop logic as below, or pretend we have play_forever + settings? + if (!vgmstream->config_enabled) { + //;VGM_LOG("SEEK: simple seek=%i, cur=%i\n", seek_sample, vgmstream->current_sample); + if (seek_sample < vgmstream->current_sample) { + decode_samples = seek_sample; + reset_vgmstream(vgmstream); + } + else { + decode_samples = seek_sample - vgmstream->current_sample; + } + + seek_force_decode(vgmstream, decode_samples); + return; + } + + //todo could improve performance bit if hit_loop wasn't lost when calling reset + //todo wrong seek with ignore fade, also for layered layers (pass count to force loop + layers) + + + /* seeking to requested sample normally means decoding and discarding up to that point (from + * the beginning, or current position), but can be optimized a bit to decode less with some tricks: + * - seek may fall in part of the song that isn't actually decoding (due to config, like padding) + * - if file loops there is no need to decode N full loops, seek can be set relative to loop region + * - can decode to seek sample from current position or loop start depending on lowest + * + * some of the cases below could be simplified but the logic to get this going is kinda mind-bending + * + * (ex. with file = 100, pad=5s, trim=3s, loop=20s..90s) + * | pad-begin | body-begin | body-loop0 | body-loop1 | body-loop2 | fade | pad-end + beyond) + * 0 5s (-3s) 25s 95s 165s 235s 245s Ns + */ + + if (seek_sample < 0) + seek_sample = 0; + if (seek_sample > ps->play_duration && !play_forever) /* play forever can seek to any loop */ + seek_sample = ps->play_duration; + + //;VGM_LOG("SEEK: seek sample=%i, is_looped=%i\n", seek_sample, is_looped); + + /* start/pad-begin: consume pad samples */ + if (seek_sample < ps->pad_begin_duration) { + /* seek=3: pad=5-3=2 */ + decode_samples = 0; + + reset_vgmstream(vgmstream); + ps->pad_begin_left = ps->pad_begin_duration - seek_sample; + + //;VGM_LOG("SEEK: pad start / dec=%i\n", decode_samples); + } + + /* body: find position relative to decoder's current sample */ + else if (play_forever || seek_sample < ps->pad_begin_duration + ps->body_duration + ps->fade_duration) { + /* seek=10 would be seekr=10-5+3=8 inside decoder */ + int32_t seek_relative = seek_sample - ps->pad_begin_duration + ps->trim_begin_duration; + + + //;VGM_LOG("SEEK: body / seekr=%i, curr=%i\n", seek_relative, vgmstream->current_sample); + + /* seek can be in some part of the body, depending on looped/decoder's current/etc */ + if (!is_looped && seek_relative < vgmstream->current_sample) { + /* seekr=50s, curr=95 > restart + decode=50s */ + decode_samples = seek_relative; + reset_vgmstream(vgmstream); + + //;VGM_LOG("SEEK: non-loop reset / dec=%i\n", decode_samples); + } + else if (!is_looped && seek_relative < vgmstream->num_samples) { + /* seekr=95s, curr=50 > decode=95-50=45s */ + decode_samples = seek_relative - vgmstream->current_sample; + + //;VGM_LOG("SEEK: non-loop forward / dec=%i\n", decode_samples); + } + else if (!is_looped) { + /* seekr=120s (outside decode, can happen when body is set manually) */ + decode_samples = 0; + vgmstream->current_sample = vgmstream->num_samples + 1; + + //;VGM_LOG("SEEK: non-loop silence / dec=%i\n", decode_samples); + } + else if (seek_relative < vgmstream->loop_start_sample) { + /* seekr=6s > 6-5+3 > seek=4s inside decoder < 20s: decode 4s from start, or 1s if current was at 3s */ + + if (seek_relative < vgmstream->current_sample) { + /* seekr=9s, current=10s > decode=9s from start */ + decode_samples = seek_relative; + reset_vgmstream(vgmstream); + + //;VGM_LOG("SEEK: loop start reset / dec=%i\n", decode_samples); + } + else { + /* seekr=9s, current=8s > decode=1s from current */ + decode_samples = seek_relative - vgmstream->current_sample; + + //;VGM_LOG("SEEK: loop start forward / dec=%i\n", decode_samples); + } + } + else { + /* seek can be clamped between loop parts (relative to decoder's current_sample) to minimize decoding */ + int32_t loop_body, loop_seek, loop_curr; + + /* current must have reached loop start at some point */ + if (!vgmstream->hit_loop) { + int32_t skip_samples; + + if (vgmstream->current_sample >= vgmstream->loop_start_sample) { + VGM_LOG("SEEK: bad current sample %i vs %i\n", vgmstream->current_sample, vgmstream->loop_start_sample); + reset_vgmstream(vgmstream); + } + + skip_samples = (vgmstream->loop_start_sample - vgmstream->current_sample); + //;VGM_LOG("SEEK: must loop / skip=%i, curr=%i\n", skip_samples, vgmstream->current_sample); + + seek_force_decode(vgmstream, skip_samples); + } + + /* current must be in loop area (shouldn't happen?) */ + if (vgmstream->current_sample < vgmstream->loop_start_sample + || vgmstream->current_sample < vgmstream->loop_end_sample) { + //;VGM_LOG("SEEK: current outside loop area / curr=%i, ls=%i, le=%i\n", vgmstream->current_sample, vgmstream->current_sample, vgmstream->loop_end_sample); + seek_force_loop(vgmstream, 0); + } + + + loop_body = (vgmstream->loop_end_sample - vgmstream->loop_start_sample); + loop_seek = seek_relative - vgmstream->loop_start_sample; + loop_count = loop_seek / loop_body; + loop_seek = loop_seek % loop_body; + loop_curr = vgmstream->current_sample - vgmstream->loop_start_sample; + + /* when "ignore fade" is used and seek falls into non-fade part, this needs to seek right before it + so when calling seek_force_loop detection kicks in, and non-fade then decodes normally */ + if (vgmstream->loop_target && vgmstream->loop_target == loop_count) { + loop_seek = loop_body; + } + + //;VGM_LOG("SEEK: in loop / seekl=%i, loops=%i, cur=%i, dec=%i\n", loop_seek, loop_count, loop_curr, decode_samples); + if (loop_seek < loop_curr) { + decode_samples += loop_seek; + seek_force_loop(vgmstream, loop_count); + + //;VGM_LOG("SEEK: loop reset / dec=%i, loop=%i\n", decode_samples, loop_count); + } + else { + decode_samples += (loop_seek - loop_curr); + + //;VGM_LOG("SEEK: loop forward / dec=%i, loop=%i\n", decode_samples, loop_count); + } + + /* adjust fade if seek ends in fade region */ + if (!play_forever + && seek_sample >= ps->pad_begin_duration + ps->body_duration + && seek_sample < ps->pad_begin_duration + ps->body_duration + ps->fade_duration) { + ps->fade_left = ps->pad_begin_duration + ps->body_duration + ps->fade_duration - seek_sample; + //;VGM_LOG("SEEK: in fade / fade=%i, %i\n", ps->fade_left, ps->fade_duration); + } + } + + /* done at the end in case of reset */ + ps->pad_begin_left = 0; + ps->trim_begin_left = 0; + } + + /* pad end and beyond: ignored */ + else { + decode_samples = 0; + ps->pad_begin_left = 0; + ps->trim_begin_left = 0; + if (!is_looped) + vgmstream->current_sample = vgmstream->num_samples + 1; + + //;VGM_LOG("SEEK: end silence / dec=%i\n", decode_samples); + /* looping decoder state isn't changed (seek backwards could use current sample) */ + } + + + seek_force_decode(vgmstream, decode_samples); + + vgmstream->pstate.play_position = seek_sample; +} diff --git a/Frameworks/vgmstream/vgmstream/src/render.h b/Frameworks/vgmstream/vgmstream/src/render.h new file mode 100644 index 000000000..d9461367d --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/render.h @@ -0,0 +1,10 @@ +#ifndef _RENDER_H +#define _RENDER_H + +#include "vgmstream.h" + +void free_layout(VGMSTREAM* vgmstream); +void reset_layout(VGMSTREAM* vgmstream); + + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.c b/Frameworks/vgmstream/vgmstream/src/streamfile.c index 47688922e..9e7cf0042 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.c @@ -890,7 +890,7 @@ STREAMFILE* open_streamfile_by_filename(STREAMFILE *streamfile, const char * fil char partname[PATH_LIMIT]; char *path, *name; - if (!streamfile || !filename) return NULL; + if (!streamfile || !filename || !filename[0]) return NULL; streamfile->get_name(streamfile, fullname, sizeof(fullname)); @@ -1000,7 +1000,8 @@ size_t read_string(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf) { size_t pos; for (pos = 0; pos < buf_size; pos++) { - char c = read_8bit(offset + pos, sf); + uint8_t byte = read_u8(offset + pos, sf); + char c = (char)byte; if (buf) buf[pos] = c; if (c == '\0') return pos; @@ -1008,7 +1009,8 @@ size_t read_string(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf) { if (buf) buf[pos] = '\0'; return buf_size; } - if (c < 0x20 || (uint8_t)c > 0xA5) + /* UTF-8 only goes to 0x7F, but allow a bunch of Windows-1252 codes that some games use */ + if (byte < 0x20 || byte > 0xF0) goto fail; } @@ -1120,6 +1122,76 @@ fail: return 0; } +STREAMFILE *read_filemap_file(STREAMFILE *sf, int file_num) { + char filename[PATH_LIMIT]; + off_t txt_offset, file_size; + STREAMFILE *sf_map = NULL; + + sf_map = open_streamfile_by_filename(sf, ".txtm"); + if (!sf_map) goto fail; + + get_streamfile_filename(sf, filename, sizeof(filename)); + + txt_offset = 0x00; + file_size = get_streamfile_size(sf_map); + + /* skip BOM if needed */ + if ((uint16_t)read_16bitLE(0x00, sf_map) == 0xFFFE || + (uint16_t)read_16bitLE(0x00, sf_map) == 0xFEFF) { + txt_offset = 0x02; + } else if (((uint32_t)read_32bitBE(0x00, sf_map) & 0xFFFFFF00) == 0xEFBBBF00) { + txt_offset = 0x03; + } + + /* read lines and find target filename, format is (filename): value1, ... valueN */ + while (txt_offset < file_size) { + char line[0x2000]; + char key[PATH_LIMIT] = { 0 }, val[0x2000] = { 0 }; + int ok, bytes_read, line_ok; + + bytes_read = read_line(line, sizeof(line), txt_offset, sf_map, &line_ok); + if (!line_ok) goto fail; + + txt_offset += bytes_read; + + /* get key/val (ignores lead spaces, stops at space/comment/separator) */ + ok = sscanf(line, " %[^ \t#:] : %[^\t#\r\n] ", key, val); + if (ok != 2) { /* ignore line if no key=val (comment or garbage) */ + continue; + } + + if (strcmp(key, filename) == 0) { + int n; + char subval[PATH_LIMIT]; + const char *current = val; + int i; + + for (i = 0; i <= file_num; i++) { + if (current[0] == '\0') + goto fail; + + ok = sscanf(current, " %[^\t#\r\n,]%n ", subval, &n); + if (ok != 1) + goto fail; + + if (i == file_num) + { + close_streamfile(sf_map); + return open_streamfile_by_filename(sf, subval); + } + + current += n; + if (current[0] == ',') + current++; + } + } + } + +fail: + close_streamfile(sf_map); + return NULL; +} + void fix_dir_separators(char * filename) { char c; int i = 0; diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.h b/Frameworks/vgmstream/vgmstream/src/streamfile.h index 4d32231c1..83d684171 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.h @@ -65,13 +65,13 @@ * to do file operations, as plugins may need to provide their own callbacks. * Reads from arbitrary offsets, meaning internally may need fseek equivalents during reads. */ typedef struct _STREAMFILE { - size_t (*read)(struct _STREAMFILE *, uint8_t * dst, off_t offset, size_t length); - size_t (*get_size)(struct _STREAMFILE *); - off_t (*get_offset)(struct _STREAMFILE *); //todo: DO NOT USE, NOT RESET PROPERLY (remove?) + size_t (*read)(struct _STREAMFILE*, uint8_t* dst, off_t offset, size_t length); + size_t (*get_size)(struct _STREAMFILE*); + off_t (*get_offset)(struct _STREAMFILE*); //todo: DO NOT USE, NOT RESET PROPERLY (remove?) /* for dual-file support */ - void (*get_name)(struct _STREAMFILE * ,char *name, size_t length); - struct _STREAMFILE * (*open)(struct _STREAMFILE *, const char * const filename, size_t buffersize); - void (*close)(struct _STREAMFILE *); + void (*get_name)(struct _STREAMFILE*, char* name, size_t length); + struct _STREAMFILE* (*open)(struct _STREAMFILE*, const char* const filename, size_t buffersize); + void (*close)(struct _STREAMFILE*); /* Substream selection for files with subsongs. Manually used in metas if supported. @@ -86,152 +86,152 @@ typedef struct _STREAMFILE { /* Opens a standard STREAMFILE, opening from path. * Uses stdio (FILE) for operations, thus plugins may not want to use it. */ -STREAMFILE* open_stdio_streamfile(const char *filename); +STREAMFILE* open_stdio_streamfile(const char* filename); /* Opens a standard STREAMFILE from a pre-opened FILE. */ -STREAMFILE* open_stdio_streamfile_by_file(FILE *file, const char *filename); +STREAMFILE* open_stdio_streamfile_by_file(FILE* file, const char* filename); /* Opens a STREAMFILE that does buffered IO. * Can be used when the underlying IO may be slow (like when using custom IO). * Buffer size is optional. */ -STREAMFILE* open_buffer_streamfile(STREAMFILE *streamfile, size_t buffer_size); -STREAMFILE* open_buffer_streamfile_f(STREAMFILE *streamfile, size_t buffer_size); +STREAMFILE* open_buffer_streamfile(STREAMFILE* sf, size_t buffer_size); +STREAMFILE* open_buffer_streamfile_f(STREAMFILE* sf, size_t buffer_size); /* Opens a STREAMFILE that doesn't close the underlying streamfile. * 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); -STREAMFILE* open_wrap_streamfile_f(STREAMFILE *streamfile); +STREAMFILE* open_wrap_streamfile(STREAMFILE* sf); +STREAMFILE* open_wrap_streamfile_f(STREAMFILE* sf); /* Opens a STREAMFILE that clamps reads to a section of a larger streamfile. * Can be used with subfiles inside a bigger file (to fool metas, or to simplify custom IO). */ -STREAMFILE* open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t size); -STREAMFILE* open_clamp_streamfile_f(STREAMFILE *streamfile, off_t start, size_t size); +STREAMFILE* open_clamp_streamfile(STREAMFILE* sf, off_t start, size_t size); +STREAMFILE* open_clamp_streamfile_f(STREAMFILE* sf, off_t start, size_t size); /* Opens a STREAMFILE that uses custom IO for streamfile reads. * Can be used to modify data on the fly (ex. decryption), or even transform it from a format to another. * Data is an optional state struct of some size what will be malloc+copied on open. */ -STREAMFILE* open_io_streamfile(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback); -STREAMFILE* open_io_streamfile_f(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback); +STREAMFILE* open_io_streamfile(STREAMFILE* sf, void* data, size_t data_size, void* read_callback, void* size_callback); +STREAMFILE* open_io_streamfile_f(STREAMFILE* sf, void* data, size_t data_size, void* read_callback, void* size_callback); /* Same, but calls init on SF open and close on close, when malloc/free is needed. * Data struct may be used to hold malloc'd pointers and stuff. */ -STREAMFILE* open_io_streamfile_ex(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback, void* init_callback, void* close_callback); -STREAMFILE* open_io_streamfile_ex_f(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback, void* init_callback, void* close_callback); +STREAMFILE* open_io_streamfile_ex(STREAMFILE* sf, void* data, size_t data_size, void* read_callback, void* size_callback, void* init_callback, void* close_callback); +STREAMFILE* open_io_streamfile_ex_f(STREAMFILE* sf, void* data, size_t data_size, void* read_callback, void* size_callback, void* init_callback, void* close_callback); /* Opens 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, const char * fakename, const char * fakeext); -STREAMFILE* open_fakename_streamfile_f(STREAMFILE *streamfile, const char * fakename, const char * fakeext); +STREAMFILE* open_fakename_streamfile(STREAMFILE* sf, const char* fakename, const char* fakeext); +STREAMFILE* open_fakename_streamfile_f(STREAMFILE* sf, const char* fakename, const char* fakeext); /* Opens 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); -STREAMFILE* open_multifile_streamfile_f(STREAMFILE **streamfiles, size_t streamfiles_size); +STREAMFILE* open_multifile_streamfile(STREAMFILE** sfs, size_t sfs_size); +STREAMFILE* open_multifile_streamfile_f(STREAMFILE** sfs, size_t sfs_size); /* Opens a STREAMFILE from a (path)+filename. * Just a wrapper, to avoid having to access the STREAMFILE's callbacks directly. */ -STREAMFILE* open_streamfile(STREAMFILE *streamfile, const char * pathname); +STREAMFILE* open_streamfile(STREAMFILE* sf, const char* pathname); /* Opens a STREAMFILE from a base pathname + new extension * Can be used to get companion headers. */ -STREAMFILE* open_streamfile_by_ext(STREAMFILE *streamfile, const char * ext); +STREAMFILE* open_streamfile_by_ext(STREAMFILE* sf, const char* ext); /* Opens a STREAMFILE from a base path + new filename. * Can be used to get companion files. Relative paths like * './filename', '../filename', 'dir/filename' also work. */ -STREAMFILE* open_streamfile_by_filename(STREAMFILE *streamfile, const char * filename); +STREAMFILE* open_streamfile_by_filename(STREAMFILE* sf, const char* filename); /* Reopen a STREAMFILE with a different buffer size, for fine-tuned bigfile parsing. * Uses default buffer size when buffer_size is 0 */ -STREAMFILE * reopen_streamfile(STREAMFILE *streamfile, size_t buffer_size); +STREAMFILE* reopen_streamfile(STREAMFILE* sf, size_t buffer_size); /* close a file, destroy the STREAMFILE object */ -static inline void close_streamfile(STREAMFILE * streamfile) { - if (streamfile!=NULL) - streamfile->close(streamfile); +static inline void close_streamfile(STREAMFILE* sf) { + if (sf != NULL) + sf->close(sf); } /* read from a file, returns number of bytes read */ -static inline size_t read_streamfile(uint8_t *dst, off_t offset, size_t length, STREAMFILE *streamfile) { - return streamfile->read(streamfile, dst, offset,length); +static inline size_t read_streamfile(uint8_t *dst, off_t offset, size_t length, STREAMFILE* sf) { + return sf->read(sf, dst, offset,length); } /* return file size */ -static inline size_t get_streamfile_size(STREAMFILE * streamfile) { - return streamfile->get_size(streamfile); +static inline size_t get_streamfile_size(STREAMFILE* sf) { + return sf->get_size(sf); } /* Sometimes you just need an int, and we're doing the buffering. * Note, however, that if these fail to read they'll return -1, * so that should not be a valid value or there should be some backup. */ -static inline int16_t read_16bitLE(off_t offset, STREAMFILE * streamfile) { +static inline int16_t read_16bitLE(off_t offset, STREAMFILE* sf) { uint8_t buf[2]; - if (read_streamfile(buf,offset,2,streamfile)!=2) return -1; + if (read_streamfile(buf,offset,2,sf)!=2) return -1; return get_16bitLE(buf); } -static inline int16_t read_16bitBE(off_t offset, STREAMFILE * streamfile) { +static inline int16_t read_16bitBE(off_t offset, STREAMFILE* sf) { uint8_t buf[2]; - if (read_streamfile(buf,offset,2,streamfile)!=2) return -1; + if (read_streamfile(buf,offset,2,sf)!=2) return -1; return get_16bitBE(buf); } -static inline int32_t read_32bitLE(off_t offset, STREAMFILE * streamfile) { +static inline int32_t read_32bitLE(off_t offset, STREAMFILE* sf) { uint8_t buf[4]; - if (read_streamfile(buf,offset,4,streamfile)!=4) return -1; + if (read_streamfile(buf,offset,4,sf)!=4) return -1; return get_32bitLE(buf); } -static inline int32_t read_32bitBE(off_t offset, STREAMFILE * streamfile) { +static inline int32_t read_32bitBE(off_t offset, STREAMFILE* sf) { uint8_t buf[4]; - if (read_streamfile(buf,offset,4,streamfile)!=4) return -1; + if (read_streamfile(buf,offset,4,sf)!=4) return -1; return get_32bitBE(buf); } -static inline int64_t read_64bitLE(off_t offset, STREAMFILE * streamfile) { +static inline int64_t read_64bitLE(off_t offset, STREAMFILE* sf) { uint8_t buf[8]; - if (read_streamfile(buf,offset,8,streamfile)!=8) return -1; + if (read_streamfile(buf,offset,8,sf)!=8) return -1; return get_64bitLE(buf); } -static inline int64_t read_64bitBE(off_t offset, STREAMFILE * streamfile) { +static inline int64_t read_64bitBE(off_t offset, STREAMFILE* sf) { uint8_t buf[8]; - if (read_streamfile(buf,offset,8,streamfile)!=8) return -1; + if (read_streamfile(buf,offset,8,sf)!=8) return -1; return get_64bitBE(buf); } -static inline int8_t read_8bit(off_t offset, STREAMFILE * streamfile) { +static inline int8_t read_8bit(off_t offset, STREAMFILE* sf) { uint8_t buf[1]; - if (read_streamfile(buf,offset,1,streamfile)!=1) return -1; + if (read_streamfile(buf,offset,1,sf)!=1) return -1; return buf[0]; } /* alias of the above */ -static inline int8_t read_s8 (off_t offset, STREAMFILE *sf) { return read_8bit(offset, sf); } -static inline uint8_t read_u8 (off_t offset, STREAMFILE *sf) { return (uint8_t) read_8bit(offset, sf); } -static inline int16_t read_s16le(off_t offset, STREAMFILE *sf) { return read_16bitLE(offset, sf); } -static inline uint16_t read_u16le(off_t offset, STREAMFILE *sf) { return (uint16_t)read_16bitLE(offset, sf); } -static inline int16_t read_s16be(off_t offset, STREAMFILE *sf) { return read_16bitBE(offset, sf); } -static inline uint16_t read_u16be(off_t offset, STREAMFILE *sf) { return (uint16_t)read_16bitBE(offset, sf); } -static inline int32_t read_s32le(off_t offset, STREAMFILE *sf) { return read_32bitLE(offset, sf); } -static inline uint32_t read_u32le(off_t offset, STREAMFILE *sf) { return (uint32_t)read_32bitLE(offset, sf); } -static inline int32_t read_s32be(off_t offset, STREAMFILE *sf) { return read_32bitBE(offset, sf); } -static inline uint32_t read_u32be(off_t offset, STREAMFILE *sf) { return (uint32_t)read_32bitBE(offset, sf); } -static inline int64_t read_s64be(off_t offset, STREAMFILE *sf) { return read_64bitBE(offset, sf); } -static inline uint64_t read_u64be(off_t offset, STREAMFILE *sf) { return (uint64_t)read_64bitBE(offset, sf); } -static inline int64_t read_s64le(off_t offset, STREAMFILE *sf) { return read_64bitLE(offset, sf); } -static inline uint64_t read_u64le(off_t offset, STREAMFILE *sf) { return (uint64_t)read_64bitLE(offset, sf); } +static inline int8_t read_s8 (off_t offset, STREAMFILE* sf) { return read_8bit(offset, sf); } +static inline uint8_t read_u8 (off_t offset, STREAMFILE* sf) { return (uint8_t) read_8bit(offset, sf); } +static inline int16_t read_s16le(off_t offset, STREAMFILE* sf) { return read_16bitLE(offset, sf); } +static inline uint16_t read_u16le(off_t offset, STREAMFILE* sf) { return (uint16_t)read_16bitLE(offset, sf); } +static inline int16_t read_s16be(off_t offset, STREAMFILE* sf) { return read_16bitBE(offset, sf); } +static inline uint16_t read_u16be(off_t offset, STREAMFILE* sf) { return (uint16_t)read_16bitBE(offset, sf); } +static inline int32_t read_s32le(off_t offset, STREAMFILE* sf) { return read_32bitLE(offset, sf); } +static inline uint32_t read_u32le(off_t offset, STREAMFILE* sf) { return (uint32_t)read_32bitLE(offset, sf); } +static inline int32_t read_s32be(off_t offset, STREAMFILE* sf) { return read_32bitBE(offset, sf); } +static inline uint32_t read_u32be(off_t offset, STREAMFILE* sf) { return (uint32_t)read_32bitBE(offset, sf); } +static inline int64_t read_s64be(off_t offset, STREAMFILE* sf) { return read_64bitBE(offset, sf); } +static inline uint64_t read_u64be(off_t offset, STREAMFILE* sf) { return (uint64_t)read_64bitBE(offset, sf); } +static inline int64_t read_s64le(off_t offset, STREAMFILE* sf) { return read_64bitLE(offset, sf); } +static inline uint64_t read_u64le(off_t offset, STREAMFILE* sf) { return (uint64_t)read_64bitLE(offset, sf); } /* The recommended int-to-float type punning in C is through union, but pointer casting * works too (though less portable due to aliasing rules?). For C++ memcpy seems * recommended. Both work in GCC and VS2015+ (not sure about older, ifdef as needed). */ -static inline float read_f32be(off_t offset, STREAMFILE *sf) { +static inline float read_f32be(off_t offset, STREAMFILE* sf) { union { uint32_t u32; float f32; @@ -239,7 +239,7 @@ static inline float read_f32be(off_t offset, STREAMFILE *sf) { temp.u32 = read_u32be(offset, sf); return temp.f32; } -static inline float read_f32le(off_t offset, STREAMFILE * sf) { +static inline float read_f32le(off_t offset, STREAMFILE* sf) { union { uint32_t u32; float f32; @@ -248,12 +248,12 @@ static inline float read_f32le(off_t offset, STREAMFILE * sf) { return temp.f32; } #if 0 -static inline float read_f32be_p(off_t offset, STREAMFILE *sf) { +static inline float read_f32be_p(off_t offset, STREAMFILE* sf) { uint32_t sample_int = read_u32be(offset, sf); float* sample_float = (float*)&sample_int; return *sample_float; } -static inline float read_f32be_m(off_t offset, STREAMFILE *sf) { +static inline float read_f32be_m(off_t offset, STREAMFILE* sf) { uint32_t sample_int = read_u32be(offset, sf); float sample_float; memcpy(&sample_float, &sample_int, sizeof(uint32_t)); @@ -288,18 +288,18 @@ void sf_reader_init(sf_reader *r, int big_endian) { */ #endif #if 0 //todo improve + test + simplify code (maybe not inline?) -static inline int read_s4h(off_t offset, STREAMFILE * streamfile) { +static inline int read_s4h(off_t offset, STREAMFILE* sf) { uint8_t byte = read_u8(offset, streamfile); return get_nibble_signed(byte, 1); } -static inline int read_u4h(off_t offset, STREAMFILE * streamfile) { +static inline int read_u4h(off_t offset, STREAMFILE* sf) { uint8_t byte = read_u8(offset, streamfile); return (byte >> 4) & 0x0f; } -static inline int read_s4l(off_t offset, STREAMFILE * streamfile) { +static inline int read_s4l(off_t offset, STREAMFILE* sf) { ... } -static inline int read_u4l(off_t offset, STREAMFILE * streamfile) { +static inline int read_u4l(off_t offset, STREAMFILE* sf) { ... } static inline int max_s32(int32_t a, int32_t b) { return a > b ? a : b; } @@ -309,14 +309,14 @@ static inline int min_s32(int32_t a, int32_t b) { return a < b ? a : b; } //TODO: maybe move to streamfile.c /* guess byte endianness from a given value, return true if big endian and false if little endian */ -static inline int guess_endianness16bit(off_t offset, STREAMFILE * streamfile) { +static inline int guess_endianness16bit(off_t offset, STREAMFILE* sf) { uint8_t buf[0x02]; - if (read_streamfile(buf,offset,0x02,streamfile) != 0x02) return -1; /* ? */ + if (read_streamfile(buf, offset, 0x02, sf) != 0x02) return -1; /* ? */ return (uint16_t)get_16bitLE(buf) > (uint16_t)get_16bitBE(buf) ? 1 : 0; } -static inline int guess_endianness32bit(off_t offset, STREAMFILE * streamfile) { +static inline int guess_endianness32bit(off_t offset, STREAMFILE* sf) { uint8_t buf[0x04]; - if (read_streamfile(buf,offset,0x04,streamfile) != 0x04) return -1; /* ? */ + if (read_streamfile(buf, offset, 0x04, sf) != 0x04) return -1; /* ? */ return (uint32_t)get_32bitLE(buf) > (uint32_t)get_32bitBE(buf) ? 1 : 0; } @@ -331,10 +331,10 @@ static inline size_t align_size_to_block(size_t value, size_t block_align) { /* Read into dst a line delimited by CRLF (Windows) / LF (Unux) / CR (Mac) / EOF, null-terminated * and without line feeds. Returns bytes read (including CR/LF), *not* the same as string length. * p_line_ok is set to 1 if the complete line was read; pass NULL to ignore. */ -size_t read_line(char *buf, int buf_size, off_t offset, STREAMFILE *sf, int *p_line_ok); +size_t read_line(char* buf, int buf_size, off_t offset, STREAMFILE* sf, int* p_line_ok); /* reads a c-string (ANSI only), up to bufsize or NULL, returning size. buf is optional (works as get_string_size). */ -size_t read_string(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf); +size_t read_string(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf); /* reads a UTF16 string... but actually only as ANSI (discards the upper byte) */ size_t read_string_utf16(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf, int big_endian); size_t read_string_utf16le(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf); @@ -343,31 +343,35 @@ size_t read_string_utf16be(char* buf, size_t buf_size, off_t offset, STREAMFILE* /* Opens a file containing decryption keys and copies to buffer. * Tries "(name.ext)key" (per song), "(.ext)key" (per folder) keynames. * returns size of key if found and copied */ -size_t read_key_file(uint8_t *buf, size_t buf_size, STREAMFILE *sf); +size_t read_key_file(uint8_t* buf, size_t buf_size, STREAMFILE* sf); + +/* Opens .txtm file containing file:companion file(-s) mappings and tries to see if there's a match + * then loads the associated companion file if one is found */ +STREAMFILE *read_filemap_file(STREAMFILE *sf, int file_num); /* hack to allow relative paths in various OSs */ -void fix_dir_separators(char *filename); +void fix_dir_separators(char* filename); /* Checks if the stream filename is one of the extensions (comma-separated, ex. "adx" or "adx,aix"). * Empty is ok to accept files without extension ("", "adx,,aix"). Returns 0 on failure */ -int check_extensions(STREAMFILE *streamFile, const char * cmp_exts); +int check_extensions(STREAMFILE* sf, const char* cmp_exts); /* chunk-style file helpers */ -int find_chunk_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size); -int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size); -int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian_size, int zero_size_end); +int find_chunk_be(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t* p_chunk_offset, size_t* p_chunk_size); +int find_chunk_le(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t* p_chunk_offset, size_t* p_chunk_size); +int find_chunk(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t* p_chunk_offset, size_t* p_chunk_size, int big_endian_size, int zero_size_end); /* find a RIFF-style chunk (with chunk_size not including id and size) */ -int find_chunk_riff_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size); -int find_chunk_riff_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size); +int find_chunk_riff_le(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t* p_chunk_offset, size_t* p_chunk_size); +int find_chunk_riff_be(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t* p_chunk_offset, size_t* p_chunk_size); /* same with chunk ids in variable endianess (so instead of "fmt " has " tmf" */ -int find_chunk_riff_ve(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian); +int find_chunk_riff_ve(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t* p_chunk_offset, size_t* p_chunk_size, int big_endian); /* filename helpers */ -void get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size); -void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size); -void get_streamfile_basename(STREAMFILE *streamFile, char * buffer, size_t size); -void get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size); -void get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size); +void get_streamfile_name(STREAMFILE* sf, char* buf, size_t size); +void get_streamfile_filename(STREAMFILE* sf, char* buf, size_t size); +void get_streamfile_basename(STREAMFILE* sf, char* buf, size_t size); +void get_streamfile_path(STREAMFILE* sf, char* buf, size_t size); +void get_streamfile_ext(STREAMFILE* sf, char* buf, size_t size); -void dump_streamfile(STREAMFILE *streamFile, int num); +void dump_streamfile(STREAMFILE* sf, int num); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/streamtypes.h b/Frameworks/vgmstream/vgmstream/src/streamtypes.h index 9907b2eeb..1de682f00 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamtypes.h +++ b/Frameworks/vgmstream/vgmstream/src/streamtypes.h @@ -22,8 +22,8 @@ #include #endif -#ifndef inline /* (_MSC_VER < 1900)? */ -#define inline _inline +#if (_MSC_VER < 1800) && !defined(__cplusplus) +#define inline __inline #endif #define strcasecmp _stricmp diff --git a/Frameworks/vgmstream/vgmstream/src/util.h b/Frameworks/vgmstream/vgmstream/src/util.h index 21a98d89b..f27a8a400 100644 --- a/Frameworks/vgmstream/vgmstream/src/util.h +++ b/Frameworks/vgmstream/vgmstream/src/util.h @@ -11,55 +11,55 @@ /* host endian independent multi-byte integer reading */ -static inline int16_t get_16bitBE(uint8_t * p) { +static inline int16_t get_16bitBE(const uint8_t* p) { return (p[0]<<8) | (p[1]); } -static inline int16_t get_16bitLE(uint8_t * p) { +static inline int16_t get_16bitLE(const uint8_t* p) { return (p[0]) | (p[1]<<8); } -static inline int32_t get_32bitBE(uint8_t * p) { +static inline int32_t get_32bitBE(const uint8_t* p) { return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | (p[3]); } -static inline int32_t get_32bitLE(uint8_t * p) { +static inline int32_t get_32bitLE(const uint8_t* p) { return (p[0]) | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); } -static inline int64_t get_64bitBE(uint8_t * p) { +static inline int64_t get_64bitBE(const uint8_t* p) { return (uint64_t)(((uint64_t)p[0]<<56) | ((uint64_t)p[1]<<48) | ((uint64_t)p[2]<<40) | ((uint64_t)p[3]<<32) | ((uint64_t)p[4]<<24) | ((uint64_t)p[5]<<16) | ((uint64_t)p[6]<<8) | ((uint64_t)p[7])); } -static inline int64_t get_64bitLE(uint8_t * p) { +static inline int64_t get_64bitLE(const uint8_t* p) { return (uint64_t)(((uint64_t)p[0]) | ((uint64_t)p[1]<<8) | ((uint64_t)p[2]<<16) | ((uint64_t)p[3]<<24) | ((uint64_t)p[4]<<32) | ((uint64_t)p[5]<<40) | ((uint64_t)p[6]<<48) | ((uint64_t)p[7]<<56)); } /* alias of the above */ -static inline int8_t get_s8 (uint8_t *p) { return ( int8_t)p[0]; } -static inline uint8_t get_u8 (uint8_t *p) { return (uint8_t)p[0]; } -static inline int16_t get_s16le(uint8_t *p) { return ( int16_t)get_16bitLE(p); } -static inline uint16_t get_u16le(uint8_t *p) { return (uint16_t)get_16bitLE(p); } -static inline int16_t get_s16be(uint8_t *p) { return ( int16_t)get_16bitBE(p); } -static inline uint16_t get_u16be(uint8_t *p) { return (uint16_t)get_16bitBE(p); } -static inline int32_t get_s32le(uint8_t *p) { return ( int32_t)get_32bitLE(p); } -static inline uint32_t get_u32le(uint8_t *p) { return (uint32_t)get_32bitLE(p); } -static inline int32_t get_s32be(uint8_t *p) { return ( int32_t)get_32bitBE(p); } -static inline uint32_t get_u32be(uint8_t *p) { return (uint32_t)get_32bitBE(p); } -static inline int64_t get_s64be(uint8_t *p) { return ( int64_t)get_64bitLE(p); } -static inline uint64_t get_u64be(uint8_t *p) { return (uint64_t)get_64bitLE(p); } -static inline int64_t get_s64le(uint8_t *p) { return ( int64_t)get_64bitBE(p); } -static inline uint64_t get_u64le(uint8_t *p) { return (uint64_t)get_64bitBE(p); } +static inline int8_t get_s8 (const uint8_t* p) { return ( int8_t)p[0]; } +static inline uint8_t get_u8 (const uint8_t* p) { return (uint8_t)p[0]; } +static inline int16_t get_s16le(const uint8_t* p) { return ( int16_t)get_16bitLE(p); } +static inline uint16_t get_u16le(const uint8_t* p) { return (uint16_t)get_16bitLE(p); } +static inline int16_t get_s16be(const uint8_t* p) { return ( int16_t)get_16bitBE(p); } +static inline uint16_t get_u16be(const uint8_t* p) { return (uint16_t)get_16bitBE(p); } +static inline int32_t get_s32le(const uint8_t* p) { return ( int32_t)get_32bitLE(p); } +static inline uint32_t get_u32le(const uint8_t* p) { return (uint32_t)get_32bitLE(p); } +static inline int32_t get_s32be(const uint8_t* p) { return ( int32_t)get_32bitBE(p); } +static inline uint32_t get_u32be(const uint8_t* p) { return (uint32_t)get_32bitBE(p); } +static inline int64_t get_s64le(const uint8_t* p) { return ( int64_t)get_64bitLE(p); } +static inline uint64_t get_u64le(const uint8_t* p) { return (uint64_t)get_64bitLE(p); } +static inline int64_t get_s64be(const uint8_t* p) { return ( int64_t)get_64bitBE(p); } +static inline uint64_t get_u64be(const uint8_t* p) { return (uint64_t)get_64bitBE(p); } -void put_8bit(uint8_t * buf, int8_t i); +void put_8bit(uint8_t* buf, int8_t i); -void put_16bitLE(uint8_t * buf, int16_t i); +void put_16bitLE(uint8_t* buf, int16_t i); -void put_32bitLE(uint8_t * buf, int32_t i); +void put_32bitLE(uint8_t* buf, int32_t i); -void put_16bitBE(uint8_t * buf, int16_t i); +void put_16bitBE(uint8_t* buf, int16_t i); -void put_32bitBE(uint8_t * buf, int32_t i); +void put_32bitBE(uint8_t* buf, int32_t i); /* alias of the above */ //TODO: improve #define put_u8 put_8bit @@ -67,6 +67,11 @@ void put_32bitBE(uint8_t * buf, int32_t i); #define put_u32le put_32bitLE #define put_u16be put_16bitBE #define put_u32be put_32bitBE +#define put_s8 put_8bit +#define put_s16le put_16bitLE +#define put_s32le put_32bitLE +#define put_s16be put_16bitBE +#define put_s32be put_32bitBE /* signed nibbles come up a lot */ diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index c8662148c..f82f4ba5d 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -9,13 +9,15 @@ #include "meta/meta.h" #include "layout/layout.h" #include "coding/coding.h" +#include "decode.h" +#include "render.h" #include "mixing.h" -static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *streamFile, VGMSTREAM* (*init_vgmstream_function)(STREAMFILE*)); +static void try_dual_file_stereo(VGMSTREAM* opened_vgmstream, STREAMFILE* sf, VGMSTREAM* (*init_vgmstream_function)(STREAMFILE*)); /* list of metadata parser functions that will recognize files, used on init */ -VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { +VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_adx, init_vgmstream_brstm, init_vgmstream_bfwav, @@ -45,7 +47,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_rxw, init_vgmstream_ngc_dsp_stm, init_vgmstream_ps2_exst, - init_vgmstream_ps2_svag, + init_vgmstream_svag_kcet, init_vgmstream_mib_mih, init_vgmstream_ngc_mpdsp, init_vgmstream_ps2_mic, @@ -165,7 +167,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_seg, init_vgmstream_nds_strm_ffta2, init_vgmstream_str_asr, - init_vgmstream_zwdsp, init_vgmstream_gca, init_vgmstream_spt_spd, init_vgmstream_ish_isd, @@ -187,7 +188,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_utf_dsp, init_vgmstream_ngc_ffcc_str, init_vgmstream_sat_baka, - init_vgmstream_nds_swav, + init_vgmstream_swav, init_vgmstream_vsf, init_vgmstream_nds_rrds, init_vgmstream_ps2_tk5, @@ -271,7 +272,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_iab, init_vgmstream_vs_str, init_vgmstream_lsf_n1nj4n, - init_vgmstream_vawx, + init_vgmstream_xwav_new, + init_vgmstream_xwav_old, init_vgmstream_ps2_wmus, init_vgmstream_hyperscan_kvag, init_vgmstream_ios_psnd, @@ -297,7 +299,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_kt_wiibgm, init_vgmstream_ktss, init_vgmstream_hca, - init_vgmstream_ps2_svag_snk, + init_vgmstream_svag_snk, init_vgmstream_ps2_vds_vdm, init_vgmstream_x360_cxs, init_vgmstream_dsp_adx, @@ -316,11 +318,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ogl, init_vgmstream_mc3, init_vgmstream_gtd, - init_vgmstream_ta_aac_x360, - init_vgmstream_ta_aac_ps3, - init_vgmstream_ta_aac_mobile, - init_vgmstream_ta_aac_mobile_vorbis, - init_vgmstream_ta_aac_vita, + init_vgmstream_ta_aac, init_vgmstream_va3, init_vgmstream_mta2, init_vgmstream_mta2_container, @@ -390,7 +388,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_txtp, init_vgmstream_smc_smh, init_vgmstream_ppst, - init_vgmstream_opus_sps_n1_segmented, + init_vgmstream_sps_n1_segmented, init_vgmstream_ubi_bao_pk, init_vgmstream_ubi_bao_atomic, init_vgmstream_dsp_switch_audio, @@ -501,6 +499,19 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_mups, init_vgmstream_kat, init_vgmstream_pcm_success, + init_vgmstream_ktsc, + init_vgmstream_adp_konami, + init_vgmstream_zwv, + init_vgmstream_dsb, + init_vgmstream_bsf, + init_vgmstream_xse_new, + init_vgmstream_xse_old, + init_vgmstream_wady, + init_vgmstream_dsp_sqex, + init_vgmstream_dsp_wiivoice, + init_vgmstream_xws, + init_vgmstream_cpk, + init_vgmstream_opus_nsopus, /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ @@ -512,24 +523,29 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_raw_pcm, /* .raw raw PCM */ init_vgmstream_s14_sss, /* .s14/sss raw siren14 */ init_vgmstream_raw_al, /* .al/al2 raw A-LAW */ + init_vgmstream_zwdsp, /* fake format */ #ifdef VGM_USE_FFMPEG init_vgmstream_ffmpeg, /* may play anything incorrectly, since FFmpeg doesn't check extensions */ #endif }; +/*****************************************************************************/ +/* INIT/META */ +/*****************************************************************************/ + /* internal version with all parameters */ -static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) { +static VGMSTREAM* init_vgmstream_internal(STREAMFILE* sf) { int i, fcns_size; - - if (!streamFile) + + if (!sf) return NULL; fcns_size = (sizeof(init_vgmstream_functions)/sizeof(init_vgmstream_functions[0])); /* try a series of formats, see which works */ for (i = 0; i < fcns_size; i++) { /* call init function and see if valid VGMSTREAM was returned */ - VGMSTREAM * vgmstream = (init_vgmstream_functions[i])(streamFile); + VGMSTREAM* vgmstream = (init_vgmstream_functions[i])(sf); if (!vgmstream) continue; @@ -562,7 +578,7 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) { /* test if candidate for dual stereo */ if (vgmstream->channels == 1 && vgmstream->allow_dual_stereo == 1) { - try_dual_file_stereo(vgmstream, streamFile, init_vgmstream_functions[i]); + try_dual_file_stereo(vgmstream, sf, init_vgmstream_functions[i]); } /* clean as loops are readable metadata but loop fields may contain garbage @@ -575,7 +591,7 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) { #ifdef VGM_USE_FFMPEG /* check FFmpeg streams here, for lack of a better place */ if (vgmstream->coding_type == coding_FFmpeg) { - ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; + ffmpeg_codec_data *data = vgmstream->codec_data; if (data && data->streamCount && !vgmstream->num_streams) { vgmstream->num_streams = data->streamCount; } @@ -612,7 +628,7 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) { /* save info */ /* stream_index 0 may be used by plugins to signal "vgmstream default" (IOW don't force to 1) */ if (vgmstream->stream_index == 0) { - vgmstream->stream_index = streamFile->stream_index; + vgmstream->stream_index = sf->stream_index; } @@ -625,7 +641,7 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) { return NULL; } -void setup_vgmstream(VGMSTREAM * vgmstream) { +void setup_vgmstream(VGMSTREAM* vgmstream) { /* save start things so we can restart when seeking */ memcpy(vgmstream->start_ch, vgmstream->ch, sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); @@ -637,22 +653,22 @@ void setup_vgmstream(VGMSTREAM * vgmstream) { /* format detection and VGMSTREAM setup, uses default parameters */ -VGMSTREAM * init_vgmstream(const char * const filename) { - VGMSTREAM *vgmstream = NULL; - STREAMFILE *streamFile = open_stdio_streamfile(filename); - if (streamFile) { - vgmstream = init_vgmstream_from_STREAMFILE(streamFile); - close_streamfile(streamFile); +VGMSTREAM* init_vgmstream(const char* const filename) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* sf = open_stdio_streamfile(filename); + if (sf) { + vgmstream = init_vgmstream_from_STREAMFILE(sf); + close_streamfile(sf); } return vgmstream; } -VGMSTREAM * init_vgmstream_from_STREAMFILE(STREAMFILE *streamFile) { - return init_vgmstream_internal(streamFile); +VGMSTREAM* init_vgmstream_from_STREAMFILE(STREAMFILE* sf) { + return init_vgmstream_internal(sf); } /* Reset a VGMSTREAM to its state at the start of playback (when a plugin seeks back to zero). */ -void reset_vgmstream(VGMSTREAM * vgmstream) { +void reset_vgmstream(VGMSTREAM* vgmstream) { /* reset the VGMSTREAM and channels back to their original state */ memcpy(vgmstream, vgmstream->start_vgmstream, sizeof(VGMSTREAM)); @@ -662,118 +678,17 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { * Otherwise hit_loop will be 0 and it will be copied over anyway when we * really hit the loop start. */ - /* reset custom codec */ -#ifdef VGM_USE_VORBIS - if (vgmstream->coding_type == coding_OGG_VORBIS) { - reset_ogg_vorbis(vgmstream); - } + reset_codec(vgmstream); - if (vgmstream->coding_type == coding_VORBIS_custom) { - reset_vorbis_custom(vgmstream); - } -#endif - - if (vgmstream->coding_type == coding_CIRCUS_VQ) { - reset_circus_vq(vgmstream->codec_data); - } - - if (vgmstream->coding_type == coding_RELIC) { - reset_relic(vgmstream->codec_data); - } - - if (vgmstream->coding_type == coding_CRI_HCA) { - reset_hca(vgmstream->codec_data); - } - - if (vgmstream->coding_type == coding_UBI_ADPCM) { - reset_ubi_adpcm(vgmstream->codec_data); - } - - if (vgmstream->coding_type == coding_IMUSE) { - reset_imuse(vgmstream->codec_data); - } - - if (vgmstream->coding_type == coding_EA_MT) { - reset_ea_mt(vgmstream); - } - -#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) - if (vgmstream->coding_type == coding_MP4_AAC) { - reset_mp4_aac(vgmstream); - } -#endif - -#ifdef VGM_USE_MPEG - if (vgmstream->coding_type == coding_MPEG_custom || - vgmstream->coding_type == coding_MPEG_ealayer3 || - vgmstream->coding_type == coding_MPEG_layer1 || - vgmstream->coding_type == coding_MPEG_layer2 || - vgmstream->coding_type == coding_MPEG_layer3) { - reset_mpeg(vgmstream); - } -#endif - -#ifdef VGM_USE_G7221 - if (vgmstream->coding_type == coding_G7221C) { - reset_g7221(vgmstream->codec_data); - } -#endif - -#ifdef VGM_USE_G719 - if (vgmstream->coding_type == coding_G719) { - reset_g719(vgmstream->codec_data, vgmstream->channels); - } -#endif - -#ifdef VGM_USE_MAIATRAC3PLUS - 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_CELT - if (vgmstream->coding_type == coding_CELT_FSB) { - reset_celt_fsb(vgmstream); - } -#endif - -#ifdef VGM_USE_FFMPEG - if (vgmstream->coding_type == coding_FFmpeg) { - reset_ffmpeg(vgmstream); - } -#endif - - if (vgmstream->coding_type == coding_ACM) { - reset_acm(vgmstream->codec_data); - } - - if (vgmstream->coding_type == coding_NWA) { - nwa_codec_data *data = vgmstream->codec_data; - if (data) reset_nwa(data->nwa); - } - - /* reset custom layouts */ - if (vgmstream->layout_type == layout_segmented) { - reset_layout_segmented(vgmstream->layout_data); - } - - if (vgmstream->layout_type == layout_layered) { - reset_layout_layered(vgmstream->layout_data); - } + reset_layout(vgmstream); /* note that this does not reset the constituent STREAMFILES - * (vgmstream->ch[N].streamfiles' internal state, though shouldn't matter) */ + * (vgmstream->ch[N].streamfiles' internal state, like internal offset, though shouldn't matter) */ } /* Allocate memory and setup a VGMSTREAM */ -VGMSTREAM * allocate_vgmstream(int channel_count, int loop_flag) { - VGMSTREAM * vgmstream; +VGMSTREAM* allocate_vgmstream(int channel_count, int loop_flag) { + VGMSTREAM* vgmstream; /* up to ~16-24 aren't too rare for multilayered files, more is probably a bug */ if (channel_count <= 0 || channel_count > VGMSTREAM_MAX_CHANNELS) { @@ -801,7 +716,7 @@ VGMSTREAM * allocate_vgmstream(int channel_count, int loop_flag) { /* create vgmstream + main structs (other data is 0'ed) */ vgmstream = calloc(1,sizeof(VGMSTREAM)); if (!vgmstream) return NULL; - + vgmstream->start_vgmstream = calloc(1,sizeof(VGMSTREAM)); if (!vgmstream->start_vgmstream) goto fail; @@ -816,16 +731,25 @@ VGMSTREAM * allocate_vgmstream(int channel_count, int loop_flag) { if (!vgmstream->loop_ch) goto fail; } + /* garbage buffer for decode discarding (local bufs may cause stack overflows with segments/layers) + * in theory the bigger the better but in practice there isn't much difference */ + vgmstream->tmpbuf_size = 0x10000; /* for all channels */ + vgmstream->tmpbuf = malloc(sizeof(sample_t) * vgmstream->tmpbuf_size); + + vgmstream->channels = channel_count; vgmstream->loop_flag = loop_flag; mixing_init(vgmstream); /* pre-init */ + /* BEWARE: try_dual_file_stereo does some free'ing too */ + //vgmstream->stream_name_size = STREAM_NAME_SIZE; return vgmstream; fail: if (vgmstream) { mixing_close(vgmstream); + free(vgmstream->tmpbuf); free(vgmstream->ch); free(vgmstream->start_ch); free(vgmstream->loop_ch); @@ -835,139 +759,15 @@ fail: return NULL; } -void close_vgmstream(VGMSTREAM * vgmstream) { +void close_vgmstream(VGMSTREAM* vgmstream) { if (!vgmstream) return; - /* free custom codecs */ -#ifdef VGM_USE_VORBIS - if (vgmstream->coding_type == coding_OGG_VORBIS) { - free_ogg_vorbis(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } + free_codec(vgmstream); + vgmstream->codec_data = NULL; - if (vgmstream->coding_type == coding_VORBIS_custom) { - free_vorbis_custom(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } -#endif - - if (vgmstream->coding_type == coding_CIRCUS_VQ) { - free_circus_vq(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } - - if (vgmstream->coding_type == coding_RELIC) { - free_relic(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } - - if (vgmstream->coding_type == coding_CRI_HCA) { - free_hca(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } - - if (vgmstream->coding_type == coding_UBI_ADPCM) { - free_ubi_adpcm(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } - - if (vgmstream->coding_type == coding_IMUSE) { - free_imuse(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } - - if (vgmstream->coding_type == coding_EA_MT) { - free_ea_mt(vgmstream->codec_data, vgmstream->channels); - vgmstream->codec_data = NULL; - } - -#ifdef VGM_USE_FFMPEG - if (vgmstream->coding_type == coding_FFmpeg) { - free_ffmpeg(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } -#endif - -#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) - if (vgmstream->coding_type == coding_MP4_AAC) { - free_mp4_aac(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } -#endif - -#ifdef VGM_USE_MPEG - if (vgmstream->coding_type == coding_MPEG_custom || - vgmstream->coding_type == coding_MPEG_ealayer3 || - vgmstream->coding_type == coding_MPEG_layer1 || - vgmstream->coding_type == coding_MPEG_layer2 || - vgmstream->coding_type == coding_MPEG_layer3) { - free_mpeg(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } -#endif - -#ifdef VGM_USE_G7221 - if (vgmstream->coding_type == coding_G7221C) { - free_g7221(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } -#endif - -#ifdef VGM_USE_G719 - if (vgmstream->coding_type == coding_G719) { - free_g719(vgmstream->codec_data, vgmstream->channels); - vgmstream->codec_data = NULL; - } -#endif - -#ifdef VGM_USE_MAIATRAC3PLUS - 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 - -#ifdef VGM_USE_CELT - if (vgmstream->coding_type == coding_CELT_FSB) { - free_celt_fsb(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } -#endif - - if (vgmstream->coding_type == coding_ACM) { - free_acm(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } - - if (vgmstream->coding_type == coding_NWA) { - if (vgmstream->codec_data) { - nwa_codec_data *data = (nwa_codec_data *) vgmstream->codec_data; - if (data->nwa) - close_nwa(data->nwa); - free(data); - vgmstream->codec_data = NULL; - } - } - - - /* free custom layouts */ - if (vgmstream->layout_type == layout_segmented) { - free_layout_segmented(vgmstream->layout_data); - vgmstream->layout_data = NULL; - } - - if (vgmstream->layout_type == layout_layered) { - free_layout_layered(vgmstream->layout_data); - vgmstream->layout_data = NULL; - } + free_layout(vgmstream); + vgmstream->layout_data = NULL; /* now that the special cases have had their chance, clean up the standard items */ @@ -990,6 +790,7 @@ void close_vgmstream(VGMSTREAM * vgmstream) { } mixing_close(vgmstream); + free(vgmstream->tmpbuf); free(vgmstream->ch); free(vgmstream->start_ch); free(vgmstream->loop_ch); @@ -997,43 +798,30 @@ void close_vgmstream(VGMSTREAM * vgmstream) { free(vgmstream); } -/* calculate samples based on player's config */ -int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM * vgmstream) { - if (vgmstream->loop_flag) { - if (vgmstream->loop_target == (int)looptimes) { /* set externally, as this function is info-only */ - /* Continue playing the file normally after looping, instead of fading. - * Most files cut abruply after the loop, but some do have proper endings. - * With looptimes = 1 this option should give the same output vs loop disabled */ - int loop_count = (int)looptimes; /* no half loops allowed */ - return vgmstream->loop_start_sample - + (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * loop_count - + (vgmstream->num_samples - vgmstream->loop_end_sample); - } - else { - return vgmstream->loop_start_sample - + (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * looptimes - + (fadedelayseconds + fadeseconds) * vgmstream->sample_rate; - } - } - else { - return vgmstream->num_samples; - } -} - void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sample, int loop_end_sample) { if (!vgmstream) return; + /* ignore bad values (may happen with layers + TXTP loop install) */ + if (loop_flag && (loop_start_sample < 0 || + loop_start_sample > loop_end_sample || + loop_end_sample > vgmstream->num_samples)) + return; + /* this requires a bit more messing with the VGMSTREAM than I'm comfortable with... */ if (loop_flag && !vgmstream->loop_flag && !vgmstream->loop_ch) { - vgmstream->loop_ch = calloc(vgmstream->channels,sizeof(VGMSTREAMCHANNEL)); + vgmstream->loop_ch = calloc(vgmstream->channels, sizeof(VGMSTREAMCHANNEL)); if (!vgmstream->loop_ch) loop_flag = 0; /* ??? */ } +#if 0 + /* allow in case loop_flag is re-enabled later */ else if (!loop_flag && vgmstream->loop_flag) { - free(vgmstream->loop_ch); /* not important though */ + free(vgmstream->loop_ch); vgmstream->loop_ch = NULL; } +#endif vgmstream->loop_flag = loop_flag; + if (loop_flag) { vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; @@ -1048,9 +836,12 @@ void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sa /* propagate changes to layouts that need them */ if (vgmstream->layout_type == layout_layered) { int i; - layered_layout_data *data = vgmstream->layout_data; + layered_layout_data* data = vgmstream->layout_data; + + /* layered loops using the internal VGMSTREAMs */ for (i = 0; i < data->layer_count; i++) { - vgmstream_force_loop(data->layers[i], loop_flag, loop_start_sample, loop_end_sample); + if (!data->layers[i]->config_enabled) /* only in simple mode */ + vgmstream_force_loop(data->layers[i], loop_flag, loop_start_sample, loop_end_sample); /* layer's force_loop also calls setup_vgmstream, no need to do it here */ } } @@ -1063,6 +854,8 @@ void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sa void vgmstream_set_loop_target(VGMSTREAM* vgmstream, int loop_target) { if (!vgmstream) return; + if (!vgmstream->loop_flag) return; + vgmstream->loop_target = loop_target; /* loop count must be rounded (int) as otherwise target is meaningless */ @@ -1080,1320 +873,13 @@ void vgmstream_set_loop_target(VGMSTREAM* vgmstream, int loop_target) { } -/* Decode data into sample buffer */ -void render_vgmstream(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - switch (vgmstream->layout_type) { - case layout_interleave: - render_vgmstream_interleave(buffer,sample_count,vgmstream); - break; - case layout_none: - render_vgmstream_flat(buffer,sample_count,vgmstream); - break; - case layout_blocked_mxch: - case layout_blocked_ast: - case layout_blocked_halpst: - case layout_blocked_xa: - case layout_blocked_ea_schl: - case layout_blocked_ea_1snh: - case layout_blocked_caf: - case layout_blocked_wsi: - case layout_blocked_str_snds: - case layout_blocked_ws_aud: - case layout_blocked_matx: - case layout_blocked_dec: - case layout_blocked_vs: - case layout_blocked_mul: - case layout_blocked_gsb: - case layout_blocked_xvas: - case layout_blocked_thp: - case layout_blocked_filp: - case layout_blocked_ivaud: - case layout_blocked_ea_swvr: - case layout_blocked_adm: - case layout_blocked_bdsp: - case layout_blocked_tra: - case layout_blocked_ps2_iab: - case layout_blocked_vs_str: - case layout_blocked_rws: - case layout_blocked_hwas: - case layout_blocked_ea_sns: - case layout_blocked_awc: - case layout_blocked_vgs: - case layout_blocked_vawx: - case layout_blocked_xvag_subsong: - case layout_blocked_ea_wve_au00: - case layout_blocked_ea_wve_ad10: - case layout_blocked_sthd: - case layout_blocked_h4m: - case layout_blocked_xa_aiff: - case layout_blocked_vs_square: - case layout_blocked_vid1: - case layout_blocked_ubi_sce: - render_vgmstream_blocked(buffer,sample_count,vgmstream); - break; - case layout_segmented: - render_vgmstream_segmented(buffer,sample_count,vgmstream); - break; - case layout_layered: - render_vgmstream_layered(buffer,sample_count,vgmstream); - break; - default: - break; - } - - mix_vgmstream(buffer, sample_count, vgmstream); -} - -/* Get the number of samples of a single frame (smallest self-contained sample group, 1/N channels) */ -int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { - /* Value returned here is the max (or less) that vgmstream will ask a decoder per - * "decode_x" call. Decoders with variable samples per frame or internal discard - * may return 0 here and handle arbitrary samples_to_do values internally - * (or some internal sample buffer max too). */ - - switch (vgmstream->coding_type) { - case coding_CRI_ADX: - case coding_CRI_ADX_fixed: - case coding_CRI_ADX_exp: - case coding_CRI_ADX_enc_8: - case coding_CRI_ADX_enc_9: - return (vgmstream->interleave_block_size - 2) * 2; - - case coding_NGC_DSP: - case coding_NGC_DSP_subint: - return 14; - case coding_NGC_AFC: - case coding_VADPCM: - return 16; - case coding_NGC_DTK: - return 28; - case coding_G721: - return 1; - - case coding_PCM16LE: - case coding_PCM16BE: - case coding_PCM16_int: - case coding_PCM8: - case coding_PCM8_int: - case coding_PCM8_U: - case coding_PCM8_U_int: - case coding_PCM8_SB: - case coding_ULAW: - case coding_ULAW_int: - case coding_ALAW: - case coding_PCMFLOAT: - return 1; -#ifdef VGM_USE_VORBIS - case coding_OGG_VORBIS: - case coding_VORBIS_custom: -#endif -#ifdef VGM_USE_MPEG - case coding_MPEG_custom: - case coding_MPEG_ealayer3: - case coding_MPEG_layer1: - case coding_MPEG_layer2: - case coding_MPEG_layer3: -#endif - case coding_SDX2: - case coding_SDX2_int: - case coding_CBD2: - case coding_ACM: - case coding_DERF: - case coding_NWA: - case coding_SASSC: - case coding_CIRCUS_ADPCM: - return 1; - - case coding_IMA: - case coding_DVI_IMA: - case coding_SNDS_IMA: - case coding_OTNS_IMA: - case coding_UBI_IMA: - case coding_OKI16: - case coding_OKI4S: - case coding_MTF_IMA: - return 1; - case coding_PCM4: - case coding_PCM4_U: - case coding_IMA_int: - case coding_DVI_IMA_int: - case coding_3DS_IMA: - case coding_WV6_IMA: - case coding_ALP_IMA: - case coding_FFTA2_IMA: - case coding_BLITZ_IMA: - case coding_PCFX: - return 2; - case coding_XBOX_IMA: - case coding_XBOX_IMA_mch: - case coding_XBOX_IMA_int: - case coding_FSB_IMA: - case coding_WWISE_IMA: - return 64; - case coding_APPLE_IMA4: - return 64; - case coding_MS_IMA: - case coding_REF_IMA: - return ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1; - case coding_RAD_IMA: - return (vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels; - case coding_NDS_IMA: - case coding_DAT4_IMA: - return (vgmstream->interleave_block_size - 0x04) * 2; - case coding_AWC_IMA: - return (0x800 - 0x04) * 2; - case coding_RAD_IMA_mono: - return 32; - case coding_H4M_IMA: - return 0; /* variable (block-controlled) */ - - case coding_XA: - return 28*8 / vgmstream->channels; /* 8 subframes per frame, mono/stereo */ - case coding_PSX: - case coding_PSX_badflags: - case coding_HEVAG: - return 28; - case coding_PSX_cfg: - case coding_PSX_pivotal: - return (vgmstream->interleave_block_size - 0x01) * 2; /* size 0x01 header */ - - case coding_EA_XA: - case coding_EA_XA_int: - case coding_EA_XA_V2: - case coding_MAXIS_XA: - return 28; - case coding_EA_XAS_V0: - return 32; - case coding_EA_XAS_V1: - return 128; - - case coding_MSADPCM: - return (vgmstream->frame_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2; - case coding_MSADPCM_int: - case coding_MSADPCM_ck: - return (vgmstream->frame_size - 0x07)*2 + 2; - case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */ - return vgmstream->ws_output_size; - case coding_AICA: - return 1; - case coding_AICA_int: - return 2; - case coding_ASKA: - return (0x40-0x04*vgmstream->channels) * 2 / vgmstream->channels; - case coding_NXAP: - return (0x40-0x04) * 2; - case coding_NDS_PROCYON: - return 30; - case coding_L5_555: - return 32; - case coding_LSF: - return 54; - -#ifdef VGM_USE_G7221 - case coding_G7221C: - return 32000/50; /* Siren7: 16000/50 */ -#endif -#ifdef VGM_USE_G719 - case coding_G719: - return 48000/50; -#endif -#ifdef VGM_USE_FFMPEG - case coding_FFmpeg: - return 0; -#endif - case coding_MTAF: - return 128*2; - case coding_MTA2: - return 128*2; - case coding_MC3: - return 10; - case coding_FADPCM: - return 256; /* (0x8c - 0xc) * 2 */ - case coding_ASF: - return 32; /* (0x11 - 0x1) * 2 */ - case coding_DSA: - return 14; /* (0x08 - 0x1) * 2 */ - case coding_XMD: - return (vgmstream->interleave_block_size - 0x06)*2 + 2; - case coding_PTADPCM: - return (vgmstream->interleave_block_size - 0x05)*2 + 2; - case coding_UBI_ADPCM: - return 0; /* varies per mode */ - case coding_IMUSE: - return 0; /* varies per frame */ - case coding_EA_MT: - return 0; /* 432, but variable in looped files */ - case coding_CIRCUS_VQ: - return 0; - case coding_RELIC: - return 0; /* 512 */ - case coding_CRI_HCA: - return 0; /* 1024 - delay/padding (which can be bigger than 1024) */ -#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) - 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; -#endif -#ifdef VGM_USE_ATRAC9 - case coding_ATRAC9: - return 0; /* varies with config data, usually 256 or 1024 */ -#endif -#ifdef VGM_USE_CELT - case coding_CELT_FSB: - return 0; /* 512? */ -#endif - default: - return 0; - } -} - -/* Get the number of bytes of a single frame (smallest self-contained byte group, 1/N channels) */ -int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { - switch (vgmstream->coding_type) { - case coding_CRI_ADX: - case coding_CRI_ADX_fixed: - case coding_CRI_ADX_exp: - case coding_CRI_ADX_enc_8: - case coding_CRI_ADX_enc_9: - return vgmstream->interleave_block_size; - - case coding_NGC_DSP: - return 0x08; - case coding_NGC_DSP_subint: - return 0x08 * vgmstream->channels; - case coding_NGC_AFC: - case coding_VADPCM: - return 0x09; - case coding_NGC_DTK: - return 0x20; - case coding_G721: - return 0; - - case coding_PCM16LE: - case coding_PCM16BE: - case coding_PCM16_int: - return 0x02; - case coding_PCM8: - case coding_PCM8_int: - case coding_PCM8_U: - case coding_PCM8_U_int: - case coding_PCM8_SB: - case coding_ULAW: - case coding_ULAW_int: - case coding_ALAW: - return 0x01; - case coding_PCMFLOAT: - return 0x04; - - case coding_SDX2: - case coding_SDX2_int: - case coding_CBD2: - case coding_DERF: - case coding_NWA: - case coding_SASSC: - case coding_CIRCUS_ADPCM: - return 0x01; - - case coding_PCM4: - case coding_PCM4_U: - case coding_IMA: - case coding_IMA_int: - case coding_DVI_IMA: - case coding_DVI_IMA_int: - case coding_3DS_IMA: - case coding_WV6_IMA: - case coding_ALP_IMA: - case coding_FFTA2_IMA: - case coding_BLITZ_IMA: - case coding_PCFX: - case coding_OKI16: - case coding_OKI4S: - case coding_MTF_IMA: - return 0x01; - case coding_MS_IMA: - case coding_RAD_IMA: - case coding_NDS_IMA: - case coding_DAT4_IMA: - case coding_REF_IMA: - return vgmstream->interleave_block_size; - case coding_AWC_IMA: - return 0x800; - case coding_RAD_IMA_mono: - return 0x14; - case coding_SNDS_IMA: - case coding_OTNS_IMA: - return 0; //todo: 0x01? - case coding_UBI_IMA: /* variable (PCM then IMA) */ - return 0; - case coding_XBOX_IMA: - //todo should be 0x48 when stereo, but blocked/interleave layout don't understand stereo codecs - return 0x24; //vgmstream->channels==1 ? 0x24 : 0x48; - case coding_XBOX_IMA_int: - case coding_WWISE_IMA: - return 0x24; - case coding_XBOX_IMA_mch: - case coding_FSB_IMA: - return 0x24 * vgmstream->channels; - case coding_APPLE_IMA4: - return 0x22; - case coding_H4M_IMA: - return 0x00; /* variable (block-controlled) */ - - case coding_XA: - return 0x80; - case coding_PSX: - case coding_PSX_badflags: - case coding_HEVAG: - return 0x10; - case coding_PSX_cfg: - case coding_PSX_pivotal: - return vgmstream->interleave_block_size; - - case coding_EA_XA: - return 0x1E; - case coding_EA_XA_int: - return 0x0F; - case coding_MAXIS_XA: - return 0x0F*vgmstream->channels; - case coding_EA_XA_V2: - return 0; /* variable (ADPCM frames of 0x0f or PCM frames of 0x3d) */ - case coding_EA_XAS_V0: - return 0xF+0x02+0x02; - case coding_EA_XAS_V1: - return 0x4c*vgmstream->channels; - - case coding_MSADPCM: - case coding_MSADPCM_int: - case coding_MSADPCM_ck: - return vgmstream->frame_size; - case coding_WS: - return vgmstream->current_block_size; - case coding_AICA: - case coding_AICA_int: - return 0x01; - case coding_ASKA: - case coding_NXAP: - return 0x40; - case coding_NDS_PROCYON: - return 0x10; - case coding_L5_555: - return 0x12; - case coding_LSF: - return 0x1C; - -#ifdef VGM_USE_G7221 - case coding_G7221C: -#endif -#ifdef VGM_USE_G719 - case coding_G719: -#endif -#ifdef VGM_USE_MAIATRAC3PLUS - case coding_AT3plus: -#endif -#ifdef VGM_USE_FFMPEG - case coding_FFmpeg: -#endif - case coding_MTAF: - return vgmstream->interleave_block_size; - case coding_MTA2: - return 0x90; - case coding_MC3: - return 0x04; - case coding_FADPCM: - return 0x8c; - case coding_ASF: - return 0x11; - case coding_DSA: - return 0x08; - case coding_XMD: - return vgmstream->interleave_block_size; - case coding_PTADPCM: - return vgmstream->interleave_block_size; - case coding_UBI_ADPCM: - return 0; /* varies per mode? */ - case coding_IMUSE: - return 0; /* varies per frame */ - 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 -#ifdef VGM_USE_CELT - case coding_CELT_FSB: - return 0; /* varies, usually 0x80-100 */ -#endif - default: /* Vorbis, MPEG, ACM, etc */ - return 0; - } -} - -/* In NDS IMA the frame size is the block size, so the last one is short */ -int get_vgmstream_samples_per_shortframe(VGMSTREAM * vgmstream) { - switch (vgmstream->coding_type) { - case coding_NDS_IMA: - return (vgmstream->interleave_last_block_size-4)*2; - default: - return get_vgmstream_samples_per_frame(vgmstream); - } -} -int get_vgmstream_shortframe_size(VGMSTREAM * vgmstream) { - switch (vgmstream->coding_type) { - case coding_NDS_IMA: - return vgmstream->interleave_last_block_size; - default: - return get_vgmstream_frame_size(vgmstream); - } -} - -/* Decode samples into the buffer. Assume that we have written samples_written into the - * buffer already, and we have samples_to_do consecutive samples ahead of us. */ -void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to_do, sample_t * buffer) { - int ch; - - switch (vgmstream->coding_type) { - case coding_CRI_ADX: - case coding_CRI_ADX_exp: - case coding_CRI_ADX_fixed: - case coding_CRI_ADX_enc_8: - case coding_CRI_ADX_enc_9: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_adx(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, - vgmstream->interleave_block_size, vgmstream->coding_type); - } - break; - case coding_NGC_DSP: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ngc_dsp(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_NGC_DSP_subint: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ngc_dsp_subint(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, - ch, vgmstream->interleave_block_size); - } - break; - - case coding_PCM16LE: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm16le(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_PCM16BE: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm16be(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_PCM16_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm16_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, - vgmstream->codec_endian); - } - break; - case coding_PCM8: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm8(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_PCM8_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm8_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_PCM8_U: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm8_unsigned(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_PCM8_U_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm8_unsigned_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_PCM8_SB: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm8_sb(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_PCM4: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm4(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do,ch); - } - break; - case coding_PCM4_U: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcm4_unsigned(vgmstream, &vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do,ch); - } - break; - - case coding_ULAW: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ulaw(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_ULAW_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ulaw_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_ALAW: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_alaw(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_PCMFLOAT: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcmfloat(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, - vgmstream->codec_endian); - } - break; - - case coding_NDS_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_nds_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_DAT4_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_dat4_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_XBOX_IMA: - case coding_XBOX_IMA_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_XBOX_IMA); - decode_xbox_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, - is_stereo); - } - break; - case coding_XBOX_IMA_mch: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_xbox_ima_mch(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_MS_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ms_ima(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_RAD_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_rad_ima(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_RAD_IMA_mono: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_rad_ima_mono(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_NGC_DTK: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ngc_dtk(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_G721: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_g721(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_NGC_AFC: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ngc_afc(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_VADPCM: { - int order = vgmstream->codec_config; - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_vadpcm(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, order); - } - break; - } - case coding_PSX: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_psx(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, 0, vgmstream->codec_config); - } - break; - case coding_PSX_badflags: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_psx(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, 1, vgmstream->codec_config); - } - break; - case coding_PSX_cfg: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_psx_configurable(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->interleave_block_size, vgmstream->codec_config); - } - break; - case coding_PSX_pivotal: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_psx_pivotal(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, - vgmstream->interleave_block_size); - } - break; - case coding_HEVAG: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_hevag(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_XA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_xa(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_EA_XA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ea_xa(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_EA_XA_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ea_xa_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_EA_XA_V2: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ea_xa_v2(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_MAXIS_XA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_maxis_xa(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_EA_XAS_V0: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ea_xas_v0(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_EA_XAS_V1: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ea_xas_v1(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; -#ifdef VGM_USE_VORBIS - case coding_OGG_VORBIS: - decode_ogg_vorbis(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, - samples_to_do,vgmstream->channels); - break; - - case coding_VORBIS_custom: - decode_vorbis_custom(vgmstream, buffer+samples_written*vgmstream->channels, - samples_to_do,vgmstream->channels); - break; -#endif - case coding_CIRCUS_VQ: - decode_circus_vq(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, samples_to_do, vgmstream->channels); - break; - case coding_RELIC: - decode_relic(&vgmstream->ch[0], vgmstream->codec_data, buffer+samples_written*vgmstream->channels, - samples_to_do); - break; - case coding_CRI_HCA: - decode_hca(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, - samples_to_do); - break; -#ifdef VGM_USE_FFMPEG - case coding_FFmpeg: - decode_ffmpeg(vgmstream, - buffer+samples_written*vgmstream->channels,samples_to_do,vgmstream->channels); - 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; -#endif - case coding_SDX2: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_sdx2(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_SDX2_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_sdx2_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_CBD2: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_cbd2(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_CBD2_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_cbd2_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_DERF: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_derf(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_CIRCUS_ADPCM: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_circus_adpcm(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - - case coding_IMA: - case coding_IMA_int: - case coding_DVI_IMA: - case coding_DVI_IMA_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_IMA) - || (vgmstream->channels > 1 && vgmstream->coding_type == coding_DVI_IMA); - int is_high_first = vgmstream->coding_type == coding_DVI_IMA || vgmstream->coding_type == coding_DVI_IMA_int; - - decode_standard_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, - is_stereo, is_high_first); - } - break; - case coding_MTF_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - int is_stereo = (vgmstream->channels > 1); - decode_mtf_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, - is_stereo); - } - break; - case coding_3DS_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_3ds_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_WV6_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_wv6_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_ALP_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_alp_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_FFTA2_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ffta2_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_BLITZ_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_blitz_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - - case coding_APPLE_IMA4: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_apple_ima4(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_SNDS_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_snds_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_OTNS_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_otns_ima(vgmstream, &vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_FSB_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_fsb_ima(vgmstream, &vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_WWISE_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_wwise_ima(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_REF_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ref_ima(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_AWC_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_awc_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_UBI_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ubi_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, vgmstream->codec_config); - } - break; - case coding_H4M_IMA: - for (ch = 0; ch < vgmstream->channels; ch++) { - uint16_t frame_format = (uint16_t)((vgmstream->codec_config >> 8) & 0xFFFF); - - decode_h4m_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, - frame_format); - } - break; - - case coding_WS: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ws(vgmstream,ch,buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - -#ifdef VGM_USE_MPEG - case coding_MPEG_custom: - case coding_MPEG_ealayer3: - case coding_MPEG_layer1: - case coding_MPEG_layer2: - case coding_MPEG_layer3: - decode_mpeg(vgmstream,buffer+samples_written*vgmstream->channels, - samples_to_do,vgmstream->channels); - break; -#endif -#ifdef VGM_USE_G7221 - case coding_G7221C: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_g7221(vgmstream, buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,samples_to_do, ch); - } - break; -#endif -#ifdef VGM_USE_G719 - case coding_G719: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_g719(vgmstream, buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,samples_to_do, ch); - } - break; -#endif -#ifdef VGM_USE_MAIATRAC3PLUS - case coding_AT3plus: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_at3plus(vgmstream, buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,samples_to_do, ch); - } - break; -#endif -#ifdef VGM_USE_ATRAC9 - case coding_ATRAC9: - decode_atrac9(vgmstream, buffer+samples_written*vgmstream->channels, - samples_to_do,vgmstream->channels); - break; -#endif -#ifdef VGM_USE_CELT - case coding_CELT_FSB: - decode_celt_fsb(vgmstream, buffer+samples_written*vgmstream->channels, - samples_to_do,vgmstream->channels); - break; -#endif - case coding_ACM: - decode_acm(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, - samples_to_do, vgmstream->channels); - break; - case coding_NWA: - decode_nwa(((nwa_codec_data*)vgmstream->codec_data)->nwa, - buffer+samples_written*vgmstream->channels, samples_to_do); - break; - case coding_MSADPCM: - case coding_MSADPCM_int: - if (vgmstream->channels == 1 || vgmstream->coding_type == coding_MSADPCM_int) { - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_msadpcm_mono(vgmstream,buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block, samples_to_do, ch); - } - } - else if (vgmstream->channels == 2) { - decode_msadpcm_stereo(vgmstream,buffer+samples_written*vgmstream->channels, - vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_MSADPCM_ck: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_msadpcm_ck(vgmstream,buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block, samples_to_do, ch); - } - break; - case coding_AICA: - case coding_AICA_int: - for (ch = 0; ch < vgmstream->channels; ch++) { - int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_AICA); - - decode_aica(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, - is_stereo); - } - break; - case coding_ASKA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_aska(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - case coding_NXAP: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_nxap(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_TGC: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_tgc(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_NDS_PROCYON: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_nds_procyon(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_L5_555: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_l5_555(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_SASSC: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_sassc(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - - break; - case coding_LSF: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_lsf(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_MTAF: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_mtaf(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); - } - break; - case coding_MTA2: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_mta2(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); - } - break; - case coding_MC3: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_mc3(vgmstream, &vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); - } - break; - case coding_FADPCM: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_fadpcm(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_ASF: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_asf(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_DSA: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_dsa(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do); - } - break; - case coding_XMD: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_xmd(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, - vgmstream->interleave_block_size); - } - break; - case coding_PTADPCM: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ptadpcm(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, - vgmstream->interleave_block_size); - } - break; - case coding_PCFX: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_pcfx(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->codec_config); - } - break; - case coding_OKI16: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_oki16(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - - case coding_OKI4S: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_oki4s(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); - } - break; - - case coding_UBI_ADPCM: - decode_ubi_adpcm(vgmstream, buffer+samples_written*vgmstream->channels, samples_to_do); - break; - - case coding_IMUSE: - decode_imuse(vgmstream, buffer+samples_written*vgmstream->channels, samples_to_do); - break; - - case coding_EA_MT: - for (ch = 0; ch < vgmstream->channels; ch++) { - decode_ea_mt(vgmstream, buffer+samples_written*vgmstream->channels+ch, - vgmstream->channels, samples_to_do, ch); - } - break; - default: - break; - } -} - -/* Calculate number of consecutive samples to do (taking into account stopping for loop start and end) */ -int vgmstream_samples_to_do(int samples_this_block, int samples_per_frame, VGMSTREAM * vgmstream) { - int samples_to_do; - int samples_left_this_block; - - samples_left_this_block = samples_this_block - vgmstream->samples_into_block; - samples_to_do = samples_left_this_block; - - /* fun loopy crap */ - /* Why did I think this would be any simpler? */ - if (vgmstream->loop_flag) { - /* are we going to hit the loop end during this block? */ - if (vgmstream->current_sample + samples_left_this_block > vgmstream->loop_end_sample) { - /* only do to just before it */ - samples_to_do = vgmstream->loop_end_sample - vgmstream->current_sample; - } - - /* are we going to hit the loop start during this block? */ - if (!vgmstream->hit_loop && vgmstream->current_sample + samples_left_this_block > vgmstream->loop_start_sample) { - /* only do to just before it */ - samples_to_do = vgmstream->loop_start_sample - vgmstream->current_sample; - } - - } - - /* if it's a framed encoding don't do more than one frame */ - if (samples_per_frame > 1 && (vgmstream->samples_into_block % samples_per_frame) + samples_to_do > samples_per_frame) - samples_to_do = samples_per_frame - (vgmstream->samples_into_block % samples_per_frame); - - return samples_to_do; -} - -/* Detect loop start and save values, or detect loop end and restore (loop back). Returns 1 if loop was done. */ -int vgmstream_do_loop(VGMSTREAM * vgmstream) { - /*if (!vgmstream->loop_flag) return 0;*/ - - /* is this the loop end? = new loop, continue from loop_start_sample */ - if (vgmstream->current_sample==vgmstream->loop_end_sample) { - - /* disable looping if target count reached and continue normally - * (only needed with the "play stream end after looping N times" option enabled) */ - vgmstream->loop_count++; - if (vgmstream->loop_target && vgmstream->loop_target == vgmstream->loop_count) { - vgmstream->loop_flag = 0; /* could be improved but works ok, will be restored on resets */ - return 0; - } - - /* against everything I hold sacred, preserve adpcm - * history through loop for certain types */ - if (vgmstream->meta_type == meta_DSP_STD || - vgmstream->meta_type == meta_DSP_RS03 || - vgmstream->meta_type == meta_DSP_CSTR || - vgmstream->coding_type == coding_PSX || - vgmstream->coding_type == coding_PSX_badflags) { - int i; - for (i = 0; i < vgmstream->channels; i++) { - vgmstream->loop_ch[i].adpcm_history1_16 = vgmstream->ch[i].adpcm_history1_16; - vgmstream->loop_ch[i].adpcm_history2_16 = vgmstream->ch[i].adpcm_history2_16; - vgmstream->loop_ch[i].adpcm_history1_32 = vgmstream->ch[i].adpcm_history1_32; - vgmstream->loop_ch[i].adpcm_history2_32 = vgmstream->ch[i].adpcm_history2_32; - } - } - - - /* prepare certain codecs' internal state for looping */ - - if (vgmstream->coding_type == coding_CIRCUS_VQ) { - seek_circus_vq(vgmstream->codec_data, vgmstream->loop_sample); - } - - if (vgmstream->coding_type == coding_RELIC) { - seek_relic(vgmstream->codec_data, vgmstream->loop_sample); - } - - if (vgmstream->coding_type == coding_CRI_HCA) { - loop_hca(vgmstream->codec_data, vgmstream->loop_sample); - } - - if (vgmstream->coding_type == coding_UBI_ADPCM) { - seek_ubi_adpcm(vgmstream->codec_data, vgmstream->loop_sample); - } - - if (vgmstream->coding_type == coding_IMUSE) { - seek_imuse(vgmstream->codec_data, vgmstream->loop_sample); - } - - if (vgmstream->coding_type == coding_EA_MT) { - seek_ea_mt(vgmstream, vgmstream->loop_sample); - } - -#ifdef VGM_USE_VORBIS - if (vgmstream->coding_type == coding_OGG_VORBIS) { - seek_ogg_vorbis(vgmstream, vgmstream->loop_sample); - } - - if (vgmstream->coding_type == coding_VORBIS_custom) { - seek_vorbis_custom(vgmstream, vgmstream->loop_sample); - } -#endif - -#ifdef VGM_USE_FFMPEG - if (vgmstream->coding_type == coding_FFmpeg) { - seek_ffmpeg(vgmstream, vgmstream->loop_sample); - } -#endif - -#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) - if (vgmstream->coding_type == coding_MP4_AAC) { - seek_mp4_aac(vgmstream, vgmstream->loop_sample); - } -#endif - -#ifdef VGM_USE_MAIATRAC3PLUS - if (vgmstream->coding_type == coding_AT3plus) { - seek_at3plus(vgmstream, vgmstream->loop_sample); - } -#endif - -#ifdef VGM_USE_ATRAC9 - if (vgmstream->coding_type == coding_ATRAC9) { - seek_atrac9(vgmstream, vgmstream->loop_sample); - } -#endif - -#ifdef VGM_USE_CELT - if (vgmstream->coding_type == coding_CELT_FSB) { - seek_celt_fsb(vgmstream, vgmstream->loop_sample); - } -#endif - -#ifdef VGM_USE_MPEG - if (vgmstream->coding_type == coding_MPEG_custom || - vgmstream->coding_type == coding_MPEG_ealayer3 || - vgmstream->coding_type == coding_MPEG_layer1 || - vgmstream->coding_type == coding_MPEG_layer2 || - vgmstream->coding_type == coding_MPEG_layer3) { - seek_mpeg(vgmstream, vgmstream->loop_sample); - } -#endif - - if (vgmstream->coding_type == coding_NWA) { - nwa_codec_data *data = vgmstream->codec_data; - if (data) - seek_nwa(data->nwa, vgmstream->loop_sample); - } - - /* restore! */ - memcpy(vgmstream->ch, vgmstream->loop_ch, sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); - vgmstream->current_sample = vgmstream->loop_sample; - vgmstream->samples_into_block = vgmstream->loop_samples_into_block; - vgmstream->current_block_size = vgmstream->loop_block_size; - vgmstream->current_block_samples = vgmstream->loop_block_samples; - vgmstream->current_block_offset = vgmstream->loop_block_offset; - vgmstream->next_block_offset = vgmstream->loop_next_block_offset; - - return 1; /* looped */ - } - - - /* is this the loop start? */ - if (!vgmstream->hit_loop && vgmstream->current_sample == vgmstream->loop_start_sample) { - /* save! */ - memcpy(vgmstream->loop_ch, vgmstream->ch, sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); - vgmstream->loop_sample = vgmstream->current_sample; - vgmstream->loop_samples_into_block = vgmstream->samples_into_block; - vgmstream->loop_block_size = vgmstream->current_block_size; - vgmstream->loop_block_samples = vgmstream->current_block_samples; - vgmstream->loop_block_offset = vgmstream->current_block_offset; - vgmstream->loop_next_block_offset = vgmstream->next_block_offset; - vgmstream->hit_loop = 1; - } - - return 0; /* not looped */ -} +/*******************************************************************************/ +/* MISC */ +/*******************************************************************************/ /* Write a description of the stream into array pointed by desc, which must be length bytes long. * Will always be null-terminated if length > 0 */ -void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { +void describe_vgmstream(VGMSTREAM* vgmstream, char* desc, int length) { #define TEMPSIZE (256+32) char temp[TEMPSIZE]; double time_mm, time_ss, seconds; @@ -2544,14 +1030,115 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { snprintf(temp,TEMPSIZE, "stream name: %s\n", vgmstream->stream_name); concatn(length,desc,temp); } + + if (vgmstream->config_enabled) { + double time_mm, time_ss, seconds; + int32_t samples = vgmstream->pstate.play_duration; + + seconds = (double)samples / vgmstream->sample_rate; + time_mm = (int)(seconds / 60.0); + time_ss = seconds - time_mm * 60.0f; + + snprintf(temp,TEMPSIZE, "play duration: %d samples (%1.0f:%06.3f seconds)\n", samples, time_mm, time_ss); + concatn(length,desc,temp); + } } +void describe_vgmstream_info(VGMSTREAM* vgmstream, vgmstream_info* info) { + if (!info) { + return; + } + + memset(info, 0, sizeof(*info)); + + if (!vgmstream) { + return; + } + + info->sample_rate = vgmstream->sample_rate; + + info->channels = vgmstream->channels; + + { + int output_channels = 0; + mixing_info(vgmstream, NULL, &output_channels); + + if (output_channels != vgmstream->channels) { + info->mixing_info.input_channels = vgmstream->channels; + info->mixing_info.output_channels = output_channels; + } + } + + info->channel_layout = vgmstream->channel_layout; + + if (vgmstream->loop_start_sample >= 0 && vgmstream->loop_end_sample > vgmstream->loop_start_sample) { + info->loop_info.start = vgmstream->loop_start_sample; + info->loop_info.end = vgmstream->loop_end_sample; + } + + info->num_samples = vgmstream->num_samples; + + get_vgmstream_coding_description(vgmstream, info->encoding, sizeof(info->encoding)); + + get_vgmstream_layout_description(vgmstream, info->layout, sizeof(info->layout)); + + if (vgmstream->layout_type == layout_interleave && vgmstream->channels > 1) { + info->interleave_info.value = vgmstream->interleave_block_size; + + if (vgmstream->interleave_first_block_size && vgmstream->interleave_first_block_size != vgmstream->interleave_block_size) { + info->interleave_info.first_block = vgmstream->interleave_first_block_size; + } + + if (vgmstream->interleave_last_block_size && vgmstream->interleave_last_block_size != vgmstream->interleave_block_size) { + info->interleave_info.last_block = vgmstream->interleave_last_block_size; + } + } + + /* codecs with configurable frame size */ + if (vgmstream->frame_size > 0 || vgmstream->interleave_block_size > 0) { + int32_t frame_size = vgmstream->frame_size > 0 ? vgmstream->frame_size : vgmstream->interleave_block_size; + switch (vgmstream->coding_type) { + case coding_MSADPCM: + case coding_MSADPCM_int: + case coding_MSADPCM_ck: + case coding_MS_IMA: + case coding_MC3: + case coding_WWISE_IMA: + case coding_REF_IMA: + case coding_PSX_cfg: + info->frame_size = frame_size; + break; + default: + break; + } + } + + get_vgmstream_meta_description(vgmstream, info->metadata, sizeof(info->metadata)); + + info->bitrate = get_vgmstream_average_bitrate(vgmstream); + + /* only interesting if more than one */ + if (vgmstream->num_streams > 1) { + info->stream_info.total = vgmstream->num_streams; + } + else { + info->stream_info.total = 1; + } + + if (vgmstream->num_streams > 1) { + info->stream_info.current = vgmstream->stream_index == 0 ? 1 : vgmstream->stream_index; + } + + if (vgmstream->stream_name[0] != '\0') { + snprintf(info->stream_info.name, sizeof(info->stream_info.name), "%s", vgmstream->stream_name); + } +} /* See if there is a second file which may be the second channel, given an already opened mono vgmstream. * If a suitable file is found, open it and change opened_vgmstream to a stereo vgmstream. */ -static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *streamFile, VGMSTREAM*(*init_vgmstream_function)(STREAMFILE *)) { +static void try_dual_file_stereo(VGMSTREAM* opened_vgmstream, STREAMFILE* sf, VGMSTREAM*(*init_vgmstream_function)(STREAMFILE*)) { /* filename search pairs for dual file stereo */ - static const char * const dfs_pairs[][2] = { + static const char* const dfs_pairs[][2] = { {"L","R"}, /* most common in .dsp and .vag */ {"l","r"}, /* same */ {"left","right"}, /* Freaky Flyers (GC) .adp, Velocity (PSP) .vag, Hyper Fighters (Wii) .dsp */ @@ -2563,10 +1150,10 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea {".adpcm","_2.adpcm"}, /* Desire: Remaster Version (Switch) */ }; char new_filename[PATH_LIMIT]; - char * extension; + char* extension; int dfs_pair = -1; /* -1=no stereo, 0=opened_vgmstream is left, 1=opened_vgmstream is right */ - VGMSTREAM *new_vgmstream = NULL; - STREAMFILE *dual_streamFile = NULL; + VGMSTREAM* new_vgmstream = NULL; + STREAMFILE* dual_sf = NULL; int i,j, dfs_pair_count, extension_len, filename_len; if (opened_vgmstream->channels != 1) @@ -2579,7 +1166,7 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea //todo other layouts work but some stereo codecs do weird things //if (opened_vgmstream->layout != layout_none) return; - get_streamfile_name(streamFile, new_filename, sizeof(new_filename)); + get_streamfile_name(sf, new_filename, sizeof(new_filename)); filename_len = strlen(new_filename); if (filename_len < 2) return; @@ -2594,8 +1181,8 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea dfs_pair_count = (sizeof(dfs_pairs)/sizeof(dfs_pairs[0])); for (i = 0; dfs_pair == -1 && i < dfs_pair_count; i++) { for (j = 0; dfs_pair == -1 && j < 2; j++) { - const char * this_suffix = dfs_pairs[i][j]; - const char * that_suffix = dfs_pairs[i][j^1]; + const char* this_suffix = dfs_pairs[i][j]; + const char* that_suffix = dfs_pairs[i][j^1]; size_t this_suffix_len = strlen(dfs_pairs[i][j]); size_t that_suffix_len = strlen(dfs_pairs[i][j^1]); @@ -2621,11 +1208,11 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea if (dfs_pair != -1) { //VGM_LOG("DFS: try %i: %s\n", dfs_pair, new_filename); /* try to init other channel (new_filename now has the opposite name) */ - dual_streamFile = open_streamfile(streamFile, new_filename); - if (!dual_streamFile) { + dual_sf = open_streamfile(sf, new_filename); + if (!dual_sf) { /* restore filename and keep trying (if found it'll break and init) */ dfs_pair = -1; - get_streamfile_name(streamFile, new_filename, sizeof(new_filename)); + get_streamfile_name(sf, new_filename, sizeof(new_filename)); } } } @@ -2636,8 +1223,8 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea goto fail; //;VGM_LOG("DFS: match %i filename=%s\n", dfs_pair, new_filename); - new_vgmstream = init_vgmstream_function(dual_streamFile); /* use the init function that just worked */ - close_streamfile(dual_streamFile); + new_vgmstream = init_vgmstream_function(dual_sf); /* use the init function that just worked */ + close_streamfile(dual_sf); /* see if we were able to open the file, and if everything matched nicely */ if (!(new_vgmstream && @@ -2668,9 +1255,9 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea /* We seem to have a usable, matching file. Merge in the second channel. */ { - VGMSTREAMCHANNEL * new_chans; - VGMSTREAMCHANNEL * new_loop_chans = NULL; - VGMSTREAMCHANNEL * new_start_chans = NULL; + VGMSTREAMCHANNEL* new_chans; + VGMSTREAMCHANNEL* new_loop_chans = NULL; + VGMSTREAMCHANNEL* new_start_chans = NULL; /* build the channels */ new_chans = calloc(2,sizeof(VGMSTREAMCHANNEL)); @@ -2718,6 +1305,7 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea /* discard the second VGMSTREAM */ mixing_close(new_vgmstream); + free(new_vgmstream->tmpbuf); free(new_vgmstream->start_vgmstream); free(new_vgmstream); @@ -2731,16 +1319,14 @@ fail: } /* average bitrate helper to get STREAMFILE for a channel, since some codecs may use their own */ -static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM * vgmstream, int channel) { +static STREAMFILE* get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM* vgmstream, int channel) { if (vgmstream->coding_type == coding_NWA) { - nwa_codec_data *data = vgmstream->codec_data; - return (data && data->nwa) ? data->nwa->file : NULL; + return nwa_get_streamfile(vgmstream->codec_data); } if (vgmstream->coding_type == coding_ACM) { - acm_codec_data *data = vgmstream->codec_data; - return (data && data->handle) ? data->streamfile : NULL; + return acm_get_streamfile(vgmstream->codec_data); } #ifdef VGM_USE_VORBIS @@ -2749,13 +1335,11 @@ static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM * } #endif if (vgmstream->coding_type == coding_CRI_HCA) { - hca_codec_data *data = vgmstream->codec_data; - return data ? data->streamfile : NULL; + return hca_get_streamfile(vgmstream->codec_data); } #ifdef VGM_USE_FFMPEG if (vgmstream->coding_type == coding_FFmpeg) { - ffmpeg_codec_data *data = vgmstream->codec_data; - return data ? data->streamfile : NULL; + return ffmpeg_get_streamfile(vgmstream->codec_data); } #endif #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) @@ -2773,12 +1357,12 @@ static int get_vgmstream_file_bitrate_from_size(size_t size, int sample_rate, in if (length_samples < 100) return 0; /* ignore stupid bitrates caused by some segments */ return (int)((int64_t)size * 8 * sample_rate / length_samples); } -static int get_vgmstream_file_bitrate_from_streamfile(STREAMFILE * streamfile, int sample_rate, int length_samples) { +static int get_vgmstream_file_bitrate_from_streamfile(STREAMFILE* streamfile, int sample_rate, int length_samples) { if (streamfile == NULL) return 0; return get_vgmstream_file_bitrate_from_size(get_streamfile_size(streamfile), sample_rate, length_samples); } -static int get_vgmstream_file_bitrate_main(VGMSTREAM * vgmstream, STREAMFILE **streamfile_pointers, int *pointers_count, int pointers_max) { +static int get_vgmstream_file_bitrate_main(VGMSTREAM* vgmstream, STREAMFILE** streamfile_pointers, int *pointers_count, int pointers_max) { int sub, ch; int bitrate = 0; @@ -2815,18 +1399,18 @@ static int get_vgmstream_file_bitrate_main(VGMSTREAM * vgmstream, STREAMFILE **s int is_unique = 1; for (ch = 0; ch < vgmstream->channels; ch++) { - STREAMFILE * currentFile = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, ch); - if (!currentFile) continue; - get_streamfile_name(currentFile, path_current, sizeof(path_current)); + STREAMFILE* sf_cur = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, ch); + if (!sf_cur) continue; + get_streamfile_name(sf_cur, path_current, sizeof(path_current)); for (sub = 0; sub < *pointers_count; sub++) { - STREAMFILE * compareFile = streamfile_pointers[sub]; - if (!compareFile) continue; - if (currentFile == compareFile) { + STREAMFILE* sf_cmp = streamfile_pointers[sub]; + if (!sf_cmp) continue; + if (sf_cur == sf_cmp) { is_unique = 0; break; } - get_streamfile_name(compareFile, path_compare, sizeof(path_compare)); + get_streamfile_name(sf_cmp, path_compare, sizeof(path_compare)); if (strcmp(path_current, path_compare) == 0) { is_unique = 0; break; @@ -2835,10 +1419,10 @@ static int get_vgmstream_file_bitrate_main(VGMSTREAM * vgmstream, STREAMFILE **s if (is_unique) { if (*pointers_count >= pointers_max) goto fail; - streamfile_pointers[*pointers_count] = currentFile; + streamfile_pointers[*pointers_count] = sf_cur; (*pointers_count)++; - bitrate += get_vgmstream_file_bitrate_from_streamfile(currentFile, vgmstream->sample_rate, vgmstream->num_samples); + bitrate += get_vgmstream_file_bitrate_from_streamfile(sf_cur, vgmstream->sample_rate, vgmstream->num_samples); } } } @@ -2852,9 +1436,9 @@ fail: * This is the bitrate of the *file*, as opposed to the bitrate of the *codec*, meaning * it counts extra data like block headers and padding. While this can be surprising * sometimes (as it's often higher than common codec bitrates) it isn't wrong per se. */ -int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) { +int get_vgmstream_average_bitrate(VGMSTREAM* vgmstream) { const size_t pointers_max = 128; /* arbitrary max, but +100 segments have been observed */ - STREAMFILE *streamfile_pointers[128]; /* list already used streamfiles */ + STREAMFILE* streamfile_pointers[128]; /* list already used streamfiles */ int pointers_count = 0; return get_vgmstream_file_bitrate_main(vgmstream, streamfile_pointers, &pointers_count, pointers_max); diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 0f8fed8ef..8c1eb292d 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -35,11 +35,7 @@ enum { VGMSTREAM_MAX_NUM_SAMPLES = 1000000000 }; /* no ~5h vgm hopefully */ #endif #ifdef VGM_USE_MPEG -#ifdef __MACOSX__ #include -#else -#include -#endif #endif #ifdef VGM_USE_MP4V2 @@ -74,6 +70,8 @@ enum { VGMSTREAM_MAX_NUM_SAMPLES = 1000000000 }; /* no ~5h vgm hopefully */ /* The encoding type specifies the format the sound data itself takes */ typedef enum { + coding_SILENCE, /* generates silence */ + /* PCM */ coding_PCM16LE, /* little endian 16-bit PCM */ coding_PCM16BE, /* big endian 16-bit PCM */ @@ -150,6 +148,7 @@ typedef enum { coding_UBI_IMA, /* Ubisoft IMA ADPCM */ coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */ coding_MTF_IMA, /* Capcom MT Framework IMA ADPCM */ + coding_CD_IMA, /* Crystal Dynamics IMA ADPCM */ coding_MSADPCM, /* Microsoft ADPCM (stereo/mono) */ coding_MSADPCM_int, /* Microsoft ADPCM (mono) */ @@ -183,11 +182,12 @@ typedef enum { coding_SDX2, /* SDX2 2:1 Squareroot-Delta-Exact compression DPCM */ coding_SDX2_int, /* SDX2 2:1 Squareroot-Delta-Exact compression with sample-level interleave */ coding_CBD2, /* CBD2 2:1 Cuberoot-Delta-Exact compression DPCM */ - coding_CBD2_int, /* CBD2 2:1 Cuberoot-Delta-Exact compression, with sample-level interleave */ + coding_CBD2_int, /* CBD2 2:1 Cuberoot-Delta-Exact compression, with sample-level interleave */ coding_SASSC, /* Activision EXAKT SASSC 8-bit DPCM */ coding_DERF, /* DERF 8-bit DPCM */ + coding_WADY, /* WADY 8-bit DPCM */ + coding_NWA, /* VisualArt's NWA DPCM */ coding_ACM, /* InterPlay ACM */ - coding_NWA, /* VisualArt's NWA */ coding_CIRCUS_ADPCM, /* Circus 8-bit ADPCM */ coding_UBI_ADPCM, /* Ubisoft 4/6-bit ADPCM */ @@ -277,7 +277,7 @@ typedef enum { layout_blocked_ea_sns, /* newest Electronic Arts blocks, found in SNS/SNU/SPS/etc formats */ layout_blocked_awc, /* Rockstar AWC */ layout_blocked_vgs, /* Guitar Hero II (PS2) */ - layout_blocked_vawx, /* No More Heroes 6ch (PS3) */ + layout_blocked_xwav, layout_blocked_xvag_subsong, /* XVAG subsongs [God of War III (PS4)] */ layout_blocked_ea_wve_au00, /* EA WVE au00 blocks */ layout_blocked_ea_wve_ad10, /* EA WVE Ad10 blocks */ @@ -297,6 +297,7 @@ typedef enum { /* The meta type specifies how we know what we know about the file. * We may know because of a header we read, some of it may have been guessed from filenames, etc. */ typedef enum { + meta_SILENCE, meta_DSP_STD, /* Nintendo standard GC ADPCM (DSP) header */ meta_DSP_CSTR, /* Star Fox Assault "Cstr" */ @@ -330,7 +331,7 @@ typedef enum { 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_SWAV, meta_NDS_RRDS, /* Ridge Racer DS */ meta_WII_BNS, /* Wii BNS Banner Sound (similar to RSTM) */ meta_WIIU_BTSND, /* Wii U Boot Sound */ @@ -357,7 +358,7 @@ typedef enum { meta_PS2_RXWS, /* Sony games (Genji, Okage Shadow King, Arc The Lad Twilight of Spirits) */ meta_RAW_INT, meta_PS2_EXST, /* Shadow of Colossus EXST */ - meta_PS2_SVAG, /* Konami SVAG */ + meta_SVAG_KCET, meta_PS_HEADERLESS, /* headerless PS-ADPCM */ meta_PS2_MIB_MIH, /* MIB File + MIH Header*/ meta_PS2_MIC, /* KOEI MIC File */ @@ -566,7 +567,7 @@ typedef enum { meta_PS2_IAB, /* Ueki no Housoku - Taosu ze Robert Juudan!! (PS2) */ meta_VS_STR, /* The Bouncer */ meta_LSF_N1NJ4N, /* .lsf n1nj4n Fastlane Street Racing (iPhone) */ - meta_VAWX, /* feelplus: No More Heroes Heroes Paradise, Moon Diver */ + meta_XWAV, meta_RAW_SNDS, meta_PS2_WMUS, /* The Warriors (PS2) */ meta_HYPERSCAN_KVAG, /* Hyperscan KVAG/BVG */ @@ -595,7 +596,7 @@ typedef enum { meta_MCA, /* Capcom MCA "MADP" */ meta_XB3D_ADX, /* Xenoblade Chronicles 3D ADX */ meta_HCA, /* CRI HCA */ - meta_PS2_SVAG_SNK, /* SNK PS2 SVAG */ + meta_SVAG_SNK, meta_PS2_VDS_VDM, /* Graffiti Kingdom */ meta_FFMPEG, /* any file supported by FFmpeg */ meta_X360_CXS, /* Eternal Sonata (Xbox 360) */ @@ -609,9 +610,7 @@ 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_MOBILE, /* tri-Ace AAC (Star Ocean Anamnesis, Heaven x Inferno) */ + meta_TA_AAC, meta_MTA2, meta_NGC_ULW, /* Burnout 1 (GC only) */ meta_XA_XA30, @@ -661,10 +660,9 @@ typedef enum { meta_TXTP, /* generic text playlist */ meta_SMC_SMH, /* Wangan Midnight (System 246) */ meta_PPST, /* PPST [Parappa the Rapper (PSP)] */ - meta_OPUS_PPP, /* .at9 Opus [Penny-Punching Princess (Switch)] */ + meta_SPS_N1, meta_UBI_BAO, /* Ubisoft BAO */ meta_DSP_SWITCH_AUDIO, /* Gal Gun 2 (Switch) */ - meta_TA_AAC_VITA, /* tri-Ace AAC (Judas Code) */ meta_H4M, /* Hudson HVQM4 video [Resident Evil 0 (GC), Tales of Symphonia (GC)] */ meta_ASF, /* Argonaut ASF [Croc 2 (PC)] */ meta_XMD, /* Konami XMD [Silent Hill 4 (Xbox), Castlevania: Curse of Darkness (Xbox)] */ @@ -742,6 +740,11 @@ typedef enum { meta_KTSR, meta_KAT, meta_PCM_SUCCESS, + meta_ADP_KONAMI, + meta_SDRH, + meta_WADY, + meta_DSP_SQEX, + meta_DSP_WIIVOICE, } meta_t; /* standard WAVEFORMATEXTENSIBLE speaker positions */ @@ -786,10 +789,76 @@ typedef enum { mapping_7POINT1_surround = speaker_FL | speaker_FR | speaker_FC | speaker_LFE | speaker_BL | speaker_BR | speaker_SL | speaker_SR, } mapping_t; +typedef struct { + int config_set; /* some of the mods below are set */ + + /* modifiers */ + int play_forever; + int ignore_loop; + int force_loop; + int really_force_loop; + int ignore_fade; + + /* processing */ + double loop_count; + int32_t pad_begin; + int32_t trim_begin; + int32_t body_time; + int32_t trim_end; + double fade_delay; /* not in samples for backwards compatibility */ + double fade_time; + int32_t pad_end; + + double pad_begin_s; + double trim_begin_s; + double body_time_s; + double trim_end_s; + //double fade_delay_s; + //double fade_time_s; + double pad_end_s; + + /* internal flags */ + int pad_begin_set; + int trim_begin_set; + int body_time_set; + int loop_count_set; + int trim_end_set; + int fade_delay_set; + int fade_time_set; + int pad_end_set; + + /* for lack of a better place... */ + int is_txtp; + int is_mini_txtp; + +} play_config_t; + + +typedef struct { + int input_channels; + int output_channels; + + int32_t pad_begin_duration; + int32_t pad_begin_left; + int32_t trim_begin_duration; + int32_t trim_begin_left; + int32_t body_duration; + int32_t fade_duration; + int32_t fade_left; + int32_t fade_start; + int32_t pad_end_duration; + //int32_t pad_end_left; + int32_t pad_end_start; + + int32_t play_duration; /* total samples that the stream lasts (after applying all config) */ + int32_t play_position; /* absolute sample where stream is */ + +} play_state_t; + /* info for a single vgmstream channel */ typedef struct { - STREAMFILE * streamfile; /* file used by this channel */ + STREAMFILE* streamfile; /* file used by this channel */ off_t channel_start_offset; /* where data for this channel begins */ off_t offset; /* current location in the file */ @@ -819,8 +888,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 */ @@ -836,6 +905,7 @@ typedef struct { } VGMSTREAMCHANNEL; + /* main vgmstream info */ typedef struct { /* basic config */ @@ -870,36 +940,25 @@ typedef struct { /* other config */ int allow_dual_stereo; /* search for dual stereo (file_L.ext + file_R.ext = single stereo file) */ - /* config requests, players must read and honor these values */ - /* (ideally internally would work as a player, but for now player must do it manually) */ - double config_loop_count; - double config_fade_time; - double config_fade_delay; - int config_ignore_loop; - int config_force_loop; - int config_ignore_fade; - /* layout/block state */ size_t full_block_size; /* actual data size of an entire block (ie. may be fixed, include padding/headers, etc) */ - int32_t current_sample; /* number of samples we've passed (for loop detection) */ + int32_t current_sample; /* sample point within the file (for loop detection) */ int32_t samples_into_block; /* number of samples into the current block/interleave/segment/etc */ off_t current_block_offset; /* start of this block (offset of block header) */ size_t current_block_size; /* size in usable bytes of the block we're in now (used to calculate num_samples per block) */ - int32_t current_block_samples; /* size in samples of the block we're in now (used over current_block_size if possible) */ + int32_t current_block_samples; /* size in samples of the block we're in now (used over current_block_size if possible) */ off_t next_block_offset; /* offset of header of the next block */ - /* layout/block loop state */ - int32_t loop_sample; /* saved from current_sample (same as loop_start_sample, but more state-like) */ + + /* loop state (saved when loop is hit to restore later) */ + int32_t loop_current_sample; /* saved from current_sample (same as loop_start_sample, but more state-like) */ int32_t loop_samples_into_block;/* saved from samples_into_block */ off_t loop_block_offset; /* saved from current_block_offset */ size_t loop_block_size; /* saved from current_block_size */ - int32_t loop_block_samples; /* saved from current_block_samples */ + int32_t loop_block_samples; /* saved from current_block_samples */ off_t loop_next_block_offset; /* saved from next_block_offset */ + int hit_loop; /* save config when loop is hit, but first time only */ - /* loop state */ - int hit_loop; /* have we seen the loop yet? */ - int loop_count; /* counter of complete loops (1=looped once) */ - int loop_target; /* max loops before continuing with the stream end (loops forever if not set) */ /* decoder config/state */ int codec_endian; /* little/big endian marker; name is left vague but usually means big endian */ @@ -908,248 +967,40 @@ typedef struct { /* main state */ - VGMSTREAMCHANNEL * ch; /* array of channels */ - VGMSTREAMCHANNEL * start_ch; /* shallow copy of channels as they were at the beginning of the stream (for resets) */ - VGMSTREAMCHANNEL * loop_ch; /* shallow copy of channels as they were at the loop point (for loops) */ + VGMSTREAMCHANNEL* ch; /* array of channels */ + VGMSTREAMCHANNEL* start_ch; /* shallow copy of channels as they were at the beginning of the stream (for resets) */ + VGMSTREAMCHANNEL* loop_ch; /* shallow copy of channels as they were at the loop point (for loops) */ void* start_vgmstream; /* shallow copy of the VGMSTREAM as it was at the beginning of the stream (for resets) */ - void * mixing_data; /* state for mixing effects */ + void* mixing_data; /* state for mixing effects */ /* Optional data the codec needs for the whole stream. This is for codecs too * different from vgmstream's structure to be reasonably shoehorned. * Note also that support must be added for resetting, looping and * closing for every codec that uses this, as it will not be handled. */ - void * codec_data; + void* codec_data; /* Same, for special layouts. layout_data + codec_data may exist at the same time. */ - void * layout_data; + void* layout_data; + + + /* play config/state */ + int config_enabled; /* config can be used */ + play_config_t config; /* player config (applied over decoding) */ + play_state_t pstate; /* player state (applied over decoding) */ + int loop_count; /* counter of complete loops (1=looped once) */ + int loop_target; /* max loops before continuing with the stream end (loops forever if not set) */ + sample_t* tmpbuf; /* garbage buffer used for seeking/trimming */ + size_t tmpbuf_size; /* for all channels (samples = tmpbuf_size / channels) */ } VGMSTREAM; -#ifdef VGM_USE_VORBIS - -/* standard Ogg Vorbis */ -typedef struct { - STREAMFILE *streamfile; - 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 */ - - /* 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 xor_value; - -} ogg_vorbis_io; - -typedef struct ogg_vorbis_codec_data ogg_vorbis_codec_data; - - -/* custom Vorbis modes */ -typedef enum { - VORBIS_FSB, /* FMOD FSB: simplified/external setup packets, custom packet headers */ - VORBIS_WWISE, /* Wwise WEM: many variations (custom setup, headers and data) */ - VORBIS_OGL, /* Shin'en OGL: custom packet headers */ - VORBIS_SK, /* Silicon Knights AUD: "OggS" replaced by "SK" */ - VORBIS_VID1, /* Neversoft VID1: custom packet blocks/headers */ -} vorbis_custom_t; - -/* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */ -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 */ - int channels; - int sample_rate; - int blocksize_0_exp; - int blocksize_1_exp; - - uint32_t setup_id; /* external setup */ - int big_endian; /* flag */ - - /* Wwise Vorbis config */ - wwise_setup_t setup_type; - wwise_header_t header_type; - wwise_packet_t packet_type; - - /* output (kinda ugly here but to simplify) */ - off_t data_start_offset; - -} vorbis_custom_config; - -/* custom Vorbis without Ogg layer */ -typedef struct { - vorbis_info vi; /* stream settings */ - vorbis_comment vc; /* stream comments */ - vorbis_dsp_state vd; /* decoder global state */ - vorbis_block vb; /* decoder local state */ - ogg_packet op; /* fake packet for internal use */ - - uint8_t * buffer; /* internal raw data buffer */ - size_t buffer_size; - - size_t samples_to_discard; /* for looping purposes */ - int samples_full; /* flag, samples available in vorbis buffers */ - - vorbis_custom_t type; /* Vorbis subtype */ - vorbis_custom_config config; /* config depending on the mode */ - - /* Wwise Vorbis: saved data to reconstruct modified packets */ - uint8_t mode_blockflag[64+1]; /* max 6b+1; flags 'n stuff */ - int mode_bits; /* bits to store mode_number */ - uint8_t prev_blockflag; /* blockflag in the last decoded packet */ - /* Ogg-style Vorbis: packet within a page */ - int current_packet; - /* reference for page/blocks */ - off_t block_offset; - size_t block_size; - - int prev_block_samples; /* count for optimization */ - -} vorbis_custom_codec_data; -#endif - - -#ifdef VGM_USE_MPEG -/* Custom MPEG modes, mostly differing in the data layout */ -typedef enum { - MPEG_STANDARD, /* 1 stream */ - MPEG_AHX, /* 1 stream with false frame headers */ - MPEG_XVAG, /* N streams of fixed interleave (frame-aligned, several data-frames of fixed size) */ - MPEG_FSB, /* N streams of 1 data-frame+padding (=interleave) */ - MPEG_P3D, /* N streams of fixed interleave (not frame-aligned) */ - MPEG_SCD, /* N streams of fixed interleave (not frame-aligned) */ - MPEG_EA, /* 1 stream (maybe N streams in absolute offsets?) */ - MPEG_EAL31, /* EALayer3 v1 (SCHl), custom frames with v1 header */ - MPEG_EAL31b, /* EALayer3 v1 (SNS), custom frames with v1 header + minor changes */ - MPEG_EAL32P, /* EALayer3 v2 "PCM", custom frames with v2 header + bigger PCM blocks? */ - MPEG_EAL32S, /* EALayer3 v2 "Spike", custom frames with v2 header + smaller PCM blocks? */ - MPEG_LYN, /* N streams of fixed interleave */ - MPEG_AWC, /* N streams in block layout (music) or absolute offsets (sfx) */ - MPEG_EAMP3 /* custom frame header + MPEG frame + PCM blocks */ -} mpeg_custom_t; - -/* config for the above modes */ -typedef struct { - int channels; /* max channels */ - int fsb_padding; /* fsb padding mode */ - int chunk_size; /* size of a data portion */ - int data_size; /* playable size */ - int interleave; /* size of stream interleave */ - int encryption; /* encryption mode */ - int big_endian; - int skip_samples; - /* for AHX */ - int cri_type; - uint16_t cri_key1; - uint16_t cri_key2; - uint16_t cri_key3; -} mpeg_custom_config; - -/* represents a single MPEG stream */ -typedef struct { - /* per stream as sometimes mpg123 must be fed in passes if data is big enough (ex. EALayer3 multichannel) */ - uint8_t *buffer; /* raw data buffer */ - size_t buffer_size; - size_t bytes_in_buffer; - int buffer_full; /* raw buffer has been filled */ - int buffer_used; /* raw buffer has been fed to the decoder */ - mpg123_handle *m; /* MPEG decoder */ - - uint8_t *output_buffer; /* decoded samples from this stream (in bytes for mpg123) */ - size_t output_buffer_size; - size_t samples_filled; /* data in the buffer (in samples) */ - size_t samples_used; /* data extracted from the buffer */ - - size_t current_size_count; /* data read (if the parser needs to know) */ - size_t current_size_target; /* max data, until something happens */ - size_t decode_to_discard; /* discard from this stream only (for EALayer3 or AWC) */ - - int channels_per_frame; /* for rare cases that streams don't share this */ -} mpeg_custom_stream; - -typedef struct { - /* regular/single MPEG internals */ - uint8_t *buffer; /* raw data buffer */ - size_t buffer_size; - size_t bytes_in_buffer; - int buffer_full; /* raw buffer has been filled */ - int buffer_used; /* raw buffer has been fed to the decoder */ - mpg123_handle *m; /* MPEG decoder */ - struct mpg123_frameinfo mi; /* start info, so it's available even when resetting */ - - /* for internal use */ - int channels_per_frame; - int samples_per_frame; - /* for some calcs */ - int bitrate_per_frame; - int sample_rate_per_frame; - - /* custom MPEG internals */ - int custom; /* flag */ - mpeg_custom_t type; /* mpeg subtype */ - mpeg_custom_config config; /* config depending on the mode */ - - size_t default_buffer_size; - mpeg_custom_stream **streams; /* array of MPEG streams (ex. 2ch+2ch) */ - size_t streams_size; - - size_t skip_samples; /* base encoder delay */ - size_t samples_to_discard; /* for custom mpeg looping */ - -} mpeg_codec_data; -#endif - -#ifdef VGM_USE_G7221 -typedef struct g7221_codec_data g7221_codec_data; -#endif - -#ifdef VGM_USE_G719 -typedef struct { - sample_t buffer[960]; - void *handle; -} g719_codec_data; -#endif - -#ifdef VGM_USE_MAIATRAC3PLUS -typedef struct { - sample_t *buffer; - int channels; - int samples_discard; - void *handle; -} maiatrac3plus_codec_data; -#endif - -#ifdef VGM_USE_ATRAC9 -/* ATRAC9 config */ -typedef struct { - int channels; /* to detect weird multichannel */ - uint32_t config_data; /* ATRAC9 config header */ - int encoder_delay; /* initial samples to discard */ -} atrac9_config; -typedef struct atrac9_codec_data atrac9_codec_data; -#endif - -#ifdef VGM_USE_CELT -typedef enum { CELT_0_06_1,CELT_0_11_0} celt_lib_t; -typedef struct celt_codec_data celt_codec_data; -#endif - -/* libacm interface */ -typedef struct { - STREAMFILE *streamfile; - void *handle; - void *io_config; -} acm_codec_data; /* for files made of "continuous" segments, one per section of a song (using a complete sub-VGMSTREAM) */ typedef struct { int segment_count; - VGMSTREAM **segments; + VGMSTREAM** segments; int current_segment; - sample_t *buffer; + sample_t* buffer; int input_channels; /* internal buffer channels */ int output_channels; /* resulting channels (after mixing, if applied) */ } segmented_layout_data; @@ -1157,39 +1008,27 @@ typedef struct { /* for files made of "parallel" layers, one per group of channels (using a complete sub-VGMSTREAM) */ typedef struct { int layer_count; - VGMSTREAM **layers; - sample_t *buffer; + VGMSTREAM** layers; + sample_t* buffer; int input_channels; /* internal buffer channels */ int output_channels; /* resulting channels (after mixing, if applied) */ + int external_looping; /* don't loop using per-layer loops, but layout's own looping */ } layered_layout_data; -/* for compressed NWA */ + + +/* libacm interface */ typedef struct { - NWAData *nwa; -} nwa_codec_data; - -typedef struct relic_codec_data relic_codec_data; - -typedef struct { - STREAMFILE *streamfile; - clHCA_stInfo info; - - signed short *sample_buffer; - size_t samples_filled; - size_t samples_consumed; - size_t samples_to_discard; - - void* data_buffer; - - unsigned int current_block; - + STREAMFILE* streamfile; void* handle; -} hca_codec_data; + void* io_config; +} acm_codec_data; + #ifdef VGM_USE_FFMPEG typedef struct { /*** IO internals ***/ - STREAMFILE *streamfile; + STREAMFILE* streamfile; uint64_t start; // absolute start within the streamfile uint64_t offset; // absolute offset within the streamfile @@ -1245,7 +1084,7 @@ typedef struct { #ifdef VGM_USE_MP4V2 typedef struct { - STREAMFILE *streamfile; + STREAMFILE* streamfile; uint64_t start; uint64_t offset; uint64_t size; @@ -1263,87 +1102,78 @@ typedef struct { INT_PCM sample_buffer[( (6) * (2048)*4 )]; } mp4_aac_codec_data; #endif -#endif +#endif //VGM_USE_MP4V2 -typedef struct ubi_adpcm_codec_data ubi_adpcm_codec_data; - -typedef struct ea_mt_codec_data ea_mt_codec_data; - - -#if 0 -//possible future public/opaque API - -/* define standard C param call and name mangling (to avoid __stdcall / .defs) */ -#define VGMSTREAM_CALL __cdecl //needed? - -/* define external function types (during compilation) */ -#if defined(VGMSTREAM_EXPORT) - #define VGMSTREAM_API __declspec(dllexport) /* when exporting/creating vgmstream DLL */ -#elif defined(VGMSTREAM_IMPORT) - #define VGMSTREAM_API __declspec(dllimport) /* when importing/linking vgmstream DLL */ -#else - #define VGMSTREAM_API /* nothing, internal/default */ -#endif - -//VGMSTREAM_API void VGMSTREAM_CALL vgmstream_function(void); - -//info for opaque VGMSTREAM +// VGMStream description in structure format typedef struct { - const int channels; - const int sample_rate; - const int num_samples; - const int loop_start_sample; - const int loop_end_sample; - const int loop_flag; - const int num_streams; - const int current_sample; - const int average_bitrate; -} VGMSTREAM_INFO; -void vgmstream_get_info(VGMSTREAM* vgmstream, VGMSTREAM_INFO *vgmstream_info); - -//or maybe -enum vgmstream_value_t { VGMSTREAM_CHANNELS, VGMSTREAM_CURRENT_SAMPLE, ... }; -int vgmstream_get_info(VGMSTREAM* vgmstream, vgmstream_value_t type); -// or -int vgmstream_get_current_sample(VGMSTREAM* vgmstream); - -#endif + int sample_rate; + int channels; + struct mixing_info { + int input_channels; + int output_channels; + } mixing_info; + int channel_layout; + struct loop_info { + int start; + int end; + } loop_info; + size_t num_samples; + char encoding[128]; + char layout[128]; + struct interleave_info { + int value; + int first_block; + int last_block; + } interleave_info; + int frame_size; + char metadata[128]; + int bitrate; + struct stream_info { + int current; + int total; + char name[128]; + } stream_info; +} vgmstream_info; /* -------------------------------------------------------------------------*/ /* vgmstream "public" API */ /* -------------------------------------------------------------------------*/ /* do format detection, return pointer to a usable VGMSTREAM, or NULL on failure */ -VGMSTREAM * init_vgmstream(const char * const filename); +VGMSTREAM* init_vgmstream(const char* const filename); /* init with custom IO via streamfile */ -VGMSTREAM * init_vgmstream_from_STREAMFILE(STREAMFILE *streamFile); +VGMSTREAM* init_vgmstream_from_STREAMFILE(STREAMFILE* sf); /* reset a VGMSTREAM to start of stream */ -void reset_vgmstream(VGMSTREAM * vgmstream); +void reset_vgmstream(VGMSTREAM* vgmstream); /* close an open vgmstream */ -void close_vgmstream(VGMSTREAM * vgmstream); +void close_vgmstream(VGMSTREAM* vgmstream); /* calculate the number of samples to be played based on looping parameters */ -int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM * vgmstream); +int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM* vgmstream); -/* Decode data into sample buffer */ -void render_vgmstream(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +/* Decode data into sample buffer. Returns < sample_count on stream end */ +int render_vgmstream(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); + +/* Seek to sample position (next render starts from that point). Use only after config is set (vgmstream_apply_config) */ +void seek_vgmstream(VGMSTREAM* vgmstream, int32_t seek_sample); /* Write a description of the stream into array pointed by desc, which must be length bytes long. * Will always be null-terminated if length > 0 */ -void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length); +void describe_vgmstream(VGMSTREAM* vgmstream, char* desc, int length); +void describe_vgmstream_info(VGMSTREAM* vgmstream, vgmstream_info* desc); /* Return the average bitrate in bps of all unique files contained within this stream. */ -int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream); +int get_vgmstream_average_bitrate(VGMSTREAM* vgmstream); /* List supported formats and return elements in the list, for plugins that need to know. * The list disables some common formats that may conflict (.wav, .ogg, etc). */ -const char ** vgmstream_get_formats(size_t * size); +const char** vgmstream_get_formats(size_t* size); /* same, but for common-but-disabled formats in the above list. */ -const char ** vgmstream_get_common_formats(size_t * size); +const char** vgmstream_get_common_formats(size_t* size); /* Force enable/disable internal looping. Should be done before playing anything (or after reset), * and not all codecs support arbitrary loop values ATM. */ @@ -1360,37 +1190,20 @@ int vgmstream_is_virtual_filename(const char* filename); /* -------------------------------------------------------------------------*/ /* Allocate initial memory for the VGMSTREAM */ -VGMSTREAM * allocate_vgmstream(int channel_count, int looped); +VGMSTREAM* allocate_vgmstream(int channel_count, int looped); /* Prepare the VGMSTREAM's initial state once parsed and ready, but before playing. */ -void setup_vgmstream(VGMSTREAM * vgmstream); - -/* Get the number of samples of a single frame (smallest self-contained sample group, 1/N channels) */ -int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream); -/* Get the number of bytes of a single frame (smallest self-contained byte group, 1/N channels) */ -int get_vgmstream_frame_size(VGMSTREAM * vgmstream); -/* In NDS IMA the frame size is the block size, so the last one is short */ -int get_vgmstream_samples_per_shortframe(VGMSTREAM * vgmstream); -int get_vgmstream_shortframe_size(VGMSTREAM * vgmstream); - -/* Decode samples into the buffer. Assume that we have written samples_written into the - * buffer already, and we have samples_to_do consecutive samples ahead of us. */ -void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to_do, sample_t * buffer); - -/* Calculate number of consecutive samples to do (taking into account stopping for loop start and end) */ -int vgmstream_samples_to_do(int samples_this_block, int samples_per_frame, VGMSTREAM * vgmstream); - -/* Detect loop start and save values, or detect loop end and restore (loop back). Returns 1 if loop was done. */ -int vgmstream_do_loop(VGMSTREAM * vgmstream); +void setup_vgmstream(VGMSTREAM* vgmstream); /* Open the stream for reading at offset (taking into account layouts, channels and so on). * Returns 0 on failure */ -int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t start_offset); -int vgmstream_open_stream_bf(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t start_offset, int force_multibuffer); +int vgmstream_open_stream(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t start_offset); +int vgmstream_open_stream_bf(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t start_offset, int force_multibuffer); /* Get description info */ -void get_vgmstream_coding_description(VGMSTREAM *vgmstream, char *out, size_t out_size); -void get_vgmstream_layout_description(VGMSTREAM *vgmstream, char *out, size_t out_size); -void get_vgmstream_meta_description(VGMSTREAM *vgmstream, char *out, size_t out_size); +void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size); +void get_vgmstream_layout_description(VGMSTREAM* vgmstream, char* out, size_t out_size); +void get_vgmstream_meta_description(VGMSTREAM* vgmstream, char* out, size_t out_size); +void setup_state_vgmstream(VGMSTREAM* vgmstream); #endif diff --git a/Frameworks/vio2sf/vio2sf.xcodeproj/project.pbxproj b/Frameworks/vio2sf/vio2sf.xcodeproj/project.pbxproj index e5948b1c4..34427c58c 100644 --- a/Frameworks/vio2sf/vio2sf.xcodeproj/project.pbxproj +++ b/Frameworks/vio2sf/vio2sf.xcodeproj/project.pbxproj @@ -286,8 +286,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 83DE0C05180A9BD400269051 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -407,7 +407,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -452,7 +452,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; }; name = Release; @@ -460,7 +460,10 @@ 83DE0C2F180A9BD400269051 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -469,6 +472,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.vio2sf; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; @@ -478,7 +482,10 @@ 83DE0C30180A9BD400269051 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; @@ -487,6 +494,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.vio2sf; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = framework; diff --git a/Frameworks/vio2sf/vio2sf/src/vio2sf/desmume/SPU.h b/Frameworks/vio2sf/vio2sf/src/vio2sf/desmume/SPU.h index beb272509..6e1620b72 100644 --- a/Frameworks/vio2sf/vio2sf/src/vio2sf/desmume/SPU.h +++ b/Frameworks/vio2sf/vio2sf/src/vio2sf/desmume/SPU.h @@ -187,13 +187,21 @@ struct SPU_struct void ShutUp(); }; +#ifdef __cplusplus +extern "C" { +#endif + SoundInterface_struct *SPU_SoundCore(NDS_state *); void SPU_Pause(NDS_state *, int pause); void SPU_SetVolume(NDS_state *, int volume); void SPU_KeyOn(NDS_state *, int channel); void SPU_Emulate_core(NDS_state *); -void SPU_Emulate_user(NDS_state *, bool mix = true); +void SPU_Emulate_user(NDS_state *, BOOL mix = true); + +#ifdef __cplusplus +} +#endif class WavWriter { @@ -207,10 +215,17 @@ private: FILE *spufp; }; +#ifdef __cplusplus +extern "C" { +#endif void WAV_End(NDS_state *); bool WAV_Begin(NDS_state *, const char* fname); bool WAV_IsRecording(NDS_state *); void WAV_WavSoundUpdate(NDS_state *, void* soundData, int numSamples); +#ifdef __cplusplus +} +#endif + #endif diff --git a/Frameworks/vio2sf/vio2sf/src/vio2sf/desmume/types.h b/Frameworks/vio2sf/vio2sf/src/vio2sf/desmume/types.h index f274e50de..1e8c2e63d 100644 --- a/Frameworks/vio2sf/vio2sf/src/vio2sf/desmume/types.h +++ b/Frameworks/vio2sf/vio2sf/src/vio2sf/desmume/types.h @@ -100,8 +100,13 @@ typedef s16 v10; #ifdef WIN32 typedef int BOOL; #else +#ifdef __aarch64__ +#include +typedef bool BOOL; +#else typedef signed char BOOL; #endif +#endif #else //apple also defines BOOL typedef int desmume_BOOL; diff --git a/Icons/icon_blank.icns b/Icons/icon_blank.icns index 7f6a5d7da..d40acd560 100644 Binary files a/Icons/icon_blank.icns and b/Icons/icon_blank.icns differ diff --git a/Icons/icon_main.icns b/Icons/icon_main.icns index 39ca09bdf..9c173e350 100644 Binary files a/Icons/icon_main.icns and b/Icons/icon_main.icns differ diff --git a/Images/pauseDockBadge.png b/Images/pauseDockBadge.png index b82b4033d..fedaf65ff 100644 Binary files a/Images/pauseDockBadge.png and b/Images/pauseDockBadge.png differ diff --git a/Images/pauseDockBadgeColorful.png b/Images/pauseDockBadgeColorful.png index 530575142..af99fcff7 100644 Binary files a/Images/pauseDockBadgeColorful.png and b/Images/pauseDockBadgeColorful.png differ diff --git a/Images/playDockBadge.png b/Images/playDockBadge.png index 99382d53a..fb400f664 100644 Binary files a/Images/playDockBadge.png and b/Images/playDockBadge.png differ diff --git a/Images/playDockBadgeColorful.png b/Images/playDockBadgeColorful.png index 782c79733..3d2d8f527 100644 Binary files a/Images/playDockBadgeColorful.png and b/Images/playDockBadgeColorful.png differ diff --git a/Images/stopDockBadge.png b/Images/stopDockBadge.png index 7c1239708..729323aa6 100644 Binary files a/Images/stopDockBadge.png and b/Images/stopDockBadge.png differ diff --git a/Images/stopDockBadgeColorful.png b/Images/stopDockBadgeColorful.png index bb90ff3ff..812b0a10c 100644 Binary files a/Images/stopDockBadgeColorful.png and b/Images/stopDockBadgeColorful.png differ diff --git a/Info.plist b/Info.plist index e8bcc7a78..862ac0861 100644 --- a/Info.plist +++ b/Info.plist @@ -649,7 +649,7 @@ CFBundleTypeIconFile ape.icns CFBundleTypeName - Monkey's Audio File + Monkey's Audio File CFBundleTypeRole Viewer LSTypeIsPackage @@ -1002,8 +1002,6 @@ Cog.help CFBundleHelpBookName org.cogx.cog.help - CFBundleIconFile - icon_main CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion @@ -1020,35 +1018,36 @@ public.app-category.music LSMinimumSystemVersion 10.8.0 + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSAppleScriptEnabled YES + NSCalendarsUsageDescription + Cog has no use for your calendar information. Why are you trying to open your Calendar with an audio player? + NSCameraUsageDescription + Cog is an audio player. It will never use your camera. Why is it asking for permission to use your camera? + NSContactsUsageDescription + Cog has no use for your contacts information. Why are you trying to open your contacts with an audio player? + NSLocationUsageDescription + Cog has no use for your location information. Something is obviously wrong with the application. NSMainNibFile MainMenu + NSMicrophoneUsageDescription + Cog is an audio player. It does not, however, record audio. Why is it asking for permission to use your microphone? + NSPhotoLibraryUsageDescription + Cog is an audio player. Why are you trying to access your Photos Library with an audio player? NSPrincipalClass MediaKeysApplication - NSAppTransportSecurity - - NSAllowsArbitraryLoads - + NSRemindersUsageDescription + Cog has no use for your reminders. Why are you trying to access them with an audio player? SUFeedURL https://f.losno.co/cog/mercury.xml SUPublicDSAKeyFile dsa_pub.pem SUPublicEDKey omxG7Rp0XK9/YEvKbVy7cd44eVAh1LJB6CmjQwjOJz4= - NSLocationUsageDescription - Cog has no use for your location information. Something is obviously wrong with the application. - NSCalendarsUsageDescription - Cog has no use for your calendar information. Why are you trying to open your Calendar with an audio player? - NSContactsUsageDescription - Cog has no use for your contacts information. Why are you trying to open your contacts with an audio player? - NSRemindersUsageDescription - Cog has no use for your reminders. Why are you trying to access them with an audio player? - NSPhotoLibraryUsageDescription - Cog is an audio player. Why are you trying to access your Photos Library with an audio player? - NSCameraUsageDescription - Cog is an audio player. It will never use your camera. Why is it asking for permission to use your camera? - NSMicrophoneUsageDescription - Cog is an audio player. It does not, however, record audio. Why is it asking for permission to use your microphone? diff --git a/Plugins/APL/APL.xcodeproj/project.pbxproj b/Plugins/APL/APL.xcodeproj/project.pbxproj index 833a26f2c..c5af62cb8 100644 --- a/Plugins/APL/APL.xcodeproj/project.pbxproj +++ b/Plugins/APL/APL.xcodeproj/project.pbxproj @@ -145,8 +145,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 99B989F30CC7E10400C256E9 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -224,7 +224,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYMROOT = ../../build; @@ -263,7 +263,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SYMROOT = ../../build; }; @@ -272,8 +272,11 @@ 99B989F80CC7E10500C256E9 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_DYNAMIC_NO_PIC = NO; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; @@ -289,6 +292,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.vasfed.cog.apl; PRODUCT_NAME = APL; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -299,8 +303,11 @@ 99B989F90CC7E10500C256E9 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; @@ -314,6 +321,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.vasfed.cog.apl; PRODUCT_NAME = APL; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; diff --git a/Plugins/AdPlug/AdPlug.xcodeproj/project.pbxproj b/Plugins/AdPlug/AdPlug.xcodeproj/project.pbxproj index 777b055dd..d1a8d535a 100644 --- a/Plugins/AdPlug/AdPlug.xcodeproj/project.pbxproj +++ b/Plugins/AdPlug/AdPlug.xcodeproj/project.pbxproj @@ -168,7 +168,7 @@ TargetAttributes = { 83D3C5F2201C674D005564CB = { CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Manual; + ProvisioningStyle = Automatic; }; }; }; @@ -290,7 +290,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -341,7 +341,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; }; @@ -350,10 +350,10 @@ 83D3C5FA201C674D005564CB /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 4S876G9VCD; HEADER_SEARCH_PATHS = ( "$(SRCROOT)/../../Frameworks/libbinio/libbinio/libbinio/src", "$(SRCROOT)/../../Frameworks/libbinio/libbinio", @@ -372,10 +372,10 @@ 83D3C5FB201C674D005564CB /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 4S876G9VCD; HEADER_SEARCH_PATHS = ( "$(SRCROOT)/../../Frameworks/libbinio/libbinio/libbinio/src", "$(SRCROOT)/../../Frameworks/libbinio/libbinio", diff --git a/Plugins/ArchiveSource/ArchiveSource.xcodeproj/project.pbxproj b/Plugins/ArchiveSource/ArchiveSource.xcodeproj/project.pbxproj index d831144fe..4ea7a7947 100644 --- a/Plugins/ArchiveSource/ArchiveSource.xcodeproj/project.pbxproj +++ b/Plugins/ArchiveSource/ArchiveSource.xcodeproj/project.pbxproj @@ -178,8 +178,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 8359FF1617FEF35C0060F3ED = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -307,7 +307,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -353,7 +353,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; }; name = Release; @@ -361,13 +361,17 @@ 8359FF2A17FEF35C0060F3ED /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "ArchiveSource/ArchiveSource-Prefix.pch"; INFOPLIST_FILE = "ArchiveSource/ArchiveSource-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.archivesource; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -377,13 +381,17 @@ 8359FF2B17FEF35C0060F3ED /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "ArchiveSource/ArchiveSource-Prefix.pch"; INFOPLIST_FILE = "ArchiveSource/ArchiveSource-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.archivesource; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; diff --git a/Plugins/CoreAudio/CoreAudio.xcodeproj/project.pbxproj b/Plugins/CoreAudio/CoreAudio.xcodeproj/project.pbxproj index 9e60376a0..149299fe4 100644 --- a/Plugins/CoreAudio/CoreAudio.xcodeproj/project.pbxproj +++ b/Plugins/CoreAudio/CoreAudio.xcodeproj/project.pbxproj @@ -149,8 +149,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8D5B49AC048680CD000E48DA = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -196,8 +196,11 @@ 1DEB913B08733D840010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_DYNAMIC_NO_PIC = NO; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; @@ -207,6 +210,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.coreaudio; PRODUCT_NAME = CoreAudio; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -217,7 +221,10 @@ 1DEB913C08733D840010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = CoreAudio_Prefix.pch; @@ -225,6 +232,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.coreaudio; PRODUCT_NAME = CoreAudio; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -265,7 +273,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYMROOT = ../../build; @@ -304,7 +312,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SYMROOT = ../../build; }; diff --git a/Plugins/CueSheet/CueSheet.xcodeproj/project.pbxproj b/Plugins/CueSheet/CueSheet.xcodeproj/project.pbxproj index 75fa4a1b3..f739c3fe2 100644 --- a/Plugins/CueSheet/CueSheet.xcodeproj/project.pbxproj +++ b/Plugins/CueSheet/CueSheet.xcodeproj/project.pbxproj @@ -165,8 +165,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8D5B49AC048680CD000E48DA = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -230,8 +230,11 @@ 1DEB913B08733D840010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_DYNAMIC_NO_PIC = NO; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; @@ -241,6 +244,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.cuesheet; PRODUCT_NAME = CueSheet; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; SYMROOT = ../../build; @@ -252,7 +256,10 @@ 1DEB913C08733D840010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = CueSheet_Prefix.pch; @@ -260,6 +267,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.cuesheet; PRODUCT_NAME = CueSheet; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; SYMROOT = ../../build; @@ -301,7 +309,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYMROOT = ../../build; @@ -340,7 +348,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SYMROOT = ../../build; }; diff --git a/Plugins/FFMPEG/FFMPEG.xcodeproj/project.pbxproj b/Plugins/FFMPEG/FFMPEG.xcodeproj/project.pbxproj index 635b91e51..5615c48f7 100644 --- a/Plugins/FFMPEG/FFMPEG.xcodeproj/project.pbxproj +++ b/Plugins/FFMPEG/FFMPEG.xcodeproj/project.pbxproj @@ -7,9 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 8352D48B1CDDAEDD009D16AA /* libavcodec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8352D4881CDDAEDD009D16AA /* libavcodec.a */; }; - 8352D48C1CDDAEDD009D16AA /* libavformat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8352D4891CDDAEDD009D16AA /* libavformat.a */; }; - 8352D48D1CDDAEDD009D16AA /* libavutil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8352D48A1CDDAEDD009D16AA /* libavutil.a */; }; 8352D48F1CDDB023009D16AA /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8352D48E1CDDB023009D16AA /* CoreFoundation.framework */; }; 8352D4911CDDB02A009D16AA /* VideoDecodeAcceleration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8352D4901CDDB02A009D16AA /* VideoDecodeAcceleration.framework */; }; 8352D4931CDDB034009D16AA /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8352D4921CDDB034009D16AA /* QuartzCore.framework */; }; @@ -19,20 +16,31 @@ 8352D49B1CDDB8B2009D16AA /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8352D49A1CDDB8B2009D16AA /* VideoToolbox.framework */; }; 8352D49D1CDDB8C0009D16AA /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8352D49C1CDDB8C0009D16AA /* CoreMedia.framework */; }; 8352D49F1CDDB8D7009D16AA /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8352D49E1CDDB8D7009D16AA /* CoreVideo.framework */; }; - 83CD428C1F7878A0000F77BE /* libswresample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 83CD428B1F78789F000F77BE /* libswresample.a */; }; - 83D2F5892356B210007646ED /* libopus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D2F5882356B210007646ED /* libopus.a */; }; + 83D11A05256CE02200565AD3 /* libavformat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D11A04256CE02200565AD3 /* libavformat.a */; }; + 83D11A09256CE04400565AD3 /* libavutil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D11A06256CE04400565AD3 /* libavutil.a */; }; + 83D11A0A256CE04400565AD3 /* libavcodec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D11A07256CE04400565AD3 /* libavcodec.a */; }; + 83D11A0B256CE04400565AD3 /* libswresample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 83D11A08256CE04400565AD3 /* libswresample.a */; }; 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; }; B09E942F0D747F410064F138 /* FFMPEGDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = B09E942E0D747F410064F138 /* FFMPEGDecoder.m */; }; /* End PBXBuildFile section */ +/* Begin PBXCopyFilesBuildPhase section */ + 83EC715524A986F200B807C1 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 6; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 32DBCF630370AF2F00C91783 /* FFMPEG_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FFMPEG_Prefix.pch; sourceTree = ""; }; - 8352D4881CDDAEDD009D16AA /* libavcodec.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavcodec.a; path = ../../ThirdParty/ffmpeg/lib/libavcodec.a; sourceTree = ""; }; - 8352D4891CDDAEDD009D16AA /* libavformat.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavformat.a; path = ../../ThirdParty/ffmpeg/lib/libavformat.a; sourceTree = ""; }; - 8352D48A1CDDAEDD009D16AA /* libavutil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavutil.a; path = ../../ThirdParty/ffmpeg/lib/libavutil.a; sourceTree = ""; }; 8352D48E1CDDB023009D16AA /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; 8352D4901CDDB02A009D16AA /* VideoDecodeAcceleration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoDecodeAcceleration.framework; path = System/Library/Frameworks/VideoDecodeAcceleration.framework; sourceTree = SDKROOT; }; 8352D4921CDDB034009D16AA /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; @@ -43,9 +51,10 @@ 8352D49C1CDDB8C0009D16AA /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; 8352D49E1CDDB8D7009D16AA /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; 8384913818081F6C00E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = ""; }; - 83CD428B1F78789F000F77BE /* libswresample.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libswresample.a; path = ../../ThirdParty/ffmpeg/lib/libswresample.a; sourceTree = ""; }; - 83D2F5862356B1BE007646ED /* libopus.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopus.a; path = ../../../../../../../usr/local/Cellar/opus/1.3.1/lib/libopus.a; sourceTree = ""; }; - 83D2F5882356B210007646ED /* libopus.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libopus.a; path = ../../ThirdParty/ffmpeg/lib/libopus.a; sourceTree = ""; }; + 83D11A04256CE02200565AD3 /* libavformat.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavformat.a; path = ../../ThirdParty/ffmpeg/lib/libavformat.a; sourceTree = ""; }; + 83D11A06256CE04400565AD3 /* libavutil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavutil.a; path = ../../ThirdParty/ffmpeg/lib/libavutil.a; sourceTree = ""; }; + 83D11A07256CE04400565AD3 /* libavcodec.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libavcodec.a; path = ../../ThirdParty/ffmpeg/lib/libavcodec.a; sourceTree = ""; }; + 83D11A08256CE04400565AD3 /* libswresample.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libswresample.a; path = ../../ThirdParty/ffmpeg/lib/libswresample.a; sourceTree = ""; }; 8D5B49B6048680CD000E48DA /* FFMPEG.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FFMPEG.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B09E942D0D747F410064F138 /* FFMPEGDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FFMPEGDecoder.h; sourceTree = ""; }; @@ -62,18 +71,17 @@ files = ( 8352D4991CDDB06A009D16AA /* libz.tbd in Frameworks */, 8352D4971CDDB05E009D16AA /* Security.framework in Frameworks */, + 83D11A05256CE02200565AD3 /* libavformat.a in Frameworks */, + 83D11A0A256CE04400565AD3 /* libavcodec.a in Frameworks */, 8352D4911CDDB02A009D16AA /* VideoDecodeAcceleration.framework in Frameworks */, 8352D49F1CDDB8D7009D16AA /* CoreVideo.framework in Frameworks */, 8352D4931CDDB034009D16AA /* QuartzCore.framework in Frameworks */, + 83D11A09256CE04400565AD3 /* libavutil.a in Frameworks */, 8352D49D1CDDB8C0009D16AA /* CoreMedia.framework in Frameworks */, 8352D49B1CDDB8B2009D16AA /* VideoToolbox.framework in Frameworks */, 8352D48F1CDDB023009D16AA /* CoreFoundation.framework in Frameworks */, 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */, - 83D2F5892356B210007646ED /* libopus.a in Frameworks */, - 8352D48C1CDDAEDD009D16AA /* libavformat.a in Frameworks */, - 8352D48D1CDDAEDD009D16AA /* libavutil.a in Frameworks */, - 8352D48B1CDDAEDD009D16AA /* libavcodec.a in Frameworks */, - 83CD428C1F7878A0000F77BE /* libswresample.a in Frameworks */, + 83D11A0B256CE04400565AD3 /* libswresample.a in Frameworks */, 8352D4951CDDB03E009D16AA /* libiconv.tbd in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -90,7 +98,6 @@ 089C1671FE841209C02AAC07 /* Frameworks and Libraries */, 19C28FB8FE9D52D311CA2CBB /* Products */, B09E95F50D74A3ED0064F138 /* Frameworks-Info.plist */, - 83D2F5852356B1BD007646ED /* Frameworks */, ); name = FFMPEG; sourceTree = ""; @@ -98,12 +105,12 @@ 089C1671FE841209C02AAC07 /* Frameworks and Libraries */ = { isa = PBXGroup; children = ( + 83D11A07256CE04400565AD3 /* libavcodec.a */, + 83D11A04256CE02200565AD3 /* libavformat.a */, + 83D11A06256CE04400565AD3 /* libavutil.a */, + 83D11A08256CE04400565AD3 /* libswresample.a */, 8352D4981CDDB06A009D16AA /* libz.tbd */, 8352D4941CDDB03E009D16AA /* libiconv.tbd */, - 8352D4881CDDAEDD009D16AA /* libavcodec.a */, - 8352D4891CDDAEDD009D16AA /* libavformat.a */, - 8352D48A1CDDAEDD009D16AA /* libavutil.a */, - 83CD428B1F78789F000F77BE /* libswresample.a */, 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */, 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */, ); @@ -170,15 +177,6 @@ name = "Other Sources"; sourceTree = ""; }; - 83D2F5852356B1BD007646ED /* Frameworks */ = { - isa = PBXGroup; - children = ( - 83D2F5882356B210007646ED /* libopus.a */, - 83D2F5862356B1BE007646ED /* libopus.a */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -189,6 +187,7 @@ 8D5B49AF048680CD000E48DA /* Resources */, 8D5B49B1048680CD000E48DA /* Sources */, 8D5B49B3048680CD000E48DA /* Frameworks */, + 83EC715524A986F200B807C1 /* CopyFiles */, ); buildRules = ( ); @@ -209,8 +208,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8D5B49AC048680CD000E48DA = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -256,8 +255,11 @@ 1DEB913B08733D840010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_DYNAMIC_NO_PIC = NO; GCC_MODEL_TUNING = G5; @@ -272,6 +274,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = org.cogx.ffmpeg; PRODUCT_NAME = FFMPEG; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; SYMROOT = ../../build; @@ -283,7 +286,10 @@ 1DEB913C08733D840010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -296,6 +302,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = org.cogx.ffmpeg; PRODUCT_NAME = FFMPEG; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; SYMROOT = ../../build; @@ -339,7 +346,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ../../ThirdParty/ffmpeg/include; LIBRARY_SEARCH_PATHS = ../../ThirdParty/ffmpeg/lib; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -379,7 +386,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ../../ThirdParty/ffmpeg/include; LIBRARY_SEARCH_PATHS = ../../ThirdParty/ffmpeg/lib; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SYMROOT = ../../build; }; diff --git a/Plugins/FileSource/FileSource.xcodeproj/project.pbxproj b/Plugins/FileSource/FileSource.xcodeproj/project.pbxproj index d0cff6c57..9907df628 100644 --- a/Plugins/FileSource/FileSource.xcodeproj/project.pbxproj +++ b/Plugins/FileSource/FileSource.xcodeproj/project.pbxproj @@ -184,8 +184,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8D5B49AC048680CD000E48DA = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -255,7 +255,10 @@ 1DEB913B08733D840010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_DYNAMIC_NO_PIC = NO; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -264,6 +267,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.filesource; PRODUCT_NAME = FileSource; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -274,7 +278,10 @@ 1DEB913C08733D840010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = FileSource_Prefix.pch; @@ -282,6 +289,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.filesource; PRODUCT_NAME = FileSource; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -323,7 +331,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYMROOT = ../../build; @@ -362,7 +370,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SYMROOT = ../../build; }; diff --git a/Plugins/Flac/Flac.xcodeproj/project.pbxproj b/Plugins/Flac/Flac.xcodeproj/project.pbxproj index 3f241eb53..9015c3597 100644 --- a/Plugins/Flac/Flac.xcodeproj/project.pbxproj +++ b/Plugins/Flac/Flac.xcodeproj/project.pbxproj @@ -186,8 +186,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8D5B49AC048680CD000E48DA = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -257,8 +257,11 @@ 1DEB913B08733D840010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)", @@ -273,6 +276,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.flac; PRODUCT_NAME = Flac; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -283,7 +287,10 @@ 1DEB913C08733D840010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)", @@ -296,6 +303,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.flac; PRODUCT_NAME = Flac; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -336,7 +344,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYMROOT = ../../build; @@ -375,7 +383,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SYMROOT = ../../build; }; diff --git a/Plugins/GME/GME.xcodeproj/project.pbxproj b/Plugins/GME/GME.xcodeproj/project.pbxproj index 1abd80abf..6cb1443b9 100644 --- a/Plugins/GME/GME.xcodeproj/project.pbxproj +++ b/Plugins/GME/GME.xcodeproj/project.pbxproj @@ -207,8 +207,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8D5B49AC048680CD000E48DA = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -295,8 +295,11 @@ 1DEB913B08733D840010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_DYNAMIC_NO_PIC = NO; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; @@ -306,6 +309,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.gme; PRODUCT_NAME = GME; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; SYMROOT = ../../build; @@ -317,7 +321,10 @@ 1DEB913C08733D840010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = GME_Prefix.pch; @@ -325,6 +332,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.gme; PRODUCT_NAME = GME; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; SYMROOT = ../../build; @@ -366,7 +374,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; OBJROOT = ../../build; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -406,7 +414,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; OBJROOT = ../../build; SDKROOT = macosx; SYMROOT = ../../build; diff --git a/Plugins/HTTPSource/HTTPSource.xcodeproj/project.pbxproj b/Plugins/HTTPSource/HTTPSource.xcodeproj/project.pbxproj index 5525d0d41..211a3d83d 100644 --- a/Plugins/HTTPSource/HTTPSource.xcodeproj/project.pbxproj +++ b/Plugins/HTTPSource/HTTPSource.xcodeproj/project.pbxproj @@ -154,8 +154,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8D5B49AC048680CD000E48DA = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -201,8 +201,11 @@ 1DEB913B08733D840010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; @@ -215,6 +218,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.httpsource; PRODUCT_NAME = HTTPSource; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -225,7 +229,10 @@ 1DEB913C08733D840010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_MODEL_TUNING = G5; @@ -236,6 +243,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.httpsource; PRODUCT_NAME = HTTPSource; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -276,7 +284,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYMROOT = ../../build; @@ -315,7 +323,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SYMROOT = ../../build; }; diff --git a/Plugins/HighlyComplete/HighlyComplete.xcodeproj/project.pbxproj b/Plugins/HighlyComplete/HighlyComplete.xcodeproj/project.pbxproj index 97556165b..9a0414469 100644 --- a/Plugins/HighlyComplete/HighlyComplete.xcodeproj/project.pbxproj +++ b/Plugins/HighlyComplete/HighlyComplete.xcodeproj/project.pbxproj @@ -408,8 +408,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 8360EEE317F92AC8005208A4 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -659,7 +659,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -704,7 +704,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; }; name = Release; @@ -712,7 +712,10 @@ 8360EEF717F92AC8005208A4 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "HighlyComplete/HighlyComplete-Prefix.pch"; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -726,6 +729,7 @@ INFOPLIST_FILE = "HighlyComplete/HighlyComplete-Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.highlycomplete; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -735,7 +739,10 @@ 8360EEF817F92AC8005208A4 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "HighlyComplete/HighlyComplete-Prefix.pch"; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -748,6 +755,7 @@ INFOPLIST_FILE = "HighlyComplete/HighlyComplete-Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.highlycomplete; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; diff --git a/Plugins/Hively/Hively.xcodeproj/project.pbxproj b/Plugins/Hively/Hively.xcodeproj/project.pbxproj index dcc91018e..d946f32c4 100644 --- a/Plugins/Hively/Hively.xcodeproj/project.pbxproj +++ b/Plugins/Hively/Hively.xcodeproj/project.pbxproj @@ -183,8 +183,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 836FB52C1820538700B3AD2D = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -314,7 +314,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -361,7 +361,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SKIP_INSTALL = YES; }; @@ -370,13 +370,17 @@ 836FB5401820538700B3AD2D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Hively/Hively-Prefix.pch"; INFOPLIST_FILE = "Hively/Hively-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.hively; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; WRAPPER_EXTENSION = bundle; }; @@ -385,13 +389,17 @@ 836FB5411820538700B3AD2D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Hively/Hively-Prefix.pch"; INFOPLIST_FILE = "Hively/Hively-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.hively; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; WRAPPER_EXTENSION = bundle; }; diff --git a/Plugins/M3u/M3u.xcodeproj/project.pbxproj b/Plugins/M3u/M3u.xcodeproj/project.pbxproj index 9a283652c..84370f3bb 100644 --- a/Plugins/M3u/M3u.xcodeproj/project.pbxproj +++ b/Plugins/M3u/M3u.xcodeproj/project.pbxproj @@ -145,8 +145,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8D5B49AC048680CD000E48DA = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -206,8 +206,11 @@ 1DEB913B08733D840010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_DYNAMIC_NO_PIC = NO; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; @@ -218,6 +221,7 @@ OBJROOT = ../../build; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.m3u; PRODUCT_NAME = M3u; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; SYMROOT = ../../build; @@ -229,7 +233,10 @@ 1DEB913C08733D840010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = M3u_Prefix.pch; @@ -238,6 +245,7 @@ OBJROOT = ../../build; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.m3u; PRODUCT_NAME = M3u; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; SYMROOT = ../../build; @@ -280,7 +288,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -318,7 +326,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; }; name = Release; diff --git a/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj b/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj index 6f62c3d00..6c2659457 100644 --- a/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj +++ b/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj @@ -366,8 +366,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 83B06686180D5668008E3612 = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -512,7 +512,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -558,7 +558,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; }; name = Release; @@ -566,7 +566,10 @@ 83B0669A180D5668008E3612 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "MIDI/MIDI-Prefix.pch"; HEADER_SEARCH_PATHS = ( @@ -581,6 +584,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = net.kode54.midi; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -590,7 +594,10 @@ 83B0669B180D5668008E3612 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "MIDI/MIDI-Prefix.pch"; HEADER_SEARCH_PATHS = ( @@ -605,6 +612,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = net.kode54.midi; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; diff --git a/Plugins/Musepack/Musepack.xcodeproj/project.pbxproj b/Plugins/Musepack/Musepack.xcodeproj/project.pbxproj index bd2d2149c..a98eb0baf 100644 --- a/Plugins/Musepack/Musepack.xcodeproj/project.pbxproj +++ b/Plugins/Musepack/Musepack.xcodeproj/project.pbxproj @@ -186,8 +186,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8D5B49AC048680CD000E48DA = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -257,8 +257,11 @@ 1DEB913B08733D840010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)", @@ -274,6 +277,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.musepack; PRODUCT_NAME = Musepack; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -284,7 +288,10 @@ 1DEB913C08733D840010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)", @@ -298,6 +305,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.musepack; PRODUCT_NAME = Musepack; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -338,7 +346,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYMROOT = ../../build; @@ -377,7 +385,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SYMROOT = ../../build; }; diff --git a/Plugins/OpenMPT/OpenMPT.xcodeproj/project.pbxproj b/Plugins/OpenMPT/OpenMPT.xcodeproj/project.pbxproj index d99025d4d..26663b547 100644 --- a/Plugins/OpenMPT/OpenMPT.xcodeproj/project.pbxproj +++ b/Plugins/OpenMPT/OpenMPT.xcodeproj/project.pbxproj @@ -162,7 +162,7 @@ TargetAttributes = { 83E5EFA21FFEF78100659F0F = { CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Manual; + ProvisioningStyle = Automatic; }; }; }; @@ -238,7 +238,7 @@ ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; @@ -282,7 +282,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -295,7 +295,7 @@ ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; @@ -333,7 +333,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; }; @@ -342,10 +342,10 @@ 83E5EFAA1FFEF78100659F0F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 4S876G9VCD; INFOPLIST_FILE = OpenMPT/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.OpenMPT; @@ -359,10 +359,10 @@ 83E5EFAB1FFEF78100659F0F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 4S876G9VCD; INFOPLIST_FILE = OpenMPT/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.OpenMPT; diff --git a/Plugins/Opus/Opus/OpusDecoder.m b/Plugins/Opus/Opus/OpusDecoder.m index 0208d97d2..71ef93572 100644 --- a/Plugins/Opus/Opus/OpusDecoder.m +++ b/Plugins/Opus/Opus/OpusDecoder.m @@ -151,7 +151,7 @@ opus_int64 sourceTell(void *_stream) + (NSArray *)fileTypes { - return [NSArray arrayWithObjects:@"opus",nil]; + return [NSArray arrayWithObjects:@"opus",@"ogg",nil]; } + (NSArray *)mimeTypes diff --git a/Plugins/Opus/OpusPlugin.xcodeproj/project.pbxproj b/Plugins/Opus/OpusPlugin.xcodeproj/project.pbxproj index dbe8389f9..e4e42e1c3 100644 --- a/Plugins/Opus/OpusPlugin.xcodeproj/project.pbxproj +++ b/Plugins/Opus/OpusPlugin.xcodeproj/project.pbxproj @@ -173,8 +173,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 8375B03B17FFEA400092A79F = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -303,7 +303,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -350,7 +350,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; }; name = Release; @@ -358,7 +358,10 @@ 8375B04F17FFEA400092A79F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Opus/OpusPlugin-Prefix.pch"; HEADER_SEARCH_PATHS = ( @@ -370,6 +373,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.opus; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -379,7 +383,10 @@ 8375B05017FFEA400092A79F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Opus/OpusPlugin-Prefix.pch"; HEADER_SEARCH_PATHS = ( @@ -391,6 +398,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = net.kode54.opus; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; diff --git a/Plugins/Pls/Pls.xcodeproj/project.pbxproj b/Plugins/Pls/Pls.xcodeproj/project.pbxproj index 3a0a48bb5..00e9534c5 100644 --- a/Plugins/Pls/Pls.xcodeproj/project.pbxproj +++ b/Plugins/Pls/Pls.xcodeproj/project.pbxproj @@ -145,8 +145,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8D5B49AC048680CD000E48DA = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -206,8 +206,11 @@ 1DEB913B08733D840010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_DYNAMIC_NO_PIC = NO; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; @@ -217,6 +220,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.pls; PRODUCT_NAME = Pls; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; SYMROOT = ../../build; @@ -228,7 +232,10 @@ 1DEB913C08733D840010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = Pls_Prefix.pch; @@ -236,6 +243,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.pls; PRODUCT_NAME = Pls; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; SYMROOT = ../../build; @@ -277,7 +285,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -315,7 +323,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; }; name = Release; diff --git a/Plugins/Shorten/Shorten.xcodeproj/project.pbxproj b/Plugins/Shorten/Shorten.xcodeproj/project.pbxproj index d510da9d4..370b003c2 100644 --- a/Plugins/Shorten/Shorten.xcodeproj/project.pbxproj +++ b/Plugins/Shorten/Shorten.xcodeproj/project.pbxproj @@ -184,8 +184,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8D5B49AC048680CD000E48DA = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -256,8 +256,11 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_CXX_LIBRARY = "libc++"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)", @@ -272,6 +275,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.shorten; PRODUCT_NAME = Shorten; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -283,7 +287,10 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_CXX_LIBRARY = "libc++"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)", @@ -296,6 +303,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.shorten; PRODUCT_NAME = Shorten; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -336,7 +344,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYMROOT = ../../build; @@ -375,7 +383,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SYMROOT = ../../build; }; diff --git a/Plugins/SilenceDecoder/SilenceDecoder.xcodeproj/project.pbxproj b/Plugins/SilenceDecoder/SilenceDecoder.xcodeproj/project.pbxproj index 492a68951..61740ca54 100644 --- a/Plugins/SilenceDecoder/SilenceDecoder.xcodeproj/project.pbxproj +++ b/Plugins/SilenceDecoder/SilenceDecoder.xcodeproj/project.pbxproj @@ -104,8 +104,8 @@ TargetAttributes = { 83F9D7E61A884B44007ABEC2 = { CreatedOnToolsVersion = 6.1.1; - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -196,7 +196,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -243,7 +243,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; }; @@ -252,11 +252,15 @@ 83F9D7EF1A884B44007ABEC2 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; INFOPLIST_FILE = SilenceDecoder/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = "org.cogx.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -266,11 +270,15 @@ 83F9D7F01A884B44007ABEC2 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; INFOPLIST_FILE = SilenceDecoder/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = "org.cogx.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; diff --git a/Plugins/Syntrax/Syntrax.xcodeproj/project.pbxproj b/Plugins/Syntrax/Syntrax.xcodeproj/project.pbxproj index f921ccd1d..31a2c023c 100644 --- a/Plugins/Syntrax/Syntrax.xcodeproj/project.pbxproj +++ b/Plugins/Syntrax/Syntrax.xcodeproj/project.pbxproj @@ -154,8 +154,8 @@ TargetAttributes = { 832FF2F71C96508E0076D662 = { CreatedOnToolsVersion = 7.2.1; - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -270,7 +270,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -316,7 +316,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; }; @@ -325,11 +325,15 @@ 832FF2FF1C96508E0076D662 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; INFOPLIST_FILE = Syntrax/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.Syntrax; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; }; @@ -338,11 +342,15 @@ 832FF3001C96508E0076D662 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; INFOPLIST_FILE = Syntrax/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.Syntrax; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; }; diff --git a/Plugins/TagLib/TagLib.xcodeproj/project.pbxproj b/Plugins/TagLib/TagLib.xcodeproj/project.pbxproj index 77cc6055d..03f7c8852 100644 --- a/Plugins/TagLib/TagLib.xcodeproj/project.pbxproj +++ b/Plugins/TagLib/TagLib.xcodeproj/project.pbxproj @@ -204,8 +204,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8D5B49AC048680CD000E48DA = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -277,8 +277,11 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_CXX_LIBRARY = "libc++"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; FRAMEWORK_SEARCH_PATHS = ""; GCC_DYNAMIC_NO_PIC = NO; GCC_MODEL_TUNING = G5; @@ -294,6 +297,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.taglib; PRODUCT_NAME = TagLib; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -305,7 +309,10 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_CXX_LIBRARY = "libc++"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; FRAMEWORK_SEARCH_PATHS = ""; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -319,6 +326,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.taglib; PRODUCT_NAME = TagLib; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -360,7 +368,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYMROOT = ../../build; @@ -400,7 +408,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SYMROOT = ../../build; }; diff --git a/Plugins/Vorbis/VorbisPlugin.xcodeproj/project.pbxproj b/Plugins/Vorbis/VorbisPlugin.xcodeproj/project.pbxproj index 8f446caba..5f0bf4cda 100644 --- a/Plugins/Vorbis/VorbisPlugin.xcodeproj/project.pbxproj +++ b/Plugins/Vorbis/VorbisPlugin.xcodeproj/project.pbxproj @@ -219,8 +219,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8D5B49AC048680CD000E48DA = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -329,8 +329,11 @@ 1DEB913B08733D840010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)", @@ -346,6 +349,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.vorbis; PRODUCT_NAME = VorbisPlugin; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -355,7 +359,10 @@ 1DEB913C08733D840010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)", @@ -369,6 +376,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.vorbis; PRODUCT_NAME = VorbisPlugin; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -409,7 +417,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYMROOT = ../../build; @@ -448,7 +456,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SYMROOT = ../../build; }; diff --git a/Plugins/WavPack/WavPack.xcodeproj/project.pbxproj b/Plugins/WavPack/WavPack.xcodeproj/project.pbxproj index 229d0f831..b4e7bd674 100644 --- a/Plugins/WavPack/WavPack.xcodeproj/project.pbxproj +++ b/Plugins/WavPack/WavPack.xcodeproj/project.pbxproj @@ -186,8 +186,8 @@ LastUpgradeCheck = 1020; TargetAttributes = { 8D5B49AC048680CD000E48DA = { - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -257,8 +257,11 @@ 1DEB913B08733D840010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 4S876G9VCD; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)", @@ -275,6 +278,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.wavpack; PRODUCT_NAME = WavPack; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -285,7 +289,10 @@ 1DEB913C08733D840010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)", @@ -300,6 +307,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.wavpack; PRODUCT_NAME = WavPack; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -340,7 +348,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYMROOT = ../../build; @@ -379,7 +387,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; SYMROOT = ../../build; }; diff --git a/Plugins/sidplay/sidplay.xcodeproj/project.pbxproj b/Plugins/sidplay/sidplay.xcodeproj/project.pbxproj index f4d234471..3acd5749d 100644 --- a/Plugins/sidplay/sidplay.xcodeproj/project.pbxproj +++ b/Plugins/sidplay/sidplay.xcodeproj/project.pbxproj @@ -167,8 +167,8 @@ TargetAttributes = { 8314D6301A354DFE00EEE8E6 = { CreatedOnToolsVersion = 6.1.1; - DevelopmentTeam = N6E749HJ2X; - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -285,7 +285,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -332,7 +332,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; }; @@ -341,11 +341,15 @@ 8314D6391A354DFE00EEE8E6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; INFOPLIST_FILE = sidplay/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = "org.cogx.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; @@ -355,11 +359,15 @@ 8314D63A1A354DFE00EEE8E6 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 4S876G9VCD; INFOPLIST_FILE = sidplay/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; PRODUCT_BUNDLE_IDENTIFIER = "org.cogx.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; WRAPPER_EXTENSION = bundle; diff --git a/Plugins/vgmstream/vgmstream.xcodeproj/project.pbxproj b/Plugins/vgmstream/vgmstream.xcodeproj/project.pbxproj index c78ea1a4d..676475fd5 100644 --- a/Plugins/vgmstream/vgmstream.xcodeproj/project.pbxproj +++ b/Plugins/vgmstream/vgmstream.xcodeproj/project.pbxproj @@ -195,7 +195,8 @@ ORGANIZATIONNAME = "Christopher Snowhill"; TargetAttributes = { 836F6B0F18BDB80D0095E648 = { - ProvisioningStyle = Manual; + DevelopmentTeam = 4S876G9VCD; + ProvisioningStyle = Automatic; }; }; }; @@ -340,7 +341,7 @@ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, ../Vorbis/include, ); - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -401,7 +402,7 @@ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, ../Vorbis/include, ); - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; SDKROOT = macosx; }; name = Release; @@ -409,10 +410,10 @@ 836F6B2318BDB80D0095E648 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "vgmstream/vgmstream-Prefix.pch"; HEADER_SEARCH_PATHS = ( @@ -436,10 +437,10 @@ 836F6B2418BDB80D0095E648 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 4S876G9VCD; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "vgmstream/vgmstream-Prefix.pch"; HEADER_SEARCH_PATHS = ( diff --git a/Plugins/vgmstream/vgmstream/VGMDecoder.h b/Plugins/vgmstream/vgmstream/VGMDecoder.h index 1b2d086ca..a15ace51b 100644 --- a/Plugins/vgmstream/vgmstream/VGMDecoder.h +++ b/Plugins/vgmstream/vgmstream/VGMDecoder.h @@ -28,6 +28,7 @@ @interface VGMDecoder : NSObject { VGMSTREAM *stream; + BOOL playForever; int sampleRate; int channels; int bitrate; diff --git a/Plugins/vgmstream/vgmstream/VGMDecoder.m b/Plugins/vgmstream/vgmstream/VGMDecoder.m index 43de625ac..4e74530ef 100644 --- a/Plugins/vgmstream/vgmstream/VGMDecoder.m +++ b/Plugins/vgmstream/vgmstream/VGMDecoder.m @@ -198,12 +198,27 @@ if ( !stream ) return NO; + vgmstream_mixing_autodownmix(stream, 6); + + playForever = IsRepeatOneSet(); + sampleRate = stream->sample_rate; channels = stream->channels; totalFrames = get_vgmstream_play_samples( 2.0, 10.0, 10.0, stream ); framesFade = stream->loop_flag ? sampleRate * 10 : 0; framesLength = totalFrames - framesFade; + vgmstream_cfg_t vcfg = {0}; + + vcfg.allow_play_forever = 1; + vcfg.play_forever = playForever; + vcfg.loop_count = 2; + vcfg.fade_time = 10; + vcfg.fade_delay = 0; + vcfg.ignore_loop = 0; + + vgmstream_apply_config(stream, &vcfg); + framesRead = 0; bitrate = get_vgmstream_average_bitrate(stream); @@ -232,6 +247,11 @@ { BOOL repeatone = IsRepeatOneSet(); + if (repeatone != playForever) { + playForever = repeatone; + vgmstream_set_play_forever(stream, repeatone); + } + BOOL loopokay = repeatone && stream->loop_flag; if (!loopokay) { diff --git a/Preferences/General/General.xcodeproj/project.pbxproj b/Preferences/General/General.xcodeproj/project.pbxproj index 318f78774..1693aadc8 100644 --- a/Preferences/General/General.xcodeproj/project.pbxproj +++ b/Preferences/General/General.xcodeproj/project.pbxproj @@ -583,7 +583,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = ( "-undefined", @@ -627,7 +627,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; + MACOSX_DEPLOYMENT_TARGET = 10.13; OTHER_LDFLAGS = ( "-undefined", dynamic_lookup, diff --git a/Preferences/PreferencesWindow.m b/Preferences/PreferencesWindow.m index bf7471dc2..bae7fc330 100644 --- a/Preferences/PreferencesWindow.m +++ b/Preferences/PreferencesWindow.m @@ -43,6 +43,13 @@ [self setShowsToolbarButton: NO]; [self setShowsResizeIndicator:NO]; [self center]; + + [[self standardWindowButton:NSWindowZoomButton] setEnabled:FALSE]; + + + if (@available(macOS 11, *)) { + [self setToolbarStyle:NSWindowToolbarStylePreference]; + } [self createToolbar]; } diff --git a/Scripts/ffmpeg-build-arm.sh b/Scripts/ffmpeg-build-arm.sh new file mode 100755 index 000000000..eb8a4a09f --- /dev/null +++ b/Scripts/ffmpeg-build-arm.sh @@ -0,0 +1,17 @@ +# This is the commands used to build the ffmpeg libs provided here +./configure --arch=arm64 --enable-neon --extra-cflags="-arch arm64 -fPIC -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -mmacosx-version-min=11.0" --extra-ldflags="-arch arm64 -mmacosx-version-min=11.0" --enable-cross-compile\ + --enable-static --disable-shared --prefix=$HOME/Source/Repos/cog/ThirdParty/ffmpeg/arm\ + --enable-pic --enable-gpl --disable-doc --disable-ffplay\ + --disable-ffprobe --disable-avdevice --disable-ffmpeg\ + --disable-postproc --disable-avfilter\ + --disable-swscale --disable-network --disable-swscale-alpha --disable-vdpau\ + --disable-dxva2 --disable-everything --enable-hwaccels\ + --enable-swresample\ + --enable-parser=ac3,mpegaudio,xma,vorbis,opus\ + --enable-demuxer=ac3,asf,xwma,mov,oma,ogg,tak,dsf,wav,aac,dts,dtshd,mp3,bink,flac,msf,xmv,caf,ape,smacker,pcm_s8,spdif,mpc,mpc8,rm\ + --enable-decoder=ac3,wmapro,wmav1,wmav2,wmavoice,wmalossless,xma1,xma2,dca,tak,dsd_lsbf,dsd_lsbf_planar,dsd_mbf,dsd_msbf_planar,aac,atrac3,atrac3p,mp3float,bink,binkaudio_dct,binkaudio_rdft,flac,pcm_s16be,pcm_s16be_planar,pcm_s16le,pcm_s16le_planar,vorbis,ape,adpcm_ima_qt,smackaud,opus,pcm_s8,pcm_s8_planar,mpc7,mpc8,alac,adpcm_ima_dk3,adpcm_ima_dk4,cook\ + --disable-parser=mpeg4video,h263\ + --disable-decoder=mpeg2video,h263,h264,mpeg1video,mpeg2video,mpeg4,hevc,vp9\ + --disable-version3 + +#make -j$(sysctl -n hw.logicalcpu) diff --git a/Scripts/ffmpeg-build.sh b/Scripts/ffmpeg-build.sh index 4f48dc2f9..7eb8a6ee7 100755 --- a/Scripts/ffmpeg-build.sh +++ b/Scripts/ffmpeg-build.sh @@ -1,16 +1,15 @@ # This is the commands used to build the ffmpeg libs provided here -./configure --extra-cflags="-fPIC -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -mmacosx-version-min=10.8" --extra-ldflags="-mmacosx-version-min=10.8"\ - --enable-static --disable-shared --prefix=$HOME/Source/Repos/cog/ThirdParty/ffmpeg\ +./configure --extra-cflags="-fPIC -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -mmacosx-version-min=10.9" --extra-ldflags="-mmacosx-version-min=10.9"\ + --enable-static --disable-shared --prefix=$HOME/Source/Repos/cog/ThirdParty/ffmpeg/x86\ --enable-pic --enable-gpl --disable-doc --disable-ffplay\ --disable-ffprobe --disable-avdevice --disable-ffmpeg\ --disable-postproc --disable-avfilter\ --disable-swscale --disable-network --disable-swscale-alpha --disable-vdpau\ --disable-dxva2 --disable-everything --enable-hwaccels\ --enable-swresample\ - --enable-libopus\ --enable-parser=ac3,mpegaudio,xma,vorbis,opus\ --enable-demuxer=ac3,asf,xwma,mov,oma,ogg,tak,dsf,wav,aac,dts,dtshd,mp3,bink,flac,msf,xmv,caf,ape,smacker,pcm_s8,spdif,mpc,mpc8,rm\ - --enable-decoder=ac3,wmapro,wmav1,wmav2,wmavoice,wmalossless,xma1,xma2,dca,tak,dsd_lsbf,dsd_lsbf_planar,dsd_mbf,dsd_msbf_planar,aac,atrac3,atrac3p,mp3float,bink,binkaudio_dct,binkaudio_rdft,flac,pcm_s16be,pcm_s16be_planar,pcm_s16le,pcm_s16le_planar,vorbis,ape,adpcm_ima_qt,smackaud,libopus,pcm_s8,pcm_s8_planar,mpc7,mpc8,alac,adpcm_ima_dk3,adpcm_ima_dk4,cook\ + --enable-decoder=ac3,wmapro,wmav1,wmav2,wmavoice,wmalossless,xma1,xma2,dca,tak,dsd_lsbf,dsd_lsbf_planar,dsd_mbf,dsd_msbf_planar,aac,atrac3,atrac3p,mp3float,bink,binkaudio_dct,binkaudio_rdft,flac,pcm_s16be,pcm_s16be_planar,pcm_s16le,pcm_s16le_planar,vorbis,ape,adpcm_ima_qt,smackaud,opus,pcm_s8,pcm_s8_planar,mpc7,mpc8,alac,adpcm_ima_dk3,adpcm_ima_dk4,cook\ --disable-parser=mpeg4video,h263\ --disable-decoder=mpeg2video,h263,h264,mpeg1video,mpeg2video,mpeg4,hevc,vp9\ --disable-version3 diff --git a/Scripts/update_feed.rb b/Scripts/update_feed.rb index 604e4aa38..342fd3522 100755 --- a/Scripts/update_feed.rb +++ b/Scripts/update_feed.rb @@ -68,7 +68,7 @@ revision_code = revision_split[1] if 1 #appcast_revision < latest_revision #Get the changelog - changelog = %x[/usr/bin/git log #{appcast_revision_code}..#{revision_code} --pretty=format:'
  • view commit • %s
  • ' --reverse] + changelog = %x[/usr/bin/git log #{appcast_revision_code}..#{revision_code} --pretty=format:'
  • view commit • %s
  • ' --reverse] description = changelog diff --git a/ThirdParty/BASS/libbass.dylib b/ThirdParty/BASS/libbass.dylib old mode 100755 new mode 100644 index b5158bb38..fe924efe0 Binary files a/ThirdParty/BASS/libbass.dylib and b/ThirdParty/BASS/libbass.dylib differ diff --git a/ThirdParty/BASS/libbass_mpc.dylib b/ThirdParty/BASS/libbass_mpc.dylib old mode 100755 new mode 100644 index df14ca80b..951e03144 Binary files a/ThirdParty/BASS/libbass_mpc.dylib and b/ThirdParty/BASS/libbass_mpc.dylib differ diff --git a/ThirdParty/BASS/libbassflac.dylib b/ThirdParty/BASS/libbassflac.dylib old mode 100755 new mode 100644 index 1a6f1a45b..8de158bfb Binary files a/ThirdParty/BASS/libbassflac.dylib and b/ThirdParty/BASS/libbassflac.dylib differ diff --git a/ThirdParty/BASS/libbassmidi.dylib b/ThirdParty/BASS/libbassmidi.dylib old mode 100755 new mode 100644 index 934640cb2..fb9049c69 Binary files a/ThirdParty/BASS/libbassmidi.dylib and b/ThirdParty/BASS/libbassmidi.dylib differ diff --git a/ThirdParty/BASS/libbassopus.dylib b/ThirdParty/BASS/libbassopus.dylib old mode 100755 new mode 100644 index 647cff2dc..bb63a8d74 Binary files a/ThirdParty/BASS/libbassopus.dylib and b/ThirdParty/BASS/libbassopus.dylib differ diff --git a/ThirdParty/BASS/libbasswv.dylib b/ThirdParty/BASS/libbasswv.dylib old mode 100755 new mode 100644 index 0c1ebffa1..6a09b0221 Binary files a/ThirdParty/BASS/libbasswv.dylib and b/ThirdParty/BASS/libbasswv.dylib differ diff --git a/ThirdParty/Frameworks/Growl.framework/Growl b/ThirdParty/Frameworks/Growl.framework/Growl deleted file mode 120000 index 85956e2d2..000000000 --- a/ThirdParty/Frameworks/Growl.framework/Growl +++ /dev/null @@ -1 +0,0 @@ -Versions/Current/Growl \ No newline at end of file diff --git a/ThirdParty/Frameworks/Growl.framework/Versions/A/Growl b/ThirdParty/Frameworks/Growl.framework/Versions/A/Growl deleted file mode 100755 index 40e59dee1..000000000 Binary files a/ThirdParty/Frameworks/Growl.framework/Versions/A/Growl and /dev/null differ diff --git a/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/Growl.h b/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/Growl.h deleted file mode 100644 index 7b1a3247d..000000000 --- a/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/Growl.h +++ /dev/null @@ -1,5 +0,0 @@ -#include - -#ifdef __OBJC__ -# include -#endif diff --git a/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h b/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h deleted file mode 100644 index 363975762..000000000 --- a/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h +++ /dev/null @@ -1,567 +0,0 @@ -// -// GrowlApplicationBridge.h -// Growl -// -// Created by Evan Schoenberg on Wed Jun 16 2004. -// Copyright 2004-2006 The Growl Project. All rights reserved. -// - -/*! - * @header GrowlApplicationBridge.h - * @abstract Defines the GrowlApplicationBridge class. - * @discussion This header defines the GrowlApplicationBridge class as well as - * the GROWL_PREFPANE_BUNDLE_IDENTIFIER constant. - */ - -#ifndef __GrowlApplicationBridge_h__ -#define __GrowlApplicationBridge_h__ - -#import -#import -#import - -//Forward declarations -@protocol GrowlApplicationBridgeDelegate; - -//------------------------------------------------------------------------------ -#pragma mark - - -/*! - * @class GrowlApplicationBridge - * @abstract A class used to interface with Growl. - * @discussion This class provides a means to interface with Growl. - * - * Currently it provides a way to detect if Growl is installed and launch the - * GrowlHelperApp if it's not already running. - */ -@interface GrowlApplicationBridge : NSObject { - -} - -/*! - * @method isGrowlInstalled - * @abstract Detects whether Growl is installed. - * @discussion Determines if the Growl prefpane and its helper app are installed. - * @result this method will forever return YES. - */ -+ (BOOL) isGrowlInstalled __attribute__((deprecated)); - -/*! - * @method isGrowlRunning - * @abstract Detects whether GrowlHelperApp is currently running. - * @discussion Cycles through the process list to find whether GrowlHelperApp is running and returns its findings. - * @result Returns YES if GrowlHelperApp is running, NO otherwise. - */ -+ (BOOL) isGrowlRunning; - - -/*! - * @method isMistEnabled - * @abstract Gives the caller a fairly good indication of whether or not built-in notifications(Mist) will be used. - * @discussion since this call makes use of isGrowlRunning it is entirely possible for this value to change between call and - * executing a notification dispatch - * @result Returns YES if Growl isn't reachable and the developer has not opted-out of - * Mist and the user hasn't set the global mist enable key to false. - */ -+ (BOOL)isMistEnabled; - -/*! - * @method setShouldUseBuiltInNotifications - * @abstract opt-out mechanism for the mist notification style in the event growl can't be reached. - * @discussion if growl is unavailable due to not being installed or as a result of being turned off then - * this option can enable/disable a built-in fire and forget display style - * @param should Specifies whether or not the developer wants to opt-in (default) or opt out - * of the built-in Mist style in the event Growl is unreachable. - */ -+ (void)setShouldUseBuiltInNotifications:(BOOL)should; - -/*! - * @method shouldUseBuiltInNotifications - * @abstract returns the current opt-in state of the framework's use of the Mist display style. - * @result Returns NO if the developer opt-ed out of Mist, the default value is YES. - */ -+ (BOOL)shouldUseBuiltInNotifications; - -#pragma mark - - -/*! - * @method setGrowlDelegate: - * @abstract Set the object which will be responsible for providing and receiving Growl information. - * @discussion This must be called before using GrowlApplicationBridge. - * - * The methods in the GrowlApplicationBridgeDelegate protocol are required - * and return the basic information needed to register with Growl. - * - * The methods in the GrowlApplicationBridgeDelegate_InformalProtocol - * informal protocol are individually optional. They provide a greater - * degree of interaction between the application and growl such as informing - * the application when one of its Growl notifications is clicked by the user. - * - * The methods in the GrowlApplicationBridgeDelegate_Installation_InformalProtocol - * informal protocol are individually optional and are only applicable when - * using the Growl-WithInstaller.framework which allows for automated Growl - * installation. - * - * When this method is called, data will be collected from inDelegate, Growl - * will be launched if it is not already running, and the application will be - * registered with Growl. - * - * If using the Growl-WithInstaller framework, if Growl is already installed - * but this copy of the framework has an updated version of Growl, the user - * will be prompted to update automatically. - * - * @param inDelegate The delegate for the GrowlApplicationBridge. It must conform to the GrowlApplicationBridgeDelegate protocol. - */ -+ (void) setGrowlDelegate:(id)inDelegate; - -/*! - * @method growlDelegate - * @abstract Return the object responsible for providing and receiving Growl information. - * @discussion See setGrowlDelegate: for details. - * @result The Growl delegate. - */ -+ (id) growlDelegate; - -#pragma mark - - -/*! - * @method notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext: - * @abstract Send a Growl notification. - * @discussion This is the preferred means for sending a Growl notification. - * The notification name and at least one of the title and description are - * required (all three are preferred). All other parameters may be - * nil (or 0 or NO as appropriate) to accept default values. - * - * If using the Growl-WithInstaller framework, if Growl is not installed the - * user will be prompted to install Growl. If the user cancels, this method - * will have no effect until the next application session, at which time when - * it is called the user will be prompted again. The user is also given the - * option to not be prompted again. If the user does choose to install Growl, - * the requested notification will be displayed once Growl is installed and - * running. - * - * @param title The title of the notification displayed to the user. - * @param description The full description of the notification displayed to the user. - * @param notifName The internal name of the notification. Should be human-readable, as it will be displayed in the Growl preference pane. - * @param iconData NSData object to show with the notification as its icon. If nil, the application's icon will be used instead. - * @param priority The priority of the notification. The default value is 0; positive values are higher priority and negative values are lower priority. Not all Growl displays support priority. - * @param isSticky If YES, the notification will remain on screen until clicked. Not all Growl displays support sticky notifications. - * @param clickContext A context passed back to the Growl delegate if it implements -(void)growlNotificationWasClicked: and the notification is clicked. Not all display plugins support clicking. The clickContext must be plist-encodable (completely of NSString, NSArray, NSNumber, NSDictionary, and NSData types). - */ -+ (void) notifyWithTitle:(NSString *)title - description:(NSString *)description - notificationName:(NSString *)notifName - iconData:(NSData *)iconData - priority:(signed int)priority - isSticky:(BOOL)isSticky - clickContext:(id)clickContext; - -/*! - * @method notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext:identifier: - * @abstract Send a Growl notification. - * @discussion This is the preferred means for sending a Growl notification. - * The notification name and at least one of the title and description are - * required (all three are preferred). All other parameters may be - * nil (or 0 or NO as appropriate) to accept default values. - * - * If using the Growl-WithInstaller framework, if Growl is not installed the - * user will be prompted to install Growl. If the user cancels, this method - * will have no effect until the next application session, at which time when - * it is called the user will be prompted again. The user is also given the - * option to not be prompted again. If the user does choose to install Growl, - * the requested notification will be displayed once Growl is installed and - * running. - * - * @param title The title of the notification displayed to the user. - * @param description The full description of the notification displayed to the user. - * @param notifName The internal name of the notification. Should be human-readable, as it will be displayed in the Growl preference pane. - * @param iconData NSData object to show with the notification as its icon. If nil, the application's icon will be used instead. - * @param priority The priority of the notification. The default value is 0; positive values are higher priority and negative values are lower priority. Not all Growl displays support priority. - * @param isSticky If YES, the notification will remain on screen until clicked. Not all Growl displays support sticky notifications. - * @param clickContext A context passed back to the Growl delegate if it implements -(void)growlNotificationWasClicked: and the notification is clicked. Not all display plugins support clicking. The clickContext must be plist-encodable (completely of NSString, NSArray, NSNumber, NSDictionary, and NSData types). - * @param identifier An identifier for this notification. Notifications with equal identifiers are coalesced. - */ -+ (void) notifyWithTitle:(NSString *)title - description:(NSString *)description - notificationName:(NSString *)notifName - iconData:(NSData *)iconData - priority:(signed int)priority - isSticky:(BOOL)isSticky - clickContext:(id)clickContext - identifier:(NSString *)identifier; - -/*! @method notifyWithDictionary: - * @abstract Notifies using a userInfo dictionary suitable for passing to - * NSDistributedNotificationCenter. - * @param userInfo The dictionary to notify with. - * @discussion Before Growl 0.6, your application would have posted - * notifications using NSDistributedNotificationCenter by - * creating a userInfo dictionary with the notification data. This had the - * advantage of allowing you to add other data to the dictionary for programs - * besides Growl that might be listening. - * - * This method allows you to use such dictionaries without being restricted - * to using NSDistributedNotificationCenter. The keys for this dictionary - * can be found in GrowlDefines.h. - */ -+ (void) notifyWithDictionary:(NSDictionary *)userInfo; - -#pragma mark - - -/*! @method registerWithDictionary: - * @abstract Register your application with Growl without setting a delegate. - * @discussion When you call this method with a dictionary, - * GrowlApplicationBridge registers your application using that dictionary. - * If you pass nil, GrowlApplicationBridge will ask the delegate - * (if there is one) for a dictionary, and if that doesn't work, it will look - * in your application's bundle for an auto-discoverable plist. - * (XXX refer to more information on that) - * - * If you pass a dictionary to this method, it must include the - * GROWL_APP_NAME key, unless a delegate is set. - * - * This method is mainly an alternative to the delegate system introduced - * with Growl 0.6. Without a delegate, you cannot receive callbacks such as - * -growlIsReady (since they are sent to the delegate). You can, - * however, set a delegate after registering without one. - * - * This method was introduced in Growl.framework 0.7. - */ -+ (BOOL) registerWithDictionary:(NSDictionary *)regDict; - -/*! @method reregisterGrowlNotifications - * @abstract Reregister the notifications for this application. - * @discussion This method does not normally need to be called. If your - * application changes what notifications it is registering with Growl, call - * this method to have the Growl delegate's - * -registrationDictionaryForGrowl method called again and the - * Growl registration information updated. - * - * This method is now implemented using -registerWithDictionary:. - */ -+ (void) reregisterGrowlNotifications; - -#pragma mark - - -/*! @method setWillRegisterWhenGrowlIsReady: - * @abstract Tells GrowlApplicationBridge to register with Growl when Growl - * launches (or not). - * @discussion When Growl has started listening for notifications, it posts a - * GROWL_IS_READY notification on the Distributed Notification - * Center. GrowlApplicationBridge listens for this notification, using it to - * perform various tasks (such as calling your delegate's - * -growlIsReady method, if it has one). If this method is - * called with YES, one of those tasks will be to reregister - * with Growl (in the manner of -reregisterGrowlNotifications). - * - * This attribute is automatically set back to NO (the default) - * after every GROWL_IS_READY notification. - * @param flag YES if you want GrowlApplicationBridge to register with - * Growl when next it is ready; NO if not. - */ -+ (void) setWillRegisterWhenGrowlIsReady:(BOOL)flag; - -/*! @method willRegisterWhenGrowlIsReady - * @abstract Reports whether GrowlApplicationBridge will register with Growl - * when Growl next launches. - * @result YES if GrowlApplicationBridge will register with Growl - * when next it posts GROWL_IS_READY; NO if not. - */ -+ (BOOL) willRegisterWhenGrowlIsReady; - -#pragma mark - - -/*! @method registrationDictionaryFromDelegate - * @abstract Asks the delegate for a registration dictionary. - * @discussion If no delegate is set, or if the delegate's - * -registrationDictionaryForGrowl method returns - * nil, this method returns nil. - * - * This method does not attempt to clean up the dictionary in any way - for - * example, if it is missing the GROWL_APP_NAME key, the result - * will be missing it too. Use +[GrowlApplicationBridge - * registrationDictionaryByFillingInDictionary:] or - * +[GrowlApplicationBridge - * registrationDictionaryByFillingInDictionary:restrictToKeys:] to try - * to fill in missing keys. - * - * This method was introduced in Growl.framework 0.7. - * @result A registration dictionary. - */ -+ (NSDictionary *) registrationDictionaryFromDelegate; - -/*! @method registrationDictionaryFromBundle: - * @abstract Looks in a bundle for a registration dictionary. - * @discussion This method looks in a bundle for an auto-discoverable - * registration dictionary file using -[NSBundle - * pathForResource:ofType:]. If it finds one, it loads the file using - * +[NSDictionary dictionaryWithContentsOfFile:] and returns the - * result. - * - * If you pass nil as the bundle, the main bundle is examined. - * - * This method does not attempt to clean up the dictionary in any way - for - * example, if it is missing the GROWL_APP_NAME key, the result - * will be missing it too. Use +[GrowlApplicationBridge - * registrationDictionaryByFillingInDictionary:] or - * +[GrowlApplicationBridge - * registrationDictionaryByFillingInDictionary:restrictToKeys:] to try - * to fill in missing keys. - * - * This method was introduced in Growl.framework 0.7. - * @result A registration dictionary. - */ -+ (NSDictionary *) registrationDictionaryFromBundle:(NSBundle *)bundle; - -/*! @method bestRegistrationDictionary - * @abstract Obtains a registration dictionary, filled out to the best of - * GrowlApplicationBridge's knowledge. - * @discussion This method creates a registration dictionary as best - * GrowlApplicationBridge knows how. - * - * First, GrowlApplicationBridge contacts the Growl delegate (if there is - * one) and gets the registration dictionary from that. If no such dictionary - * was obtained, GrowlApplicationBridge looks in your application's main - * bundle for an auto-discoverable registration dictionary file. If that - * doesn't exist either, this method returns nil. - * - * Second, GrowlApplicationBridge calls - * +registrationDictionaryByFillingInDictionary: with whatever - * dictionary was obtained. The result of that method is the result of this - * method. - * - * GrowlApplicationBridge uses this method when you call - * +setGrowlDelegate:, or when you call - * +registerWithDictionary: with nil. - * - * This method was introduced in Growl.framework 0.7. - * @result A registration dictionary. - */ -+ (NSDictionary *) bestRegistrationDictionary; - -#pragma mark - - -/*! @method registrationDictionaryByFillingInDictionary: - * @abstract Tries to fill in missing keys in a registration dictionary. - * @discussion This method examines the passed-in dictionary for missing keys, - * and tries to work out correct values for them. As of 0.7, it uses: - * - * Key Value - * --- ----- - * GROWL_APP_NAME CFBundleExecutableName - * GROWL_APP_ICON_DATA The data of the icon of the application. - * GROWL_APP_LOCATION The location of the application. - * GROWL_NOTIFICATIONS_DEFAULT GROWL_NOTIFICATIONS_ALL - * - * Keys are only filled in if missing; if a key is present in the dictionary, - * its value will not be changed. - * - * This method was introduced in Growl.framework 0.7. - * @param regDict The dictionary to fill in. - * @result The dictionary with the keys filled in. This is an autoreleased - * copy of regDict. - */ -+ (NSDictionary *) registrationDictionaryByFillingInDictionary:(NSDictionary *)regDict; - -/*! @method registrationDictionaryByFillingInDictionary:restrictToKeys: - * @abstract Tries to fill in missing keys in a registration dictionary. - * @discussion This method examines the passed-in dictionary for missing keys, - * and tries to work out correct values for them. As of 0.7, it uses: - * - * Key Value - * --- ----- - * GROWL_APP_NAME CFBundleExecutableName - * GROWL_APP_ICON_DATA The data of the icon of the application. - * GROWL_APP_LOCATION The location of the application. - * GROWL_NOTIFICATIONS_DEFAULT GROWL_NOTIFICATIONS_ALL - * - * Only those keys that are listed in keys will be filled in. - * Other missing keys are ignored. Also, keys are only filled in if missing; - * if a key is present in the dictionary, its value will not be changed. - * - * This method was introduced in Growl.framework 0.7. - * @param regDict The dictionary to fill in. - * @param keys The keys to fill in. If nil, any missing keys are filled in. - * @result The dictionary with the keys filled in. This is an autoreleased - * copy of regDict. - */ -+ (NSDictionary *) registrationDictionaryByFillingInDictionary:(NSDictionary *)regDict restrictToKeys:(NSSet *)keys; - -/*! @brief Tries to fill in missing keys in a notification dictionary. - * @param notifDict The dictionary to fill in. - * @return The dictionary with the keys filled in. This will be a separate instance from \a notifDict. - * @discussion This function examines the \a notifDict for missing keys, and - * tries to get them from the last known registration dictionary. As of 1.1, - * the keys that it will look for are: - * - * \li GROWL_APP_NAME - * \li GROWL_APP_ICON_DATA - * - * @since Growl.framework 1.1 - */ -+ (NSDictionary *) notificationDictionaryByFillingInDictionary:(NSDictionary *)regDict; - -+ (NSDictionary *) frameworkInfoDictionary; - -#pragma mark - - -/*! - *@method growlURLSchemeAvailable - *@abstract Lets the app know whether growl:// is registered on the system, used for certain methods below this - *@return Returns whether growl:// is registered on the system - *@discussion Methods such as openGrowlPreferences rely on the growl:// URL scheme to function - * Further, this method can provide a check on whether Growl is installed, - * however, the framework will not be relying on this method for choosing when/how to notify, - * and it is not recommended that the app rely on it for other than whether to use growl:// methods - *@since Growl.framework 1.4 - */ -+ (BOOL) isGrowlURLSchemeAvailable; - -/*! - * @method openGrowlPreferences: - * @abstract Open Growl preferences, optionally to this app's settings, growl:// method - * @param showApp Whether to show the application's settings, otherwise just opens to the last position - * @return Return's whether opening the URL was succesfull or not. - * @discussion Will launch if Growl is installed, but not running, and open the preferences window - * Uses growl:// URL scheme - * @since Growl.framework 1.4 - */ -+ (BOOL) openGrowlPreferences:(BOOL)showApp; - -@end - -//------------------------------------------------------------------------------ -#pragma mark - - -/*! - * @protocol GrowlApplicationBridgeDelegate - * @abstract Required protocol for the Growl delegate. - * @discussion The methods in this protocol are optional and are called - * automatically as needed by GrowlApplicationBridge. See - * +[GrowlApplicationBridge setGrowlDelegate:]. - * See also GrowlApplicationBridgeDelegate_InformalProtocol. - */ - -@protocol GrowlApplicationBridgeDelegate - -@optional - -/*! - * @method registrationDictionaryForGrowl - * @abstract Return the dictionary used to register this application with Growl. - * @discussion The returned dictionary gives Growl the complete list of - * notifications this application will ever send, and it also specifies which - * notifications should be enabled by default. Each is specified by an array - * of NSString objects. - * - * For most applications, these two arrays can be the same (if all sent - * notifications should be displayed by default). - * - * The NSString objects of these arrays will correspond to the - * notificationName: parameter passed in - * +[GrowlApplicationBridge - * notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext:] calls. - * - * The dictionary should have the required key object pairs: - * key: GROWL_NOTIFICATIONS_ALL object: NSArray of NSString objects - * key: GROWL_NOTIFICATIONS_DEFAULT object: NSArray of NSString objects - * - * The dictionary may have the following key object pairs: - * key: GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES object: NSDictionary of key: notification name object: human-readable notification name - * - * You do not need to implement this method if you have an auto-discoverable - * plist file in your app bundle. (XXX refer to more information on that) - * - * @result The NSDictionary to use for registration. - */ -- (NSDictionary *) registrationDictionaryForGrowl; - -/*! - * @method applicationNameForGrowl - * @abstract Return the name of this application which will be used for Growl bookkeeping. - * @discussion This name is used both internally and in the Growl preferences. - * - * This should remain stable between different versions and incarnations of - * your application. - * For example, "SurfWriter" is a good app name, whereas "SurfWriter 2.0" and - * "SurfWriter Lite" are not. - * - * You do not need to implement this method if you are providing the - * application name elsewhere, meaning in an auto-discoverable plist file in - * your app bundle (XXX refer to more information on that) or in the result - * of -registrationDictionaryForGrowl. - * - * @result The name of the application using Growl. - */ -- (NSString *) applicationNameForGrowl; - -/*! - * @method applicationIconForGrowl - * @abstract Return the NSImage to treat as the application icon. - * @discussion The delegate may optionally return an NSImage - * object to use as the application icon. If this method is not implemented, - * {{{-applicationIconDataForGrowl}}} is tried. If that method is not - * implemented, the application's own icon is used. Neither method is - * generally needed. - * @result The NSImage to treat as the application icon. - */ -- (NSImage *) applicationIconForGrowl; - -/*! - * @method applicationIconDataForGrowl - * @abstract Return the NSData to treat as the application icon. - * @discussion The delegate may optionally return an NSData - * object to use as the application icon; if this is not implemented, the - * application's own icon is used. This is not generally needed. - * @result The NSData to treat as the application icon. - * @deprecated In version 1.1, in favor of {{{-applicationIconForGrowl}}}. - */ -- (NSData *) applicationIconDataForGrowl; - -/*! - * @method growlIsReady - * @abstract Informs the delegate that Growl has launched. - * @discussion Informs the delegate that Growl (specifically, the - * GrowlHelperApp) was launched successfully. The application can take actions - * with the knowledge that Growl is installed and functional. - */ -- (void) growlIsReady; - -/*! - * @method growlNotificationWasClicked: - * @abstract Informs the delegate that a Growl notification was clicked. - * @discussion Informs the delegate that a Growl notification was clicked. It - * is only sent for notifications sent with a non-nil - * clickContext, so if you want to receive a message when a notification is - * clicked, clickContext must not be nil when calling - * +[GrowlApplicationBridge notifyWithTitle: description:notificationName:iconData:priority:isSticky:clickContext:]. - * @param clickContext The clickContext passed when displaying the notification originally via +[GrowlApplicationBridge notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext:]. - */ -- (void) growlNotificationWasClicked:(id)clickContext; - -/*! - * @method growlNotificationTimedOut: - * @abstract Informs the delegate that a Growl notification timed out. - * @discussion Informs the delegate that a Growl notification timed out. It - * is only sent for notifications sent with a non-nil - * clickContext, so if you want to receive a message when a notification is - * clicked, clickContext must not be nil when calling - * +[GrowlApplicationBridge notifyWithTitle: description:notificationName:iconData:priority:isSticky:clickContext:]. - * @param clickContext The clickContext passed when displaying the notification originally via +[GrowlApplicationBridge notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext:]. - */ -- (void) growlNotificationTimedOut:(id)clickContext; - - -/*! - * @method hasNetworkClientEntitlement - * @abstract Used only in sandboxed situations since we don't know whether the app has com.apple.security.network.client entitlement - * @discussion GrowlDelegate calls to find out if we have the com.apple.security.network.client entitlement, - * since we can't find this out without hitting the sandbox. We only call it if we detect that the application is sandboxed. - */ -- (BOOL) hasNetworkClientEntitlement; - -@end - -#pragma mark - - -#endif /* __GrowlApplicationBridge_h__ */ diff --git a/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlDefines.h b/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlDefines.h deleted file mode 100644 index 0a196f1e3..000000000 --- a/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlDefines.h +++ /dev/null @@ -1,386 +0,0 @@ -// -// GrowlDefines.h -// - -#ifndef _GROWLDEFINES_H -#define _GROWLDEFINES_H - -#ifdef __OBJC__ -#define XSTR(x) (@x) -#else -#define XSTR CFSTR -#endif - -/*! @header GrowlDefines.h - * @abstract Defines all the notification keys. - * @discussion Defines all the keys used for registration with Growl and for - * Growl notifications. - * - * Most applications should use the functions or methods of Growl.framework - * instead of posting notifications such as those described here. - * @updated 2004-01-25 - */ - -// UserInfo Keys for Registration -#pragma mark UserInfo Keys for Registration - -/*! @group Registration userInfo keys */ -/* @abstract Keys for the userInfo dictionary of a GROWL_APP_REGISTRATION distributed notification. - * @discussion The values of these keys describe the application and the - * notifications it may post. - * - * Your application must register with Growl before it can post Growl - * notifications (and have them not be ignored). However, as of Growl 0.6, - * posting GROWL_APP_REGISTRATION notifications directly is no longer the - * preferred way to register your application. Your application should instead - * use Growl.framework's delegate system. - * See +[GrowlApplicationBridge setGrowlDelegate:] or Growl_SetDelegate for - * more information. - */ - -/*! @defined GROWL_APP_NAME - * @abstract The name of your application. - * @discussion The name of your application. This should remain stable between - * different versions and incarnations of your application. - * For example, "SurfWriter" is a good app name, whereas "SurfWriter 2.0" and - * "SurfWriter Lite" are not. - */ -#define GROWL_APP_NAME XSTR("ApplicationName") -/*! @defined GROWL_APP_ID - * @abstract The bundle identifier of your application. - * @discussion The bundle identifier of your application. This key should - * be unique for your application while there may be several applications - * with the same GROWL_APP_NAME. - * This key is optional. - */ -#define GROWL_APP_ID XSTR("ApplicationId") -/*! @defined GROWL_APP_ICON_DATA - * @abstract The image data for your application's icon. - * @discussion Image data representing your application's icon. This may be - * superimposed on a notification icon as a badge, used as the notification - * icon when a notification-specific icon is not supplied, or ignored - * altogether, depending on the display. Must be in a format supported by - * NSImage, such as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF. - * - * Optional. Not supported by all display plugins. - */ -#define GROWL_APP_ICON_DATA XSTR("ApplicationIcon") -/*! @defined GROWL_NOTIFICATIONS_DEFAULT - * @abstract The array of notifications to turn on by default. - * @discussion These are the names of the notifications that should be enabled - * by default when your application registers for the first time. If your - * application reregisters, Growl will look here for any new notification - * names found in GROWL_NOTIFICATIONS_ALL, but ignore any others. - */ -#define GROWL_NOTIFICATIONS_DEFAULT XSTR("DefaultNotifications") -/*! @defined GROWL_NOTIFICATIONS_ALL - * @abstract The array of all notifications your application can send. - * @discussion These are the names of all of the notifications that your - * application may post. See GROWL_NOTIFICATION_NAME for a discussion of good - * notification names. - */ -#define GROWL_NOTIFICATIONS_ALL XSTR("AllNotifications") -/*! @defined GROWL_NOTIFICATIONS_HUMAN_READABLE_DESCRIPTIONS - * @abstract A dictionary of human-readable names for your notifications. - * @discussion By default, the Growl UI will display notifications by the names given in GROWL_NOTIFICATIONS_ALL - * which correspond to the GROWL_NOTIFICATION_NAME. This dictionary specifies the human-readable name to display. - * The keys of the dictionary are GROWL_NOTIFICATION_NAME strings; the objects are the human-readable versions. - * For any GROWL_NOTIFICATION_NAME not specific in this dictionary, the GROWL_NOTIFICATION_NAME will be displayed. - * - * This key is optional. - */ -#define GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES XSTR("HumanReadableNames") -/*! @defined GROWL_NOTIFICATIONS_DESCRIPTIONS -* @abstract A dictionary of descriptions of _when_ each notification occurs -* @discussion This is an NSDictionary whose keys are GROWL_NOTIFICATION_NAME strings and whose objects are -* descriptions of _when_ each notification occurs, such as "You received a new mail message" or -* "A file finished downloading". -* -* This key is optional. -*/ -#define GROWL_NOTIFICATIONS_DESCRIPTIONS XSTR("NotificationDescriptions") -/*! @defined GROWL_NOTIFICATIONS_ICONS - * @abstract A dictionary of icons for each notification - * @discussion This is an NSDictionary whose keys are GROWL_NOTIFICATION_NAME strings and whose objects are - * icons for each notification, for GNTP spec - * - * This key is optional. - */ -#define GROWL_NOTIFICATIONS_ICONS XSTR("NotificationIcons") - -/*! @defined GROWL_TICKET_VERSION - * @abstract The version of your registration ticket. - * @discussion Include this key in a ticket plist file that you put in your - * application bundle for auto-discovery. The current ticket version is 1. - */ -#define GROWL_TICKET_VERSION XSTR("TicketVersion") -// UserInfo Keys for Notifications -#pragma mark UserInfo Keys for Notifications - -/*! @group Notification userInfo keys */ -/* @abstract Keys for the userInfo dictionary of a GROWL_NOTIFICATION distributed notification. - * @discussion The values of these keys describe the content of a Growl - * notification. - * - * Not all of these keys are supported by all displays. Only the name, title, - * and description of a notification are universal. Most of the built-in - * displays do support all of these keys, and most other visual displays - * probably will also. But, as of 0.6, the Log, MailMe, and Speech displays - * support only textual data. - */ - -/*! @defined GROWL_NOTIFICATION_NAME - * @abstract The name of the notification. - * @discussion The name of the notification. Note that if you do not define - * GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES when registering your ticket originally this name - * will the one displayed within the Growl preference pane and should be human-readable. - */ -#define GROWL_NOTIFICATION_NAME XSTR("NotificationName") -/*! @defined GROWL_NOTIFICATION_TITLE - * @abstract The title to display in the notification. - * @discussion The title of the notification. Should be very brief. - * The title usually says what happened, e.g. "Download complete". - */ -#define GROWL_NOTIFICATION_TITLE XSTR("NotificationTitle") -/*! @defined GROWL_NOTIFICATION_DESCRIPTION - * @abstract The description to display in the notification. - * @discussion The description should be longer and more verbose than the title. - * The description usually tells the subject of the action, - * e.g. "Growl-0.6.dmg downloaded in 5.02 minutes". - */ -#define GROWL_NOTIFICATION_DESCRIPTION XSTR("NotificationDescription") -/*! @defined GROWL_NOTIFICATION_ICON - * @discussion Image data for the notification icon. Image data must be in a format - * supported by NSImage, such as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF. - * - * Optional. Not supported by all display plugins. - */ -#define GROWL_NOTIFICATION_ICON_DATA XSTR("NotificationIcon") -/*! @defined GROWL_NOTIFICATION_APP_ICON - * @discussion Image data for the application icon, in case GROWL_APP_ICON does - * not apply for some reason. Image data be in a format supported by NSImage, such - * as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF. - * - * Optional. Not supported by all display plugins. - */ -#define GROWL_NOTIFICATION_APP_ICON_DATA XSTR("NotificationAppIcon") -/*! @defined GROWL_NOTIFICATION_PRIORITY - * @discussion The priority of the notification as an integer number from - * -2 to +2 (+2 being highest). - * - * Optional. Not supported by all display plugins. - */ -#define GROWL_NOTIFICATION_PRIORITY XSTR("NotificationPriority") -/*! @defined GROWL_NOTIFICATION_STICKY - * @discussion A Boolean number controlling whether the notification is sticky. - * - * Optional. Not supported by all display plugins. - */ -#define GROWL_NOTIFICATION_STICKY XSTR("NotificationSticky") -/*! @defined GROWL_NOTIFICATION_CLICK_CONTEXT - * @abstract Identifies which notification was clicked. - * @discussion An identifier for the notification for clicking purposes. - * - * This will be passed back to the application when the notification is - * clicked. It must be plist-encodable (a data, dictionary, array, number, or - * string object), and it should be unique for each notification you post. - * A good click context would be a UUID string returned by NSProcessInfo or - * CFUUID. - * - * Optional. Not supported by all display plugins. - */ -#define GROWL_NOTIFICATION_CLICK_CONTEXT XSTR("NotificationClickContext") - -/*! @defined GROWL_NOTIFICATION_IDENTIFIER - * @abstract An identifier for the notification for coalescing purposes. - * Notifications with the same identifier fall into the same class; only - * the last notification of a class is displayed on the screen. If a - * notification of the same class is currently being displayed, it is - * replaced by this notification. - * - * Optional. Not supported by all display plugins. - */ -#define GROWL_NOTIFICATION_IDENTIFIER XSTR("GrowlNotificationIdentifier") - -/*! @defined GROWL_APP_PID - * @abstract The process identifier of the process which sends this - * notification. If this field is set, the application will only receive - * clicked and timed out notifications which originate from this process. - * - * Optional. - */ -#define GROWL_APP_PID XSTR("ApplicationPID") - -/*! @defined GROWL_NOTIFICATION_PROGRESS -* @abstract If this key is set, it should contain a double value wrapped -* in a NSNumber which describes some sort of progress (from 0.0 to 100.0). -* If this is key is not set, no progress bar is shown. -* -* Optional. Not supported by all display plugins. -*/ -#define GROWL_NOTIFICATION_PROGRESS XSTR("NotificationProgress") - -/*! @defined GROWL_NOTIFICATION_ALREADY_SHOWN - * @abstract If this key is set, it should contain a bool value wrapped - * in a NSNumber which describes whether the notification has - * already been displayed, for instance by built in Notification - * Center support. This value can be used to allow display - * plugins to skip a notification, while still allowing Growl - * actions to run on them. - * - * Optional. Not supported by all display plugins. - */ -#define GROWL_NOTIFICATION_ALREADY_SHOWN XSTR("AlreadyShown") - - -// Notifications -#pragma mark Notifications - -/*! @group Notification names */ -/* @abstract Names of distributed notifications used by Growl. - * @discussion These are notifications used by applications (directly or - * indirectly) to interact with Growl, and by Growl for interaction between - * its components. - * - * Most of these should no longer be used in Growl 0.6 and later, in favor of - * Growl.framework's GrowlApplicationBridge APIs. - */ - -/*! @defined GROWL_APP_REGISTRATION - * @abstract The distributed notification for registering your application. - * @discussion This is the name of the distributed notification that can be - * used to register applications with Growl. - * - * The userInfo dictionary for this notification can contain these keys: - *
      - *
    • GROWL_APP_NAME
    • - *
    • GROWL_APP_ICON_DATA
    • - *
    • GROWL_NOTIFICATIONS_ALL
    • - *
    • GROWL_NOTIFICATIONS_DEFAULT
    • - *
    - * - * No longer recommended as of Growl 0.6. An alternate method of registering - * is to use Growl.framework's delegate system. - * See +[GrowlApplicationBridge setGrowlDelegate:] or Growl_SetDelegate for - * more information. - */ -#define GROWL_APP_REGISTRATION XSTR("GrowlApplicationRegistrationNotification") -/*! @defined GROWL_APP_REGISTRATION_CONF - * @abstract The distributed notification for confirming registration. - * @discussion The name of the distributed notification sent to confirm the - * registration. Used by the Growl preference pane. Your application probably - * does not need to use this notification. - */ -#define GROWL_APP_REGISTRATION_CONF XSTR("GrowlApplicationRegistrationConfirmationNotification") -/*! @defined GROWL_NOTIFICATION - * @abstract The distributed notification for Growl notifications. - * @discussion This is what it all comes down to. This is the name of the - * distributed notification that your application posts to actually send a - * Growl notification. - * - * The userInfo dictionary for this notification can contain these keys: - *
      - *
    • GROWL_NOTIFICATION_NAME (required)
    • - *
    • GROWL_NOTIFICATION_TITLE (required)
    • - *
    • GROWL_NOTIFICATION_DESCRIPTION (required)
    • - *
    • GROWL_NOTIFICATION_ICON
    • - *
    • GROWL_NOTIFICATION_APP_ICON
    • - *
    • GROWL_NOTIFICATION_PRIORITY
    • - *
    • GROWL_NOTIFICATION_STICKY
    • - *
    • GROWL_NOTIFICATION_CLICK_CONTEXT
    • - *
    • GROWL_APP_NAME (required)
    • - *
    - * - * No longer recommended as of Growl 0.6. Three alternate methods of posting - * notifications are +[GrowlApplicationBridge notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext:], - * Growl_NotifyWithTitleDescriptionNameIconPriorityStickyClickContext, and - * Growl_PostNotification. - */ -#define GROWL_NOTIFICATION XSTR("GrowlNotification") -/*! @defined GROWL_PING - * @abstract A distributed notification to check whether Growl is running. - * @discussion This is used by the Growl preference pane. If it receives a - * GROWL_PONG, the preference pane takes this to mean that Growl is running. - */ -#define GROWL_PING XSTR("Honey, Mind Taking Out The Trash") -/*! @defined GROWL_PONG - * @abstract The distributed notification sent in reply to GROWL_PING. - * @discussion GrowlHelperApp posts this in reply to GROWL_PING. - */ -#define GROWL_PONG XSTR("What Do You Want From Me, Woman") -/*! @defined GROWL_IS_READY - * @abstract The distributed notification sent when Growl starts up. - * @discussion GrowlHelperApp posts this when it has begin listening on all of - * its sources for new notifications. GrowlApplicationBridge (in - * Growl.framework), upon receiving this notification, reregisters using the - * registration dictionary supplied by its delegate. - */ -#define GROWL_IS_READY XSTR("Lend Me Some Sugar; I Am Your Neighbor!") - - -/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_CLICKED_SUFFIX - * @abstract Part of the name of the distributed notification sent when a supported notification is clicked. - * @discussion When a Growl notification with a click context is clicked on by - * the user, Growl posts a distributed notification whose name is in the format: - * [NSString stringWithFormat:@"%@-%d-%@", appName, pid, GROWL_DISTRIBUTED_NOTIFICATION_CLICKED_SUFFIX] - * The GrowlApplicationBridge responds to this notification by calling a callback in its delegate. - */ -#define GROWL_DISTRIBUTED_NOTIFICATION_CLICKED_SUFFIX XSTR("GrowlClicked!") - -/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_TIMED_OUT_SUFFIX - * @abstract Part of the name of the distributed notification sent when a supported notification times out without being clicked. - * @discussion When a Growl notification with a click context times out, Growl posts a distributed notification - * whose name is in the format: - * [NSString stringWithFormat:@"%@-%d-%@", appName, pid, GROWL_DISTRIBUTED_NOTIFICATION_TIMED_OUT_SUFFIX] - * The GrowlApplicationBridge responds to this notification by calling a callback in its delegate. - * NOTE: The user may have actually clicked the 'close' button; this triggers an *immediate* time-out of the notification. - */ -#define GROWL_DISTRIBUTED_NOTIFICATION_TIMED_OUT_SUFFIX XSTR("GrowlTimedOut!") - -/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_ON - * @abstract The distributed notification sent when the Notification Center support is toggled on in Growl 2.0 - * @discussion When the user enables Notification Center support in Growl 2.0, this notification is sent - * to inform all running apps that they should now speak to Notification Center directly. - */ -#define GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_ON XSTR("GrowlNotificationCenterOn!") - -/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_OFF - * @abstract The distributed notification sent when the Notification Center support is toggled off in Growl 2.0 - * @discussion When the user enables Notification Center support in Growl 2.0, this notification is sent - * to inform all running apps that they should no longer speak to Notification Center directly. - */ -#define GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_OFF XSTR("GrowlNotificationCenterOff!") - -/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_QUERY - * @abstract The distributed notification sent by an application to query Growl 2.0's notification center support. - * @discussion When an app starts up, it will send this query to get Growl 2.0 to spit out whether notification - * center support is on or off. - */ -#define GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_QUERY XSTR("GrowlNotificationCenterYN?") - - -/*! @group Other symbols */ -/* Symbols which don't fit into any of the other categories. */ - -/*! @defined GROWL_KEY_CLICKED_CONTEXT - * @abstract Used internally as the key for the clickedContext passed over DNC. - * @discussion This key is used in GROWL_NOTIFICATION_CLICKED, and contains the - * click context that was supplied in the original notification. - */ -#define GROWL_KEY_CLICKED_CONTEXT XSTR("ClickedContext") -/*! @defined GROWL_REG_DICT_EXTENSION - * @abstract The filename extension for registration dictionaries. - * @discussion The GrowlApplicationBridge in Growl.framework registers with - * Growl by creating a file with the extension of .(GROWL_REG_DICT_EXTENSION) - * and opening it in the GrowlHelperApp. This happens whether or not Growl is - * running; if it was stopped, it quits immediately without listening for - * notifications. - */ -#define GROWL_REG_DICT_EXTENSION XSTR("growlRegDict") - - -#define GROWL_POSITION_PREFERENCE_KEY @"GrowlSelectedPosition" - -#define GROWL_PLUGIN_CONFIG_ID XSTR("GrowlPluginConfigurationID") - -#endif //ndef _GROWLDEFINES_H diff --git a/ThirdParty/Frameworks/Growl.framework/Versions/A/Resources/Info.plist b/ThirdParty/Frameworks/Growl.framework/Versions/A/Resources/Info.plist deleted file mode 100644 index 6a90f41b9..000000000 --- a/ThirdParty/Frameworks/Growl.framework/Versions/A/Resources/Info.plist +++ /dev/null @@ -1,40 +0,0 @@ - - - - - BuildMachineOSBuild - 12C60 - CFBundleDevelopmentRegion - English - CFBundleExecutable - Growl - CFBundleIdentifier - com.growl.growlframework - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - FMWK - CFBundleShortVersionString - 2.0.1 - CFBundleSignature - GRRR - CFBundleVersion - 2.0.1 - DTCompiler - com.apple.compilers.llvm.clang.1_0 - DTPlatformBuild - 4G2008a - DTPlatformVersion - GM - DTSDKBuild - 12C37 - DTSDKName - macosx10.8 - DTXcode - 0452 - DTXcodeBuild - 4G2008a - NSPrincipalClass - GrowlApplicationBridge - - diff --git a/ThirdParty/Frameworks/Growl.framework/Versions/A/_CodeSignature/CodeResources b/ThirdParty/Frameworks/Growl.framework/Versions/A/_CodeSignature/CodeResources deleted file mode 100644 index 2ba9d3b92..000000000 --- a/ThirdParty/Frameworks/Growl.framework/Versions/A/_CodeSignature/CodeResources +++ /dev/null @@ -1,127 +0,0 @@ - - - - - files - - Resources/Info.plist - - VZb3f8My4te/5JwcjfvotgCXTAs= - - - files2 - - Headers/Growl.h - - j9Ob2u9aIsHa7g0mIrcn/eowg/U= - - Headers/GrowlApplicationBridge.h - - EnGmx27UHXu/cw04qVrVgF74lt8= - - Headers/GrowlDefines.h - - lEpJMIumoTSF3icoXL/GM/1lEzE= - - Resources/Info.plist - - VZb3f8My4te/5JwcjfvotgCXTAs= - - - rules - - ^Resources/ - - ^Resources/.*\.lproj/ - - optional - - weight - 1000 - - ^Resources/.*\.lproj/locversion.plist$ - - omit - - weight - 1100 - - ^version.plist$ - - - rules2 - - .*\.dSYM($|/) - - weight - 11 - - ^(.*/)?\.DS_Store$ - - omit - - weight - 2000 - - ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ - - nested - - weight - 10 - - ^.* - - ^Info\.plist$ - - omit - - weight - 20 - - ^PkgInfo$ - - omit - - weight - 20 - - ^Resources/ - - weight - 20 - - ^Resources/.*\.lproj/ - - optional - - weight - 1000 - - ^Resources/.*\.lproj/locversion.plist$ - - omit - - weight - 1100 - - ^[^/]+$ - - nested - - weight - 10 - - ^embedded\.provisionprofile$ - - weight - 20 - - ^version\.plist$ - - weight - 20 - - - - diff --git a/ThirdParty/Frameworks/Growl.framework/Headers b/ThirdParty/Frameworks/Sparkle.framework/Headers similarity index 100% rename from ThirdParty/Frameworks/Growl.framework/Headers rename to ThirdParty/Frameworks/Sparkle.framework/Headers diff --git a/ThirdParty/Sparkle/Sparkle.framework/Modules b/ThirdParty/Frameworks/Sparkle.framework/Modules similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Modules rename to ThirdParty/Frameworks/Sparkle.framework/Modules diff --git a/ThirdParty/Sparkle/Sparkle.framework/PrivateHeaders b/ThirdParty/Frameworks/Sparkle.framework/PrivateHeaders similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/PrivateHeaders rename to ThirdParty/Frameworks/Sparkle.framework/PrivateHeaders diff --git a/ThirdParty/Frameworks/Growl.framework/Resources b/ThirdParty/Frameworks/Sparkle.framework/Resources similarity index 100% rename from ThirdParty/Frameworks/Growl.framework/Resources rename to ThirdParty/Frameworks/Sparkle.framework/Resources diff --git a/ThirdParty/Sparkle/Sparkle.framework/Sparkle b/ThirdParty/Frameworks/Sparkle.framework/Sparkle similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Sparkle rename to ThirdParty/Frameworks/Sparkle.framework/Sparkle diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloadData.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SPUDownloadData.h similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloadData.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SPUDownloadData.h diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloader.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SPUDownloader.h similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloader.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SPUDownloader.h diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderDelegate.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SPUDownloaderDelegate.h similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderDelegate.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SPUDownloaderDelegate.h diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderDeprecated.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SPUDownloaderDeprecated.h similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderDeprecated.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SPUDownloaderDeprecated.h diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderProtocol.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SPUDownloaderProtocol.h similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderProtocol.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SPUDownloaderProtocol.h diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderSession.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SPUDownloaderSession.h similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderSession.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SPUDownloaderSession.h diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SPUURLRequest.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SPUURLRequest.h similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SPUURLRequest.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SPUURLRequest.h diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUAppcast.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUAppcast.h similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUAppcast.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUAppcast.h diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUCodeSigningVerifier.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUCodeSigningVerifier.h similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUCodeSigningVerifier.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUCodeSigningVerifier.h diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUErrors.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUErrors.h similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUErrors.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUErrors.h diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUExport.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUExport.h similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUExport.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUExport.h diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUStandardVersionComparator.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUStandardVersionComparator.h similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUStandardVersionComparator.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUStandardVersionComparator.h diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUUpdater.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUUpdater.h similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUUpdater.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUUpdater.h diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUUpdaterDelegate.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUUpdaterDelegate.h similarity index 99% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUUpdaterDelegate.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUUpdaterDelegate.h index ea8197b52..ec844d046 100644 --- a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUUpdaterDelegate.h +++ b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUUpdaterDelegate.h @@ -36,7 +36,7 @@ SU_EXPORT extern NSString *const SUUpdaterAppcastItemNotificationKey; SU_EXPORT extern NSString *const SUUpdaterAppcastNotificationKey; // ----------------------------------------------------------------------------- -// SUUpdater Delegate: +// SUUpdater Delegate: // ----------------------------------------------------------------------------- /*! diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/Sparkle.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/Sparkle.h similarity index 78% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/Sparkle.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/Sparkle.h index 5ae2e6a6d..1085d419e 100644 --- a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Headers/Sparkle.h +++ b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Headers/Sparkle.h @@ -12,6 +12,10 @@ // This list should include the shared headers. It doesn't matter if some of them aren't shared (unless // there are name-space collisions) so we can list all of them to start with: +#pragma clang diagnostic push +// Do not use <> style includes since 2.x has two frameworks that need to work: Sparkle and SparkleCore +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" + #import "SUAppcast.h" #import "SUAppcastItem.h" #import "SUStandardVersionComparator.h" @@ -30,4 +34,6 @@ #import "SPUURLRequest.h" #import "SUCodeSigningVerifier.h" +#pragma clang diagnostic pop + #endif diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Modules/module.modulemap b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Modules/module.modulemap similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Modules/module.modulemap rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Modules/module.modulemap diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/PrivateHeaders/SUUnarchiver.h b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/PrivateHeaders/SUUnarchiver.h similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/PrivateHeaders/SUUnarchiver.h rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/PrivateHeaders/SUUnarchiver.h diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist similarity index 81% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist index 86abd3b38..676181f9f 100644 --- a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist +++ b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist @@ -3,7 +3,7 @@ BuildMachineOSBuild - 19E224g + 20B28 CFBundleDevelopmentRegion English CFBundleExecutable @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.23.0 34-g962878b84 + 1.24.0 a-67-g0e162c98 CFBundleSignature ???? CFBundleSupportedPlatforms @@ -25,21 +25,23 @@ MacOSX
    CFBundleVersion - 1.23.0 + 1.24.0 DTCompiler com.apple.compilers.llvm.clang.1_0 DTPlatformBuild - 11B44 + 12C5020f + DTPlatformName + macosx DTPlatformVersion - GM + 11.1 DTSDKBuild - 19B68e + 20C5048g DTSDKName - macosx10.15 + macosx11.1 DTXcode - 1120 + 1230 DTXcodeBuild - 11B44 + 12C5020f LSBackgroundOnly 1 LSMinimumSystemVersion diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate new file mode 100755 index 000000000..164511d4f Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop new file mode 100755 index 000000000..29dae8a10 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/PkgInfo b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/PkgInfo similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/PkgInfo rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/PkgInfo diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib new file mode 100644 index 000000000..f9c39a032 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings similarity index 99% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings index 1464f3493..02e077cf5 100644 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings similarity index 99% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings index 49ed0dfe2..1d70063c3 100644 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings similarity index 84% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings index fe6543556..ab8fe1a3e 100644 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings similarity index 90% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings index 7e808f8a8..71cf325f2 100644 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources similarity index 95% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources index 3239aff16..bb4125f0f 100644 --- a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources +++ b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources @@ -10,7 +10,7 @@ Resources/SUStatus.nib - +3Tn7V2+cRw0Aw2VoWT1DNAPpkE= + ECVWRExfxyDt5uvKRD+70wc9J6s= Resources/ar.lproj/Sparkle.strings @@ -34,7 +34,7 @@ hash - OjCfTDR+NbVLvirUPiJKJF6UiS0= + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= optional @@ -70,7 +70,7 @@ hash - i3TNbMzmKR52wTyfnD/bkZ12elE= + cHZov5FaqzfNhnBo0XdRuTMT4SY= optional @@ -115,7 +115,7 @@ hash - vyI2nHyZlhhSXXrQGoflg8oB9Ig= + 7LLOVs76ioMwEDV8Gah+6sV/5No= optional @@ -286,7 +286,7 @@ hash - 0vdFowZuJ1qLE3rDgG3BZ9SlNRw= + OnR96Z9tB0noODRSYssSs63+zGA= optional @@ -307,10 +307,10 @@ cdhash - m93UAGlWG1VExsGEVOlzraeDVJU= + JwkAFJqL9xY1mTI+1Kki3oSFsik= requirement - cdhash H"034b541bc0ce637d7c6cbab430b6ae5bf60b18d2" or cdhash H"9bddd40069561b5544c6c18454e973ada7835495" + cdhash H"d5bc45cc18a448c02d5c4dd6859a64524a5b8a85" or cdhash H"270900149a8bf7163599323ed4a922de8485b229" or cdhash H"1b27242b81a5a51561703e2bb8a5e01acac436e9" or cdhash H"ead0c4c63eafc5d32327f0dbf958b7bd0993ec75" Resources/AppIcon.icns @@ -327,11 +327,11 @@ hash - +3Tn7V2+cRw0Aw2VoWT1DNAPpkE= + ECVWRExfxyDt5uvKRD+70wc9J6s= hash2 - 5qy5Hmt7D8O4gK4CkA7/lWikR3cX9ZSe6yTyluAeOh8= + AtY9YmPv7cUlbFWP2vCyVdi3/M+XQn98wOlrIES2Dgk= Resources/ar.lproj/Sparkle.strings @@ -364,11 +364,11 @@ hash - OjCfTDR+NbVLvirUPiJKJF6UiS0= + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= hash2 - 06z1nY8VfM4M1SdHCc1jqewmP5Ue0g3mPDcNbeDVNIM= + RfJgT2b3STcLu71+1iU9ZcSXbfwMWG1EE1C7Wrf3xBk= optional @@ -416,11 +416,11 @@ hash - i3TNbMzmKR52wTyfnD/bkZ12elE= + cHZov5FaqzfNhnBo0XdRuTMT4SY= hash2 - Xl/5yA/K9T7cscvPi/4/lWUtjJlIvO+esCF4SRaguz4= + 39CdfZZ1CQQz1Gd1+Ukxo2JHl0XESoc/cqWKF091WUk= optional @@ -481,11 +481,11 @@ hash - vyI2nHyZlhhSXXrQGoflg8oB9Ig= + 7LLOVs76ioMwEDV8Gah+6sV/5No= hash2 - I1njQLVyH2bFsJecQOLDAB9N8q8niH7u+hqzxRqqerA= + TwklhrooHTXgV6Q9fbvvAB3mPIh7qDbEsNtUzo2fQuU= optional @@ -728,11 +728,11 @@ hash - 0vdFowZuJ1qLE3rDgG3BZ9SlNRw= + OnR96Z9tB0noODRSYssSs63+zGA= hash2 - ecJXF6vvj1f80iCr+Gk52cTyumQrea6H0NpOjwbbLiE= + zvMbFdgVGI0ls9vIRT+sie7dj2g1UjQu7iS+pOgyBo4= optional diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/DarkAqua.css b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/DarkAqua.css similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/DarkAqua.css rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/DarkAqua.css diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Info.plist b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Info.plist similarity index 78% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Info.plist rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Info.plist index 4ee07eaed..8786d4875 100644 --- a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Info.plist +++ b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/Info.plist @@ -3,7 +3,7 @@ BuildMachineOSBuild - 19E224g + 20B28 CFBundleDevelopmentRegion en CFBundleExecutable @@ -17,7 +17,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.23.0 35-gec1536277 + 1.24.0 a-67-g0e162c98 CFBundleSignature ???? CFBundleSupportedPlatforms @@ -25,21 +25,23 @@ MacOSX
    CFBundleVersion - 1.23.0 + 1.24.0 DTCompiler com.apple.compilers.llvm.clang.1_0 DTPlatformBuild - 11B44 + 12C5020f + DTPlatformName + macosx DTPlatformVersion - GM + 11.1 DTSDKBuild - 19B68e + 20C5048g DTSDKName - macosx10.15 + macosx11.1 DTXcode - 1120 + 1230 DTXcodeBuild - 11B44 + 12C5020f LSMinimumSystemVersion 10.7 diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/SUStatus.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/SUStatus.nib new file mode 100644 index 000000000..f9c39a032 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/SUStatus.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..65f118ac4 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..44b6741d1 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..4f215f099 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ca.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ca.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ca.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ca.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..103886fe7 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..68e44511d Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..465e87dda Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings similarity index 99% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings index 1464f3493..02e077cf5 100644 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..d22bba56d Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..dc1aa3fa1 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..3515d02ce Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..cf6264295 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..258734439 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..37c880524 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..ddcb7b3ae Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..0085ef14f Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..8668aa87f Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..c5732e8fe Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..352a9a84e Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..8ef47f956 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings similarity index 99% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings index 49ed0dfe2..1d70063c3 100644 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..01694b254 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..9216cb743 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..49fce2082 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..86011659a Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..795d05d36 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..5cb6f89c5 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..74c1e5a33 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..dea32c6da Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..06a9205bc Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fr_CA.lproj b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fr_CA.lproj similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fr_CA.lproj rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/fr_CA.lproj diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/he.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/he.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/he.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/he.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..12476cc00 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..60659fe7c Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..3837f8afb Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/Sparkle.strings similarity index 84% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/Sparkle.strings index fe6543556..ab8fe1a3e 100644 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/Sparkle.strings and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/Sparkle.strings differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..dd46e650d Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..b2400c732 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..a4791c6b8 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..2e2c6025b Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..43990e174 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..00c3b4cf2 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..fb389d236 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..7eea1a8d8 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..966065f66 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..9200a22c0 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..abe1d0bf3 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..6ec0cbdf0 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..b29426766 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..d5e01aded Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..db6622c94 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..cbc7a9fdd Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..3a875f2bd Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..9dc8dc710 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..96e672b3d Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..ccec5f8f5 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..2b64274f0 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..aa19d7e3f Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..4fcb4eecd Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..aced373c6 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt.lproj b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt.lproj similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt.lproj rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt.lproj diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..0e8fdc60e Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..0a8994379 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..c9bd57830 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..7056ce7b7 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..d77700f05 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..554151eb6 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..c5907ff1d Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..9e45a85c1 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..56c269037 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..9889fb2d3 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..4743f8f9f Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..e0d48aa4f Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..912d1dd95 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..0aa042e4d Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..a7119055c Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..745260237 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..96b5964ad Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..12fcd8c12 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..ae7ceb14e Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..07518c343 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..6d16cc67f Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..0ee49a1f4 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..5241b1f23 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..d66c6ff00 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..24605f177 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..e722b695b Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..5c8a7d84f Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..dd540a60d Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..8ebec67dc Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..dd31811c0 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..1cb9d7074 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..2c066da9d Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..1e5b4eb20 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings similarity index 90% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings index 7e808f8a8..71cf325f2 100644 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 000000000..3c2028109 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib new file mode 100644 index 000000000..27d22201b Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib differ diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 000000000..794336528 Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings similarity index 100% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings diff --git a/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Sparkle b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Sparkle new file mode 100755 index 000000000..a89d9ae9d Binary files /dev/null and b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/Sparkle differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/_CodeSignature/CodeResources b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/_CodeSignature/CodeResources similarity index 85% rename from ThirdParty/Sparkle/Sparkle.framework/Versions/A/_CodeSignature/CodeResources rename to ThirdParty/Frameworks/Sparkle.framework/Versions/A/_CodeSignature/CodeResources index a39fbd99e..beb3429f5 100644 --- a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/_CodeSignature/CodeResources +++ b/ThirdParty/Frameworks/Sparkle.framework/Versions/A/_CodeSignature/CodeResources @@ -6,15 +6,15 @@ Resources/Autoupdate.app/Contents/Info.plist - WmC7M6tAuCZzYQbwA5YQRyp8gSg= + vcMwRErKskmkeyJJVI3Z3sof/sE= Resources/Autoupdate.app/Contents/MacOS/Autoupdate - 5y0GuAJnqQGaZwa1ZFJFmjv/kcU= + +dZZmoYTS+EoOzyj5DHipiL7vdA= Resources/Autoupdate.app/Contents/MacOS/fileop - avkb6Tg4QLKYpmzZ6Re443E1Lsc= + rsEt+sjfXAsfWD9h538L+27pg/s= Resources/Autoupdate.app/Contents/PkgInfo @@ -26,7 +26,7 @@ Resources/Autoupdate.app/Contents/Resources/SUStatus.nib - +3Tn7V2+cRw0Aw2VoWT1DNAPpkE= + ECVWRExfxyDt5uvKRD+70wc9J6s= Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings @@ -50,7 +50,7 @@ hash - OjCfTDR+NbVLvirUPiJKJF6UiS0= + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= optional @@ -86,7 +86,7 @@ hash - i3TNbMzmKR52wTyfnD/bkZ12elE= + cHZov5FaqzfNhnBo0XdRuTMT4SY= optional @@ -131,7 +131,7 @@ hash - vyI2nHyZlhhSXXrQGoflg8oB9Ig= + 7LLOVs76ioMwEDV8Gah+6sV/5No= optional @@ -302,7 +302,7 @@ hash - 0vdFowZuJ1qLE3rDgG3BZ9SlNRw= + OnR96Z9tB0noODRSYssSs63+zGA= optional @@ -318,7 +318,7 @@ Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources - hJU8+OcP90jEOKOwukBr8O69j7w= + VHoMteoopEbbYVG87rxkm0sjkA4= Resources/DarkAqua.css @@ -326,7 +326,7 @@ Resources/Info.plist - FcaJWh4GlAbLilVFN39FnMGCZjU= + Vjt5kOIAFdWzUgFgF/aP2v3owEc= Resources/SUModelTranslation.plist @@ -334,13 +334,13 @@ Resources/SUStatus.nib - +3Tn7V2+cRw0Aw2VoWT1DNAPpkE= + ECVWRExfxyDt5uvKRD+70wc9J6s= Resources/ar.lproj/SUAutomaticUpdateAlert.nib hash - oDbeH//n57rFaGaLkwI8UQ/txa0= + LtkONVbhTzwCPtbjkr06qSniXCI= optional @@ -349,7 +349,7 @@ hash - rj3mNv5WCDqnrsLmtCWdWwnQzJE= + JeZDdP1OuZbqkm8UKYiyH00A7ss= optional @@ -358,7 +358,7 @@ hash - xsWIpjC5TTnPtuprvEvnTUMyKYo= + Heb65H1UseXl7rEaFwVxKauBWnI= optional @@ -385,7 +385,7 @@ hash - iLsEEL5N+u3x0igCMtLbvUxqiI0= + YpT086oHMS9O2TvSNLZh+39oy80= optional @@ -394,7 +394,7 @@ hash - reUhWQTuJfS9z/Hf8LwQASnszvc= + v2ac1JQZvkm8EHZiTUc/q4aBcU0= optional @@ -403,7 +403,7 @@ hash - AvWpT2/bR/hQ2SrkSIJn1HaAXi0= + 2ANG1NY1o8ndm0xcmHwYUvrRk6w= optional @@ -412,7 +412,7 @@ hash - OjCfTDR+NbVLvirUPiJKJF6UiS0= + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= optional @@ -421,7 +421,7 @@ hash - u1F8d4d/aOeByXDdvXMugGpU/Fk= + XCqcLv38cTpbjAE4zjN/JWeT3+U= optional @@ -430,7 +430,7 @@ hash - ZW7bZ9L3EhY/tyvqZWgYznuPaTY= + BY0imp6dA7C0GSOK81VXTJsRccM= optional @@ -439,7 +439,7 @@ hash - uMINTHGG6dgMjyUAYQha7LXmlFg= + Ev2Nvw9c6bVU5ZF63yVhcyNp84w= optional @@ -457,7 +457,7 @@ hash - VRG/ZYuz0KOyMjBw4bu4NIlO3MY= + i/BaQXOSENNulhl0b5jssezuU3Y= optional @@ -466,7 +466,7 @@ hash - cT+nRuofaW12I0wjvG697gXwMfA= + lieYpCoCaCKAA3EL3/EsBr46vqI= optional @@ -475,7 +475,7 @@ hash - P1qisftBIKKBMlb5+8fD2vZn8C4= + oy0dHoyKmH2uV/KCHJzCagE+QIE= optional @@ -493,7 +493,7 @@ hash - ccB0As39qPJ0x4XkN34YZI8IUHs= + s3rpfaKP5+1+vGc44qpcWy+h0t8= optional @@ -502,7 +502,7 @@ hash - 8BqXnIfQOb3RBiybY1uUgI2a0oc= + sJcnQqAH4BsB+2rz9riB7iqePh0= optional @@ -511,7 +511,7 @@ hash - WujzpfOdW+H1RXGYDSAWcHDghE4= + mYyXqqWSoYqVG1zNp1vopIw8r1k= optional @@ -529,7 +529,7 @@ hash - smFdnjqh5FfLp75QJkkVpDg+CPs= + Y6bIF/+bAP3t5gBwPcdqxsj4co4= optional @@ -538,7 +538,7 @@ hash - uQPgA8QNrJ6dkBofaA4cYiep7Nw= + L3hoxekBQAtpmyDXNhTX7kRXRtc= optional @@ -547,7 +547,7 @@ hash - jc/+PgCV2twCZZQSjQHHAer/o9M= + uLKIwoprHw35+b4+/KP/j9X2zVg= optional @@ -556,7 +556,7 @@ hash - i3TNbMzmKR52wTyfnD/bkZ12elE= + cHZov5FaqzfNhnBo0XdRuTMT4SY= optional @@ -565,7 +565,7 @@ hash - t79VYhRMP0dQCdKKtn83sAtFAkU= + BoS6NAq1zyVcmkbrKJhcI9Zrezk= optional @@ -574,7 +574,7 @@ hash - BPV01tBvjSwDHnD/WkxFJ+W7eFI= + UMa1QcJf8zfpVUnIZUGFfJ64wTk= optional @@ -583,7 +583,7 @@ hash - NrVNwqMfVUN8CR/qLP3WzI74QKg= + tre2iSm68OK3ztgNotyXuz1MkzI= optional @@ -601,7 +601,7 @@ hash - 3Jy7fCDJXPgxfGrSLnrq/BrR8Fo= + 6aUnn3XSgWKnVuYVA/PVSrwora8= optional @@ -610,7 +610,7 @@ hash - fWlU1Y804x2510X3QDdE/kgfBjY= + k8QjxmBhk5B6v1fGnFkwwX6oakg= optional @@ -619,7 +619,7 @@ hash - phFF3/dcygmnknefGj4i3cgz294= + HB5ASms7UIZfv0WaGh6tCLBEDP8= optional @@ -637,7 +637,7 @@ hash - RuJWtV4NU5tNgg+IHFiksMNUfIA= + Nj5v0wIECbpjSTU74xKBngH4DeY= optional @@ -646,7 +646,7 @@ hash - Fx5MmhvVqSx8rSHFOIibPCl3xBs= + RPgJubd38D/WH1H6B1jSejqILE8= optional @@ -655,7 +655,7 @@ hash - ZwgBvgFW99rtuKNAPLcCPieEdM8= + cVJfS2Nx3QvdbWEq+tSt8xi9hIg= optional @@ -682,7 +682,7 @@ hash - 3rBSNPdcL/Nl7+0rpUZqGhljLQY= + SkgPcXJYp5dizLAgiXfyl9EsPoI= optional @@ -691,7 +691,7 @@ hash - +OYMTGeQfzz+9ImWkCbDKJG4N6w= + 47iMWOA+94RZGJW+QJCeM4xOUsA= optional @@ -700,7 +700,7 @@ hash - eWFDPqngmPB+nrvPXjpU207PI9w= + S2YV0JmEwfPtYsMBBMuvddrPEis= optional @@ -709,7 +709,7 @@ hash - vyI2nHyZlhhSXXrQGoflg8oB9Ig= + 7LLOVs76ioMwEDV8Gah+6sV/5No= optional @@ -718,7 +718,7 @@ hash - KqZCmcd+fhq4qCb7DbEOVkCcC5M= + PfYCBbOThC1gBDzoxD+ijdyQ3T0= optional @@ -727,7 +727,7 @@ hash - VjGGKPTkcvGmYWc2ejOXIOR2Pec= + 6RlHCvHc9GNh1M7iJhvn12iFGpg= optional @@ -736,7 +736,7 @@ hash - 5EiQOlMoRKSojP+6FNbyMwNhgVk= + 1Yz7vPBCFCly2cHjtbQJPK9PzjE= optional @@ -754,7 +754,7 @@ hash - ab4gaq47381T+4qaedPAv/JPk6o= + 9eOJ/dQvTMu45Z1UowMPaKHYQOI= optional @@ -763,7 +763,7 @@ hash - HC1ZY4ZwpdC8g7whfpr8B68+Wsg= + F0aP96zh7QOSkAdFsBlIzBhmCIg= optional @@ -772,7 +772,7 @@ hash - KEnwS7P4M7RXREXDBrYzFPAB5VU= + xRBgLwOX0xZhrXGjHDHL6S+qCQc= optional @@ -790,7 +790,7 @@ hash - /GUIYGR9WmveeKz5MV+ulysFDGE= + JXGMS3rtry8HTWEbBibxVCkBEmw= optional @@ -799,7 +799,7 @@ hash - C+frin/RV6PMceoJWhnBHw/UiSo= + W45+n9zTkxt1E3v6cZYLzXNtDlc= optional @@ -808,7 +808,7 @@ hash - NU+8Up7T7RaEJJ6XUGU315MmJEA= + o6d6uYDAajCHTJJOXT7zDECTmIM= optional @@ -826,7 +826,7 @@ hash - WXXUfAzkoQeox52T+w/9xy/eZzU= + 9fRyIZvZMUaRTaWCda7NsPqJle0= optional @@ -835,7 +835,7 @@ hash - hdYuZq1xueHEJ8e5TeRx/9nxlJA= + L07PCZt4pHgRzMPxV0N6F2QK9kM= optional @@ -844,7 +844,7 @@ hash - PLPFBaQnQsXytVrJabv0nvald10= + b1mgRruuDPTLXfhBEjTV72kV1m0= optional @@ -862,7 +862,7 @@ hash - l0zDEsFdyHwzPuayHe8R2wkbv0c= + gi+8llNjjuFSKRkNAA7vN/mf1uA= optional @@ -871,7 +871,7 @@ hash - R9RsJCIJrFMpzTJNUsMio/R2jNs= + UNAQQTvtG7+MN/4w4ouu9ZHXfCM= optional @@ -880,7 +880,7 @@ hash - o0FtQN9F2+i8SA46MW2lvxZAFfI= + ycrHyxLA68Lf8rq4IXBVK62UpGc= optional @@ -898,7 +898,7 @@ hash - 4ylPEFzA3WhK/BiqiKjlyeehlPI= + kJLypTD4VsCOwsXiOd6700pn0Cc= optional @@ -907,7 +907,7 @@ hash - Kcse19bpeWkKxkDiEabgvpb2Dk8= + WIJIO1qR0uNQtJpVkhIarVOfgFw= optional @@ -916,7 +916,7 @@ hash - mS+H7dDl1Yn5HoNY6FXQ+INWQRw= + sGjRl91qI6175CwJYuqSYLYylJg= optional @@ -934,7 +934,7 @@ hash - DblM97MbrUGSoF8qs4qy1pFAvXw= + wPmqlbK7p5mjPDrezXrYK8CG3NA= optional @@ -943,7 +943,7 @@ hash - mk176kIJR7A90H6Qq+hi8D2Q6Vo= + 7EXAJEeeN0k32jvOHKr7Icq4644= optional @@ -952,7 +952,7 @@ hash - z6/rLqlkgii529Dut+XHavZ4Pq4= + QHLHLZGOJJ4eN75aG1K1VUHNPz8= optional @@ -970,7 +970,7 @@ hash - kWDLMC25Z+sfNh5zpNFI8c9mBOg= + 4aIS8LbPLTj63EhS7xEk+qjNzD8= optional @@ -979,7 +979,7 @@ hash - QSgvMCGfu3qwt4LSPDoITv7Zj30= + pLII26utl65JwmAFqLiMefDsrGs= optional @@ -988,7 +988,7 @@ hash - uf+l2wCxifW3p/OPcOg7gXrO7ec= + pZo0pXve6jqBertG5IixAzSpgV4= optional @@ -1006,7 +1006,7 @@ hash - 93bUtDO+GDxBXAEl9unvmFe1mWo= + 8fQxJZw+LQiExfyPaqUbNnASsWU= optional @@ -1015,7 +1015,7 @@ hash - E4Vxj5LZDhvc7xttfztZBkDwo5s= + zW5zEa6rDpqLuTDfixuKKE93E5o= optional @@ -1024,7 +1024,7 @@ hash - XF0m/MqiO9kZb/K0uWKvnIi0jbo= + 2dtbduILRWtmfjobyd2yOVhQNH4= optional @@ -1042,7 +1042,7 @@ hash - H+9mRnaSNpf3x2lnvZJQYmNOeDE= + Svc6e9pHrp1wA7rxw7oKc5HB8qQ= optional @@ -1051,7 +1051,7 @@ hash - 7VloEUr/e0D0uiCgmSYM89car10= + NtD144OppS+BPXNmsReGJoN2Qdo= optional @@ -1060,7 +1060,7 @@ hash - KRVl5KSZqqi3JII8+JbAWE/vyeM= + 5B8hYUrKag0Unyt6Uk0D2K5opL8= optional @@ -1078,7 +1078,7 @@ hash - 5qfGbw+bZ8nIzRjTdFCQ4ajYuLg= + 7HEo1dlbwSnit0+4DsAqKDz1jR4= optional @@ -1087,7 +1087,7 @@ hash - 68qUvif5bsy3gh88hEcrGvT0sg8= + FPWtaRuYrVSPrfAozq/4bSQfMK4= optional @@ -1096,7 +1096,7 @@ hash - SweZv0/gqhOhvQDoTkvh0MNpjxs= + GY/ufItfyKYpgw54TfqJlPlymb0= optional @@ -1114,7 +1114,7 @@ hash - 3ymMt+zeZFb6zTVVK2kHUBbiGsE= + YHane6xWVhvpJGf5HuoxCyQ/gDs= optional @@ -1123,7 +1123,7 @@ hash - Shn/KmVDoD3wypQA4uFjnIJqQzY= + NxM+W+qAegxK4lKy0uzCclpkVjo= optional @@ -1132,7 +1132,7 @@ hash - W/T4ULA1IJxyDbETvrpiJrnX63w= + AqJRrBMp2yA+umSXxQIQVmpnCN4= optional @@ -1150,7 +1150,7 @@ hash - dkv1LXTd6KP9qyGoGQWM/beHi58= + 5k+e1kFtgoVo77RhyhZSXpRQGOQ= optional @@ -1159,7 +1159,7 @@ hash - ZYpm1VcCFFWBaIzjYYwhekhYPDE= + l8XR02tvN10SX2aM9CtZ7BpIcqw= optional @@ -1168,7 +1168,7 @@ hash - WHCeNcsEET1G546RnCG1GQc9BTk= + burLhfFkzbPjAIqMXw1qKn94xm8= optional @@ -1186,7 +1186,7 @@ hash - M8OEz9UpXZ+CwFLwnPmpw73qCRk= + htsZnsf4S+omnk4Z8En8qOOjrPM= optional @@ -1195,7 +1195,7 @@ hash - qRwnTGgz/ZS11VE0PbfWNnLPg0o= + /9xrGwHXOdPKFp82w4OjF+Q0WG8= optional @@ -1204,7 +1204,7 @@ hash - TLk0Rv9HN/Y8bYaF3vr8kg9KPuI= + lT25Wn73ZrIgcDJsgzSvIQ97TtY= optional @@ -1222,7 +1222,7 @@ hash - 0FRDiL99dypvNgSR6/b2hEh8LC8= + K0QpkucJxh5CRA9TYJCT5+gSHZY= optional @@ -1231,7 +1231,7 @@ hash - nhRSdsw8i1SblxHBOLBvYjBPO8g= + dOxfomMC/X9MFFdsk1MyjKv1yi8= optional @@ -1240,7 +1240,7 @@ hash - 6qyk1r0zm0331mer2SxkuzuSm64= + W5vyz7ueX3DVKxQC82/3FnvJfeQ= optional @@ -1258,7 +1258,7 @@ hash - 02wDrcnMUo4dg7fm4RSe/Sy2pFs= + /iyQcDW4+Is40OSlKcWlEe9zKdo= optional @@ -1267,7 +1267,7 @@ hash - j2iSaoDJRMV3kq+UFuzkbVAojSg= + cQCWeOMdd6mZEd9k0pl3FrZDT9g= optional @@ -1276,7 +1276,7 @@ hash - Pb4ma8CWTT89suTVjHa3iQeWepg= + JIh/Ueyxh8+us+26dxQRmrPiVAE= optional @@ -1294,7 +1294,7 @@ hash - 5l2VJ1hqtebg0BM6zmyz63tMhR0= + fjnMF3XlYLFPJSpmIFizoGwOVl8= optional @@ -1303,7 +1303,7 @@ hash - w4Qf7do1kg75nGVgX0TP8MHOCOs= + Y9dabfD0a7F1cV9OuFnyQL5BIIc= optional @@ -1312,7 +1312,7 @@ hash - CT6KO7Zy073NY0kp2tcbUgEdNRo= + 5wxy4Op51XjVl1MvUlCnSUfvsj8= optional @@ -1330,7 +1330,7 @@ hash - NgeN7x6U1IHvnglysoHMw5YH7RU= + YRRVgJ26NZd9+ebTI3UGdpi35eo= optional @@ -1339,7 +1339,7 @@ hash - 5P3DitlPL1sqOb0WkiXq8ZWq2kQ= + PoeaXUHUKNIm0bkX+GNnvFHlq9w= optional @@ -1348,7 +1348,7 @@ hash - aY4PHFMl4vj5sqgVBNGxePP5CCU= + HEubU7VtIHZcWJ6RfdC038Os1gw= optional @@ -1366,7 +1366,7 @@ hash - qhLsJNXC4lbz9xO+bC41ILw8CUg= + 2pvLfCu7EiI6OkCxu3+aLyeTPcU= optional @@ -1375,7 +1375,7 @@ hash - dQk9OZiBuGL0rOXiWivc2ld9Z+4= + +xvQE3bFW1QXIUggZBlZkKn0gag= optional @@ -1384,7 +1384,7 @@ hash - TYmHdlcZRxyoGQ6FNldZKp8Xl1I= + FT+kQgUNxKGrbheU8uSqkYFSHtI= optional @@ -1393,7 +1393,7 @@ hash - 0vdFowZuJ1qLE3rDgG3BZ9SlNRw= + OnR96Z9tB0noODRSYssSs63+zGA= optional @@ -1402,7 +1402,7 @@ hash - E7e3G1hg4vqRfzno4FdzvgH0yng= + KQcqbpIyw3yhI0eEyo06cNq5MlM= optional @@ -1411,7 +1411,7 @@ hash - bm6w3bCQbsrYbFszNCANq+DHcdQ= + 6/Rgln3/89vly1RFa1gBfRhITxU= optional @@ -1420,7 +1420,7 @@ hash - 1WN5IQ6j/AsxDG2fbAuKZAwoyzM= + qO2OAmNcqk2/bSzwAjGcXTD4+PY= optional @@ -1595,11 +1595,11 @@ hash - UWIKKrtpG8ETIKMCaSRiq4ujPJs= + MHw1NrGxR116E8+4fIwJlj7xhlU= hash2 - d9LBmTLBxcQda+JzY5Y0rdqUU3ZwG0B/xUrAF68d2Xk= + I9h48457oU5WcQiRBj2su4zWt34+hDtHwnoeLWmZ7Ko= Headers/SUVersionComparisonProtocol.h @@ -1628,11 +1628,11 @@ hash - kAc+dA9KctczULg0aLWypDdBvy0= + wr5DylsSwG9e5D1UOhUWQxcyz/g= hash2 - qx9pH2muu7OaSYEeZEfIzGFTWCk/gIo7ZQ+6TExPhyA= + OWWeiknWNtWrmGQq0pZ+Hst0PnbN6WbPhGHqIhk4MOM= Modules/module.modulemap @@ -1661,33 +1661,33 @@ hash - WmC7M6tAuCZzYQbwA5YQRyp8gSg= + vcMwRErKskmkeyJJVI3Z3sof/sE= hash2 - DF5sAWodrqG3GqtUmnteKhxPsxEVxHX4Yk3PwYMWAuk= + KAQWAU9pYYHyY0ffDykJoZXUPjiewvVWowIwn5qsBNc= Resources/Autoupdate.app/Contents/MacOS/Autoupdate hash - 5y0GuAJnqQGaZwa1ZFJFmjv/kcU= + +dZZmoYTS+EoOzyj5DHipiL7vdA= hash2 - G3T7ewvJhbpZWG1YsYs4KU+ON/kBCCqVQqkgQ7rJH5A= + /CLwW6G+QiK5ETp+clGZsGSksu5/2ppP1elEQVje7VY= Resources/Autoupdate.app/Contents/MacOS/fileop hash - avkb6Tg4QLKYpmzZ6Re443E1Lsc= + rsEt+sjfXAsfWD9h538L+27pg/s= hash2 - ad6uMw/E+PkHWrq2KfATyxPEttn+TcNlEj4ATmFEDPY= + kI/01mUGzFnWLufOtr6ca07s6VBhqA7INTCKBS4xA+8= Resources/Autoupdate.app/Contents/PkgInfo @@ -1716,11 +1716,11 @@ hash - +3Tn7V2+cRw0Aw2VoWT1DNAPpkE= + ECVWRExfxyDt5uvKRD+70wc9J6s= hash2 - 5qy5Hmt7D8O4gK4CkA7/lWikR3cX9ZSe6yTyluAeOh8= + AtY9YmPv7cUlbFWP2vCyVdi3/M+XQn98wOlrIES2Dgk= Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings @@ -1753,11 +1753,11 @@ hash - OjCfTDR+NbVLvirUPiJKJF6UiS0= + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= hash2 - 06z1nY8VfM4M1SdHCc1jqewmP5Ue0g3mPDcNbeDVNIM= + RfJgT2b3STcLu71+1iU9ZcSXbfwMWG1EE1C7Wrf3xBk= optional @@ -1805,11 +1805,11 @@ hash - i3TNbMzmKR52wTyfnD/bkZ12elE= + cHZov5FaqzfNhnBo0XdRuTMT4SY= hash2 - Xl/5yA/K9T7cscvPi/4/lWUtjJlIvO+esCF4SRaguz4= + 39CdfZZ1CQQz1Gd1+Ukxo2JHl0XESoc/cqWKF091WUk= optional @@ -1870,11 +1870,11 @@ hash - vyI2nHyZlhhSXXrQGoflg8oB9Ig= + 7LLOVs76ioMwEDV8Gah+6sV/5No= hash2 - I1njQLVyH2bFsJecQOLDAB9N8q8niH7u+hqzxRqqerA= + TwklhrooHTXgV6Q9fbvvAB3mPIh7qDbEsNtUzo2fQuU= optional @@ -2117,11 +2117,11 @@ hash - 0vdFowZuJ1qLE3rDgG3BZ9SlNRw= + OnR96Z9tB0noODRSYssSs63+zGA= hash2 - ecJXF6vvj1f80iCr+Gk52cTyumQrea6H0NpOjwbbLiE= + zvMbFdgVGI0ls9vIRT+sie7dj2g1UjQu7iS+pOgyBo4= optional @@ -2143,11 +2143,11 @@ hash - hJU8+OcP90jEOKOwukBr8O69j7w= + VHoMteoopEbbYVG87rxkm0sjkA4= hash2 - A3YNvkAp2jm0jj6vFfI1Vkv+v5qIbvf6nM267YddEW4= + lxIEfRslquAFN4m0UvN8ZbULT6U6J3cPAYY9sqR1HPE= Resources/DarkAqua.css @@ -2165,11 +2165,11 @@ hash - FcaJWh4GlAbLilVFN39FnMGCZjU= + Vjt5kOIAFdWzUgFgF/aP2v3owEc= hash2 - erViMEuBOBVHmWoZtghmdWrXzQMPabSQpwAr329HIHs= + F8ePDHN9JgUrW5JHoFYN8yT7d6AnZBL5ZflSVh3ri4Q= Resources/SUModelTranslation.plist @@ -2187,22 +2187,22 @@ hash - +3Tn7V2+cRw0Aw2VoWT1DNAPpkE= + ECVWRExfxyDt5uvKRD+70wc9J6s= hash2 - 5qy5Hmt7D8O4gK4CkA7/lWikR3cX9ZSe6yTyluAeOh8= + AtY9YmPv7cUlbFWP2vCyVdi3/M+XQn98wOlrIES2Dgk= Resources/ar.lproj/SUAutomaticUpdateAlert.nib hash - oDbeH//n57rFaGaLkwI8UQ/txa0= + LtkONVbhTzwCPtbjkr06qSniXCI= hash2 - dQ68sBwTswbrSiq+ls8SWXOAQ9cpeCqQBQi+Yru4z64= + CL6tBm495f4yM6z7y8UHRhtooR3NLGfDUOMHspa3d6k= optional @@ -2211,11 +2211,11 @@ hash - rj3mNv5WCDqnrsLmtCWdWwnQzJE= + JeZDdP1OuZbqkm8UKYiyH00A7ss= hash2 - 2vErcEF1YAaT6SZXgz6raIMCUQYxXdMB3QkILHlni6k= + CE1qJ1jrwUiTKTlZajb/bhplzo/rdEH6pm5cABwD/rQ= optional @@ -2224,11 +2224,11 @@ hash - xsWIpjC5TTnPtuprvEvnTUMyKYo= + Heb65H1UseXl7rEaFwVxKauBWnI= hash2 - c9whsShePvQ1tRA1xU0wB92pYmq20rnE+IWc6MwPWlw= + WUx1KM1Bz75vbTlcj3FvUEEJ3niP2QNBe7/lPioCMgY= optional @@ -2263,11 +2263,11 @@ hash - iLsEEL5N+u3x0igCMtLbvUxqiI0= + YpT086oHMS9O2TvSNLZh+39oy80= hash2 - cts6s/bHdYbmGip/jv7k0pj55p4HJFj0ROZcwO+lbL4= + YyE1WN1/ryPt2H0D9gYJv/r0SSv8VYTcxiiNeELiJIQ= optional @@ -2276,11 +2276,11 @@ hash - reUhWQTuJfS9z/Hf8LwQASnszvc= + v2ac1JQZvkm8EHZiTUc/q4aBcU0= hash2 - HUwSYNKaqdTpVIblAHt8y56HJM5ZGo94XVl+0j9Qpfs= + LWRxgLZHNGYOe63gf0aOD8zoP03Z1s7ldxndzkWbzGw= optional @@ -2289,11 +2289,11 @@ hash - AvWpT2/bR/hQ2SrkSIJn1HaAXi0= + 2ANG1NY1o8ndm0xcmHwYUvrRk6w= hash2 - DigL8qo0eo3Dcd1Pjnb2SN7WfCYMB466pjCyjM/N+u0= + dyM1bkEKAH1sW3J5pxDKHpNZ6ZJX7YH/x6jeICRqpkc= optional @@ -2302,11 +2302,11 @@ hash - OjCfTDR+NbVLvirUPiJKJF6UiS0= + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= hash2 - 06z1nY8VfM4M1SdHCc1jqewmP5Ue0g3mPDcNbeDVNIM= + RfJgT2b3STcLu71+1iU9ZcSXbfwMWG1EE1C7Wrf3xBk= optional @@ -2315,11 +2315,11 @@ hash - u1F8d4d/aOeByXDdvXMugGpU/Fk= + XCqcLv38cTpbjAE4zjN/JWeT3+U= hash2 - brs9IleYaJnps9htzlB1pQR3SzgDBlHi8EzhO+dGBHE= + ivTLD912Rxy2BqIYFAQjsqh4PofwMLljqh6ncdYEdy8= optional @@ -2328,11 +2328,11 @@ hash - ZW7bZ9L3EhY/tyvqZWgYznuPaTY= + BY0imp6dA7C0GSOK81VXTJsRccM= hash2 - SDIBQ7s3g5y2vcBJ6P79ig3kkYSGLORFSwVXhMP09xc= + BZL9bUc/f5RpZHoQGkA/XXKvykMh/LwkqI+1XW14Bxk= optional @@ -2341,11 +2341,11 @@ hash - uMINTHGG6dgMjyUAYQha7LXmlFg= + Ev2Nvw9c6bVU5ZF63yVhcyNp84w= hash2 - SSiMHMhYqbVuTek2hXbaVXclcxyQLxC4wQ7GKW7Hs3k= + N3Os+6xHdP9Y/QLv2okENWzAaaY73ZZ1wAa+vhQKSWg= optional @@ -2367,11 +2367,11 @@ hash - VRG/ZYuz0KOyMjBw4bu4NIlO3MY= + i/BaQXOSENNulhl0b5jssezuU3Y= hash2 - pBFk2uov3ctfdeBe1zZCawQpB8Wc4uRFVFly419/710= + vZn/tXvSzWZPBBo0cVnIiPjRYfiMEtABs2gGlmJ3DKo= optional @@ -2380,11 +2380,11 @@ hash - cT+nRuofaW12I0wjvG697gXwMfA= + lieYpCoCaCKAA3EL3/EsBr46vqI= hash2 - OX6zOOf44FBTL/dEMERt3B5/nGvLa8BfaLT3+5PocVk= + pOQG4CEenyMCs6E53Yf2+yYR99NwtjC9ESL6Hp719iM= optional @@ -2393,11 +2393,11 @@ hash - P1qisftBIKKBMlb5+8fD2vZn8C4= + oy0dHoyKmH2uV/KCHJzCagE+QIE= hash2 - MROTTYkU1LpXor4Z0t/PYQmJbNapMi1hN3PqN3qTm7I= + aXEwUfPGaGK1ndjF84VGCstTDsw+y3qn6bW9197R/wc= optional @@ -2419,11 +2419,11 @@ hash - ccB0As39qPJ0x4XkN34YZI8IUHs= + s3rpfaKP5+1+vGc44qpcWy+h0t8= hash2 - cG40LBFmRabj9NvgWnkU1ktlhXXqYwQD/qBRmOplaE0= + 8Dy4OJ7vlhUCXCV6cjyExPoQWOtUSRnuNLpDxfel5ss= optional @@ -2432,11 +2432,11 @@ hash - 8BqXnIfQOb3RBiybY1uUgI2a0oc= + sJcnQqAH4BsB+2rz9riB7iqePh0= hash2 - lHU9xUOsSTjNngzu5IT7RYOa0xkhZPFHMdRdYTgUOJU= + oZ9SfHTeIGNZtJjH75VsT01y5Vo2tq2VCPVF8bDddeE= optional @@ -2445,11 +2445,11 @@ hash - WujzpfOdW+H1RXGYDSAWcHDghE4= + mYyXqqWSoYqVG1zNp1vopIw8r1k= hash2 - 8sKAhoOk1Fg59L9B1ivTMLhtDsfk/jOPoHQLdNEiiwk= + j3xNys0dFAL/2iqvjfz2PopHNj9kPZSLHI5SyE8Pb5c= optional @@ -2471,11 +2471,11 @@ hash - smFdnjqh5FfLp75QJkkVpDg+CPs= + Y6bIF/+bAP3t5gBwPcdqxsj4co4= hash2 - e6c/DVRcIt7/HDR6s6fclGtvSgpv8tHSEETWVffaFTA= + LK+XUVI/B5vkE00baFJQzgTVPcWQu2vfztwnjkmtAdg= optional @@ -2484,11 +2484,11 @@ hash - uQPgA8QNrJ6dkBofaA4cYiep7Nw= + L3hoxekBQAtpmyDXNhTX7kRXRtc= hash2 - barcVEzpJLLj7IF2fqx8fLfYaKNig4ysWENyX9ecMSk= + c1eSzlRx9vqCBLiF84w+iiiGeii8RIOVaoC8Ds3gndI= optional @@ -2497,11 +2497,11 @@ hash - jc/+PgCV2twCZZQSjQHHAer/o9M= + uLKIwoprHw35+b4+/KP/j9X2zVg= hash2 - GHDASo65+rZmj9G3godOHzOaBA+plsqF/Q+gh9/xVPQ= + kXFQNOUYJFVMleLIk/wvetRZoFi+Es/ChIGsKEkPdTs= optional @@ -2510,11 +2510,11 @@ hash - i3TNbMzmKR52wTyfnD/bkZ12elE= + cHZov5FaqzfNhnBo0XdRuTMT4SY= hash2 - Xl/5yA/K9T7cscvPi/4/lWUtjJlIvO+esCF4SRaguz4= + 39CdfZZ1CQQz1Gd1+Ukxo2JHl0XESoc/cqWKF091WUk= optional @@ -2523,11 +2523,11 @@ hash - t79VYhRMP0dQCdKKtn83sAtFAkU= + BoS6NAq1zyVcmkbrKJhcI9Zrezk= hash2 - /ekWm2QPndpW/HYcv0rdJxLkqjRkGoDMoILTYBpUEWk= + GtD3UAnIT5BoshJo4areAKSruPfavkvTIyNd0gjejDM= optional @@ -2536,11 +2536,11 @@ hash - BPV01tBvjSwDHnD/WkxFJ+W7eFI= + UMa1QcJf8zfpVUnIZUGFfJ64wTk= hash2 - HgGgfZG0CcxhaB8YAZyY7fXR7NmPa2QrJ3HJAtq0NhI= + 3KPglR1oBAj4L7IA3Y4fYKtWrk2kpbl7jPZwPCByWfo= optional @@ -2549,11 +2549,11 @@ hash - NrVNwqMfVUN8CR/qLP3WzI74QKg= + tre2iSm68OK3ztgNotyXuz1MkzI= hash2 - EIPFVayBB7EGZiWM0iCyDJlAtLFcTa5YJ9oS5SQIoBM= + nPkeNlTVifGs1wwWJbh4cKzN6KGsoSoFWhhX8gcD+Zc= optional @@ -2575,11 +2575,11 @@ hash - 3Jy7fCDJXPgxfGrSLnrq/BrR8Fo= + 6aUnn3XSgWKnVuYVA/PVSrwora8= hash2 - roOmNaY4r8XeA/0LuIvxyfyG0dbL9R8qin7RVs3KbOU= + QVnq+Bn52L27jHSSWCd5PGIBzAeU4HwlTMuL0+M3JMI= optional @@ -2588,11 +2588,11 @@ hash - fWlU1Y804x2510X3QDdE/kgfBjY= + k8QjxmBhk5B6v1fGnFkwwX6oakg= hash2 - XHd3Zg700vX7/XedyFMLwFCeM8dTStXSd9D7s5qQl3Y= + R5U4ry1iLGtnxFs4Ex8GdV7tpXKbo1HoH0rsSQKIO1M= optional @@ -2601,11 +2601,11 @@ hash - phFF3/dcygmnknefGj4i3cgz294= + HB5ASms7UIZfv0WaGh6tCLBEDP8= hash2 - IKx5AqlulXQOTJCPJWcWfXHip04AcQb/tu/b8qgLmDM= + QgdpF8+b+1E1FOKUQmo9gfa/0naxptxTvxgPAiBPnzc= optional @@ -2627,11 +2627,11 @@ hash - RuJWtV4NU5tNgg+IHFiksMNUfIA= + Nj5v0wIECbpjSTU74xKBngH4DeY= hash2 - zY+dWgnrRmrv5j1zVDFX0TRh8F7uDUIO7JNZASTNiLY= + iJPr/YNl1hGgBNcbjpEttX7EgNKwsu+R3oVtXyoxxV0= optional @@ -2640,11 +2640,11 @@ hash - Fx5MmhvVqSx8rSHFOIibPCl3xBs= + RPgJubd38D/WH1H6B1jSejqILE8= hash2 - CAUJN9Nut+E/KiLnEKvskkGnl3GzIgL7SaK4wkCAaNs= + KtHl8n7bnlZ3Ir0ymG0RdRWNezTCdzyBh9HO0AB2TrA= optional @@ -2653,11 +2653,11 @@ hash - ZwgBvgFW99rtuKNAPLcCPieEdM8= + cVJfS2Nx3QvdbWEq+tSt8xi9hIg= hash2 - 2jQtALwqUDzChWK+/BdSxUffDRRnqjw6maR2HjHeouk= + LTWELwsYH8j9IGZy23C/qmUvJ0/E498TrPfWsXFOM8c= optional @@ -2697,11 +2697,11 @@ hash - 3rBSNPdcL/Nl7+0rpUZqGhljLQY= + SkgPcXJYp5dizLAgiXfyl9EsPoI= hash2 - O9VtLe/h827vQxnbkRD4puhEtK/BHASbao9oBqKkln0= + 2h4d04V7H95/KuIy1kBjzDQqtfxnZrRzKlxEjk3NYRo= optional @@ -2710,11 +2710,11 @@ hash - +OYMTGeQfzz+9ImWkCbDKJG4N6w= + 47iMWOA+94RZGJW+QJCeM4xOUsA= hash2 - Dkj72VU5xRyMciYhJaXanrauMtbMEiE9xDd/RsEKtao= + ReDJwTVXlm8iJWToPp6haL3A35LkgyNtEYfEYQ38l+A= optional @@ -2723,11 +2723,11 @@ hash - eWFDPqngmPB+nrvPXjpU207PI9w= + S2YV0JmEwfPtYsMBBMuvddrPEis= hash2 - vHYoiJA75oqy6uH3P2vblP9/RSwVaKDIO6kIlzx4g/M= + s4Q/66AafiJ25LzYFLwYJRMdqr1W2awMSkxlZjy9JtM= optional @@ -2736,11 +2736,11 @@ hash - vyI2nHyZlhhSXXrQGoflg8oB9Ig= + 7LLOVs76ioMwEDV8Gah+6sV/5No= hash2 - I1njQLVyH2bFsJecQOLDAB9N8q8niH7u+hqzxRqqerA= + TwklhrooHTXgV6Q9fbvvAB3mPIh7qDbEsNtUzo2fQuU= optional @@ -2749,11 +2749,11 @@ hash - KqZCmcd+fhq4qCb7DbEOVkCcC5M= + PfYCBbOThC1gBDzoxD+ijdyQ3T0= hash2 - u6rYN30G+CjP88+9L3cf4IevFhrHkKalaX+R0Job43o= + C9vH4mEYy0VzQEvjXYfCMPM4ggBQF1APABRkUOUQwPA= optional @@ -2762,11 +2762,11 @@ hash - VjGGKPTkcvGmYWc2ejOXIOR2Pec= + 6RlHCvHc9GNh1M7iJhvn12iFGpg= hash2 - f++tXz3pkLWUBCHeqRoQyUFy1e8wbz2nK0Amh00bag0= + 8vAkRUe47lFmMm7zUZM55/XRK21KahmSbRy0Axp6gw0= optional @@ -2775,11 +2775,11 @@ hash - 5EiQOlMoRKSojP+6FNbyMwNhgVk= + 1Yz7vPBCFCly2cHjtbQJPK9PzjE= hash2 - ry1VDAek0QDeYVURAQ4/zvNwMxxl0K3Ke6cJgWCFvNY= + GyEkgG0mW1s+T6Nz7aQ/eEvLYoysvr7BYots62oHX1w= optional @@ -2801,11 +2801,11 @@ hash - ab4gaq47381T+4qaedPAv/JPk6o= + 9eOJ/dQvTMu45Z1UowMPaKHYQOI= hash2 - 6ca8INJMEv0U7uSPFflECJqr251vmLQ53o/cps10vCs= + QpgLimdJiwdp2DVF/DMQyJ2Zg8L2ihsreE7vcI8Uqh0= optional @@ -2814,11 +2814,11 @@ hash - HC1ZY4ZwpdC8g7whfpr8B68+Wsg= + F0aP96zh7QOSkAdFsBlIzBhmCIg= hash2 - DTba0O8vZoZc3qpArrUqtl/IOWetvAix+U2lOlvSOyE= + t7A1i/lrse5T6UZtyfTdB/7HRH5vPNuOj2I+QkIjAEI= optional @@ -2827,11 +2827,11 @@ hash - KEnwS7P4M7RXREXDBrYzFPAB5VU= + xRBgLwOX0xZhrXGjHDHL6S+qCQc= hash2 - Yq55K5ud/RB3Wot/55dgW5/njRC/el2lgpBybaKJU18= + VWBW48lOFIc7lprCjCV9s4BfRYheTgsJnhe5dnQbqOY= optional @@ -2853,11 +2853,11 @@ hash - /GUIYGR9WmveeKz5MV+ulysFDGE= + JXGMS3rtry8HTWEbBibxVCkBEmw= hash2 - qbC2gUIWl9w6DM8zYA93h7iq2wBmVuEwKk4fT0Rm5bw= + 5+DJlDvCzlPxtarex6vBp6GBNUjc//XUg1dP+YKgQvo= optional @@ -2866,11 +2866,11 @@ hash - C+frin/RV6PMceoJWhnBHw/UiSo= + W45+n9zTkxt1E3v6cZYLzXNtDlc= hash2 - wIkClqPQf4IXnoNjs+HkDal17JIkVt2EHOlGgYO/H/w= + dMHKLXO9jQ/ephXEzJ8zaCiJ2TD94Xdtlfwqn0liUVo= optional @@ -2879,11 +2879,11 @@ hash - NU+8Up7T7RaEJJ6XUGU315MmJEA= + o6d6uYDAajCHTJJOXT7zDECTmIM= hash2 - OjABEzLU+FuhNACHA+RNl1F0avqHGhKvVesU+kuWrHc= + abK36qjugFrOyJCWuehmLWtrUUaY2xV+kVIPmNJXKjA= optional @@ -2905,11 +2905,11 @@ hash - WXXUfAzkoQeox52T+w/9xy/eZzU= + 9fRyIZvZMUaRTaWCda7NsPqJle0= hash2 - EeiyXpNY0m79CbZ/K5HAW+kmg26NXDLBJxEHPscuSVU= + 17Hm2P7maddnurefS4zzxjw/i66hgAqMfPCnw5etp8c= optional @@ -2918,11 +2918,11 @@ hash - hdYuZq1xueHEJ8e5TeRx/9nxlJA= + L07PCZt4pHgRzMPxV0N6F2QK9kM= hash2 - nLQRmyyyE+47KEGz+XIIPKURIitfBD8zHjx1FRw1Rig= + qzSS0s4cMsrK7155WvW8tp+ToVcFs5pmCwrBC9Lr/ec= optional @@ -2931,11 +2931,11 @@ hash - PLPFBaQnQsXytVrJabv0nvald10= + b1mgRruuDPTLXfhBEjTV72kV1m0= hash2 - yX5t1+2etONNwDG/m2gZsp3+Bff/OKNyNOLDFs7l3FQ= + 5nEPrrpTyzn6ealGjKbkHDtrZ2hvu6zXmQjlL7x8UGY= optional @@ -2957,11 +2957,11 @@ hash - l0zDEsFdyHwzPuayHe8R2wkbv0c= + gi+8llNjjuFSKRkNAA7vN/mf1uA= hash2 - vQKakhmXQaXzDmNZ3obfuFn64bN4WryGbv4PhhFQHwg= + xhSLkCd/oWSClxzjFJOGmPOAS3u0od2to6thPF+1hi8= optional @@ -2970,11 +2970,11 @@ hash - R9RsJCIJrFMpzTJNUsMio/R2jNs= + UNAQQTvtG7+MN/4w4ouu9ZHXfCM= hash2 - GXyd600apB2bkqxvoE1TGtXaubIgM5Ax/FVnutaFaDM= + rU7gnpi3PsnD0n0noPJN7LKUwxXFHCjixcW7WHa1q2Y= optional @@ -2983,11 +2983,11 @@ hash - o0FtQN9F2+i8SA46MW2lvxZAFfI= + ycrHyxLA68Lf8rq4IXBVK62UpGc= hash2 - KwRitjy5RjA9+Y1cWmvv2CPfybp1Uyx3sl3VWZLk0gM= + bvNquF3puRjnZvG2nQUQsz5WyXUsO3LC6BCtEjw2+9g= optional @@ -3009,11 +3009,11 @@ hash - 4ylPEFzA3WhK/BiqiKjlyeehlPI= + kJLypTD4VsCOwsXiOd6700pn0Cc= hash2 - rqcn7eEk4ZPHhVzzdFKnA7+WveGWS5Lph3/wPCx4Ftc= + k7N16zCtXUz9SCO2rA2PSAb+B9zsqeF9kPAlIPeRQq0= optional @@ -3022,11 +3022,11 @@ hash - Kcse19bpeWkKxkDiEabgvpb2Dk8= + WIJIO1qR0uNQtJpVkhIarVOfgFw= hash2 - p8QGRW/hKOb2t9wFm6JsGGmu5w5UdmjVs4fiCui5KZA= + 8g/ElSO4yIXKBPWT/sbST8vdzCsbEVIeHZSxJbCzJ4M= optional @@ -3035,11 +3035,11 @@ hash - mS+H7dDl1Yn5HoNY6FXQ+INWQRw= + sGjRl91qI6175CwJYuqSYLYylJg= hash2 - Z2MTlzTbGARVxBQvKQ452Bkq9u4BnPgCG9ZlOOnJDoY= + /oxFNXMvdoD2D7ykStspwXUrcEpRCaVbYebxNXyRuI4= optional @@ -3061,11 +3061,11 @@ hash - DblM97MbrUGSoF8qs4qy1pFAvXw= + wPmqlbK7p5mjPDrezXrYK8CG3NA= hash2 - 5OgJjQxAR7yLmtz6RBIsr5OkwcXnth/cM63p2FvgOw8= + zV9zmt6+b1fbS8Nzwh35PAkW5vdYcJ64kjUjQyde+Mo= optional @@ -3074,11 +3074,11 @@ hash - mk176kIJR7A90H6Qq+hi8D2Q6Vo= + 7EXAJEeeN0k32jvOHKr7Icq4644= hash2 - pU7sKL/A7sGeR2YEaEIeUGlJVwknXecQJwROC5+SOuQ= + 3I8r+QxaqIEqBcdZogXU5VuDg87Ls1S5Ss1nhmfM8Po= optional @@ -3087,11 +3087,11 @@ hash - z6/rLqlkgii529Dut+XHavZ4Pq4= + QHLHLZGOJJ4eN75aG1K1VUHNPz8= hash2 - J/b7Dlfe9gRCdvIhwT49YgjY4NCJbixIAQ1iDQvYb7U= + arRlqOWae64Pqn6yNw466S0RNtPGSxvbi7FEd00g1x8= optional @@ -3113,11 +3113,11 @@ hash - kWDLMC25Z+sfNh5zpNFI8c9mBOg= + 4aIS8LbPLTj63EhS7xEk+qjNzD8= hash2 - nB3ArKP+IGttgVDlWptopBUWsiW7pUEg1SexiKHRpeg= + PvUb+gVq9mfBJ4c4Rp3cyMOr6Fw7EqVwOSYCXdskwUA= optional @@ -3126,11 +3126,11 @@ hash - QSgvMCGfu3qwt4LSPDoITv7Zj30= + pLII26utl65JwmAFqLiMefDsrGs= hash2 - MYMDphfT1GFsLte5/xEl+sPWNm1UZ/WHfkSku7xucAQ= + KNo3HyQuwczwWYBLgpAwz1nk4Lo/IuCN4SZpFhn8diY= optional @@ -3139,11 +3139,11 @@ hash - uf+l2wCxifW3p/OPcOg7gXrO7ec= + pZo0pXve6jqBertG5IixAzSpgV4= hash2 - yyYkFwGRv6WZVMDrQWsMQ02vJnELSDjwxkQf2hdtEtk= + ILzDGh3jJc3hPKR/ADk0jiaDQaHZu7Zq8YLRXT+OXjc= optional @@ -3170,11 +3170,11 @@ hash - 93bUtDO+GDxBXAEl9unvmFe1mWo= + 8fQxJZw+LQiExfyPaqUbNnASsWU= hash2 - QKkX1N4sEhMe7SjVGf/bu8FiVf+NExALLtf9w1suM0A= + 7Xu6H76LF/BEqK9o0LJHpt54NV3JbY/TPoinZv9GfSQ= optional @@ -3183,11 +3183,11 @@ hash - E4Vxj5LZDhvc7xttfztZBkDwo5s= + zW5zEa6rDpqLuTDfixuKKE93E5o= hash2 - X+MqqDxYn3dZQ6CToFhySC7HpWnL3gnWKXHqoC79jcw= + fbgvSsPkysitkmLfP26OmYpP/r+044gpfMHqQoQp9Ns= optional @@ -3196,11 +3196,11 @@ hash - XF0m/MqiO9kZb/K0uWKvnIi0jbo= + 2dtbduILRWtmfjobyd2yOVhQNH4= hash2 - qz2/kSLfvNv6AT4fTkGVtPL4dgdZUVB1kEpLziLBKKs= + 18VMio0CRtcvr8i0M+O3/t41QS15KVxSGxoVhQYapxE= optional @@ -3222,11 +3222,11 @@ hash - H+9mRnaSNpf3x2lnvZJQYmNOeDE= + Svc6e9pHrp1wA7rxw7oKc5HB8qQ= hash2 - jYB20+6wcyncLcSLxsM3ttyN+vgEUqpYwMIO24URR18= + L4KDh1UJEm3ta+qgzCe3s3RI7xXrAY8y5h5eqneS3Uo= optional @@ -3235,11 +3235,11 @@ hash - 7VloEUr/e0D0uiCgmSYM89car10= + NtD144OppS+BPXNmsReGJoN2Qdo= hash2 - rAGAuAtK5sQllVDRJhBK7Ad9lDQiCIAq52juKTIIR0g= + NOQ75dz/Mq7PLhwssExcWXdtTFQzx8m/lLpLBDCaZCI= optional @@ -3248,11 +3248,11 @@ hash - KRVl5KSZqqi3JII8+JbAWE/vyeM= + 5B8hYUrKag0Unyt6Uk0D2K5opL8= hash2 - J0mpkmwJqhfdt+rXpPy1h0DuPIHwDPnH24upIZoHyF0= + D42TQ5lV8E73WOOXTsUDSu2jqOFUt1+WMcLUTVab8W8= optional @@ -3274,11 +3274,11 @@ hash - 5qfGbw+bZ8nIzRjTdFCQ4ajYuLg= + 7HEo1dlbwSnit0+4DsAqKDz1jR4= hash2 - fPjA/C43Um7wWvUaur323ogQW0AuyPed11ObgYGvnZU= + KUqcmkrRCh+XjXh9F7fudt94MreG4bKqDh1PLat/FpI= optional @@ -3287,11 +3287,11 @@ hash - 68qUvif5bsy3gh88hEcrGvT0sg8= + FPWtaRuYrVSPrfAozq/4bSQfMK4= hash2 - BJcZCaOTTIniVHe6UVhTM64qvlF1YJjpbVMjcR49RnE= + 2mIpfTwoRvVuhY/Aa3Bqw5VsjpV93xFNcBMQG83Q8DU= optional @@ -3300,11 +3300,11 @@ hash - SweZv0/gqhOhvQDoTkvh0MNpjxs= + GY/ufItfyKYpgw54TfqJlPlymb0= hash2 - Kz5vNOAnbJxLThCGzwuqO6EiiHX9RlZzMMQvEnWUZZQ= + hMvl0YhsMlkusdQxVcUiUDeQQqNn/KFwXfIqWaDSrG0= optional @@ -3326,11 +3326,11 @@ hash - 3ymMt+zeZFb6zTVVK2kHUBbiGsE= + YHane6xWVhvpJGf5HuoxCyQ/gDs= hash2 - X7YCJx6kmSYPVxAUo3p1eYRwgD7S5hBHZgBQOZQv8Qs= + qwuyYXCO4H5IuXiRhZR3ucGl5S1CtG2e7kxxCkM8vtA= optional @@ -3339,11 +3339,11 @@ hash - Shn/KmVDoD3wypQA4uFjnIJqQzY= + NxM+W+qAegxK4lKy0uzCclpkVjo= hash2 - Vxdy+ff5uN+MXH+NI2M6HpJdJvwpXqhAEtGx2mO0eUU= + Kvykt9h+o+SUuINROlMCXornVL/uEH2Uz5Kd0bNSm6k= optional @@ -3352,11 +3352,11 @@ hash - W/T4ULA1IJxyDbETvrpiJrnX63w= + AqJRrBMp2yA+umSXxQIQVmpnCN4= hash2 - P6Zo/1jCMYHERVwoRx+khhdvH/u5iSsm3+RRCwNMfAg= + 7Guwtd2PDpjTWg/qX1UODxgRfQDDxNUgN4qr4Siv/Cc= optional @@ -3378,11 +3378,11 @@ hash - dkv1LXTd6KP9qyGoGQWM/beHi58= + 5k+e1kFtgoVo77RhyhZSXpRQGOQ= hash2 - J5G58cAJGJ2pBsqTcrNOkofcgh2VfGB+0uws94G6/XU= + qZSxDMjtIXyNwOZYeUz2g9w6NFmKFStqy5UbKU9N3BA= optional @@ -3391,11 +3391,11 @@ hash - ZYpm1VcCFFWBaIzjYYwhekhYPDE= + l8XR02tvN10SX2aM9CtZ7BpIcqw= hash2 - 6cfM72jyl2AyPw5m9Th9KetQkc4sfI9UbLDBu6cTLfs= + fD0W6cMr/MZ0C0qrsMD3jEbXdK+eq8L7tl87ZPDspmY= optional @@ -3404,11 +3404,11 @@ hash - WHCeNcsEET1G546RnCG1GQc9BTk= + burLhfFkzbPjAIqMXw1qKn94xm8= hash2 - gOYxvZJa69CV4kNRXhXrAUJXJ2q8G3hoIi92tYheA+I= + D5XPrSBItdufc2zVCFazX3SEmRVqQBdCZ0ADlLmHE4E= optional @@ -3430,11 +3430,11 @@ hash - M8OEz9UpXZ+CwFLwnPmpw73qCRk= + htsZnsf4S+omnk4Z8En8qOOjrPM= hash2 - AEBmaE/8o6IVDOwbRj2uIV9ulZZbNumwcgRnx2PRX3M= + 9kzvdHcdVqdxsioITBt+Am06twXcJpKMreMKHDDj4RY= optional @@ -3443,11 +3443,11 @@ hash - qRwnTGgz/ZS11VE0PbfWNnLPg0o= + /9xrGwHXOdPKFp82w4OjF+Q0WG8= hash2 - UY1iVEmOI8TR2drCQMvPEnzmLwf4PFrjsZAbsRIhqWY= + r48ahwyC8EFi+44X/EtUfWvh8QCu9klpjqwwwzQHiXE= optional @@ -3456,11 +3456,11 @@ hash - TLk0Rv9HN/Y8bYaF3vr8kg9KPuI= + lT25Wn73ZrIgcDJsgzSvIQ97TtY= hash2 - GauWEEqjnCIluWc2cIye1VCm3+HMs+33/ZvlpKA+mrI= + L8TvfX3/5nLCXk5oNeUzyieunqgEUrKzk/t+6Ldxn/0= optional @@ -3482,11 +3482,11 @@ hash - 0FRDiL99dypvNgSR6/b2hEh8LC8= + K0QpkucJxh5CRA9TYJCT5+gSHZY= hash2 - mG/8PYApBOsVBpFsNdtopxfzO+fhFwdGb1fDlN9X3nM= + 2GEt92QWRuf/s8NnoWD/HidalRNoLUoeJoC7UM3Y2Ng= optional @@ -3495,11 +3495,11 @@ hash - nhRSdsw8i1SblxHBOLBvYjBPO8g= + dOxfomMC/X9MFFdsk1MyjKv1yi8= hash2 - 3xO38LcvSzV2aayRGeqxuwRpeAHtVtbW97n30hTtHJg= + wIPOqj0JUePKauMIl6sT8YUjoxw1q36g+rrQvwkxfw4= optional @@ -3508,11 +3508,11 @@ hash - 6qyk1r0zm0331mer2SxkuzuSm64= + W5vyz7ueX3DVKxQC82/3FnvJfeQ= hash2 - pNhcoQht+GSCe3u2VN5VMnksoSO4T/x74rgSR8e5kYo= + C0r3bNAO7WAAZa4WiYUeBQMt+kmEndHyzPT1//HBsYo= optional @@ -3534,11 +3534,11 @@ hash - 02wDrcnMUo4dg7fm4RSe/Sy2pFs= + /iyQcDW4+Is40OSlKcWlEe9zKdo= hash2 - u2L+Ej2lKhOG+lt9pl3u/uFrIXqQRz5/KYWiLvs7DmQ= + gFeu5nmtM/aL1yTSsW9TPh9ZdN8r/lX5jhns53LlcfA= optional @@ -3547,11 +3547,11 @@ hash - j2iSaoDJRMV3kq+UFuzkbVAojSg= + cQCWeOMdd6mZEd9k0pl3FrZDT9g= hash2 - a+GnhfwduPlBKDaFP4pbkqIWkjh5Z3zrRFnk5+z3N5I= + vmbxfC0cI7IAAQRNDQ43V6vocH93TiejQmBr23NDAlQ= optional @@ -3560,11 +3560,11 @@ hash - Pb4ma8CWTT89suTVjHa3iQeWepg= + JIh/Ueyxh8+us+26dxQRmrPiVAE= hash2 - OZ8/AwcFW8AnwDwmcB0wJSewEu1uLmk7AVahLPbiVyI= + Hbc+V8H4hfQh3PucUY1S+6vXRBZErhTaZLYJ0jQooLo= optional @@ -3586,11 +3586,11 @@ hash - 5l2VJ1hqtebg0BM6zmyz63tMhR0= + fjnMF3XlYLFPJSpmIFizoGwOVl8= hash2 - +1xo5rrW5YkvwMXLySevTi8kqkSdwcxdS8NnTjcfOA8= + 3T/cO/cEIAM8QBGVg53bYvcYseEMPOhn0C+yReVVJEU= optional @@ -3599,11 +3599,11 @@ hash - w4Qf7do1kg75nGVgX0TP8MHOCOs= + Y9dabfD0a7F1cV9OuFnyQL5BIIc= hash2 - Xc85Ymsn5+Gfq4XBPGT7FXZaNCnkcbdnsYhjrdf8fw8= + qwss8xg3cxWWQXqCLxXd6z1ygf6MHYjMDKtVGzTpF0M= optional @@ -3612,11 +3612,11 @@ hash - CT6KO7Zy073NY0kp2tcbUgEdNRo= + 5wxy4Op51XjVl1MvUlCnSUfvsj8= hash2 - LLtC9hMEtJiFkq2WjI6LK5NTo/dKtGHrnZPpTu8R8VU= + eXL2eqxEpbACqNQkixNqqVrLeqRbdnpPCndQcIXqdas= optional @@ -3638,11 +3638,11 @@ hash - NgeN7x6U1IHvnglysoHMw5YH7RU= + YRRVgJ26NZd9+ebTI3UGdpi35eo= hash2 - hJvGUThsw01zx2VSDgANljwnztqw6X4E6LYqGTafDOk= + /lUZP3n163WFPUjg+ohhY7pnbmtrxuc164ew0tFmDd4= optional @@ -3651,11 +3651,11 @@ hash - 5P3DitlPL1sqOb0WkiXq8ZWq2kQ= + PoeaXUHUKNIm0bkX+GNnvFHlq9w= hash2 - 5RfdwO9tONR0Be13RKAI93KHTfHTkwP3UVE3Pmgvrwg= + Pmyouw5QFzbN7VYg1RXFNm3IB4jOmZagTi9k8g2CyQE= optional @@ -3664,11 +3664,11 @@ hash - aY4PHFMl4vj5sqgVBNGxePP5CCU= + HEubU7VtIHZcWJ6RfdC038Os1gw= hash2 - A2qcg9s1wwOO5ybjmzy3+svoSbceqrK/NCshSDYyRdU= + 7ljWihx3qnfD/0BWdbNtzQirBF95hZ1sSXu5vTiVHe0= optional @@ -3690,11 +3690,11 @@ hash - qhLsJNXC4lbz9xO+bC41ILw8CUg= + 2pvLfCu7EiI6OkCxu3+aLyeTPcU= hash2 - lKSMzLmYxfhhNJM9lKXxXxLgtoEB8N78Fqq3FfQguHc= + tu9oMdBCiHiyRSJMEmm5x3oGY3aCnmwXPsRf1hjj7fY= optional @@ -3703,11 +3703,11 @@ hash - dQk9OZiBuGL0rOXiWivc2ld9Z+4= + +xvQE3bFW1QXIUggZBlZkKn0gag= hash2 - +GlPCq3h0uvVX3/pbHT70nCYcCTBUU4P09xZeodkbUc= + b6nNjM4vCBrXBbjH5GtbkPjZjJyMeSSBXO/tCRn+LFY= optional @@ -3716,11 +3716,11 @@ hash - TYmHdlcZRxyoGQ6FNldZKp8Xl1I= + FT+kQgUNxKGrbheU8uSqkYFSHtI= hash2 - Q6gWz+lFSfZb5n7fs9kPa61ZVmKklBCDYAPctup8+l4= + 2J873kYqy/0EyE6QXT6tqYkydTGcrw3/Ncv9cnSRl7M= optional @@ -3729,11 +3729,11 @@ hash - 0vdFowZuJ1qLE3rDgG3BZ9SlNRw= + OnR96Z9tB0noODRSYssSs63+zGA= hash2 - ecJXF6vvj1f80iCr+Gk52cTyumQrea6H0NpOjwbbLiE= + zvMbFdgVGI0ls9vIRT+sie7dj2g1UjQu7iS+pOgyBo4= optional @@ -3742,11 +3742,11 @@ hash - E7e3G1hg4vqRfzno4FdzvgH0yng= + KQcqbpIyw3yhI0eEyo06cNq5MlM= hash2 - ajeSjYpa3DsZGQzDVK9kEkpwv44C/eyrIxbxU+SHRW4= + POFOdG9uGjcVXkx39sW2Rwl2FJ2zs6A007yF1UpDskE= optional @@ -3755,11 +3755,11 @@ hash - bm6w3bCQbsrYbFszNCANq+DHcdQ= + 6/Rgln3/89vly1RFa1gBfRhITxU= hash2 - TQgKt3xRvEsmHbRLgWIJxg/xYVEnmDxUc7favyLd/10= + AFd6Yy2xbmYtz1+r9u+r9qNka3oTzho3/n3DCwxWKoE= optional @@ -3768,11 +3768,11 @@ hash - 1WN5IQ6j/AsxDG2fbAuKZAwoyzM= + qO2OAmNcqk2/bSzwAjGcXTD4+PY= hash2 - mlYhm7xduytdXH7U9j6ytinOA28ueShg37NKsyReI+k= + JNnNodE8g22fkHlnQzxC9vap/jefD5NkjUjmErvl940= optional diff --git a/ThirdParty/Frameworks/Growl.framework/Versions/Current b/ThirdParty/Frameworks/Sparkle.framework/Versions/Current similarity index 100% rename from ThirdParty/Frameworks/Growl.framework/Versions/Current rename to ThirdParty/Frameworks/Sparkle.framework/Versions/Current diff --git a/ThirdParty/Sparkle/Sparkle.framework/Headers b/ThirdParty/Sparkle/Sparkle.framework/Headers deleted file mode 120000 index a177d2a6b..000000000 --- a/ThirdParty/Sparkle/Sparkle.framework/Headers +++ /dev/null @@ -1 +0,0 @@ -Versions/Current/Headers \ No newline at end of file diff --git a/ThirdParty/Sparkle/Sparkle.framework/Resources b/ThirdParty/Sparkle/Sparkle.framework/Resources deleted file mode 120000 index 953ee36f3..000000000 --- a/ThirdParty/Sparkle/Sparkle.framework/Resources +++ /dev/null @@ -1 +0,0 @@ -Versions/Current/Resources \ No newline at end of file diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate deleted file mode 100755 index e7c054ad7..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop deleted file mode 100755 index f7355ce40..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib deleted file mode 100644 index 0db550896..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/SUStatus.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/SUStatus.nib deleted file mode 100644 index 0db550896..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/SUStatus.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 37662d76a..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib deleted file mode 100644 index 5b92ee10e..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index a328d26a8..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 8d76772d2..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib deleted file mode 100644 index 6d50f65ab..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index fd17b58c5..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 8a7558750..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib deleted file mode 100644 index 6e0336e1f..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index 1e5dd16a0..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 7d3186df5..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib deleted file mode 100644 index 993a696ab..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index a28251c79..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index a5f23c8ca..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib deleted file mode 100644 index 5c0cacac4..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index b4b0879da..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 643cfbfae..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib deleted file mode 100644 index 89f7db4e7..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index 85a98b851..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 250bf1b6d..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib deleted file mode 100644 index 9e3ad130f..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index 01a7fe611..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 29475743f..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdateAlert.nib deleted file mode 100644 index 0eb20b901..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index af32dda28..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 6da162892..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib deleted file mode 100644 index 80a7ea9fe..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index b34645762..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 30c68d7d7..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdateAlert.nib deleted file mode 100644 index 8693cbc3a..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index 43c993948..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 9ac3a7296..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdateAlert.nib deleted file mode 100644 index fa26f7071..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index 5366d6cc7..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 768beb950..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib deleted file mode 100644 index 2c5015432..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index 8d9071575..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 5329daf83..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib deleted file mode 100644 index 9f34c0f02..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index df99d5952..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 359fdd3b1..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib deleted file mode 100644 index ee645583c..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index c05c936f1..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 3df405258..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib deleted file mode 100644 index 24d7c6fc4..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index 6131a89f5..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 8afc9a794..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib deleted file mode 100644 index 464fe83cf..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index 1d8c21a40..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 6c19e84ea..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib deleted file mode 100644 index 72ec70976..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index 2745a9693..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 7fb38ae22..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib deleted file mode 100644 index 7770c8748..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index c3cc61aa6..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 0aceaddbb..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib deleted file mode 100644 index 39e170301..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index 878210dda..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 713f39bd0..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib deleted file mode 100644 index 4ac653cb1..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index 5d62dc878..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index a35c2719e..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib deleted file mode 100644 index 5cd107ed9..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index bd546ca26..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 488f5d85f..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib deleted file mode 100644 index c4967d123..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index 5ab495b31..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 4d3100100..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib deleted file mode 100644 index 19b6d829d..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index f1773f7f4..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 1696cb9b1..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib deleted file mode 100644 index 7d28633df..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index cf882d0e5..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 823cdeb8c..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib deleted file mode 100644 index a2eb4446e..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index 303cccb88..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index a1a98b92b..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib deleted file mode 100644 index 9122cfadd..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index 5f6434149..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index c1173aa47..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib deleted file mode 100644 index 4784b3295..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index e76fe0533..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index e610aae0b..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib deleted file mode 100644 index 6d353b7ab..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index 0c9ab54d6..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index d92714048..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib deleted file mode 100644 index 77cd12278..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index 9a758df31..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib deleted file mode 100644 index 3d41afb55..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib deleted file mode 100644 index 74418e82f..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib deleted file mode 100644 index b47db00bf..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Sparkle b/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Sparkle deleted file mode 100755 index 6295e585b..000000000 Binary files a/ThirdParty/Sparkle/Sparkle.framework/Versions/A/Sparkle and /dev/null differ diff --git a/ThirdParty/Sparkle/Sparkle.framework/Versions/Current b/ThirdParty/Sparkle/Sparkle.framework/Versions/Current deleted file mode 120000 index 8c7e5a667..000000000 --- a/ThirdParty/Sparkle/Sparkle.framework/Versions/Current +++ /dev/null @@ -1 +0,0 @@ -A \ No newline at end of file diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/ac3_parser.h b/ThirdParty/ffmpeg/arm/include/libavcodec/ac3_parser.h new file mode 100644 index 000000000..ff8cc4cf0 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/ac3_parser.h @@ -0,0 +1,36 @@ +/* + * AC-3 parser prototypes + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2003 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_AC3_PARSER_H +#define AVCODEC_AC3_PARSER_H + +#include +#include + +/** + * Extract the bitstream ID and the frame size from AC-3 data. + */ +int av_ac3_parse_header(const uint8_t *buf, size_t size, + uint8_t *bitstream_id, uint16_t *frame_size); + + +#endif /* AVCODEC_AC3_PARSER_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/adts_parser.h b/ThirdParty/ffmpeg/arm/include/libavcodec/adts_parser.h new file mode 100644 index 000000000..f85becd13 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/adts_parser.h @@ -0,0 +1,37 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_ADTS_PARSER_H +#define AVCODEC_ADTS_PARSER_H + +#include +#include + +#define AV_AAC_ADTS_HEADER_SIZE 7 + +/** + * Extract the number of samples and frames from AAC data. + * @param[in] buf pointer to AAC data buffer + * @param[out] samples Pointer to where number of samples is written + * @param[out] frames Pointer to where number of frames is written + * @return Returns 0 on success, error code on failure. + */ +int av_adts_header_parse(const uint8_t *buf, uint32_t *samples, + uint8_t *frames); + +#endif /* AVCODEC_ADTS_PARSER_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/avcodec.h b/ThirdParty/ffmpeg/arm/include/libavcodec/avcodec.h new file mode 100644 index 000000000..c91b2fd16 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/avcodec.h @@ -0,0 +1,4142 @@ +/* + * copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_AVCODEC_H +#define AVCODEC_AVCODEC_H + +/** + * @file + * @ingroup libavc + * Libavcodec external API header + */ + +#include +#include "libavutil/samplefmt.h" +#include "libavutil/attributes.h" +#include "libavutil/avutil.h" +#include "libavutil/buffer.h" +#include "libavutil/cpu.h" +#include "libavutil/channel_layout.h" +#include "libavutil/dict.h" +#include "libavutil/frame.h" +#include "libavutil/hwcontext.h" +#include "libavutil/log.h" +#include "libavutil/pixfmt.h" +#include "libavutil/rational.h" + +#include "bsf.h" +#include "codec.h" +#include "codec_desc.h" +#include "codec_par.h" +#include "codec_id.h" +#include "packet.h" +#include "version.h" + +/** + * @defgroup libavc libavcodec + * Encoding/Decoding Library + * + * @{ + * + * @defgroup lavc_decoding Decoding + * @{ + * @} + * + * @defgroup lavc_encoding Encoding + * @{ + * @} + * + * @defgroup lavc_codec Codecs + * @{ + * @defgroup lavc_codec_native Native Codecs + * @{ + * @} + * @defgroup lavc_codec_wrappers External library wrappers + * @{ + * @} + * @defgroup lavc_codec_hwaccel Hardware Accelerators bridge + * @{ + * @} + * @} + * @defgroup lavc_internal Internal + * @{ + * @} + * @} + */ + +/** + * @ingroup libavc + * @defgroup lavc_encdec send/receive encoding and decoding API overview + * @{ + * + * The avcodec_send_packet()/avcodec_receive_frame()/avcodec_send_frame()/ + * avcodec_receive_packet() functions provide an encode/decode API, which + * decouples input and output. + * + * The API is very similar for encoding/decoding and audio/video, and works as + * follows: + * - Set up and open the AVCodecContext as usual. + * - Send valid input: + * - For decoding, call avcodec_send_packet() to give the decoder raw + * compressed data in an AVPacket. + * - For encoding, call avcodec_send_frame() to give the encoder an AVFrame + * containing uncompressed audio or video. + * + * In both cases, it is recommended that AVPackets and AVFrames are + * refcounted, or libavcodec might have to copy the input data. (libavformat + * always returns refcounted AVPackets, and av_frame_get_buffer() allocates + * refcounted AVFrames.) + * - Receive output in a loop. Periodically call one of the avcodec_receive_*() + * functions and process their output: + * - For decoding, call avcodec_receive_frame(). On success, it will return + * an AVFrame containing uncompressed audio or video data. + * - For encoding, call avcodec_receive_packet(). On success, it will return + * an AVPacket with a compressed frame. + * + * Repeat this call until it returns AVERROR(EAGAIN) or an error. The + * AVERROR(EAGAIN) return value means that new input data is required to + * return new output. In this case, continue with sending input. For each + * input frame/packet, the codec will typically return 1 output frame/packet, + * but it can also be 0 or more than 1. + * + * At the beginning of decoding or encoding, the codec might accept multiple + * input frames/packets without returning a frame, until its internal buffers + * are filled. This situation is handled transparently if you follow the steps + * outlined above. + * + * In theory, sending input can result in EAGAIN - this should happen only if + * not all output was received. You can use this to structure alternative decode + * or encode loops other than the one suggested above. For example, you could + * try sending new input on each iteration, and try to receive output if that + * returns EAGAIN. + * + * End of stream situations. These require "flushing" (aka draining) the codec, + * as the codec might buffer multiple frames or packets internally for + * performance or out of necessity (consider B-frames). + * This is handled as follows: + * - Instead of valid input, send NULL to the avcodec_send_packet() (decoding) + * or avcodec_send_frame() (encoding) functions. This will enter draining + * mode. + * - Call avcodec_receive_frame() (decoding) or avcodec_receive_packet() + * (encoding) in a loop until AVERROR_EOF is returned. The functions will + * not return AVERROR(EAGAIN), unless you forgot to enter draining mode. + * - Before decoding can be resumed again, the codec has to be reset with + * avcodec_flush_buffers(). + * + * Using the API as outlined above is highly recommended. But it is also + * possible to call functions outside of this rigid schema. For example, you can + * call avcodec_send_packet() repeatedly without calling + * avcodec_receive_frame(). In this case, avcodec_send_packet() will succeed + * until the codec's internal buffer has been filled up (which is typically of + * size 1 per output frame, after initial input), and then reject input with + * AVERROR(EAGAIN). Once it starts rejecting input, you have no choice but to + * read at least some output. + * + * Not all codecs will follow a rigid and predictable dataflow; the only + * guarantee is that an AVERROR(EAGAIN) return value on a send/receive call on + * one end implies that a receive/send call on the other end will succeed, or + * at least will not fail with AVERROR(EAGAIN). In general, no codec will + * permit unlimited buffering of input or output. + * + * This API replaces the following legacy functions: + * - avcodec_decode_video2() and avcodec_decode_audio4(): + * Use avcodec_send_packet() to feed input to the decoder, then use + * avcodec_receive_frame() to receive decoded frames after each packet. + * Unlike with the old video decoding API, multiple frames might result from + * a packet. For audio, splitting the input packet into frames by partially + * decoding packets becomes transparent to the API user. You never need to + * feed an AVPacket to the API twice (unless it is rejected with AVERROR(EAGAIN) - then + * no data was read from the packet). + * Additionally, sending a flush/draining packet is required only once. + * - avcodec_encode_video2()/avcodec_encode_audio2(): + * Use avcodec_send_frame() to feed input to the encoder, then use + * avcodec_receive_packet() to receive encoded packets. + * Providing user-allocated buffers for avcodec_receive_packet() is not + * possible. + * - The new API does not handle subtitles yet. + * + * Mixing new and old function calls on the same AVCodecContext is not allowed, + * and will result in undefined behavior. + * + * Some codecs might require using the new API; using the old API will return + * an error when calling it. All codecs support the new API. + * + * A codec is not allowed to return AVERROR(EAGAIN) for both sending and receiving. This + * would be an invalid state, which could put the codec user into an endless + * loop. The API has no concept of time either: it cannot happen that trying to + * do avcodec_send_packet() results in AVERROR(EAGAIN), but a repeated call 1 second + * later accepts the packet (with no other receive/flush API calls involved). + * The API is a strict state machine, and the passage of time is not supposed + * to influence it. Some timing-dependent behavior might still be deemed + * acceptable in certain cases. But it must never result in both send/receive + * returning EAGAIN at the same time at any point. It must also absolutely be + * avoided that the current state is "unstable" and can "flip-flop" between + * the send/receive APIs allowing progress. For example, it's not allowed that + * the codec randomly decides that it actually wants to consume a packet now + * instead of returning a frame, after it just returned AVERROR(EAGAIN) on an + * avcodec_send_packet() call. + * @} + */ + +/** + * @defgroup lavc_core Core functions/structures. + * @ingroup libavc + * + * Basic definitions, functions for querying libavcodec capabilities, + * allocating core structures, etc. + * @{ + */ + +/** + * @ingroup lavc_decoding + * Required number of additionally allocated bytes at the end of the input bitstream for decoding. + * This is mainly needed because some optimized bitstream readers read + * 32 or 64 bit at once and could read over the end.
    + * Note: If the first 23 bits of the additional bytes are not 0, then damaged + * MPEG bitstreams could cause overread and segfault. + */ +#define AV_INPUT_BUFFER_PADDING_SIZE 64 + +/** + * @ingroup lavc_encoding + * minimum encoding buffer size + * Used to avoid some checks during header writing. + */ +#define AV_INPUT_BUFFER_MIN_SIZE 16384 + +/** + * @ingroup lavc_decoding + */ +enum AVDiscard{ + /* We leave some space between them for extensions (drop some + * keyframes for intra-only or drop just some bidir frames). */ + AVDISCARD_NONE =-16, ///< discard nothing + AVDISCARD_DEFAULT = 0, ///< discard useless packets like 0 size packets in avi + AVDISCARD_NONREF = 8, ///< discard all non reference + AVDISCARD_BIDIR = 16, ///< discard all bidirectional frames + AVDISCARD_NONINTRA= 24, ///< discard all non intra frames + AVDISCARD_NONKEY = 32, ///< discard all frames except keyframes + AVDISCARD_ALL = 48, ///< discard all +}; + +enum AVAudioServiceType { + AV_AUDIO_SERVICE_TYPE_MAIN = 0, + AV_AUDIO_SERVICE_TYPE_EFFECTS = 1, + AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED = 2, + AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED = 3, + AV_AUDIO_SERVICE_TYPE_DIALOGUE = 4, + AV_AUDIO_SERVICE_TYPE_COMMENTARY = 5, + AV_AUDIO_SERVICE_TYPE_EMERGENCY = 6, + AV_AUDIO_SERVICE_TYPE_VOICE_OVER = 7, + AV_AUDIO_SERVICE_TYPE_KARAOKE = 8, + AV_AUDIO_SERVICE_TYPE_NB , ///< Not part of ABI +}; + +/** + * @ingroup lavc_encoding + */ +typedef struct RcOverride{ + int start_frame; + int end_frame; + int qscale; // If this is 0 then quality_factor will be used instead. + float quality_factor; +} RcOverride; + +/* encoding support + These flags can be passed in AVCodecContext.flags before initialization. + Note: Not everything is supported yet. +*/ + +/** + * Allow decoders to produce frames with data planes that are not aligned + * to CPU requirements (e.g. due to cropping). + */ +#define AV_CODEC_FLAG_UNALIGNED (1 << 0) +/** + * Use fixed qscale. + */ +#define AV_CODEC_FLAG_QSCALE (1 << 1) +/** + * 4 MV per MB allowed / advanced prediction for H.263. + */ +#define AV_CODEC_FLAG_4MV (1 << 2) +/** + * Output even those frames that might be corrupted. + */ +#define AV_CODEC_FLAG_OUTPUT_CORRUPT (1 << 3) +/** + * Use qpel MC. + */ +#define AV_CODEC_FLAG_QPEL (1 << 4) +/** + * Don't output frames whose parameters differ from first + * decoded frame in stream. + */ +#define AV_CODEC_FLAG_DROPCHANGED (1 << 5) +/** + * Use internal 2pass ratecontrol in first pass mode. + */ +#define AV_CODEC_FLAG_PASS1 (1 << 9) +/** + * Use internal 2pass ratecontrol in second pass mode. + */ +#define AV_CODEC_FLAG_PASS2 (1 << 10) +/** + * loop filter. + */ +#define AV_CODEC_FLAG_LOOP_FILTER (1 << 11) +/** + * Only decode/encode grayscale. + */ +#define AV_CODEC_FLAG_GRAY (1 << 13) +/** + * error[?] variables will be set during encoding. + */ +#define AV_CODEC_FLAG_PSNR (1 << 15) +/** + * Input bitstream might be truncated at a random location + * instead of only at frame boundaries. + */ +#define AV_CODEC_FLAG_TRUNCATED (1 << 16) +/** + * Use interlaced DCT. + */ +#define AV_CODEC_FLAG_INTERLACED_DCT (1 << 18) +/** + * Force low delay. + */ +#define AV_CODEC_FLAG_LOW_DELAY (1 << 19) +/** + * Place global headers in extradata instead of every keyframe. + */ +#define AV_CODEC_FLAG_GLOBAL_HEADER (1 << 22) +/** + * Use only bitexact stuff (except (I)DCT). + */ +#define AV_CODEC_FLAG_BITEXACT (1 << 23) +/* Fx : Flag for H.263+ extra options */ +/** + * H.263 advanced intra coding / MPEG-4 AC prediction + */ +#define AV_CODEC_FLAG_AC_PRED (1 << 24) +/** + * interlaced motion estimation + */ +#define AV_CODEC_FLAG_INTERLACED_ME (1 << 29) +#define AV_CODEC_FLAG_CLOSED_GOP (1U << 31) + +/** + * Allow non spec compliant speedup tricks. + */ +#define AV_CODEC_FLAG2_FAST (1 << 0) +/** + * Skip bitstream encoding. + */ +#define AV_CODEC_FLAG2_NO_OUTPUT (1 << 2) +/** + * Place global headers at every keyframe instead of in extradata. + */ +#define AV_CODEC_FLAG2_LOCAL_HEADER (1 << 3) + +/** + * timecode is in drop frame format. DEPRECATED!!!! + */ +#define AV_CODEC_FLAG2_DROP_FRAME_TIMECODE (1 << 13) + +/** + * Input bitstream might be truncated at a packet boundaries + * instead of only at frame boundaries. + */ +#define AV_CODEC_FLAG2_CHUNKS (1 << 15) +/** + * Discard cropping information from SPS. + */ +#define AV_CODEC_FLAG2_IGNORE_CROP (1 << 16) + +/** + * Show all frames before the first keyframe + */ +#define AV_CODEC_FLAG2_SHOW_ALL (1 << 22) +/** + * Export motion vectors through frame side data + */ +#define AV_CODEC_FLAG2_EXPORT_MVS (1 << 28) +/** + * Do not skip samples and export skip information as frame side data + */ +#define AV_CODEC_FLAG2_SKIP_MANUAL (1 << 29) +/** + * Do not reset ASS ReadOrder field on flush (subtitles decoding) + */ +#define AV_CODEC_FLAG2_RO_FLUSH_NOOP (1 << 30) + +/* Unsupported options : + * Syntax Arithmetic coding (SAC) + * Reference Picture Selection + * Independent Segment Decoding */ +/* /Fx */ +/* codec capabilities */ + +/* Exported side data. + These flags can be passed in AVCodecContext.export_side_data before initialization. +*/ +/** + * Export motion vectors through frame side data + */ +#define AV_CODEC_EXPORT_DATA_MVS (1 << 0) +/** + * Export encoder Producer Reference Time through packet side data + */ +#define AV_CODEC_EXPORT_DATA_PRFT (1 << 1) +/** + * Decoding only. + * Export the AVVideoEncParams structure through frame side data. + */ +#define AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS (1 << 2) + +/** + * Pan Scan area. + * This specifies the area which should be displayed. + * Note there may be multiple such areas for one frame. + */ +typedef struct AVPanScan { + /** + * id + * - encoding: Set by user. + * - decoding: Set by libavcodec. + */ + int id; + + /** + * width and height in 1/16 pel + * - encoding: Set by user. + * - decoding: Set by libavcodec. + */ + int width; + int height; + + /** + * position of the top left corner in 1/16 pel for up to 3 fields/frames + * - encoding: Set by user. + * - decoding: Set by libavcodec. + */ + int16_t position[3][2]; +} AVPanScan; + +/** + * This structure describes the bitrate properties of an encoded bitstream. It + * roughly corresponds to a subset the VBV parameters for MPEG-2 or HRD + * parameters for H.264/HEVC. + */ +typedef struct AVCPBProperties { + /** + * Maximum bitrate of the stream, in bits per second. + * Zero if unknown or unspecified. + */ +#if FF_API_UNSANITIZED_BITRATES + int max_bitrate; +#else + int64_t max_bitrate; +#endif + /** + * Minimum bitrate of the stream, in bits per second. + * Zero if unknown or unspecified. + */ +#if FF_API_UNSANITIZED_BITRATES + int min_bitrate; +#else + int64_t min_bitrate; +#endif + /** + * Average bitrate of the stream, in bits per second. + * Zero if unknown or unspecified. + */ +#if FF_API_UNSANITIZED_BITRATES + int avg_bitrate; +#else + int64_t avg_bitrate; +#endif + + /** + * The size of the buffer to which the ratecontrol is applied, in bits. + * Zero if unknown or unspecified. + */ + int buffer_size; + + /** + * The delay between the time the packet this structure is associated with + * is received and the time when it should be decoded, in periods of a 27MHz + * clock. + * + * UINT64_MAX when unknown or unspecified. + */ + uint64_t vbv_delay; +} AVCPBProperties; + +/** + * This structure supplies correlation between a packet timestamp and a wall clock + * production time. The definition follows the Producer Reference Time ('prft') + * as defined in ISO/IEC 14496-12 + */ +typedef struct AVProducerReferenceTime { + /** + * A UTC timestamp, in microseconds, since Unix epoch (e.g, av_gettime()). + */ + int64_t wallclock; + int flags; +} AVProducerReferenceTime; + +/** + * The decoder will keep a reference to the frame and may reuse it later. + */ +#define AV_GET_BUFFER_FLAG_REF (1 << 0) + +struct AVCodecInternal; + +/** + * main external API structure. + * New fields can be added to the end with minor version bumps. + * Removal, reordering and changes to existing fields require a major + * version bump. + * You can use AVOptions (av_opt* / av_set/get*()) to access these fields from user + * applications. + * The name string for AVOptions options matches the associated command line + * parameter name and can be found in libavcodec/options_table.h + * The AVOption/command line parameter names differ in some cases from the C + * structure field names for historic reasons or brevity. + * sizeof(AVCodecContext) must not be used outside libav*. + */ +typedef struct AVCodecContext { + /** + * information on struct for av_log + * - set by avcodec_alloc_context3 + */ + const AVClass *av_class; + int log_level_offset; + + enum AVMediaType codec_type; /* see AVMEDIA_TYPE_xxx */ + const struct AVCodec *codec; + enum AVCodecID codec_id; /* see AV_CODEC_ID_xxx */ + + /** + * fourcc (LSB first, so "ABCD" -> ('D'<<24) + ('C'<<16) + ('B'<<8) + 'A'). + * This is used to work around some encoder bugs. + * A demuxer should set this to what is stored in the field used to identify the codec. + * If there are multiple such fields in a container then the demuxer should choose the one + * which maximizes the information about the used codec. + * If the codec tag field in a container is larger than 32 bits then the demuxer should + * remap the longer ID to 32 bits with a table or other structure. Alternatively a new + * extra_codec_tag + size could be added but for this a clear advantage must be demonstrated + * first. + * - encoding: Set by user, if not then the default based on codec_id will be used. + * - decoding: Set by user, will be converted to uppercase by libavcodec during init. + */ + unsigned int codec_tag; + + void *priv_data; + + /** + * Private context used for internal data. + * + * Unlike priv_data, this is not codec-specific. It is used in general + * libavcodec functions. + */ + struct AVCodecInternal *internal; + + /** + * Private data of the user, can be used to carry app specific stuff. + * - encoding: Set by user. + * - decoding: Set by user. + */ + void *opaque; + + /** + * the average bitrate + * - encoding: Set by user; unused for constant quantizer encoding. + * - decoding: Set by user, may be overwritten by libavcodec + * if this info is available in the stream + */ + int64_t bit_rate; + + /** + * number of bits the bitstream is allowed to diverge from the reference. + * the reference can be CBR (for CBR pass1) or VBR (for pass2) + * - encoding: Set by user; unused for constant quantizer encoding. + * - decoding: unused + */ + int bit_rate_tolerance; + + /** + * Global quality for codecs which cannot change it per frame. + * This should be proportional to MPEG-1/2/4 qscale. + * - encoding: Set by user. + * - decoding: unused + */ + int global_quality; + + /** + * - encoding: Set by user. + * - decoding: unused + */ + int compression_level; +#define FF_COMPRESSION_DEFAULT -1 + + /** + * AV_CODEC_FLAG_*. + * - encoding: Set by user. + * - decoding: Set by user. + */ + int flags; + + /** + * AV_CODEC_FLAG2_* + * - encoding: Set by user. + * - decoding: Set by user. + */ + int flags2; + + /** + * some codecs need / can use extradata like Huffman tables. + * MJPEG: Huffman tables + * rv10: additional flags + * MPEG-4: global headers (they can be in the bitstream or here) + * The allocated memory should be AV_INPUT_BUFFER_PADDING_SIZE bytes larger + * than extradata_size to avoid problems if it is read with the bitstream reader. + * The bytewise contents of extradata must not depend on the architecture or CPU endianness. + * Must be allocated with the av_malloc() family of functions. + * - encoding: Set/allocated/freed by libavcodec. + * - decoding: Set/allocated/freed by user. + */ + uint8_t *extradata; + int extradata_size; + + /** + * This is the fundamental unit of time (in seconds) in terms + * of which frame timestamps are represented. For fixed-fps content, + * timebase should be 1/framerate and timestamp increments should be + * identically 1. + * This often, but not always is the inverse of the frame rate or field rate + * for video. 1/time_base is not the average frame rate if the frame rate is not + * constant. + * + * Like containers, elementary streams also can store timestamps, 1/time_base + * is the unit in which these timestamps are specified. + * As example of such codec time base see ISO/IEC 14496-2:2001(E) + * vop_time_increment_resolution and fixed_vop_rate + * (fixed_vop_rate == 0 implies that it is different from the framerate) + * + * - encoding: MUST be set by user. + * - decoding: the use of this field for decoding is deprecated. + * Use framerate instead. + */ + AVRational time_base; + + /** + * For some codecs, the time base is closer to the field rate than the frame rate. + * Most notably, H.264 and MPEG-2 specify time_base as half of frame duration + * if no telecine is used ... + * + * Set to time_base ticks per frame. Default 1, e.g., H.264/MPEG-2 set it to 2. + */ + int ticks_per_frame; + + /** + * Codec delay. + * + * Encoding: Number of frames delay there will be from the encoder input to + * the decoder output. (we assume the decoder matches the spec) + * Decoding: Number of frames delay in addition to what a standard decoder + * as specified in the spec would produce. + * + * Video: + * Number of frames the decoded output will be delayed relative to the + * encoded input. + * + * Audio: + * For encoding, this field is unused (see initial_padding). + * + * For decoding, this is the number of samples the decoder needs to + * output before the decoder's output is valid. When seeking, you should + * start decoding this many samples prior to your desired seek point. + * + * - encoding: Set by libavcodec. + * - decoding: Set by libavcodec. + */ + int delay; + + + /* video only */ + /** + * picture width / height. + * + * @note Those fields may not match the values of the last + * AVFrame output by avcodec_decode_video2 due frame + * reordering. + * + * - encoding: MUST be set by user. + * - decoding: May be set by the user before opening the decoder if known e.g. + * from the container. Some decoders will require the dimensions + * to be set by the caller. During decoding, the decoder may + * overwrite those values as required while parsing the data. + */ + int width, height; + + /** + * Bitstream width / height, may be different from width/height e.g. when + * the decoded frame is cropped before being output or lowres is enabled. + * + * @note Those field may not match the value of the last + * AVFrame output by avcodec_receive_frame() due frame + * reordering. + * + * - encoding: unused + * - decoding: May be set by the user before opening the decoder if known + * e.g. from the container. During decoding, the decoder may + * overwrite those values as required while parsing the data. + */ + int coded_width, coded_height; + + /** + * the number of pictures in a group of pictures, or 0 for intra_only + * - encoding: Set by user. + * - decoding: unused + */ + int gop_size; + + /** + * Pixel format, see AV_PIX_FMT_xxx. + * May be set by the demuxer if known from headers. + * May be overridden by the decoder if it knows better. + * + * @note This field may not match the value of the last + * AVFrame output by avcodec_receive_frame() due frame + * reordering. + * + * - encoding: Set by user. + * - decoding: Set by user if known, overridden by libavcodec while + * parsing the data. + */ + enum AVPixelFormat pix_fmt; + + /** + * If non NULL, 'draw_horiz_band' is called by the libavcodec + * decoder to draw a horizontal band. It improves cache usage. Not + * all codecs can do that. You must check the codec capabilities + * beforehand. + * When multithreading is used, it may be called from multiple threads + * at the same time; threads might draw different parts of the same AVFrame, + * or multiple AVFrames, and there is no guarantee that slices will be drawn + * in order. + * The function is also used by hardware acceleration APIs. + * It is called at least once during frame decoding to pass + * the data needed for hardware render. + * In that mode instead of pixel data, AVFrame points to + * a structure specific to the acceleration API. The application + * reads the structure and can change some fields to indicate progress + * or mark state. + * - encoding: unused + * - decoding: Set by user. + * @param height the height of the slice + * @param y the y position of the slice + * @param type 1->top field, 2->bottom field, 3->frame + * @param offset offset into the AVFrame.data from which the slice should be read + */ + void (*draw_horiz_band)(struct AVCodecContext *s, + const AVFrame *src, int offset[AV_NUM_DATA_POINTERS], + int y, int type, int height); + + /** + * callback to negotiate the pixelFormat + * @param fmt is the list of formats which are supported by the codec, + * it is terminated by -1 as 0 is a valid format, the formats are ordered by quality. + * The first is always the native one. + * @note The callback may be called again immediately if initialization for + * the selected (hardware-accelerated) pixel format failed. + * @warning Behavior is undefined if the callback returns a value not + * in the fmt list of formats. + * @return the chosen format + * - encoding: unused + * - decoding: Set by user, if not set the native format will be chosen. + */ + enum AVPixelFormat (*get_format)(struct AVCodecContext *s, const enum AVPixelFormat * fmt); + + /** + * maximum number of B-frames between non-B-frames + * Note: The output will be delayed by max_b_frames+1 relative to the input. + * - encoding: Set by user. + * - decoding: unused + */ + int max_b_frames; + + /** + * qscale factor between IP and B-frames + * If > 0 then the last P-frame quantizer will be used (q= lastp_q*factor+offset). + * If < 0 then normal ratecontrol will be done (q= -normal_q*factor+offset). + * - encoding: Set by user. + * - decoding: unused + */ + float b_quant_factor; + +#if FF_API_PRIVATE_OPT + /** @deprecated use encoder private options instead */ + attribute_deprecated + int b_frame_strategy; +#endif + + /** + * qscale offset between IP and B-frames + * - encoding: Set by user. + * - decoding: unused + */ + float b_quant_offset; + + /** + * Size of the frame reordering buffer in the decoder. + * For MPEG-2 it is 1 IPB or 0 low delay IP. + * - encoding: Set by libavcodec. + * - decoding: Set by libavcodec. + */ + int has_b_frames; + +#if FF_API_PRIVATE_OPT + /** @deprecated use encoder private options instead */ + attribute_deprecated + int mpeg_quant; +#endif + + /** + * qscale factor between P- and I-frames + * If > 0 then the last P-frame quantizer will be used (q = lastp_q * factor + offset). + * If < 0 then normal ratecontrol will be done (q= -normal_q*factor+offset). + * - encoding: Set by user. + * - decoding: unused + */ + float i_quant_factor; + + /** + * qscale offset between P and I-frames + * - encoding: Set by user. + * - decoding: unused + */ + float i_quant_offset; + + /** + * luminance masking (0-> disabled) + * - encoding: Set by user. + * - decoding: unused + */ + float lumi_masking; + + /** + * temporary complexity masking (0-> disabled) + * - encoding: Set by user. + * - decoding: unused + */ + float temporal_cplx_masking; + + /** + * spatial complexity masking (0-> disabled) + * - encoding: Set by user. + * - decoding: unused + */ + float spatial_cplx_masking; + + /** + * p block masking (0-> disabled) + * - encoding: Set by user. + * - decoding: unused + */ + float p_masking; + + /** + * darkness masking (0-> disabled) + * - encoding: Set by user. + * - decoding: unused + */ + float dark_masking; + + /** + * slice count + * - encoding: Set by libavcodec. + * - decoding: Set by user (or 0). + */ + int slice_count; + +#if FF_API_PRIVATE_OPT + /** @deprecated use encoder private options instead */ + attribute_deprecated + int prediction_method; +#define FF_PRED_LEFT 0 +#define FF_PRED_PLANE 1 +#define FF_PRED_MEDIAN 2 +#endif + + /** + * slice offsets in the frame in bytes + * - encoding: Set/allocated by libavcodec. + * - decoding: Set/allocated by user (or NULL). + */ + int *slice_offset; + + /** + * sample aspect ratio (0 if unknown) + * That is the width of a pixel divided by the height of the pixel. + * Numerator and denominator must be relatively prime and smaller than 256 for some video standards. + * - encoding: Set by user. + * - decoding: Set by libavcodec. + */ + AVRational sample_aspect_ratio; + + /** + * motion estimation comparison function + * - encoding: Set by user. + * - decoding: unused + */ + int me_cmp; + /** + * subpixel motion estimation comparison function + * - encoding: Set by user. + * - decoding: unused + */ + int me_sub_cmp; + /** + * macroblock comparison function (not supported yet) + * - encoding: Set by user. + * - decoding: unused + */ + int mb_cmp; + /** + * interlaced DCT comparison function + * - encoding: Set by user. + * - decoding: unused + */ + int ildct_cmp; +#define FF_CMP_SAD 0 +#define FF_CMP_SSE 1 +#define FF_CMP_SATD 2 +#define FF_CMP_DCT 3 +#define FF_CMP_PSNR 4 +#define FF_CMP_BIT 5 +#define FF_CMP_RD 6 +#define FF_CMP_ZERO 7 +#define FF_CMP_VSAD 8 +#define FF_CMP_VSSE 9 +#define FF_CMP_NSSE 10 +#define FF_CMP_W53 11 +#define FF_CMP_W97 12 +#define FF_CMP_DCTMAX 13 +#define FF_CMP_DCT264 14 +#define FF_CMP_MEDIAN_SAD 15 +#define FF_CMP_CHROMA 256 + + /** + * ME diamond size & shape + * - encoding: Set by user. + * - decoding: unused + */ + int dia_size; + + /** + * amount of previous MV predictors (2a+1 x 2a+1 square) + * - encoding: Set by user. + * - decoding: unused + */ + int last_predictor_count; + +#if FF_API_PRIVATE_OPT + /** @deprecated use encoder private options instead */ + attribute_deprecated + int pre_me; +#endif + + /** + * motion estimation prepass comparison function + * - encoding: Set by user. + * - decoding: unused + */ + int me_pre_cmp; + + /** + * ME prepass diamond size & shape + * - encoding: Set by user. + * - decoding: unused + */ + int pre_dia_size; + + /** + * subpel ME quality + * - encoding: Set by user. + * - decoding: unused + */ + int me_subpel_quality; + + /** + * maximum motion estimation search range in subpel units + * If 0 then no limit. + * + * - encoding: Set by user. + * - decoding: unused + */ + int me_range; + + /** + * slice flags + * - encoding: unused + * - decoding: Set by user. + */ + int slice_flags; +#define SLICE_FLAG_CODED_ORDER 0x0001 ///< draw_horiz_band() is called in coded order instead of display +#define SLICE_FLAG_ALLOW_FIELD 0x0002 ///< allow draw_horiz_band() with field slices (MPEG-2 field pics) +#define SLICE_FLAG_ALLOW_PLANE 0x0004 ///< allow draw_horiz_band() with 1 component at a time (SVQ1) + + /** + * macroblock decision mode + * - encoding: Set by user. + * - decoding: unused + */ + int mb_decision; +#define FF_MB_DECISION_SIMPLE 0 ///< uses mb_cmp +#define FF_MB_DECISION_BITS 1 ///< chooses the one which needs the fewest bits +#define FF_MB_DECISION_RD 2 ///< rate distortion + + /** + * custom intra quantization matrix + * Must be allocated with the av_malloc() family of functions, and will be freed in + * avcodec_free_context(). + * - encoding: Set/allocated by user, freed by libavcodec. Can be NULL. + * - decoding: Set/allocated/freed by libavcodec. + */ + uint16_t *intra_matrix; + + /** + * custom inter quantization matrix + * Must be allocated with the av_malloc() family of functions, and will be freed in + * avcodec_free_context(). + * - encoding: Set/allocated by user, freed by libavcodec. Can be NULL. + * - decoding: Set/allocated/freed by libavcodec. + */ + uint16_t *inter_matrix; + +#if FF_API_PRIVATE_OPT + /** @deprecated use encoder private options instead */ + attribute_deprecated + int scenechange_threshold; + + /** @deprecated use encoder private options instead */ + attribute_deprecated + int noise_reduction; +#endif + + /** + * precision of the intra DC coefficient - 8 + * - encoding: Set by user. + * - decoding: Set by libavcodec + */ + int intra_dc_precision; + + /** + * Number of macroblock rows at the top which are skipped. + * - encoding: unused + * - decoding: Set by user. + */ + int skip_top; + + /** + * Number of macroblock rows at the bottom which are skipped. + * - encoding: unused + * - decoding: Set by user. + */ + int skip_bottom; + + /** + * minimum MB Lagrange multiplier + * - encoding: Set by user. + * - decoding: unused + */ + int mb_lmin; + + /** + * maximum MB Lagrange multiplier + * - encoding: Set by user. + * - decoding: unused + */ + int mb_lmax; + +#if FF_API_PRIVATE_OPT + /** + * @deprecated use encoder private options instead + */ + attribute_deprecated + int me_penalty_compensation; +#endif + + /** + * - encoding: Set by user. + * - decoding: unused + */ + int bidir_refine; + +#if FF_API_PRIVATE_OPT + /** @deprecated use encoder private options instead */ + attribute_deprecated + int brd_scale; +#endif + + /** + * minimum GOP size + * - encoding: Set by user. + * - decoding: unused + */ + int keyint_min; + + /** + * number of reference frames + * - encoding: Set by user. + * - decoding: Set by lavc. + */ + int refs; + +#if FF_API_PRIVATE_OPT + /** @deprecated use encoder private options instead */ + attribute_deprecated + int chromaoffset; +#endif + + /** + * Note: Value depends upon the compare function used for fullpel ME. + * - encoding: Set by user. + * - decoding: unused + */ + int mv0_threshold; + +#if FF_API_PRIVATE_OPT + /** @deprecated use encoder private options instead */ + attribute_deprecated + int b_sensitivity; +#endif + + /** + * Chromaticity coordinates of the source primaries. + * - encoding: Set by user + * - decoding: Set by libavcodec + */ + enum AVColorPrimaries color_primaries; + + /** + * Color Transfer Characteristic. + * - encoding: Set by user + * - decoding: Set by libavcodec + */ + enum AVColorTransferCharacteristic color_trc; + + /** + * YUV colorspace type. + * - encoding: Set by user + * - decoding: Set by libavcodec + */ + enum AVColorSpace colorspace; + + /** + * MPEG vs JPEG YUV range. + * - encoding: Set by user + * - decoding: Set by libavcodec + */ + enum AVColorRange color_range; + + /** + * This defines the location of chroma samples. + * - encoding: Set by user + * - decoding: Set by libavcodec + */ + enum AVChromaLocation chroma_sample_location; + + /** + * Number of slices. + * Indicates number of picture subdivisions. Used for parallelized + * decoding. + * - encoding: Set by user + * - decoding: unused + */ + int slices; + + /** Field order + * - encoding: set by libavcodec + * - decoding: Set by user. + */ + enum AVFieldOrder field_order; + + /* audio only */ + int sample_rate; ///< samples per second + int channels; ///< number of audio channels + + /** + * audio sample format + * - encoding: Set by user. + * - decoding: Set by libavcodec. + */ + enum AVSampleFormat sample_fmt; ///< sample format + + /* The following data should not be initialized. */ + /** + * Number of samples per channel in an audio frame. + * + * - encoding: set by libavcodec in avcodec_open2(). Each submitted frame + * except the last must contain exactly frame_size samples per channel. + * May be 0 when the codec has AV_CODEC_CAP_VARIABLE_FRAME_SIZE set, then the + * frame size is not restricted. + * - decoding: may be set by some decoders to indicate constant frame size + */ + int frame_size; + + /** + * Frame counter, set by libavcodec. + * + * - decoding: total number of frames returned from the decoder so far. + * - encoding: total number of frames passed to the encoder so far. + * + * @note the counter is not incremented if encoding/decoding resulted in + * an error. + */ + int frame_number; + + /** + * number of bytes per packet if constant and known or 0 + * Used by some WAV based audio codecs. + */ + int block_align; + + /** + * Audio cutoff bandwidth (0 means "automatic") + * - encoding: Set by user. + * - decoding: unused + */ + int cutoff; + + /** + * Audio channel layout. + * - encoding: set by user. + * - decoding: set by user, may be overwritten by libavcodec. + */ + uint64_t channel_layout; + + /** + * Request decoder to use this channel layout if it can (0 for default) + * - encoding: unused + * - decoding: Set by user. + */ + uint64_t request_channel_layout; + + /** + * Type of service that the audio stream conveys. + * - encoding: Set by user. + * - decoding: Set by libavcodec. + */ + enum AVAudioServiceType audio_service_type; + + /** + * desired sample format + * - encoding: Not used. + * - decoding: Set by user. + * Decoder will decode to this format if it can. + */ + enum AVSampleFormat request_sample_fmt; + + /** + * This callback is called at the beginning of each frame to get data + * buffer(s) for it. There may be one contiguous buffer for all the data or + * there may be a buffer per each data plane or anything in between. What + * this means is, you may set however many entries in buf[] you feel necessary. + * Each buffer must be reference-counted using the AVBuffer API (see description + * of buf[] below). + * + * The following fields will be set in the frame before this callback is + * called: + * - format + * - width, height (video only) + * - sample_rate, channel_layout, nb_samples (audio only) + * Their values may differ from the corresponding values in + * AVCodecContext. This callback must use the frame values, not the codec + * context values, to calculate the required buffer size. + * + * This callback must fill the following fields in the frame: + * - data[] + * - linesize[] + * - extended_data: + * * if the data is planar audio with more than 8 channels, then this + * callback must allocate and fill extended_data to contain all pointers + * to all data planes. data[] must hold as many pointers as it can. + * extended_data must be allocated with av_malloc() and will be freed in + * av_frame_unref(). + * * otherwise extended_data must point to data + * - buf[] must contain one or more pointers to AVBufferRef structures. Each of + * the frame's data and extended_data pointers must be contained in these. That + * is, one AVBufferRef for each allocated chunk of memory, not necessarily one + * AVBufferRef per data[] entry. See: av_buffer_create(), av_buffer_alloc(), + * and av_buffer_ref(). + * - extended_buf and nb_extended_buf must be allocated with av_malloc() by + * this callback and filled with the extra buffers if there are more + * buffers than buf[] can hold. extended_buf will be freed in + * av_frame_unref(). + * + * If AV_CODEC_CAP_DR1 is not set then get_buffer2() must call + * avcodec_default_get_buffer2() instead of providing buffers allocated by + * some other means. + * + * Each data plane must be aligned to the maximum required by the target + * CPU. + * + * @see avcodec_default_get_buffer2() + * + * Video: + * + * If AV_GET_BUFFER_FLAG_REF is set in flags then the frame may be reused + * (read and/or written to if it is writable) later by libavcodec. + * + * avcodec_align_dimensions2() should be used to find the required width and + * height, as they normally need to be rounded up to the next multiple of 16. + * + * Some decoders do not support linesizes changing between frames. + * + * If frame multithreading is used and thread_safe_callbacks is set, + * this callback may be called from a different thread, but not from more + * than one at once. Does not need to be reentrant. + * + * @see avcodec_align_dimensions2() + * + * Audio: + * + * Decoders request a buffer of a particular size by setting + * AVFrame.nb_samples prior to calling get_buffer2(). The decoder may, + * however, utilize only part of the buffer by setting AVFrame.nb_samples + * to a smaller value in the output frame. + * + * As a convenience, av_samples_get_buffer_size() and + * av_samples_fill_arrays() in libavutil may be used by custom get_buffer2() + * functions to find the required data size and to fill data pointers and + * linesize. In AVFrame.linesize, only linesize[0] may be set for audio + * since all planes must be the same size. + * + * @see av_samples_get_buffer_size(), av_samples_fill_arrays() + * + * - encoding: unused + * - decoding: Set by libavcodec, user can override. + */ + int (*get_buffer2)(struct AVCodecContext *s, AVFrame *frame, int flags); + + /** + * If non-zero, the decoded audio and video frames returned from + * avcodec_decode_video2() and avcodec_decode_audio4() are reference-counted + * and are valid indefinitely. The caller must free them with + * av_frame_unref() when they are not needed anymore. + * Otherwise, the decoded frames must not be freed by the caller and are + * only valid until the next decode call. + * + * This is always automatically enabled if avcodec_receive_frame() is used. + * + * - encoding: unused + * - decoding: set by the caller before avcodec_open2(). + */ + attribute_deprecated + int refcounted_frames; + + /* - encoding parameters */ + float qcompress; ///< amount of qscale change between easy & hard scenes (0.0-1.0) + float qblur; ///< amount of qscale smoothing over time (0.0-1.0) + + /** + * minimum quantizer + * - encoding: Set by user. + * - decoding: unused + */ + int qmin; + + /** + * maximum quantizer + * - encoding: Set by user. + * - decoding: unused + */ + int qmax; + + /** + * maximum quantizer difference between frames + * - encoding: Set by user. + * - decoding: unused + */ + int max_qdiff; + + /** + * decoder bitstream buffer size + * - encoding: Set by user. + * - decoding: unused + */ + int rc_buffer_size; + + /** + * ratecontrol override, see RcOverride + * - encoding: Allocated/set/freed by user. + * - decoding: unused + */ + int rc_override_count; + RcOverride *rc_override; + + /** + * maximum bitrate + * - encoding: Set by user. + * - decoding: Set by user, may be overwritten by libavcodec. + */ + int64_t rc_max_rate; + + /** + * minimum bitrate + * - encoding: Set by user. + * - decoding: unused + */ + int64_t rc_min_rate; + + /** + * Ratecontrol attempt to use, at maximum, of what can be used without an underflow. + * - encoding: Set by user. + * - decoding: unused. + */ + float rc_max_available_vbv_use; + + /** + * Ratecontrol attempt to use, at least, times the amount needed to prevent a vbv overflow. + * - encoding: Set by user. + * - decoding: unused. + */ + float rc_min_vbv_overflow_use; + + /** + * Number of bits which should be loaded into the rc buffer before decoding starts. + * - encoding: Set by user. + * - decoding: unused + */ + int rc_initial_buffer_occupancy; + +#if FF_API_CODER_TYPE +#define FF_CODER_TYPE_VLC 0 +#define FF_CODER_TYPE_AC 1 +#define FF_CODER_TYPE_RAW 2 +#define FF_CODER_TYPE_RLE 3 + /** + * @deprecated use encoder private options instead + */ + attribute_deprecated + int coder_type; +#endif /* FF_API_CODER_TYPE */ + +#if FF_API_PRIVATE_OPT + /** @deprecated use encoder private options instead */ + attribute_deprecated + int context_model; +#endif + +#if FF_API_PRIVATE_OPT + /** @deprecated use encoder private options instead */ + attribute_deprecated + int frame_skip_threshold; + + /** @deprecated use encoder private options instead */ + attribute_deprecated + int frame_skip_factor; + + /** @deprecated use encoder private options instead */ + attribute_deprecated + int frame_skip_exp; + + /** @deprecated use encoder private options instead */ + attribute_deprecated + int frame_skip_cmp; +#endif /* FF_API_PRIVATE_OPT */ + + /** + * trellis RD quantization + * - encoding: Set by user. + * - decoding: unused + */ + int trellis; + +#if FF_API_PRIVATE_OPT + /** @deprecated use encoder private options instead */ + attribute_deprecated + int min_prediction_order; + + /** @deprecated use encoder private options instead */ + attribute_deprecated + int max_prediction_order; + + /** @deprecated use encoder private options instead */ + attribute_deprecated + int64_t timecode_frame_start; +#endif + +#if FF_API_RTP_CALLBACK + /** + * @deprecated unused + */ + /* The RTP callback: This function is called */ + /* every time the encoder has a packet to send. */ + /* It depends on the encoder if the data starts */ + /* with a Start Code (it should). H.263 does. */ + /* mb_nb contains the number of macroblocks */ + /* encoded in the RTP payload. */ + attribute_deprecated + void (*rtp_callback)(struct AVCodecContext *avctx, void *data, int size, int mb_nb); +#endif + +#if FF_API_PRIVATE_OPT + /** @deprecated use encoder private options instead */ + attribute_deprecated + int rtp_payload_size; /* The size of the RTP payload: the coder will */ + /* do its best to deliver a chunk with size */ + /* below rtp_payload_size, the chunk will start */ + /* with a start code on some codecs like H.263. */ + /* This doesn't take account of any particular */ + /* headers inside the transmitted RTP payload. */ +#endif + +#if FF_API_STAT_BITS + /* statistics, used for 2-pass encoding */ + attribute_deprecated + int mv_bits; + attribute_deprecated + int header_bits; + attribute_deprecated + int i_tex_bits; + attribute_deprecated + int p_tex_bits; + attribute_deprecated + int i_count; + attribute_deprecated + int p_count; + attribute_deprecated + int skip_count; + attribute_deprecated + int misc_bits; + + /** @deprecated this field is unused */ + attribute_deprecated + int frame_bits; +#endif + + /** + * pass1 encoding statistics output buffer + * - encoding: Set by libavcodec. + * - decoding: unused + */ + char *stats_out; + + /** + * pass2 encoding statistics input buffer + * Concatenated stuff from stats_out of pass1 should be placed here. + * - encoding: Allocated/set/freed by user. + * - decoding: unused + */ + char *stats_in; + + /** + * Work around bugs in encoders which sometimes cannot be detected automatically. + * - encoding: Set by user + * - decoding: Set by user + */ + int workaround_bugs; +#define FF_BUG_AUTODETECT 1 ///< autodetection +#define FF_BUG_XVID_ILACE 4 +#define FF_BUG_UMP4 8 +#define FF_BUG_NO_PADDING 16 +#define FF_BUG_AMV 32 +#define FF_BUG_QPEL_CHROMA 64 +#define FF_BUG_STD_QPEL 128 +#define FF_BUG_QPEL_CHROMA2 256 +#define FF_BUG_DIRECT_BLOCKSIZE 512 +#define FF_BUG_EDGE 1024 +#define FF_BUG_HPEL_CHROMA 2048 +#define FF_BUG_DC_CLIP 4096 +#define FF_BUG_MS 8192 ///< Work around various bugs in Microsoft's broken decoders. +#define FF_BUG_TRUNCATED 16384 +#define FF_BUG_IEDGE 32768 + + /** + * strictly follow the standard (MPEG-4, ...). + * - encoding: Set by user. + * - decoding: Set by user. + * Setting this to STRICT or higher means the encoder and decoder will + * generally do stupid things, whereas setting it to unofficial or lower + * will mean the encoder might produce output that is not supported by all + * spec-compliant decoders. Decoders don't differentiate between normal, + * unofficial and experimental (that is, they always try to decode things + * when they can) unless they are explicitly asked to behave stupidly + * (=strictly conform to the specs) + */ + int strict_std_compliance; +#define FF_COMPLIANCE_VERY_STRICT 2 ///< Strictly conform to an older more strict version of the spec or reference software. +#define FF_COMPLIANCE_STRICT 1 ///< Strictly conform to all the things in the spec no matter what consequences. +#define FF_COMPLIANCE_NORMAL 0 +#define FF_COMPLIANCE_UNOFFICIAL -1 ///< Allow unofficial extensions +#define FF_COMPLIANCE_EXPERIMENTAL -2 ///< Allow nonstandardized experimental things. + + /** + * error concealment flags + * - encoding: unused + * - decoding: Set by user. + */ + int error_concealment; +#define FF_EC_GUESS_MVS 1 +#define FF_EC_DEBLOCK 2 +#define FF_EC_FAVOR_INTER 256 + + /** + * debug + * - encoding: Set by user. + * - decoding: Set by user. + */ + int debug; +#define FF_DEBUG_PICT_INFO 1 +#define FF_DEBUG_RC 2 +#define FF_DEBUG_BITSTREAM 4 +#define FF_DEBUG_MB_TYPE 8 +#define FF_DEBUG_QP 16 +#if FF_API_DEBUG_MV +/** + * @deprecated this option does nothing + */ +#define FF_DEBUG_MV 32 +#endif +#define FF_DEBUG_DCT_COEFF 0x00000040 +#define FF_DEBUG_SKIP 0x00000080 +#define FF_DEBUG_STARTCODE 0x00000100 +#define FF_DEBUG_ER 0x00000400 +#define FF_DEBUG_MMCO 0x00000800 +#define FF_DEBUG_BUGS 0x00001000 +#if FF_API_DEBUG_MV +#define FF_DEBUG_VIS_QP 0x00002000 +#define FF_DEBUG_VIS_MB_TYPE 0x00004000 +#endif +#define FF_DEBUG_BUFFERS 0x00008000 +#define FF_DEBUG_THREADS 0x00010000 +#define FF_DEBUG_GREEN_MD 0x00800000 +#define FF_DEBUG_NOMC 0x01000000 + +#if FF_API_DEBUG_MV + /** + * debug + * - encoding: Set by user. + * - decoding: Set by user. + */ + int debug_mv; +#define FF_DEBUG_VIS_MV_P_FOR 0x00000001 // visualize forward predicted MVs of P-frames +#define FF_DEBUG_VIS_MV_B_FOR 0x00000002 // visualize forward predicted MVs of B-frames +#define FF_DEBUG_VIS_MV_B_BACK 0x00000004 // visualize backward predicted MVs of B-frames +#endif + + /** + * Error recognition; may misdetect some more or less valid parts as errors. + * - encoding: unused + * - decoding: Set by user. + */ + int err_recognition; + +/** + * Verify checksums embedded in the bitstream (could be of either encoded or + * decoded data, depending on the codec) and print an error message on mismatch. + * If AV_EF_EXPLODE is also set, a mismatching checksum will result in the + * decoder returning an error. + */ +#define AV_EF_CRCCHECK (1<<0) +#define AV_EF_BITSTREAM (1<<1) ///< detect bitstream specification deviations +#define AV_EF_BUFFER (1<<2) ///< detect improper bitstream length +#define AV_EF_EXPLODE (1<<3) ///< abort decoding on minor error detection + +#define AV_EF_IGNORE_ERR (1<<15) ///< ignore errors and continue +#define AV_EF_CAREFUL (1<<16) ///< consider things that violate the spec, are fast to calculate and have not been seen in the wild as errors +#define AV_EF_COMPLIANT (1<<17) ///< consider all spec non compliances as errors +#define AV_EF_AGGRESSIVE (1<<18) ///< consider things that a sane encoder should not do as an error + + + /** + * opaque 64-bit number (generally a PTS) that will be reordered and + * output in AVFrame.reordered_opaque + * - encoding: Set by libavcodec to the reordered_opaque of the input + * frame corresponding to the last returned packet. Only + * supported by encoders with the + * AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE capability. + * - decoding: Set by user. + */ + int64_t reordered_opaque; + + /** + * Hardware accelerator in use + * - encoding: unused. + * - decoding: Set by libavcodec + */ + const struct AVHWAccel *hwaccel; + + /** + * Hardware accelerator context. + * For some hardware accelerators, a global context needs to be + * provided by the user. In that case, this holds display-dependent + * data FFmpeg cannot instantiate itself. Please refer to the + * FFmpeg HW accelerator documentation to know how to fill this + * is. e.g. for VA API, this is a struct vaapi_context. + * - encoding: unused + * - decoding: Set by user + */ + void *hwaccel_context; + + /** + * error + * - encoding: Set by libavcodec if flags & AV_CODEC_FLAG_PSNR. + * - decoding: unused + */ + uint64_t error[AV_NUM_DATA_POINTERS]; + + /** + * DCT algorithm, see FF_DCT_* below + * - encoding: Set by user. + * - decoding: unused + */ + int dct_algo; +#define FF_DCT_AUTO 0 +#define FF_DCT_FASTINT 1 +#define FF_DCT_INT 2 +#define FF_DCT_MMX 3 +#define FF_DCT_ALTIVEC 5 +#define FF_DCT_FAAN 6 + + /** + * IDCT algorithm, see FF_IDCT_* below. + * - encoding: Set by user. + * - decoding: Set by user. + */ + int idct_algo; +#define FF_IDCT_AUTO 0 +#define FF_IDCT_INT 1 +#define FF_IDCT_SIMPLE 2 +#define FF_IDCT_SIMPLEMMX 3 +#define FF_IDCT_ARM 7 +#define FF_IDCT_ALTIVEC 8 +#define FF_IDCT_SIMPLEARM 10 +#define FF_IDCT_XVID 14 +#define FF_IDCT_SIMPLEARMV5TE 16 +#define FF_IDCT_SIMPLEARMV6 17 +#define FF_IDCT_FAAN 20 +#define FF_IDCT_SIMPLENEON 22 +#define FF_IDCT_NONE 24 /* Used by XvMC to extract IDCT coefficients with FF_IDCT_PERM_NONE */ +#define FF_IDCT_SIMPLEAUTO 128 + + /** + * bits per sample/pixel from the demuxer (needed for huffyuv). + * - encoding: Set by libavcodec. + * - decoding: Set by user. + */ + int bits_per_coded_sample; + + /** + * Bits per sample/pixel of internal libavcodec pixel/sample format. + * - encoding: set by user. + * - decoding: set by libavcodec. + */ + int bits_per_raw_sample; + +#if FF_API_LOWRES + /** + * low resolution decoding, 1-> 1/2 size, 2->1/4 size + * - encoding: unused + * - decoding: Set by user. + */ + int lowres; +#endif + +#if FF_API_CODED_FRAME + /** + * the picture in the bitstream + * - encoding: Set by libavcodec. + * - decoding: unused + * + * @deprecated use the quality factor packet side data instead + */ + attribute_deprecated AVFrame *coded_frame; +#endif + + /** + * thread count + * is used to decide how many independent tasks should be passed to execute() + * - encoding: Set by user. + * - decoding: Set by user. + */ + int thread_count; + + /** + * Which multithreading methods to use. + * Use of FF_THREAD_FRAME will increase decoding delay by one frame per thread, + * so clients which cannot provide future frames should not use it. + * + * - encoding: Set by user, otherwise the default is used. + * - decoding: Set by user, otherwise the default is used. + */ + int thread_type; +#define FF_THREAD_FRAME 1 ///< Decode more than one frame at once +#define FF_THREAD_SLICE 2 ///< Decode more than one part of a single frame at once + + /** + * Which multithreading methods are in use by the codec. + * - encoding: Set by libavcodec. + * - decoding: Set by libavcodec. + */ + int active_thread_type; + + /** + * Set by the client if its custom get_buffer() callback can be called + * synchronously from another thread, which allows faster multithreaded decoding. + * draw_horiz_band() will be called from other threads regardless of this setting. + * Ignored if the default get_buffer() is used. + * - encoding: Set by user. + * - decoding: Set by user. + */ + int thread_safe_callbacks; + + /** + * The codec may call this to execute several independent things. + * It will return only after finishing all tasks. + * The user may replace this with some multithreaded implementation, + * the default implementation will execute the parts serially. + * @param count the number of things to execute + * - encoding: Set by libavcodec, user can override. + * - decoding: Set by libavcodec, user can override. + */ + int (*execute)(struct AVCodecContext *c, int (*func)(struct AVCodecContext *c2, void *arg), void *arg2, int *ret, int count, int size); + + /** + * The codec may call this to execute several independent things. + * It will return only after finishing all tasks. + * The user may replace this with some multithreaded implementation, + * the default implementation will execute the parts serially. + * Also see avcodec_thread_init and e.g. the --enable-pthread configure option. + * @param c context passed also to func + * @param count the number of things to execute + * @param arg2 argument passed unchanged to func + * @param ret return values of executed functions, must have space for "count" values. May be NULL. + * @param func function that will be called count times, with jobnr from 0 to count-1. + * threadnr will be in the range 0 to c->thread_count-1 < MAX_THREADS and so that no + * two instances of func executing at the same time will have the same threadnr. + * @return always 0 currently, but code should handle a future improvement where when any call to func + * returns < 0 no further calls to func may be done and < 0 is returned. + * - encoding: Set by libavcodec, user can override. + * - decoding: Set by libavcodec, user can override. + */ + int (*execute2)(struct AVCodecContext *c, int (*func)(struct AVCodecContext *c2, void *arg, int jobnr, int threadnr), void *arg2, int *ret, int count); + + /** + * noise vs. sse weight for the nsse comparison function + * - encoding: Set by user. + * - decoding: unused + */ + int nsse_weight; + + /** + * profile + * - encoding: Set by user. + * - decoding: Set by libavcodec. + */ + int profile; +#define FF_PROFILE_UNKNOWN -99 +#define FF_PROFILE_RESERVED -100 + +#define FF_PROFILE_AAC_MAIN 0 +#define FF_PROFILE_AAC_LOW 1 +#define FF_PROFILE_AAC_SSR 2 +#define FF_PROFILE_AAC_LTP 3 +#define FF_PROFILE_AAC_HE 4 +#define FF_PROFILE_AAC_HE_V2 28 +#define FF_PROFILE_AAC_LD 22 +#define FF_PROFILE_AAC_ELD 38 +#define FF_PROFILE_MPEG2_AAC_LOW 128 +#define FF_PROFILE_MPEG2_AAC_HE 131 + +#define FF_PROFILE_DNXHD 0 +#define FF_PROFILE_DNXHR_LB 1 +#define FF_PROFILE_DNXHR_SQ 2 +#define FF_PROFILE_DNXHR_HQ 3 +#define FF_PROFILE_DNXHR_HQX 4 +#define FF_PROFILE_DNXHR_444 5 + +#define FF_PROFILE_DTS 20 +#define FF_PROFILE_DTS_ES 30 +#define FF_PROFILE_DTS_96_24 40 +#define FF_PROFILE_DTS_HD_HRA 50 +#define FF_PROFILE_DTS_HD_MA 60 +#define FF_PROFILE_DTS_EXPRESS 70 + +#define FF_PROFILE_MPEG2_422 0 +#define FF_PROFILE_MPEG2_HIGH 1 +#define FF_PROFILE_MPEG2_SS 2 +#define FF_PROFILE_MPEG2_SNR_SCALABLE 3 +#define FF_PROFILE_MPEG2_MAIN 4 +#define FF_PROFILE_MPEG2_SIMPLE 5 + +#define FF_PROFILE_H264_CONSTRAINED (1<<9) // 8+1; constraint_set1_flag +#define FF_PROFILE_H264_INTRA (1<<11) // 8+3; constraint_set3_flag + +#define FF_PROFILE_H264_BASELINE 66 +#define FF_PROFILE_H264_CONSTRAINED_BASELINE (66|FF_PROFILE_H264_CONSTRAINED) +#define FF_PROFILE_H264_MAIN 77 +#define FF_PROFILE_H264_EXTENDED 88 +#define FF_PROFILE_H264_HIGH 100 +#define FF_PROFILE_H264_HIGH_10 110 +#define FF_PROFILE_H264_HIGH_10_INTRA (110|FF_PROFILE_H264_INTRA) +#define FF_PROFILE_H264_MULTIVIEW_HIGH 118 +#define FF_PROFILE_H264_HIGH_422 122 +#define FF_PROFILE_H264_HIGH_422_INTRA (122|FF_PROFILE_H264_INTRA) +#define FF_PROFILE_H264_STEREO_HIGH 128 +#define FF_PROFILE_H264_HIGH_444 144 +#define FF_PROFILE_H264_HIGH_444_PREDICTIVE 244 +#define FF_PROFILE_H264_HIGH_444_INTRA (244|FF_PROFILE_H264_INTRA) +#define FF_PROFILE_H264_CAVLC_444 44 + +#define FF_PROFILE_VC1_SIMPLE 0 +#define FF_PROFILE_VC1_MAIN 1 +#define FF_PROFILE_VC1_COMPLEX 2 +#define FF_PROFILE_VC1_ADVANCED 3 + +#define FF_PROFILE_MPEG4_SIMPLE 0 +#define FF_PROFILE_MPEG4_SIMPLE_SCALABLE 1 +#define FF_PROFILE_MPEG4_CORE 2 +#define FF_PROFILE_MPEG4_MAIN 3 +#define FF_PROFILE_MPEG4_N_BIT 4 +#define FF_PROFILE_MPEG4_SCALABLE_TEXTURE 5 +#define FF_PROFILE_MPEG4_SIMPLE_FACE_ANIMATION 6 +#define FF_PROFILE_MPEG4_BASIC_ANIMATED_TEXTURE 7 +#define FF_PROFILE_MPEG4_HYBRID 8 +#define FF_PROFILE_MPEG4_ADVANCED_REAL_TIME 9 +#define FF_PROFILE_MPEG4_CORE_SCALABLE 10 +#define FF_PROFILE_MPEG4_ADVANCED_CODING 11 +#define FF_PROFILE_MPEG4_ADVANCED_CORE 12 +#define FF_PROFILE_MPEG4_ADVANCED_SCALABLE_TEXTURE 13 +#define FF_PROFILE_MPEG4_SIMPLE_STUDIO 14 +#define FF_PROFILE_MPEG4_ADVANCED_SIMPLE 15 + +#define FF_PROFILE_JPEG2000_CSTREAM_RESTRICTION_0 1 +#define FF_PROFILE_JPEG2000_CSTREAM_RESTRICTION_1 2 +#define FF_PROFILE_JPEG2000_CSTREAM_NO_RESTRICTION 32768 +#define FF_PROFILE_JPEG2000_DCINEMA_2K 3 +#define FF_PROFILE_JPEG2000_DCINEMA_4K 4 + +#define FF_PROFILE_VP9_0 0 +#define FF_PROFILE_VP9_1 1 +#define FF_PROFILE_VP9_2 2 +#define FF_PROFILE_VP9_3 3 + +#define FF_PROFILE_HEVC_MAIN 1 +#define FF_PROFILE_HEVC_MAIN_10 2 +#define FF_PROFILE_HEVC_MAIN_STILL_PICTURE 3 +#define FF_PROFILE_HEVC_REXT 4 + +#define FF_PROFILE_AV1_MAIN 0 +#define FF_PROFILE_AV1_HIGH 1 +#define FF_PROFILE_AV1_PROFESSIONAL 2 + +#define FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT 0xc0 +#define FF_PROFILE_MJPEG_HUFFMAN_EXTENDED_SEQUENTIAL_DCT 0xc1 +#define FF_PROFILE_MJPEG_HUFFMAN_PROGRESSIVE_DCT 0xc2 +#define FF_PROFILE_MJPEG_HUFFMAN_LOSSLESS 0xc3 +#define FF_PROFILE_MJPEG_JPEG_LS 0xf7 + +#define FF_PROFILE_SBC_MSBC 1 + +#define FF_PROFILE_PRORES_PROXY 0 +#define FF_PROFILE_PRORES_LT 1 +#define FF_PROFILE_PRORES_STANDARD 2 +#define FF_PROFILE_PRORES_HQ 3 +#define FF_PROFILE_PRORES_4444 4 +#define FF_PROFILE_PRORES_XQ 5 + +#define FF_PROFILE_ARIB_PROFILE_A 0 +#define FF_PROFILE_ARIB_PROFILE_C 1 + +#define FF_PROFILE_KLVA_SYNC 0 +#define FF_PROFILE_KLVA_ASYNC 1 + + /** + * level + * - encoding: Set by user. + * - decoding: Set by libavcodec. + */ + int level; +#define FF_LEVEL_UNKNOWN -99 + + /** + * Skip loop filtering for selected frames. + * - encoding: unused + * - decoding: Set by user. + */ + enum AVDiscard skip_loop_filter; + + /** + * Skip IDCT/dequantization for selected frames. + * - encoding: unused + * - decoding: Set by user. + */ + enum AVDiscard skip_idct; + + /** + * Skip decoding for selected frames. + * - encoding: unused + * - decoding: Set by user. + */ + enum AVDiscard skip_frame; + + /** + * Header containing style information for text subtitles. + * For SUBTITLE_ASS subtitle type, it should contain the whole ASS + * [Script Info] and [V4+ Styles] section, plus the [Events] line and + * the Format line following. It shouldn't include any Dialogue line. + * - encoding: Set/allocated/freed by user (before avcodec_open2()) + * - decoding: Set/allocated/freed by libavcodec (by avcodec_open2()) + */ + uint8_t *subtitle_header; + int subtitle_header_size; + +#if FF_API_VBV_DELAY + /** + * VBV delay coded in the last frame (in periods of a 27 MHz clock). + * Used for compliant TS muxing. + * - encoding: Set by libavcodec. + * - decoding: unused. + * @deprecated this value is now exported as a part of + * AV_PKT_DATA_CPB_PROPERTIES packet side data + */ + attribute_deprecated + uint64_t vbv_delay; +#endif + +#if FF_API_SIDEDATA_ONLY_PKT + /** + * Encoding only and set by default. Allow encoders to output packets + * that do not contain any encoded data, only side data. + * + * Some encoders need to output such packets, e.g. to update some stream + * parameters at the end of encoding. + * + * @deprecated this field disables the default behaviour and + * it is kept only for compatibility. + */ + attribute_deprecated + int side_data_only_packets; +#endif + + /** + * Audio only. The number of "priming" samples (padding) inserted by the + * encoder at the beginning of the audio. I.e. this number of leading + * decoded samples must be discarded by the caller to get the original audio + * without leading padding. + * + * - decoding: unused + * - encoding: Set by libavcodec. The timestamps on the output packets are + * adjusted by the encoder so that they always refer to the + * first sample of the data actually contained in the packet, + * including any added padding. E.g. if the timebase is + * 1/samplerate and the timestamp of the first input sample is + * 0, the timestamp of the first output packet will be + * -initial_padding. + */ + int initial_padding; + + /** + * - decoding: For codecs that store a framerate value in the compressed + * bitstream, the decoder may export it here. { 0, 1} when + * unknown. + * - encoding: May be used to signal the framerate of CFR content to an + * encoder. + */ + AVRational framerate; + + /** + * Nominal unaccelerated pixel format, see AV_PIX_FMT_xxx. + * - encoding: unused. + * - decoding: Set by libavcodec before calling get_format() + */ + enum AVPixelFormat sw_pix_fmt; + + /** + * Timebase in which pkt_dts/pts and AVPacket.dts/pts are. + * - encoding unused. + * - decoding set by user. + */ + AVRational pkt_timebase; + + /** + * AVCodecDescriptor + * - encoding: unused. + * - decoding: set by libavcodec. + */ + const AVCodecDescriptor *codec_descriptor; + +#if !FF_API_LOWRES + /** + * low resolution decoding, 1-> 1/2 size, 2->1/4 size + * - encoding: unused + * - decoding: Set by user. + */ + int lowres; +#endif + + /** + * Current statistics for PTS correction. + * - decoding: maintained and used by libavcodec, not intended to be used by user apps + * - encoding: unused + */ + int64_t pts_correction_num_faulty_pts; /// Number of incorrect PTS values so far + int64_t pts_correction_num_faulty_dts; /// Number of incorrect DTS values so far + int64_t pts_correction_last_pts; /// PTS of the last frame + int64_t pts_correction_last_dts; /// DTS of the last frame + + /** + * Character encoding of the input subtitles file. + * - decoding: set by user + * - encoding: unused + */ + char *sub_charenc; + + /** + * Subtitles character encoding mode. Formats or codecs might be adjusting + * this setting (if they are doing the conversion themselves for instance). + * - decoding: set by libavcodec + * - encoding: unused + */ + int sub_charenc_mode; +#define FF_SUB_CHARENC_MODE_DO_NOTHING -1 ///< do nothing (demuxer outputs a stream supposed to be already in UTF-8, or the codec is bitmap for instance) +#define FF_SUB_CHARENC_MODE_AUTOMATIC 0 ///< libavcodec will select the mode itself +#define FF_SUB_CHARENC_MODE_PRE_DECODER 1 ///< the AVPacket data needs to be recoded to UTF-8 before being fed to the decoder, requires iconv +#define FF_SUB_CHARENC_MODE_IGNORE 2 ///< neither convert the subtitles, nor check them for valid UTF-8 + + /** + * Skip processing alpha if supported by codec. + * Note that if the format uses pre-multiplied alpha (common with VP6, + * and recommended due to better video quality/compression) + * the image will look as if alpha-blended onto a black background. + * However for formats that do not use pre-multiplied alpha + * there might be serious artefacts (though e.g. libswscale currently + * assumes pre-multiplied alpha anyway). + * + * - decoding: set by user + * - encoding: unused + */ + int skip_alpha; + + /** + * Number of samples to skip after a discontinuity + * - decoding: unused + * - encoding: set by libavcodec + */ + int seek_preroll; + +#if !FF_API_DEBUG_MV + /** + * debug motion vectors + * - encoding: Set by user. + * - decoding: Set by user. + */ + int debug_mv; +#define FF_DEBUG_VIS_MV_P_FOR 0x00000001 //visualize forward predicted MVs of P frames +#define FF_DEBUG_VIS_MV_B_FOR 0x00000002 //visualize forward predicted MVs of B frames +#define FF_DEBUG_VIS_MV_B_BACK 0x00000004 //visualize backward predicted MVs of B frames +#endif + + /** + * custom intra quantization matrix + * - encoding: Set by user, can be NULL. + * - decoding: unused. + */ + uint16_t *chroma_intra_matrix; + + /** + * dump format separator. + * can be ", " or "\n " or anything else + * - encoding: Set by user. + * - decoding: Set by user. + */ + uint8_t *dump_separator; + + /** + * ',' separated list of allowed decoders. + * If NULL then all are allowed + * - encoding: unused + * - decoding: set by user + */ + char *codec_whitelist; + + /** + * Properties of the stream that gets decoded + * - encoding: unused + * - decoding: set by libavcodec + */ + unsigned properties; +#define FF_CODEC_PROPERTY_LOSSLESS 0x00000001 +#define FF_CODEC_PROPERTY_CLOSED_CAPTIONS 0x00000002 + + /** + * Additional data associated with the entire coded stream. + * + * - decoding: unused + * - encoding: may be set by libavcodec after avcodec_open2(). + */ + AVPacketSideData *coded_side_data; + int nb_coded_side_data; + + /** + * A reference to the AVHWFramesContext describing the input (for encoding) + * or output (decoding) frames. The reference is set by the caller and + * afterwards owned (and freed) by libavcodec - it should never be read by + * the caller after being set. + * + * - decoding: This field should be set by the caller from the get_format() + * callback. The previous reference (if any) will always be + * unreffed by libavcodec before the get_format() call. + * + * If the default get_buffer2() is used with a hwaccel pixel + * format, then this AVHWFramesContext will be used for + * allocating the frame buffers. + * + * - encoding: For hardware encoders configured to use a hwaccel pixel + * format, this field should be set by the caller to a reference + * to the AVHWFramesContext describing input frames. + * AVHWFramesContext.format must be equal to + * AVCodecContext.pix_fmt. + * + * This field should be set before avcodec_open2() is called. + */ + AVBufferRef *hw_frames_ctx; + + /** + * Control the form of AVSubtitle.rects[N]->ass + * - decoding: set by user + * - encoding: unused + */ + int sub_text_format; +#define FF_SUB_TEXT_FMT_ASS 0 +#if FF_API_ASS_TIMING +#define FF_SUB_TEXT_FMT_ASS_WITH_TIMINGS 1 +#endif + + /** + * Audio only. The amount of padding (in samples) appended by the encoder to + * the end of the audio. I.e. this number of decoded samples must be + * discarded by the caller from the end of the stream to get the original + * audio without any trailing padding. + * + * - decoding: unused + * - encoding: unused + */ + int trailing_padding; + + /** + * The number of pixels per image to maximally accept. + * + * - decoding: set by user + * - encoding: set by user + */ + int64_t max_pixels; + + /** + * A reference to the AVHWDeviceContext describing the device which will + * be used by a hardware encoder/decoder. The reference is set by the + * caller and afterwards owned (and freed) by libavcodec. + * + * This should be used if either the codec device does not require + * hardware frames or any that are used are to be allocated internally by + * libavcodec. If the user wishes to supply any of the frames used as + * encoder input or decoder output then hw_frames_ctx should be used + * instead. When hw_frames_ctx is set in get_format() for a decoder, this + * field will be ignored while decoding the associated stream segment, but + * may again be used on a following one after another get_format() call. + * + * For both encoders and decoders this field should be set before + * avcodec_open2() is called and must not be written to thereafter. + * + * Note that some decoders may require this field to be set initially in + * order to support hw_frames_ctx at all - in that case, all frames + * contexts used must be created on the same device. + */ + AVBufferRef *hw_device_ctx; + + /** + * Bit set of AV_HWACCEL_FLAG_* flags, which affect hardware accelerated + * decoding (if active). + * - encoding: unused + * - decoding: Set by user (either before avcodec_open2(), or in the + * AVCodecContext.get_format callback) + */ + int hwaccel_flags; + + /** + * Video decoding only. Certain video codecs support cropping, meaning that + * only a sub-rectangle of the decoded frame is intended for display. This + * option controls how cropping is handled by libavcodec. + * + * When set to 1 (the default), libavcodec will apply cropping internally. + * I.e. it will modify the output frame width/height fields and offset the + * data pointers (only by as much as possible while preserving alignment, or + * by the full amount if the AV_CODEC_FLAG_UNALIGNED flag is set) so that + * the frames output by the decoder refer only to the cropped area. The + * crop_* fields of the output frames will be zero. + * + * When set to 0, the width/height fields of the output frames will be set + * to the coded dimensions and the crop_* fields will describe the cropping + * rectangle. Applying the cropping is left to the caller. + * + * @warning When hardware acceleration with opaque output frames is used, + * libavcodec is unable to apply cropping from the top/left border. + * + * @note when this option is set to zero, the width/height fields of the + * AVCodecContext and output AVFrames have different meanings. The codec + * context fields store display dimensions (with the coded dimensions in + * coded_width/height), while the frame fields store the coded dimensions + * (with the display dimensions being determined by the crop_* fields). + */ + int apply_cropping; + + /* + * Video decoding only. Sets the number of extra hardware frames which + * the decoder will allocate for use by the caller. This must be set + * before avcodec_open2() is called. + * + * Some hardware decoders require all frames that they will use for + * output to be defined in advance before decoding starts. For such + * decoders, the hardware frame pool must therefore be of a fixed size. + * The extra frames set here are on top of any number that the decoder + * needs internally in order to operate normally (for example, frames + * used as reference pictures). + */ + int extra_hw_frames; + + /** + * The percentage of damaged samples to discard a frame. + * + * - decoding: set by user + * - encoding: unused + */ + int discard_damaged_percentage; + + /** + * The number of samples per frame to maximally accept. + * + * - decoding: set by user + * - encoding: set by user + */ + int64_t max_samples; + + /** + * Bit set of AV_CODEC_EXPORT_DATA_* flags, which affects the kind of + * metadata exported in frame, packet, or coded stream side data by + * decoders and encoders. + * + * - decoding: set by user + * - encoding: set by user + */ + int export_side_data; +} AVCodecContext; + +#if FF_API_CODEC_GET_SET +/** + * Accessors for some AVCodecContext fields. These used to be provided for ABI + * compatibility, and do not need to be used anymore. + */ +attribute_deprecated +AVRational av_codec_get_pkt_timebase (const AVCodecContext *avctx); +attribute_deprecated +void av_codec_set_pkt_timebase (AVCodecContext *avctx, AVRational val); + +attribute_deprecated +const AVCodecDescriptor *av_codec_get_codec_descriptor(const AVCodecContext *avctx); +attribute_deprecated +void av_codec_set_codec_descriptor(AVCodecContext *avctx, const AVCodecDescriptor *desc); + +attribute_deprecated +unsigned av_codec_get_codec_properties(const AVCodecContext *avctx); + +#if FF_API_LOWRES +attribute_deprecated +int av_codec_get_lowres(const AVCodecContext *avctx); +attribute_deprecated +void av_codec_set_lowres(AVCodecContext *avctx, int val); +#endif + +attribute_deprecated +int av_codec_get_seek_preroll(const AVCodecContext *avctx); +attribute_deprecated +void av_codec_set_seek_preroll(AVCodecContext *avctx, int val); + +attribute_deprecated +uint16_t *av_codec_get_chroma_intra_matrix(const AVCodecContext *avctx); +attribute_deprecated +void av_codec_set_chroma_intra_matrix(AVCodecContext *avctx, uint16_t *val); +#endif + +struct AVSubtitle; + +#if FF_API_CODEC_GET_SET +attribute_deprecated +int av_codec_get_max_lowres(const AVCodec *codec); +#endif + +struct MpegEncContext; + +/** + * @defgroup lavc_hwaccel AVHWAccel + * + * @note Nothing in this structure should be accessed by the user. At some + * point in future it will not be externally visible at all. + * + * @{ + */ +typedef struct AVHWAccel { + /** + * Name of the hardware accelerated codec. + * The name is globally unique among encoders and among decoders (but an + * encoder and a decoder can share the same name). + */ + const char *name; + + /** + * Type of codec implemented by the hardware accelerator. + * + * See AVMEDIA_TYPE_xxx + */ + enum AVMediaType type; + + /** + * Codec implemented by the hardware accelerator. + * + * See AV_CODEC_ID_xxx + */ + enum AVCodecID id; + + /** + * Supported pixel format. + * + * Only hardware accelerated formats are supported here. + */ + enum AVPixelFormat pix_fmt; + + /** + * Hardware accelerated codec capabilities. + * see AV_HWACCEL_CODEC_CAP_* + */ + int capabilities; + + /***************************************************************** + * No fields below this line are part of the public API. They + * may not be used outside of libavcodec and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + + /** + * Allocate a custom buffer + */ + int (*alloc_frame)(AVCodecContext *avctx, AVFrame *frame); + + /** + * Called at the beginning of each frame or field picture. + * + * Meaningful frame information (codec specific) is guaranteed to + * be parsed at this point. This function is mandatory. + * + * Note that buf can be NULL along with buf_size set to 0. + * Otherwise, this means the whole frame is available at this point. + * + * @param avctx the codec context + * @param buf the frame data buffer base + * @param buf_size the size of the frame in bytes + * @return zero if successful, a negative value otherwise + */ + int (*start_frame)(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size); + + /** + * Callback for parameter data (SPS/PPS/VPS etc). + * + * Useful for hardware decoders which keep persistent state about the + * video parameters, and need to receive any changes to update that state. + * + * @param avctx the codec context + * @param type the nal unit type + * @param buf the nal unit data buffer + * @param buf_size the size of the nal unit in bytes + * @return zero if successful, a negative value otherwise + */ + int (*decode_params)(AVCodecContext *avctx, int type, const uint8_t *buf, uint32_t buf_size); + + /** + * Callback for each slice. + * + * Meaningful slice information (codec specific) is guaranteed to + * be parsed at this point. This function is mandatory. + * The only exception is XvMC, that works on MB level. + * + * @param avctx the codec context + * @param buf the slice data buffer base + * @param buf_size the size of the slice in bytes + * @return zero if successful, a negative value otherwise + */ + int (*decode_slice)(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size); + + /** + * Called at the end of each frame or field picture. + * + * The whole picture is parsed at this point and can now be sent + * to the hardware accelerator. This function is mandatory. + * + * @param avctx the codec context + * @return zero if successful, a negative value otherwise + */ + int (*end_frame)(AVCodecContext *avctx); + + /** + * Size of per-frame hardware accelerator private data. + * + * Private data is allocated with av_mallocz() before + * AVCodecContext.get_buffer() and deallocated after + * AVCodecContext.release_buffer(). + */ + int frame_priv_data_size; + + /** + * Called for every Macroblock in a slice. + * + * XvMC uses it to replace the ff_mpv_reconstruct_mb(). + * Instead of decoding to raw picture, MB parameters are + * stored in an array provided by the video driver. + * + * @param s the mpeg context + */ + void (*decode_mb)(struct MpegEncContext *s); + + /** + * Initialize the hwaccel private data. + * + * This will be called from ff_get_format(), after hwaccel and + * hwaccel_context are set and the hwaccel private data in AVCodecInternal + * is allocated. + */ + int (*init)(AVCodecContext *avctx); + + /** + * Uninitialize the hwaccel private data. + * + * This will be called from get_format() or avcodec_close(), after hwaccel + * and hwaccel_context are already uninitialized. + */ + int (*uninit)(AVCodecContext *avctx); + + /** + * Size of the private data to allocate in + * AVCodecInternal.hwaccel_priv_data. + */ + int priv_data_size; + + /** + * Internal hwaccel capabilities. + */ + int caps_internal; + + /** + * Fill the given hw_frames context with current codec parameters. Called + * from get_format. Refer to avcodec_get_hw_frames_parameters() for + * details. + * + * This CAN be called before AVHWAccel.init is called, and you must assume + * that avctx->hwaccel_priv_data is invalid. + */ + int (*frame_params)(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx); +} AVHWAccel; + +/** + * HWAccel is experimental and is thus avoided in favor of non experimental + * codecs + */ +#define AV_HWACCEL_CODEC_CAP_EXPERIMENTAL 0x0200 + +/** + * Hardware acceleration should be used for decoding even if the codec level + * used is unknown or higher than the maximum supported level reported by the + * hardware driver. + * + * It's generally a good idea to pass this flag unless you have a specific + * reason not to, as hardware tends to under-report supported levels. + */ +#define AV_HWACCEL_FLAG_IGNORE_LEVEL (1 << 0) + +/** + * Hardware acceleration can output YUV pixel formats with a different chroma + * sampling than 4:2:0 and/or other than 8 bits per component. + */ +#define AV_HWACCEL_FLAG_ALLOW_HIGH_DEPTH (1 << 1) + +/** + * Hardware acceleration should still be attempted for decoding when the + * codec profile does not match the reported capabilities of the hardware. + * + * For example, this can be used to try to decode baseline profile H.264 + * streams in hardware - it will often succeed, because many streams marked + * as baseline profile actually conform to constrained baseline profile. + * + * @warning If the stream is actually not supported then the behaviour is + * undefined, and may include returning entirely incorrect output + * while indicating success. + */ +#define AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH (1 << 2) + +/** + * @} + */ + +#if FF_API_AVPICTURE +/** + * @defgroup lavc_picture AVPicture + * + * Functions for working with AVPicture + * @{ + */ + +/** + * Picture data structure. + * + * Up to four components can be stored into it, the last component is + * alpha. + * @deprecated use AVFrame or imgutils functions instead + */ +typedef struct AVPicture { + attribute_deprecated + uint8_t *data[AV_NUM_DATA_POINTERS]; ///< pointers to the image data planes + attribute_deprecated + int linesize[AV_NUM_DATA_POINTERS]; ///< number of bytes per line +} AVPicture; + +/** + * @} + */ +#endif + +enum AVSubtitleType { + SUBTITLE_NONE, + + SUBTITLE_BITMAP, ///< A bitmap, pict will be set + + /** + * Plain text, the text field must be set by the decoder and is + * authoritative. ass and pict fields may contain approximations. + */ + SUBTITLE_TEXT, + + /** + * Formatted text, the ass field must be set by the decoder and is + * authoritative. pict and text fields may contain approximations. + */ + SUBTITLE_ASS, +}; + +#define AV_SUBTITLE_FLAG_FORCED 0x00000001 + +typedef struct AVSubtitleRect { + int x; ///< top left corner of pict, undefined when pict is not set + int y; ///< top left corner of pict, undefined when pict is not set + int w; ///< width of pict, undefined when pict is not set + int h; ///< height of pict, undefined when pict is not set + int nb_colors; ///< number of colors in pict, undefined when pict is not set + +#if FF_API_AVPICTURE + /** + * @deprecated unused + */ + attribute_deprecated + AVPicture pict; +#endif + /** + * data+linesize for the bitmap of this subtitle. + * Can be set for text/ass as well once they are rendered. + */ + uint8_t *data[4]; + int linesize[4]; + + enum AVSubtitleType type; + + char *text; ///< 0 terminated plain UTF-8 text + + /** + * 0 terminated ASS/SSA compatible event line. + * The presentation of this is unaffected by the other values in this + * struct. + */ + char *ass; + + int flags; +} AVSubtitleRect; + +typedef struct AVSubtitle { + uint16_t format; /* 0 = graphics */ + uint32_t start_display_time; /* relative to packet pts, in ms */ + uint32_t end_display_time; /* relative to packet pts, in ms */ + unsigned num_rects; + AVSubtitleRect **rects; + int64_t pts; ///< Same as packet pts, in AV_TIME_BASE +} AVSubtitle; + +#if FF_API_NEXT +/** + * If c is NULL, returns the first registered codec, + * if c is non-NULL, returns the next registered codec after c, + * or NULL if c is the last one. + */ +attribute_deprecated +AVCodec *av_codec_next(const AVCodec *c); +#endif + +/** + * Return the LIBAVCODEC_VERSION_INT constant. + */ +unsigned avcodec_version(void); + +/** + * Return the libavcodec build-time configuration. + */ +const char *avcodec_configuration(void); + +/** + * Return the libavcodec license. + */ +const char *avcodec_license(void); + +#if FF_API_NEXT +/** + * Register the codec codec and initialize libavcodec. + * + * @warning either this function or avcodec_register_all() must be called + * before any other libavcodec functions. + * + * @see avcodec_register_all() + */ +attribute_deprecated +void avcodec_register(AVCodec *codec); + +/** + * Register all the codecs, parsers and bitstream filters which were enabled at + * configuration time. If you do not call this function you can select exactly + * which formats you want to support, by using the individual registration + * functions. + * + * @see avcodec_register + * @see av_register_codec_parser + * @see av_register_bitstream_filter + */ +attribute_deprecated +void avcodec_register_all(void); +#endif + +/** + * Allocate an AVCodecContext and set its fields to default values. The + * resulting struct should be freed with avcodec_free_context(). + * + * @param codec if non-NULL, allocate private data and initialize defaults + * for the given codec. It is illegal to then call avcodec_open2() + * with a different codec. + * If NULL, then the codec-specific defaults won't be initialized, + * which may result in suboptimal default settings (this is + * important mainly for encoders, e.g. libx264). + * + * @return An AVCodecContext filled with default values or NULL on failure. + */ +AVCodecContext *avcodec_alloc_context3(const AVCodec *codec); + +/** + * Free the codec context and everything associated with it and write NULL to + * the provided pointer. + */ +void avcodec_free_context(AVCodecContext **avctx); + +#if FF_API_GET_CONTEXT_DEFAULTS +/** + * @deprecated This function should not be used, as closing and opening a codec + * context multiple time is not supported. A new codec context should be + * allocated for each new use. + */ +int avcodec_get_context_defaults3(AVCodecContext *s, const AVCodec *codec); +#endif + +/** + * Get the AVClass for AVCodecContext. It can be used in combination with + * AV_OPT_SEARCH_FAKE_OBJ for examining options. + * + * @see av_opt_find(). + */ +const AVClass *avcodec_get_class(void); + +#if FF_API_COPY_CONTEXT +/** + * Get the AVClass for AVFrame. It can be used in combination with + * AV_OPT_SEARCH_FAKE_OBJ for examining options. + * + * @see av_opt_find(). + */ +const AVClass *avcodec_get_frame_class(void); + +/** + * Get the AVClass for AVSubtitleRect. It can be used in combination with + * AV_OPT_SEARCH_FAKE_OBJ for examining options. + * + * @see av_opt_find(). + */ +const AVClass *avcodec_get_subtitle_rect_class(void); + +/** + * Copy the settings of the source AVCodecContext into the destination + * AVCodecContext. The resulting destination codec context will be + * unopened, i.e. you are required to call avcodec_open2() before you + * can use this AVCodecContext to decode/encode video/audio data. + * + * @param dest target codec context, should be initialized with + * avcodec_alloc_context3(NULL), but otherwise uninitialized + * @param src source codec context + * @return AVERROR() on error (e.g. memory allocation error), 0 on success + * + * @deprecated The semantics of this function are ill-defined and it should not + * be used. If you need to transfer the stream parameters from one codec context + * to another, use an intermediate AVCodecParameters instance and the + * avcodec_parameters_from_context() / avcodec_parameters_to_context() + * functions. + */ +attribute_deprecated +int avcodec_copy_context(AVCodecContext *dest, const AVCodecContext *src); +#endif + +/** + * Fill the parameters struct based on the values from the supplied codec + * context. Any allocated fields in par are freed and replaced with duplicates + * of the corresponding fields in codec. + * + * @return >= 0 on success, a negative AVERROR code on failure + */ +int avcodec_parameters_from_context(AVCodecParameters *par, + const AVCodecContext *codec); + +/** + * Fill the codec context based on the values from the supplied codec + * parameters. Any allocated fields in codec that have a corresponding field in + * par are freed and replaced with duplicates of the corresponding field in par. + * Fields in codec that do not have a counterpart in par are not touched. + * + * @return >= 0 on success, a negative AVERROR code on failure. + */ +int avcodec_parameters_to_context(AVCodecContext *codec, + const AVCodecParameters *par); + +/** + * Initialize the AVCodecContext to use the given AVCodec. Prior to using this + * function the context has to be allocated with avcodec_alloc_context3(). + * + * The functions avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(), + * avcodec_find_decoder() and avcodec_find_encoder() provide an easy way for + * retrieving a codec. + * + * @warning This function is not thread safe! + * + * @note Always call this function before using decoding routines (such as + * @ref avcodec_receive_frame()). + * + * @code + * avcodec_register_all(); + * av_dict_set(&opts, "b", "2.5M", 0); + * codec = avcodec_find_decoder(AV_CODEC_ID_H264); + * if (!codec) + * exit(1); + * + * context = avcodec_alloc_context3(codec); + * + * if (avcodec_open2(context, codec, opts) < 0) + * exit(1); + * @endcode + * + * @param avctx The context to initialize. + * @param codec The codec to open this context for. If a non-NULL codec has been + * previously passed to avcodec_alloc_context3() or + * for this context, then this parameter MUST be either NULL or + * equal to the previously passed codec. + * @param options A dictionary filled with AVCodecContext and codec-private options. + * On return this object will be filled with options that were not found. + * + * @return zero on success, a negative value on error + * @see avcodec_alloc_context3(), avcodec_find_decoder(), avcodec_find_encoder(), + * av_dict_set(), av_opt_find(). + */ +int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options); + +/** + * Close a given AVCodecContext and free all the data associated with it + * (but not the AVCodecContext itself). + * + * Calling this function on an AVCodecContext that hasn't been opened will free + * the codec-specific data allocated in avcodec_alloc_context3() with a non-NULL + * codec. Subsequent calls will do nothing. + * + * @note Do not use this function. Use avcodec_free_context() to destroy a + * codec context (either open or closed). Opening and closing a codec context + * multiple times is not supported anymore -- use multiple codec contexts + * instead. + */ +int avcodec_close(AVCodecContext *avctx); + +/** + * Free all allocated data in the given subtitle struct. + * + * @param sub AVSubtitle to free. + */ +void avsubtitle_free(AVSubtitle *sub); + +/** + * @} + */ + +/** + * @addtogroup lavc_decoding + * @{ + */ + +/** + * The default callback for AVCodecContext.get_buffer2(). It is made public so + * it can be called by custom get_buffer2() implementations for decoders without + * AV_CODEC_CAP_DR1 set. + */ +int avcodec_default_get_buffer2(AVCodecContext *s, AVFrame *frame, int flags); + +/** + * Modify width and height values so that they will result in a memory + * buffer that is acceptable for the codec if you do not use any horizontal + * padding. + * + * May only be used if a codec with AV_CODEC_CAP_DR1 has been opened. + */ +void avcodec_align_dimensions(AVCodecContext *s, int *width, int *height); + +/** + * Modify width and height values so that they will result in a memory + * buffer that is acceptable for the codec if you also ensure that all + * line sizes are a multiple of the respective linesize_align[i]. + * + * May only be used if a codec with AV_CODEC_CAP_DR1 has been opened. + */ +void avcodec_align_dimensions2(AVCodecContext *s, int *width, int *height, + int linesize_align[AV_NUM_DATA_POINTERS]); + +/** + * Converts AVChromaLocation to swscale x/y chroma position. + * + * The positions represent the chroma (0,0) position in a coordinates system + * with luma (0,0) representing the origin and luma(1,1) representing 256,256 + * + * @param xpos horizontal chroma sample position + * @param ypos vertical chroma sample position + */ +int avcodec_enum_to_chroma_pos(int *xpos, int *ypos, enum AVChromaLocation pos); + +/** + * Converts swscale x/y chroma position to AVChromaLocation. + * + * The positions represent the chroma (0,0) position in a coordinates system + * with luma (0,0) representing the origin and luma(1,1) representing 256,256 + * + * @param xpos horizontal chroma sample position + * @param ypos vertical chroma sample position + */ +enum AVChromaLocation avcodec_chroma_pos_to_enum(int xpos, int ypos); + +/** + * Decode the audio frame of size avpkt->size from avpkt->data into frame. + * + * Some decoders may support multiple frames in a single AVPacket. Such + * decoders would then just decode the first frame and the return value would be + * less than the packet size. In this case, avcodec_decode_audio4 has to be + * called again with an AVPacket containing the remaining data in order to + * decode the second frame, etc... Even if no frames are returned, the packet + * needs to be fed to the decoder with remaining data until it is completely + * consumed or an error occurs. + * + * Some decoders (those marked with AV_CODEC_CAP_DELAY) have a delay between input + * and output. This means that for some packets they will not immediately + * produce decoded output and need to be flushed at the end of decoding to get + * all the decoded data. Flushing is done by calling this function with packets + * with avpkt->data set to NULL and avpkt->size set to 0 until it stops + * returning samples. It is safe to flush even those decoders that are not + * marked with AV_CODEC_CAP_DELAY, then no samples will be returned. + * + * @warning The input buffer, avpkt->data must be AV_INPUT_BUFFER_PADDING_SIZE + * larger than the actual read bytes because some optimized bitstream + * readers read 32 or 64 bits at once and could read over the end. + * + * @note The AVCodecContext MUST have been opened with @ref avcodec_open2() + * before packets may be fed to the decoder. + * + * @param avctx the codec context + * @param[out] frame The AVFrame in which to store decoded audio samples. + * The decoder will allocate a buffer for the decoded frame by + * calling the AVCodecContext.get_buffer2() callback. + * When AVCodecContext.refcounted_frames is set to 1, the frame is + * reference counted and the returned reference belongs to the + * caller. The caller must release the frame using av_frame_unref() + * when the frame is no longer needed. The caller may safely write + * to the frame if av_frame_is_writable() returns 1. + * When AVCodecContext.refcounted_frames is set to 0, the returned + * reference belongs to the decoder and is valid only until the + * next call to this function or until closing or flushing the + * decoder. The caller may not write to it. + * @param[out] got_frame_ptr Zero if no frame could be decoded, otherwise it is + * non-zero. Note that this field being set to zero + * does not mean that an error has occurred. For + * decoders with AV_CODEC_CAP_DELAY set, no given decode + * call is guaranteed to produce a frame. + * @param[in] avpkt The input AVPacket containing the input buffer. + * At least avpkt->data and avpkt->size should be set. Some + * decoders might also require additional fields to be set. + * @return A negative error code is returned if an error occurred during + * decoding, otherwise the number of bytes consumed from the input + * AVPacket is returned. + * +* @deprecated Use avcodec_send_packet() and avcodec_receive_frame(). + */ +attribute_deprecated +int avcodec_decode_audio4(AVCodecContext *avctx, AVFrame *frame, + int *got_frame_ptr, const AVPacket *avpkt); + +/** + * Decode the video frame of size avpkt->size from avpkt->data into picture. + * Some decoders may support multiple frames in a single AVPacket, such + * decoders would then just decode the first frame. + * + * @warning The input buffer must be AV_INPUT_BUFFER_PADDING_SIZE larger than + * the actual read bytes because some optimized bitstream readers read 32 or 64 + * bits at once and could read over the end. + * + * @warning The end of the input buffer buf should be set to 0 to ensure that + * no overreading happens for damaged MPEG streams. + * + * @note Codecs which have the AV_CODEC_CAP_DELAY capability set have a delay + * between input and output, these need to be fed with avpkt->data=NULL, + * avpkt->size=0 at the end to return the remaining frames. + * + * @note The AVCodecContext MUST have been opened with @ref avcodec_open2() + * before packets may be fed to the decoder. + * + * @param avctx the codec context + * @param[out] picture The AVFrame in which the decoded video frame will be stored. + * Use av_frame_alloc() to get an AVFrame. The codec will + * allocate memory for the actual bitmap by calling the + * AVCodecContext.get_buffer2() callback. + * When AVCodecContext.refcounted_frames is set to 1, the frame is + * reference counted and the returned reference belongs to the + * caller. The caller must release the frame using av_frame_unref() + * when the frame is no longer needed. The caller may safely write + * to the frame if av_frame_is_writable() returns 1. + * When AVCodecContext.refcounted_frames is set to 0, the returned + * reference belongs to the decoder and is valid only until the + * next call to this function or until closing or flushing the + * decoder. The caller may not write to it. + * + * @param[in] avpkt The input AVPacket containing the input buffer. + * You can create such packet with av_init_packet() and by then setting + * data and size, some decoders might in addition need other fields like + * flags&AV_PKT_FLAG_KEY. All decoders are designed to use the least + * fields possible. + * @param[in,out] got_picture_ptr Zero if no frame could be decompressed, otherwise, it is nonzero. + * @return On error a negative value is returned, otherwise the number of bytes + * used or zero if no frame could be decompressed. + * + * @deprecated Use avcodec_send_packet() and avcodec_receive_frame(). + */ +attribute_deprecated +int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture, + int *got_picture_ptr, + const AVPacket *avpkt); + +/** + * Decode a subtitle message. + * Return a negative value on error, otherwise return the number of bytes used. + * If no subtitle could be decompressed, got_sub_ptr is zero. + * Otherwise, the subtitle is stored in *sub. + * Note that AV_CODEC_CAP_DR1 is not available for subtitle codecs. This is for + * simplicity, because the performance difference is expected to be negligible + * and reusing a get_buffer written for video codecs would probably perform badly + * due to a potentially very different allocation pattern. + * + * Some decoders (those marked with AV_CODEC_CAP_DELAY) have a delay between input + * and output. This means that for some packets they will not immediately + * produce decoded output and need to be flushed at the end of decoding to get + * all the decoded data. Flushing is done by calling this function with packets + * with avpkt->data set to NULL and avpkt->size set to 0 until it stops + * returning subtitles. It is safe to flush even those decoders that are not + * marked with AV_CODEC_CAP_DELAY, then no subtitles will be returned. + * + * @note The AVCodecContext MUST have been opened with @ref avcodec_open2() + * before packets may be fed to the decoder. + * + * @param avctx the codec context + * @param[out] sub The preallocated AVSubtitle in which the decoded subtitle will be stored, + * must be freed with avsubtitle_free if *got_sub_ptr is set. + * @param[in,out] got_sub_ptr Zero if no subtitle could be decompressed, otherwise, it is nonzero. + * @param[in] avpkt The input AVPacket containing the input buffer. + */ +int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, + int *got_sub_ptr, + AVPacket *avpkt); + +/** + * Supply raw packet data as input to a decoder. + * + * Internally, this call will copy relevant AVCodecContext fields, which can + * influence decoding per-packet, and apply them when the packet is actually + * decoded. (For example AVCodecContext.skip_frame, which might direct the + * decoder to drop the frame contained by the packet sent with this function.) + * + * @warning The input buffer, avpkt->data must be AV_INPUT_BUFFER_PADDING_SIZE + * larger than the actual read bytes because some optimized bitstream + * readers read 32 or 64 bits at once and could read over the end. + * + * @warning Do not mix this API with the legacy API (like avcodec_decode_video2()) + * on the same AVCodecContext. It will return unexpected results now + * or in future libavcodec versions. + * + * @note The AVCodecContext MUST have been opened with @ref avcodec_open2() + * before packets may be fed to the decoder. + * + * @param avctx codec context + * @param[in] avpkt The input AVPacket. Usually, this will be a single video + * frame, or several complete audio frames. + * Ownership of the packet remains with the caller, and the + * decoder will not write to the packet. The decoder may create + * a reference to the packet data (or copy it if the packet is + * not reference-counted). + * Unlike with older APIs, the packet is always fully consumed, + * and if it contains multiple frames (e.g. some audio codecs), + * will require you to call avcodec_receive_frame() multiple + * times afterwards before you can send a new packet. + * It can be NULL (or an AVPacket with data set to NULL and + * size set to 0); in this case, it is considered a flush + * packet, which signals the end of the stream. Sending the + * first flush packet will return success. Subsequent ones are + * unnecessary and will return AVERROR_EOF. If the decoder + * still has frames buffered, it will return them after sending + * a flush packet. + * + * @return 0 on success, otherwise negative error code: + * AVERROR(EAGAIN): input is not accepted in the current state - user + * must read output with avcodec_receive_frame() (once + * all output is read, the packet should be resent, and + * the call will not fail with EAGAIN). + * AVERROR_EOF: the decoder has been flushed, and no new packets can + * be sent to it (also returned if more than 1 flush + * packet is sent) + * AVERROR(EINVAL): codec not opened, it is an encoder, or requires flush + * AVERROR(ENOMEM): failed to add packet to internal queue, or similar + * other errors: legitimate decoding errors + */ +int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt); + +/** + * Return decoded output data from a decoder. + * + * @param avctx codec context + * @param frame This will be set to a reference-counted video or audio + * frame (depending on the decoder type) allocated by the + * decoder. Note that the function will always call + * av_frame_unref(frame) before doing anything else. + * + * @return + * 0: success, a frame was returned + * AVERROR(EAGAIN): output is not available in this state - user must try + * to send new input + * AVERROR_EOF: the decoder has been fully flushed, and there will be + * no more output frames + * AVERROR(EINVAL): codec not opened, or it is an encoder + * AVERROR_INPUT_CHANGED: current decoded frame has changed parameters + * with respect to first decoded frame. Applicable + * when flag AV_CODEC_FLAG_DROPCHANGED is set. + * other negative values: legitimate decoding errors + */ +int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame); + +/** + * Supply a raw video or audio frame to the encoder. Use avcodec_receive_packet() + * to retrieve buffered output packets. + * + * @param avctx codec context + * @param[in] frame AVFrame containing the raw audio or video frame to be encoded. + * Ownership of the frame remains with the caller, and the + * encoder will not write to the frame. The encoder may create + * a reference to the frame data (or copy it if the frame is + * not reference-counted). + * It can be NULL, in which case it is considered a flush + * packet. This signals the end of the stream. If the encoder + * still has packets buffered, it will return them after this + * call. Once flushing mode has been entered, additional flush + * packets are ignored, and sending frames will return + * AVERROR_EOF. + * + * For audio: + * If AV_CODEC_CAP_VARIABLE_FRAME_SIZE is set, then each frame + * can have any number of samples. + * If it is not set, frame->nb_samples must be equal to + * avctx->frame_size for all frames except the last. + * The final frame may be smaller than avctx->frame_size. + * @return 0 on success, otherwise negative error code: + * AVERROR(EAGAIN): input is not accepted in the current state - user + * must read output with avcodec_receive_packet() (once + * all output is read, the packet should be resent, and + * the call will not fail with EAGAIN). + * AVERROR_EOF: the encoder has been flushed, and no new frames can + * be sent to it + * AVERROR(EINVAL): codec not opened, refcounted_frames not set, it is a + * decoder, or requires flush + * AVERROR(ENOMEM): failed to add packet to internal queue, or similar + * other errors: legitimate encoding errors + */ +int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame); + +/** + * Read encoded data from the encoder. + * + * @param avctx codec context + * @param avpkt This will be set to a reference-counted packet allocated by the + * encoder. Note that the function will always call + * av_packet_unref(avpkt) before doing anything else. + * @return 0 on success, otherwise negative error code: + * AVERROR(EAGAIN): output is not available in the current state - user + * must try to send input + * AVERROR_EOF: the encoder has been fully flushed, and there will be + * no more output packets + * AVERROR(EINVAL): codec not opened, or it is a decoder + * other errors: legitimate encoding errors + */ +int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt); + +/** + * Create and return a AVHWFramesContext with values adequate for hardware + * decoding. This is meant to get called from the get_format callback, and is + * a helper for preparing a AVHWFramesContext for AVCodecContext.hw_frames_ctx. + * This API is for decoding with certain hardware acceleration modes/APIs only. + * + * The returned AVHWFramesContext is not initialized. The caller must do this + * with av_hwframe_ctx_init(). + * + * Calling this function is not a requirement, but makes it simpler to avoid + * codec or hardware API specific details when manually allocating frames. + * + * Alternatively to this, an API user can set AVCodecContext.hw_device_ctx, + * which sets up AVCodecContext.hw_frames_ctx fully automatically, and makes + * it unnecessary to call this function or having to care about + * AVHWFramesContext initialization at all. + * + * There are a number of requirements for calling this function: + * + * - It must be called from get_format with the same avctx parameter that was + * passed to get_format. Calling it outside of get_format is not allowed, and + * can trigger undefined behavior. + * - The function is not always supported (see description of return values). + * Even if this function returns successfully, hwaccel initialization could + * fail later. (The degree to which implementations check whether the stream + * is actually supported varies. Some do this check only after the user's + * get_format callback returns.) + * - The hw_pix_fmt must be one of the choices suggested by get_format. If the + * user decides to use a AVHWFramesContext prepared with this API function, + * the user must return the same hw_pix_fmt from get_format. + * - The device_ref passed to this function must support the given hw_pix_fmt. + * - After calling this API function, it is the user's responsibility to + * initialize the AVHWFramesContext (returned by the out_frames_ref parameter), + * and to set AVCodecContext.hw_frames_ctx to it. If done, this must be done + * before returning from get_format (this is implied by the normal + * AVCodecContext.hw_frames_ctx API rules). + * - The AVHWFramesContext parameters may change every time time get_format is + * called. Also, AVCodecContext.hw_frames_ctx is reset before get_format. So + * you are inherently required to go through this process again on every + * get_format call. + * - It is perfectly possible to call this function without actually using + * the resulting AVHWFramesContext. One use-case might be trying to reuse a + * previously initialized AVHWFramesContext, and calling this API function + * only to test whether the required frame parameters have changed. + * - Fields that use dynamically allocated values of any kind must not be set + * by the user unless setting them is explicitly allowed by the documentation. + * If the user sets AVHWFramesContext.free and AVHWFramesContext.user_opaque, + * the new free callback must call the potentially set previous free callback. + * This API call may set any dynamically allocated fields, including the free + * callback. + * + * The function will set at least the following fields on AVHWFramesContext + * (potentially more, depending on hwaccel API): + * + * - All fields set by av_hwframe_ctx_alloc(). + * - Set the format field to hw_pix_fmt. + * - Set the sw_format field to the most suited and most versatile format. (An + * implication is that this will prefer generic formats over opaque formats + * with arbitrary restrictions, if possible.) + * - Set the width/height fields to the coded frame size, rounded up to the + * API-specific minimum alignment. + * - Only _if_ the hwaccel requires a pre-allocated pool: set the initial_pool_size + * field to the number of maximum reference surfaces possible with the codec, + * plus 1 surface for the user to work (meaning the user can safely reference + * at most 1 decoded surface at a time), plus additional buffering introduced + * by frame threading. If the hwaccel does not require pre-allocation, the + * field is left to 0, and the decoder will allocate new surfaces on demand + * during decoding. + * - Possibly AVHWFramesContext.hwctx fields, depending on the underlying + * hardware API. + * + * Essentially, out_frames_ref returns the same as av_hwframe_ctx_alloc(), but + * with basic frame parameters set. + * + * The function is stateless, and does not change the AVCodecContext or the + * device_ref AVHWDeviceContext. + * + * @param avctx The context which is currently calling get_format, and which + * implicitly contains all state needed for filling the returned + * AVHWFramesContext properly. + * @param device_ref A reference to the AVHWDeviceContext describing the device + * which will be used by the hardware decoder. + * @param hw_pix_fmt The hwaccel format you are going to return from get_format. + * @param out_frames_ref On success, set to a reference to an _uninitialized_ + * AVHWFramesContext, created from the given device_ref. + * Fields will be set to values required for decoding. + * Not changed if an error is returned. + * @return zero on success, a negative value on error. The following error codes + * have special semantics: + * AVERROR(ENOENT): the decoder does not support this functionality. Setup + * is always manual, or it is a decoder which does not + * support setting AVCodecContext.hw_frames_ctx at all, + * or it is a software format. + * AVERROR(EINVAL): it is known that hardware decoding is not supported for + * this configuration, or the device_ref is not supported + * for the hwaccel referenced by hw_pix_fmt. + */ +int avcodec_get_hw_frames_parameters(AVCodecContext *avctx, + AVBufferRef *device_ref, + enum AVPixelFormat hw_pix_fmt, + AVBufferRef **out_frames_ref); + + + +/** + * @defgroup lavc_parsing Frame parsing + * @{ + */ + +enum AVPictureStructure { + AV_PICTURE_STRUCTURE_UNKNOWN, //< unknown + AV_PICTURE_STRUCTURE_TOP_FIELD, //< coded as top field + AV_PICTURE_STRUCTURE_BOTTOM_FIELD, //< coded as bottom field + AV_PICTURE_STRUCTURE_FRAME, //< coded as frame +}; + +typedef struct AVCodecParserContext { + void *priv_data; + struct AVCodecParser *parser; + int64_t frame_offset; /* offset of the current frame */ + int64_t cur_offset; /* current offset + (incremented by each av_parser_parse()) */ + int64_t next_frame_offset; /* offset of the next frame */ + /* video info */ + int pict_type; /* XXX: Put it back in AVCodecContext. */ + /** + * This field is used for proper frame duration computation in lavf. + * It signals, how much longer the frame duration of the current frame + * is compared to normal frame duration. + * + * frame_duration = (1 + repeat_pict) * time_base + * + * It is used by codecs like H.264 to display telecined material. + */ + int repeat_pict; /* XXX: Put it back in AVCodecContext. */ + int64_t pts; /* pts of the current frame */ + int64_t dts; /* dts of the current frame */ + + /* private data */ + int64_t last_pts; + int64_t last_dts; + int fetch_timestamp; + +#define AV_PARSER_PTS_NB 4 + int cur_frame_start_index; + int64_t cur_frame_offset[AV_PARSER_PTS_NB]; + int64_t cur_frame_pts[AV_PARSER_PTS_NB]; + int64_t cur_frame_dts[AV_PARSER_PTS_NB]; + + int flags; +#define PARSER_FLAG_COMPLETE_FRAMES 0x0001 +#define PARSER_FLAG_ONCE 0x0002 +/// Set if the parser has a valid file offset +#define PARSER_FLAG_FETCHED_OFFSET 0x0004 +#define PARSER_FLAG_USE_CODEC_TS 0x1000 + + int64_t offset; ///< byte offset from starting packet start + int64_t cur_frame_end[AV_PARSER_PTS_NB]; + + /** + * Set by parser to 1 for key frames and 0 for non-key frames. + * It is initialized to -1, so if the parser doesn't set this flag, + * old-style fallback using AV_PICTURE_TYPE_I picture type as key frames + * will be used. + */ + int key_frame; + +#if FF_API_CONVERGENCE_DURATION + /** + * @deprecated unused + */ + attribute_deprecated + int64_t convergence_duration; +#endif + + // Timestamp generation support: + /** + * Synchronization point for start of timestamp generation. + * + * Set to >0 for sync point, 0 for no sync point and <0 for undefined + * (default). + * + * For example, this corresponds to presence of H.264 buffering period + * SEI message. + */ + int dts_sync_point; + + /** + * Offset of the current timestamp against last timestamp sync point in + * units of AVCodecContext.time_base. + * + * Set to INT_MIN when dts_sync_point unused. Otherwise, it must + * contain a valid timestamp offset. + * + * Note that the timestamp of sync point has usually a nonzero + * dts_ref_dts_delta, which refers to the previous sync point. Offset of + * the next frame after timestamp sync point will be usually 1. + * + * For example, this corresponds to H.264 cpb_removal_delay. + */ + int dts_ref_dts_delta; + + /** + * Presentation delay of current frame in units of AVCodecContext.time_base. + * + * Set to INT_MIN when dts_sync_point unused. Otherwise, it must + * contain valid non-negative timestamp delta (presentation time of a frame + * must not lie in the past). + * + * This delay represents the difference between decoding and presentation + * time of the frame. + * + * For example, this corresponds to H.264 dpb_output_delay. + */ + int pts_dts_delta; + + /** + * Position of the packet in file. + * + * Analogous to cur_frame_pts/dts + */ + int64_t cur_frame_pos[AV_PARSER_PTS_NB]; + + /** + * Byte position of currently parsed frame in stream. + */ + int64_t pos; + + /** + * Previous frame byte position. + */ + int64_t last_pos; + + /** + * Duration of the current frame. + * For audio, this is in units of 1 / AVCodecContext.sample_rate. + * For all other types, this is in units of AVCodecContext.time_base. + */ + int duration; + + enum AVFieldOrder field_order; + + /** + * Indicate whether a picture is coded as a frame, top field or bottom field. + * + * For example, H.264 field_pic_flag equal to 0 corresponds to + * AV_PICTURE_STRUCTURE_FRAME. An H.264 picture with field_pic_flag + * equal to 1 and bottom_field_flag equal to 0 corresponds to + * AV_PICTURE_STRUCTURE_TOP_FIELD. + */ + enum AVPictureStructure picture_structure; + + /** + * Picture number incremented in presentation or output order. + * This field may be reinitialized at the first picture of a new sequence. + * + * For example, this corresponds to H.264 PicOrderCnt. + */ + int output_picture_number; + + /** + * Dimensions of the decoded video intended for presentation. + */ + int width; + int height; + + /** + * Dimensions of the coded video. + */ + int coded_width; + int coded_height; + + /** + * The format of the coded data, corresponds to enum AVPixelFormat for video + * and for enum AVSampleFormat for audio. + * + * Note that a decoder can have considerable freedom in how exactly it + * decodes the data, so the format reported here might be different from the + * one returned by a decoder. + */ + int format; +} AVCodecParserContext; + +typedef struct AVCodecParser { + int codec_ids[5]; /* several codec IDs are permitted */ + int priv_data_size; + int (*parser_init)(AVCodecParserContext *s); + /* This callback never returns an error, a negative value means that + * the frame start was in a previous packet. */ + int (*parser_parse)(AVCodecParserContext *s, + AVCodecContext *avctx, + const uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size); + void (*parser_close)(AVCodecParserContext *s); + int (*split)(AVCodecContext *avctx, const uint8_t *buf, int buf_size); + struct AVCodecParser *next; +} AVCodecParser; + +/** + * Iterate over all registered codec parsers. + * + * @param opaque a pointer where libavcodec will store the iteration state. Must + * point to NULL to start the iteration. + * + * @return the next registered codec parser or NULL when the iteration is + * finished + */ +const AVCodecParser *av_parser_iterate(void **opaque); + +attribute_deprecated +AVCodecParser *av_parser_next(const AVCodecParser *c); + +attribute_deprecated +void av_register_codec_parser(AVCodecParser *parser); +AVCodecParserContext *av_parser_init(int codec_id); + +/** + * Parse a packet. + * + * @param s parser context. + * @param avctx codec context. + * @param poutbuf set to pointer to parsed buffer or NULL if not yet finished. + * @param poutbuf_size set to size of parsed buffer or zero if not yet finished. + * @param buf input buffer. + * @param buf_size buffer size in bytes without the padding. I.e. the full buffer + size is assumed to be buf_size + AV_INPUT_BUFFER_PADDING_SIZE. + To signal EOF, this should be 0 (so that the last frame + can be output). + * @param pts input presentation timestamp. + * @param dts input decoding timestamp. + * @param pos input byte position in stream. + * @return the number of bytes of the input bitstream used. + * + * Example: + * @code + * while(in_len){ + * len = av_parser_parse2(myparser, AVCodecContext, &data, &size, + * in_data, in_len, + * pts, dts, pos); + * in_data += len; + * in_len -= len; + * + * if(size) + * decode_frame(data, size); + * } + * @endcode + */ +int av_parser_parse2(AVCodecParserContext *s, + AVCodecContext *avctx, + uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size, + int64_t pts, int64_t dts, + int64_t pos); + +/** + * @return 0 if the output buffer is a subset of the input, 1 if it is allocated and must be freed + * @deprecated use AVBitStreamFilter + */ +int av_parser_change(AVCodecParserContext *s, + AVCodecContext *avctx, + uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size, int keyframe); +void av_parser_close(AVCodecParserContext *s); + +/** + * @} + * @} + */ + +/** + * @addtogroup lavc_encoding + * @{ + */ + +/** + * Encode a frame of audio. + * + * Takes input samples from frame and writes the next output packet, if + * available, to avpkt. The output packet does not necessarily contain data for + * the most recent frame, as encoders can delay, split, and combine input frames + * internally as needed. + * + * @param avctx codec context + * @param avpkt output AVPacket. + * The user can supply an output buffer by setting + * avpkt->data and avpkt->size prior to calling the + * function, but if the size of the user-provided data is not + * large enough, encoding will fail. If avpkt->data and + * avpkt->size are set, avpkt->destruct must also be set. All + * other AVPacket fields will be reset by the encoder using + * av_init_packet(). If avpkt->data is NULL, the encoder will + * allocate it. The encoder will set avpkt->size to the size + * of the output packet. + * + * If this function fails or produces no output, avpkt will be + * freed using av_packet_unref(). + * @param[in] frame AVFrame containing the raw audio data to be encoded. + * May be NULL when flushing an encoder that has the + * AV_CODEC_CAP_DELAY capability set. + * If AV_CODEC_CAP_VARIABLE_FRAME_SIZE is set, then each frame + * can have any number of samples. + * If it is not set, frame->nb_samples must be equal to + * avctx->frame_size for all frames except the last. + * The final frame may be smaller than avctx->frame_size. + * @param[out] got_packet_ptr This field is set to 1 by libavcodec if the + * output packet is non-empty, and to 0 if it is + * empty. If the function returns an error, the + * packet can be assumed to be invalid, and the + * value of got_packet_ptr is undefined and should + * not be used. + * @return 0 on success, negative error code on failure + * + * @deprecated use avcodec_send_frame()/avcodec_receive_packet() instead + */ +attribute_deprecated +int avcodec_encode_audio2(AVCodecContext *avctx, AVPacket *avpkt, + const AVFrame *frame, int *got_packet_ptr); + +/** + * Encode a frame of video. + * + * Takes input raw video data from frame and writes the next output packet, if + * available, to avpkt. The output packet does not necessarily contain data for + * the most recent frame, as encoders can delay and reorder input frames + * internally as needed. + * + * @param avctx codec context + * @param avpkt output AVPacket. + * The user can supply an output buffer by setting + * avpkt->data and avpkt->size prior to calling the + * function, but if the size of the user-provided data is not + * large enough, encoding will fail. All other AVPacket fields + * will be reset by the encoder using av_init_packet(). If + * avpkt->data is NULL, the encoder will allocate it. + * The encoder will set avpkt->size to the size of the + * output packet. The returned data (if any) belongs to the + * caller, he is responsible for freeing it. + * + * If this function fails or produces no output, avpkt will be + * freed using av_packet_unref(). + * @param[in] frame AVFrame containing the raw video data to be encoded. + * May be NULL when flushing an encoder that has the + * AV_CODEC_CAP_DELAY capability set. + * @param[out] got_packet_ptr This field is set to 1 by libavcodec if the + * output packet is non-empty, and to 0 if it is + * empty. If the function returns an error, the + * packet can be assumed to be invalid, and the + * value of got_packet_ptr is undefined and should + * not be used. + * @return 0 on success, negative error code on failure + * + * @deprecated use avcodec_send_frame()/avcodec_receive_packet() instead + */ +attribute_deprecated +int avcodec_encode_video2(AVCodecContext *avctx, AVPacket *avpkt, + const AVFrame *frame, int *got_packet_ptr); + +int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size, + const AVSubtitle *sub); + + +/** + * @} + */ + +#if FF_API_AVPICTURE +/** + * @addtogroup lavc_picture + * @{ + */ + +/** + * @deprecated unused + */ +attribute_deprecated +int avpicture_alloc(AVPicture *picture, enum AVPixelFormat pix_fmt, int width, int height); + +/** + * @deprecated unused + */ +attribute_deprecated +void avpicture_free(AVPicture *picture); + +/** + * @deprecated use av_image_fill_arrays() instead. + */ +attribute_deprecated +int avpicture_fill(AVPicture *picture, const uint8_t *ptr, + enum AVPixelFormat pix_fmt, int width, int height); + +/** + * @deprecated use av_image_copy_to_buffer() instead. + */ +attribute_deprecated +int avpicture_layout(const AVPicture *src, enum AVPixelFormat pix_fmt, + int width, int height, + unsigned char *dest, int dest_size); + +/** + * @deprecated use av_image_get_buffer_size() instead. + */ +attribute_deprecated +int avpicture_get_size(enum AVPixelFormat pix_fmt, int width, int height); + +/** + * @deprecated av_image_copy() instead. + */ +attribute_deprecated +void av_picture_copy(AVPicture *dst, const AVPicture *src, + enum AVPixelFormat pix_fmt, int width, int height); + +/** + * @deprecated unused + */ +attribute_deprecated +int av_picture_crop(AVPicture *dst, const AVPicture *src, + enum AVPixelFormat pix_fmt, int top_band, int left_band); + +/** + * @deprecated unused + */ +attribute_deprecated +int av_picture_pad(AVPicture *dst, const AVPicture *src, int height, int width, enum AVPixelFormat pix_fmt, + int padtop, int padbottom, int padleft, int padright, int *color); + +/** + * @} + */ +#endif + +/** + * @defgroup lavc_misc Utility functions + * @ingroup libavc + * + * Miscellaneous utility functions related to both encoding and decoding + * (or neither). + * @{ + */ + +/** + * @defgroup lavc_misc_pixfmt Pixel formats + * + * Functions for working with pixel formats. + * @{ + */ + +#if FF_API_GETCHROMA +/** + * @deprecated Use av_pix_fmt_get_chroma_sub_sample + */ + +attribute_deprecated +void avcodec_get_chroma_sub_sample(enum AVPixelFormat pix_fmt, int *h_shift, int *v_shift); +#endif + +/** + * Return a value representing the fourCC code associated to the + * pixel format pix_fmt, or 0 if no associated fourCC code can be + * found. + */ +unsigned int avcodec_pix_fmt_to_codec_tag(enum AVPixelFormat pix_fmt); + +/** + * @deprecated see av_get_pix_fmt_loss() + */ +int avcodec_get_pix_fmt_loss(enum AVPixelFormat dst_pix_fmt, enum AVPixelFormat src_pix_fmt, + int has_alpha); + +/** + * Find the best pixel format to convert to given a certain source pixel + * format. When converting from one pixel format to another, information loss + * may occur. For example, when converting from RGB24 to GRAY, the color + * information will be lost. Similarly, other losses occur when converting from + * some formats to other formats. avcodec_find_best_pix_fmt_of_2() searches which of + * the given pixel formats should be used to suffer the least amount of loss. + * The pixel formats from which it chooses one, are determined by the + * pix_fmt_list parameter. + * + * + * @param[in] pix_fmt_list AV_PIX_FMT_NONE terminated array of pixel formats to choose from + * @param[in] src_pix_fmt source pixel format + * @param[in] has_alpha Whether the source pixel format alpha channel is used. + * @param[out] loss_ptr Combination of flags informing you what kind of losses will occur. + * @return The best pixel format to convert to or -1 if none was found. + */ +enum AVPixelFormat avcodec_find_best_pix_fmt_of_list(const enum AVPixelFormat *pix_fmt_list, + enum AVPixelFormat src_pix_fmt, + int has_alpha, int *loss_ptr); + +/** + * @deprecated see av_find_best_pix_fmt_of_2() + */ +enum AVPixelFormat avcodec_find_best_pix_fmt_of_2(enum AVPixelFormat dst_pix_fmt1, enum AVPixelFormat dst_pix_fmt2, + enum AVPixelFormat src_pix_fmt, int has_alpha, int *loss_ptr); + +attribute_deprecated +enum AVPixelFormat avcodec_find_best_pix_fmt2(enum AVPixelFormat dst_pix_fmt1, enum AVPixelFormat dst_pix_fmt2, + enum AVPixelFormat src_pix_fmt, int has_alpha, int *loss_ptr); + +enum AVPixelFormat avcodec_default_get_format(struct AVCodecContext *s, const enum AVPixelFormat * fmt); + +/** + * @} + */ + +#if FF_API_TAG_STRING +/** + * Put a string representing the codec tag codec_tag in buf. + * + * @param buf buffer to place codec tag in + * @param buf_size size in bytes of buf + * @param codec_tag codec tag to assign + * @return the length of the string that would have been generated if + * enough space had been available, excluding the trailing null + * + * @deprecated see av_fourcc_make_string() and av_fourcc2str(). + */ +attribute_deprecated +size_t av_get_codec_tag_string(char *buf, size_t buf_size, unsigned int codec_tag); +#endif + +void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode); + +/** + * Return a name for the specified profile, if available. + * + * @param codec the codec that is searched for the given profile + * @param profile the profile value for which a name is requested + * @return A name for the profile if found, NULL otherwise. + */ +const char *av_get_profile_name(const AVCodec *codec, int profile); + +/** + * Return a name for the specified profile, if available. + * + * @param codec_id the ID of the codec to which the requested profile belongs + * @param profile the profile value for which a name is requested + * @return A name for the profile if found, NULL otherwise. + * + * @note unlike av_get_profile_name(), which searches a list of profiles + * supported by a specific decoder or encoder implementation, this + * function searches the list of profiles from the AVCodecDescriptor + */ +const char *avcodec_profile_name(enum AVCodecID codec_id, int profile); + +int avcodec_default_execute(AVCodecContext *c, int (*func)(AVCodecContext *c2, void *arg2),void *arg, int *ret, int count, int size); +int avcodec_default_execute2(AVCodecContext *c, int (*func)(AVCodecContext *c2, void *arg2, int, int),void *arg, int *ret, int count); +//FIXME func typedef + +/** + * Fill AVFrame audio data and linesize pointers. + * + * The buffer buf must be a preallocated buffer with a size big enough + * to contain the specified samples amount. The filled AVFrame data + * pointers will point to this buffer. + * + * AVFrame extended_data channel pointers are allocated if necessary for + * planar audio. + * + * @param frame the AVFrame + * frame->nb_samples must be set prior to calling the + * function. This function fills in frame->data, + * frame->extended_data, frame->linesize[0]. + * @param nb_channels channel count + * @param sample_fmt sample format + * @param buf buffer to use for frame data + * @param buf_size size of buffer + * @param align plane size sample alignment (0 = default) + * @return >=0 on success, negative error code on failure + * @todo return the size in bytes required to store the samples in + * case of success, at the next libavutil bump + */ +int avcodec_fill_audio_frame(AVFrame *frame, int nb_channels, + enum AVSampleFormat sample_fmt, const uint8_t *buf, + int buf_size, int align); + +/** + * Reset the internal codec state / flush internal buffers. Should be called + * e.g. when seeking or when switching to a different stream. + * + * @note for decoders, when refcounted frames are not used + * (i.e. avctx->refcounted_frames is 0), this invalidates the frames previously + * returned from the decoder. When refcounted frames are used, the decoder just + * releases any references it might keep internally, but the caller's reference + * remains valid. + * + * @note for encoders, this function will only do something if the encoder + * declares support for AV_CODEC_CAP_ENCODER_FLUSH. When called, the encoder + * will drain any remaining packets, and can then be re-used for a different + * stream (as opposed to sending a null frame which will leave the encoder + * in a permanent EOF state after draining). This can be desirable if the + * cost of tearing down and replacing the encoder instance is high. + */ +void avcodec_flush_buffers(AVCodecContext *avctx); + +/** + * Return codec bits per sample. + * + * @param[in] codec_id the codec + * @return Number of bits per sample or zero if unknown for the given codec. + */ +int av_get_bits_per_sample(enum AVCodecID codec_id); + +/** + * Return the PCM codec associated with a sample format. + * @param be endianness, 0 for little, 1 for big, + * -1 (or anything else) for native + * @return AV_CODEC_ID_PCM_* or AV_CODEC_ID_NONE + */ +enum AVCodecID av_get_pcm_codec(enum AVSampleFormat fmt, int be); + +/** + * Return codec bits per sample. + * Only return non-zero if the bits per sample is exactly correct, not an + * approximation. + * + * @param[in] codec_id the codec + * @return Number of bits per sample or zero if unknown for the given codec. + */ +int av_get_exact_bits_per_sample(enum AVCodecID codec_id); + +/** + * Return audio frame duration. + * + * @param avctx codec context + * @param frame_bytes size of the frame, or 0 if unknown + * @return frame duration, in samples, if known. 0 if not able to + * determine. + */ +int av_get_audio_frame_duration(AVCodecContext *avctx, int frame_bytes); + +/** + * This function is the same as av_get_audio_frame_duration(), except it works + * with AVCodecParameters instead of an AVCodecContext. + */ +int av_get_audio_frame_duration2(AVCodecParameters *par, int frame_bytes); + +#if FF_API_OLD_BSF +typedef struct AVBitStreamFilterContext { + void *priv_data; + const struct AVBitStreamFilter *filter; + AVCodecParserContext *parser; + struct AVBitStreamFilterContext *next; + /** + * Internal default arguments, used if NULL is passed to av_bitstream_filter_filter(). + * Not for access by library users. + */ + char *args; +} AVBitStreamFilterContext; + +/** + * @deprecated the old bitstream filtering API (using AVBitStreamFilterContext) + * is deprecated. Use the new bitstream filtering API (using AVBSFContext). + */ +attribute_deprecated +void av_register_bitstream_filter(AVBitStreamFilter *bsf); +/** + * @deprecated the old bitstream filtering API (using AVBitStreamFilterContext) + * is deprecated. Use av_bsf_get_by_name(), av_bsf_alloc(), and av_bsf_init() + * from the new bitstream filtering API (using AVBSFContext). + */ +attribute_deprecated +AVBitStreamFilterContext *av_bitstream_filter_init(const char *name); +/** + * @deprecated the old bitstream filtering API (using AVBitStreamFilterContext) + * is deprecated. Use av_bsf_send_packet() and av_bsf_receive_packet() from the + * new bitstream filtering API (using AVBSFContext). + */ +attribute_deprecated +int av_bitstream_filter_filter(AVBitStreamFilterContext *bsfc, + AVCodecContext *avctx, const char *args, + uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size, int keyframe); +/** + * @deprecated the old bitstream filtering API (using AVBitStreamFilterContext) + * is deprecated. Use av_bsf_free() from the new bitstream filtering API (using + * AVBSFContext). + */ +attribute_deprecated +void av_bitstream_filter_close(AVBitStreamFilterContext *bsf); +/** + * @deprecated the old bitstream filtering API (using AVBitStreamFilterContext) + * is deprecated. Use av_bsf_iterate() from the new bitstream filtering API (using + * AVBSFContext). + */ +attribute_deprecated +const AVBitStreamFilter *av_bitstream_filter_next(const AVBitStreamFilter *f); +#endif + +#if FF_API_NEXT +attribute_deprecated +const AVBitStreamFilter *av_bsf_next(void **opaque); +#endif + +/* memory */ + +/** + * Same behaviour av_fast_malloc but the buffer has additional + * AV_INPUT_BUFFER_PADDING_SIZE at the end which will always be 0. + * + * In addition the whole buffer will initially and after resizes + * be 0-initialized so that no uninitialized data will ever appear. + */ +void av_fast_padded_malloc(void *ptr, unsigned int *size, size_t min_size); + +/** + * Same behaviour av_fast_padded_malloc except that buffer will always + * be 0-initialized after call. + */ +void av_fast_padded_mallocz(void *ptr, unsigned int *size, size_t min_size); + +/** + * Encode extradata length to a buffer. Used by xiph codecs. + * + * @param s buffer to write to; must be at least (v/255+1) bytes long + * @param v size of extradata in bytes + * @return number of bytes written to the buffer. + */ +unsigned int av_xiphlacing(unsigned char *s, unsigned int v); + +#if FF_API_USER_VISIBLE_AVHWACCEL +/** + * Register the hardware accelerator hwaccel. + * + * @deprecated This function doesn't do anything. + */ +attribute_deprecated +void av_register_hwaccel(AVHWAccel *hwaccel); + +/** + * If hwaccel is NULL, returns the first registered hardware accelerator, + * if hwaccel is non-NULL, returns the next registered hardware accelerator + * after hwaccel, or NULL if hwaccel is the last one. + * + * @deprecated AVHWaccel structures contain no user-serviceable parts, so + * this function should not be used. + */ +attribute_deprecated +AVHWAccel *av_hwaccel_next(const AVHWAccel *hwaccel); +#endif + +#if FF_API_LOCKMGR +/** + * Lock operation used by lockmgr + * + * @deprecated Deprecated together with av_lockmgr_register(). + */ +enum AVLockOp { + AV_LOCK_CREATE, ///< Create a mutex + AV_LOCK_OBTAIN, ///< Lock the mutex + AV_LOCK_RELEASE, ///< Unlock the mutex + AV_LOCK_DESTROY, ///< Free mutex resources +}; + +/** + * Register a user provided lock manager supporting the operations + * specified by AVLockOp. The "mutex" argument to the function points + * to a (void *) where the lockmgr should store/get a pointer to a user + * allocated mutex. It is NULL upon AV_LOCK_CREATE and equal to the + * value left by the last call for all other ops. If the lock manager is + * unable to perform the op then it should leave the mutex in the same + * state as when it was called and return a non-zero value. However, + * when called with AV_LOCK_DESTROY the mutex will always be assumed to + * have been successfully destroyed. If av_lockmgr_register succeeds + * it will return a non-negative value, if it fails it will return a + * negative value and destroy all mutex and unregister all callbacks. + * av_lockmgr_register is not thread-safe, it must be called from a + * single thread before any calls which make use of locking are used. + * + * @param cb User defined callback. av_lockmgr_register invokes calls + * to this callback and the previously registered callback. + * The callback will be used to create more than one mutex + * each of which must be backed by its own underlying locking + * mechanism (i.e. do not use a single static object to + * implement your lock manager). If cb is set to NULL the + * lockmgr will be unregistered. + * + * @deprecated This function does nothing, and always returns 0. Be sure to + * build with thread support to get basic thread safety. + */ +attribute_deprecated +int av_lockmgr_register(int (*cb)(void **mutex, enum AVLockOp op)); +#endif + +/** + * @return a positive value if s is open (i.e. avcodec_open2() was called on it + * with no corresponding avcodec_close()), 0 otherwise. + */ +int avcodec_is_open(AVCodecContext *s); + +/** + * Allocate a CPB properties structure and initialize its fields to default + * values. + * + * @param size if non-NULL, the size of the allocated struct will be written + * here. This is useful for embedding it in side data. + * + * @return the newly allocated struct or NULL on failure + */ +AVCPBProperties *av_cpb_properties_alloc(size_t *size); + +/** + * @} + */ + +#endif /* AVCODEC_AVCODEC_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/avdct.h b/ThirdParty/ffmpeg/arm/include/libavcodec/avdct.h new file mode 100644 index 000000000..6411fab6f --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/avdct.h @@ -0,0 +1,88 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_AVDCT_H +#define AVCODEC_AVDCT_H + +#include "libavutil/opt.h" + +/** + * AVDCT context. + * @note function pointers can be NULL if the specific features have been + * disabled at build time. + */ +typedef struct AVDCT { + const AVClass *av_class; + + void (*idct)(int16_t *block /* align 16 */); + + /** + * IDCT input permutation. + * Several optimized IDCTs need a permutated input (relative to the + * normal order of the reference IDCT). + * This permutation must be performed before the idct_put/add. + * Note, normally this can be merged with the zigzag/alternate scan
    + * An example to avoid confusion: + * - (->decode coeffs -> zigzag reorder -> dequant -> reference IDCT -> ...) + * - (x -> reference DCT -> reference IDCT -> x) + * - (x -> reference DCT -> simple_mmx_perm = idct_permutation + * -> simple_idct_mmx -> x) + * - (-> decode coeffs -> zigzag reorder -> simple_mmx_perm -> dequant + * -> simple_idct_mmx -> ...) + */ + uint8_t idct_permutation[64]; + + void (*fdct)(int16_t *block /* align 16 */); + + + /** + * DCT algorithm. + * must use AVOptions to set this field. + */ + int dct_algo; + + /** + * IDCT algorithm. + * must use AVOptions to set this field. + */ + int idct_algo; + + void (*get_pixels)(int16_t *block /* align 16 */, + const uint8_t *pixels /* align 8 */, + ptrdiff_t line_size); + + int bits_per_sample; + + void (*get_pixels_unaligned)(int16_t *block /* align 16 */, + const uint8_t *pixels, + ptrdiff_t line_size); +} AVDCT; + +/** + * Allocates a AVDCT context. + * This needs to be initialized with avcodec_dct_init() after optionally + * configuring it with AVOptions. + * + * To free it use av_free() + */ +AVDCT *avcodec_dct_alloc(void); +int avcodec_dct_init(AVDCT *); + +const AVClass *avcodec_dct_get_class(void); + +#endif /* AVCODEC_AVDCT_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/avfft.h b/ThirdParty/ffmpeg/arm/include/libavcodec/avfft.h new file mode 100644 index 000000000..0c0f9b8d8 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/avfft.h @@ -0,0 +1,118 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_AVFFT_H +#define AVCODEC_AVFFT_H + +/** + * @file + * @ingroup lavc_fft + * FFT functions + */ + +/** + * @defgroup lavc_fft FFT functions + * @ingroup lavc_misc + * + * @{ + */ + +typedef float FFTSample; + +typedef struct FFTComplex { + FFTSample re, im; +} FFTComplex; + +typedef struct FFTContext FFTContext; + +/** + * Set up a complex FFT. + * @param nbits log2 of the length of the input array + * @param inverse if 0 perform the forward transform, if 1 perform the inverse + */ +FFTContext *av_fft_init(int nbits, int inverse); + +/** + * Do the permutation needed BEFORE calling ff_fft_calc(). + */ +void av_fft_permute(FFTContext *s, FFTComplex *z); + +/** + * Do a complex FFT with the parameters defined in av_fft_init(). The + * input data must be permuted before. No 1.0/sqrt(n) normalization is done. + */ +void av_fft_calc(FFTContext *s, FFTComplex *z); + +void av_fft_end(FFTContext *s); + +FFTContext *av_mdct_init(int nbits, int inverse, double scale); +void av_imdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input); +void av_imdct_half(FFTContext *s, FFTSample *output, const FFTSample *input); +void av_mdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input); +void av_mdct_end(FFTContext *s); + +/* Real Discrete Fourier Transform */ + +enum RDFTransformType { + DFT_R2C, + IDFT_C2R, + IDFT_R2C, + DFT_C2R, +}; + +typedef struct RDFTContext RDFTContext; + +/** + * Set up a real FFT. + * @param nbits log2 of the length of the input array + * @param trans the type of transform + */ +RDFTContext *av_rdft_init(int nbits, enum RDFTransformType trans); +void av_rdft_calc(RDFTContext *s, FFTSample *data); +void av_rdft_end(RDFTContext *s); + +/* Discrete Cosine Transform */ + +typedef struct DCTContext DCTContext; + +enum DCTTransformType { + DCT_II = 0, + DCT_III, + DCT_I, + DST_I, +}; + +/** + * Set up DCT. + * + * @param nbits size of the input array: + * (1 << nbits) for DCT-II, DCT-III and DST-I + * (1 << nbits) + 1 for DCT-I + * @param type the type of transform + * + * @note the first element of the input of DST-I is ignored + */ +DCTContext *av_dct_init(int nbits, enum DCTTransformType type); +void av_dct_calc(DCTContext *s, FFTSample *data); +void av_dct_end (DCTContext *s); + +/** + * @} + */ + +#endif /* AVCODEC_AVFFT_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/bsf.h b/ThirdParty/ffmpeg/arm/include/libavcodec/bsf.h new file mode 100644 index 000000000..7ed51677e --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/bsf.h @@ -0,0 +1,325 @@ +/* + * Bitstream filters public API + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_BSF_H +#define AVCODEC_BSF_H + +#include "libavutil/dict.h" +#include "libavutil/log.h" +#include "libavutil/rational.h" + +#include "codec_id.h" +#include "codec_par.h" +#include "packet.h" + +/** + * @addtogroup lavc_core + * @{ + */ + +typedef struct AVBSFInternal AVBSFInternal; + +/** + * The bitstream filter state. + * + * This struct must be allocated with av_bsf_alloc() and freed with + * av_bsf_free(). + * + * The fields in the struct will only be changed (by the caller or by the + * filter) as described in their documentation, and are to be considered + * immutable otherwise. + */ +typedef struct AVBSFContext { + /** + * A class for logging and AVOptions + */ + const AVClass *av_class; + + /** + * The bitstream filter this context is an instance of. + */ + const struct AVBitStreamFilter *filter; + + /** + * Opaque libavcodec internal data. Must not be touched by the caller in any + * way. + */ + AVBSFInternal *internal; + + /** + * Opaque filter-specific private data. If filter->priv_class is non-NULL, + * this is an AVOptions-enabled struct. + */ + void *priv_data; + + /** + * Parameters of the input stream. This field is allocated in + * av_bsf_alloc(), it needs to be filled by the caller before + * av_bsf_init(). + */ + AVCodecParameters *par_in; + + /** + * Parameters of the output stream. This field is allocated in + * av_bsf_alloc(), it is set by the filter in av_bsf_init(). + */ + AVCodecParameters *par_out; + + /** + * The timebase used for the timestamps of the input packets. Set by the + * caller before av_bsf_init(). + */ + AVRational time_base_in; + + /** + * The timebase used for the timestamps of the output packets. Set by the + * filter in av_bsf_init(). + */ + AVRational time_base_out; +} AVBSFContext; + +typedef struct AVBitStreamFilter { + const char *name; + + /** + * A list of codec ids supported by the filter, terminated by + * AV_CODEC_ID_NONE. + * May be NULL, in that case the bitstream filter works with any codec id. + */ + const enum AVCodecID *codec_ids; + + /** + * A class for the private data, used to declare bitstream filter private + * AVOptions. This field is NULL for bitstream filters that do not declare + * any options. + * + * If this field is non-NULL, the first member of the filter private data + * must be a pointer to AVClass, which will be set by libavcodec generic + * code to this class. + */ + const AVClass *priv_class; + + /***************************************************************** + * No fields below this line are part of the public API. They + * may not be used outside of libavcodec and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + + int priv_data_size; + int (*init)(AVBSFContext *ctx); + int (*filter)(AVBSFContext *ctx, AVPacket *pkt); + void (*close)(AVBSFContext *ctx); + void (*flush)(AVBSFContext *ctx); +} AVBitStreamFilter; + +/** + * @return a bitstream filter with the specified name or NULL if no such + * bitstream filter exists. + */ +const AVBitStreamFilter *av_bsf_get_by_name(const char *name); + +/** + * Iterate over all registered bitstream filters. + * + * @param opaque a pointer where libavcodec will store the iteration state. Must + * point to NULL to start the iteration. + * + * @return the next registered bitstream filter or NULL when the iteration is + * finished + */ +const AVBitStreamFilter *av_bsf_iterate(void **opaque); + +/** + * Allocate a context for a given bitstream filter. The caller must fill in the + * context parameters as described in the documentation and then call + * av_bsf_init() before sending any data to the filter. + * + * @param filter the filter for which to allocate an instance. + * @param ctx a pointer into which the pointer to the newly-allocated context + * will be written. It must be freed with av_bsf_free() after the + * filtering is done. + * + * @return 0 on success, a negative AVERROR code on failure + */ +int av_bsf_alloc(const AVBitStreamFilter *filter, AVBSFContext **ctx); + +/** + * Prepare the filter for use, after all the parameters and options have been + * set. + */ +int av_bsf_init(AVBSFContext *ctx); + +/** + * Submit a packet for filtering. + * + * After sending each packet, the filter must be completely drained by calling + * av_bsf_receive_packet() repeatedly until it returns AVERROR(EAGAIN) or + * AVERROR_EOF. + * + * @param pkt the packet to filter. The bitstream filter will take ownership of + * the packet and reset the contents of pkt. pkt is not touched if an error occurs. + * If pkt is empty (i.e. NULL, or pkt->data is NULL and pkt->side_data_elems zero), + * it signals the end of the stream (i.e. no more non-empty packets will be sent; + * sending more empty packets does nothing) and will cause the filter to output + * any packets it may have buffered internally. + * + * @return 0 on success. AVERROR(EAGAIN) if packets need to be retrieved from the + * filter (using av_bsf_receive_packet()) before new input can be consumed. Another + * negative AVERROR value if an error occurs. + */ +int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt); + +/** + * Retrieve a filtered packet. + * + * @param[out] pkt this struct will be filled with the contents of the filtered + * packet. It is owned by the caller and must be freed using + * av_packet_unref() when it is no longer needed. + * This parameter should be "clean" (i.e. freshly allocated + * with av_packet_alloc() or unreffed with av_packet_unref()) + * when this function is called. If this function returns + * successfully, the contents of pkt will be completely + * overwritten by the returned data. On failure, pkt is not + * touched. + * + * @return 0 on success. AVERROR(EAGAIN) if more packets need to be sent to the + * filter (using av_bsf_send_packet()) to get more output. AVERROR_EOF if there + * will be no further output from the filter. Another negative AVERROR value if + * an error occurs. + * + * @note one input packet may result in several output packets, so after sending + * a packet with av_bsf_send_packet(), this function needs to be called + * repeatedly until it stops returning 0. It is also possible for a filter to + * output fewer packets than were sent to it, so this function may return + * AVERROR(EAGAIN) immediately after a successful av_bsf_send_packet() call. + */ +int av_bsf_receive_packet(AVBSFContext *ctx, AVPacket *pkt); + +/** + * Reset the internal bitstream filter state / flush internal buffers. + */ +void av_bsf_flush(AVBSFContext *ctx); + +/** + * Free a bitstream filter context and everything associated with it; write NULL + * into the supplied pointer. + */ +void av_bsf_free(AVBSFContext **ctx); + +/** + * Get the AVClass for AVBSFContext. It can be used in combination with + * AV_OPT_SEARCH_FAKE_OBJ for examining options. + * + * @see av_opt_find(). + */ +const AVClass *av_bsf_get_class(void); + +/** + * Structure for chain/list of bitstream filters. + * Empty list can be allocated by av_bsf_list_alloc(). + */ +typedef struct AVBSFList AVBSFList; + +/** + * Allocate empty list of bitstream filters. + * The list must be later freed by av_bsf_list_free() + * or finalized by av_bsf_list_finalize(). + * + * @return Pointer to @ref AVBSFList on success, NULL in case of failure + */ +AVBSFList *av_bsf_list_alloc(void); + +/** + * Free list of bitstream filters. + * + * @param lst Pointer to pointer returned by av_bsf_list_alloc() + */ +void av_bsf_list_free(AVBSFList **lst); + +/** + * Append bitstream filter to the list of bitstream filters. + * + * @param lst List to append to + * @param bsf Filter context to be appended + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_append(AVBSFList *lst, AVBSFContext *bsf); + +/** + * Construct new bitstream filter context given it's name and options + * and append it to the list of bitstream filters. + * + * @param lst List to append to + * @param bsf_name Name of the bitstream filter + * @param options Options for the bitstream filter, can be set to NULL + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_append2(AVBSFList *lst, const char * bsf_name, AVDictionary **options); +/** + * Finalize list of bitstream filters. + * + * This function will transform @ref AVBSFList to single @ref AVBSFContext, + * so the whole chain of bitstream filters can be treated as single filter + * freshly allocated by av_bsf_alloc(). + * If the call is successful, @ref AVBSFList structure is freed and lst + * will be set to NULL. In case of failure, caller is responsible for + * freeing the structure by av_bsf_list_free() + * + * @param lst Filter list structure to be transformed + * @param[out] bsf Pointer to be set to newly created @ref AVBSFContext structure + * representing the chain of bitstream filters + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_finalize(AVBSFList **lst, AVBSFContext **bsf); + +/** + * Parse string describing list of bitstream filters and create single + * @ref AVBSFContext describing the whole chain of bitstream filters. + * Resulting @ref AVBSFContext can be treated as any other @ref AVBSFContext freshly + * allocated by av_bsf_alloc(). + * + * @param str String describing chain of bitstream filters in format + * `bsf1[=opt1=val1:opt2=val2][,bsf2]` + * @param[out] bsf Pointer to be set to newly created @ref AVBSFContext structure + * representing the chain of bitstream filters + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_parse_str(const char *str, AVBSFContext **bsf); + +/** + * Get null/pass-through bitstream filter. + * + * @param[out] bsf Pointer to be set to new instance of pass-through bitstream filter + * + * @return + */ +int av_bsf_get_null_filter(AVBSFContext **bsf); + +/** + * @} + */ + +#endif // AVCODEC_BSF_H diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/codec.h b/ThirdParty/ffmpeg/arm/include/libavcodec/codec.h new file mode 100644 index 000000000..1fda619ee --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/codec.h @@ -0,0 +1,462 @@ +/* + * AVCodec public API + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CODEC_H +#define AVCODEC_CODEC_H + +#include + +#include "libavutil/avutil.h" +#include "libavutil/hwcontext.h" +#include "libavutil/log.h" +#include "libavutil/pixfmt.h" +#include "libavutil/rational.h" +#include "libavutil/samplefmt.h" + +#include "libavcodec/codec_id.h" +#include "libavcodec/version.h" + +/** + * @addtogroup lavc_core + * @{ + */ + +/** + * Decoder can use draw_horiz_band callback. + */ +#define AV_CODEC_CAP_DRAW_HORIZ_BAND (1 << 0) +/** + * Codec uses get_buffer() for allocating buffers and supports custom allocators. + * If not set, it might not use get_buffer() at all or use operations that + * assume the buffer was allocated by avcodec_default_get_buffer. + */ +#define AV_CODEC_CAP_DR1 (1 << 1) +#define AV_CODEC_CAP_TRUNCATED (1 << 3) +/** + * Encoder or decoder requires flushing with NULL input at the end in order to + * give the complete and correct output. + * + * NOTE: If this flag is not set, the codec is guaranteed to never be fed with + * with NULL data. The user can still send NULL data to the public encode + * or decode function, but libavcodec will not pass it along to the codec + * unless this flag is set. + * + * Decoders: + * The decoder has a non-zero delay and needs to be fed with avpkt->data=NULL, + * avpkt->size=0 at the end to get the delayed data until the decoder no longer + * returns frames. + * + * Encoders: + * The encoder needs to be fed with NULL data at the end of encoding until the + * encoder no longer returns data. + * + * NOTE: For encoders implementing the AVCodec.encode2() function, setting this + * flag also means that the encoder must set the pts and duration for + * each output packet. If this flag is not set, the pts and duration will + * be determined by libavcodec from the input frame. + */ +#define AV_CODEC_CAP_DELAY (1 << 5) +/** + * Codec can be fed a final frame with a smaller size. + * This can be used to prevent truncation of the last audio samples. + */ +#define AV_CODEC_CAP_SMALL_LAST_FRAME (1 << 6) + +/** + * Codec can output multiple frames per AVPacket + * Normally demuxers return one frame at a time, demuxers which do not do + * are connected to a parser to split what they return into proper frames. + * This flag is reserved to the very rare category of codecs which have a + * bitstream that cannot be split into frames without timeconsuming + * operations like full decoding. Demuxers carrying such bitstreams thus + * may return multiple frames in a packet. This has many disadvantages like + * prohibiting stream copy in many cases thus it should only be considered + * as a last resort. + */ +#define AV_CODEC_CAP_SUBFRAMES (1 << 8) +/** + * Codec is experimental and is thus avoided in favor of non experimental + * encoders + */ +#define AV_CODEC_CAP_EXPERIMENTAL (1 << 9) +/** + * Codec should fill in channel configuration and samplerate instead of container + */ +#define AV_CODEC_CAP_CHANNEL_CONF (1 << 10) +/** + * Codec supports frame-level multithreading. + */ +#define AV_CODEC_CAP_FRAME_THREADS (1 << 12) +/** + * Codec supports slice-based (or partition-based) multithreading. + */ +#define AV_CODEC_CAP_SLICE_THREADS (1 << 13) +/** + * Codec supports changed parameters at any point. + */ +#define AV_CODEC_CAP_PARAM_CHANGE (1 << 14) +/** + * Codec supports avctx->thread_count == 0 (auto). + */ +#define AV_CODEC_CAP_AUTO_THREADS (1 << 15) +/** + * Audio encoder supports receiving a different number of samples in each call. + */ +#define AV_CODEC_CAP_VARIABLE_FRAME_SIZE (1 << 16) +/** + * Decoder is not a preferred choice for probing. + * This indicates that the decoder is not a good choice for probing. + * It could for example be an expensive to spin up hardware decoder, + * or it could simply not provide a lot of useful information about + * the stream. + * A decoder marked with this flag should only be used as last resort + * choice for probing. + */ +#define AV_CODEC_CAP_AVOID_PROBING (1 << 17) + +#if FF_API_UNUSED_CODEC_CAPS +/** + * Deprecated and unused. Use AVCodecDescriptor.props instead + */ +#define AV_CODEC_CAP_INTRA_ONLY 0x40000000 +/** + * Deprecated and unused. Use AVCodecDescriptor.props instead + */ +#define AV_CODEC_CAP_LOSSLESS 0x80000000 +#endif + +/** + * Codec is backed by a hardware implementation. Typically used to + * identify a non-hwaccel hardware decoder. For information about hwaccels, use + * avcodec_get_hw_config() instead. + */ +#define AV_CODEC_CAP_HARDWARE (1 << 18) + +/** + * Codec is potentially backed by a hardware implementation, but not + * necessarily. This is used instead of AV_CODEC_CAP_HARDWARE, if the + * implementation provides some sort of internal fallback. + */ +#define AV_CODEC_CAP_HYBRID (1 << 19) + +/** + * This codec takes the reordered_opaque field from input AVFrames + * and returns it in the corresponding field in AVCodecContext after + * encoding. + */ +#define AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE (1 << 20) + +/** + * This encoder can be flushed using avcodec_flush_buffers(). If this flag is + * not set, the encoder must be closed and reopened to ensure that no frames + * remain pending. + */ +#define AV_CODEC_CAP_ENCODER_FLUSH (1 << 21) + +/** + * AVProfile. + */ +typedef struct AVProfile { + int profile; + const char *name; ///< short name for the profile +} AVProfile; + +typedef struct AVCodecDefault AVCodecDefault; + +struct AVCodecContext; +struct AVSubtitle; +struct AVPacket; + +/** + * AVCodec. + */ +typedef struct AVCodec { + /** + * Name of the codec implementation. + * The name is globally unique among encoders and among decoders (but an + * encoder and a decoder can share the same name). + * This is the primary way to find a codec from the user perspective. + */ + const char *name; + /** + * Descriptive name for the codec, meant to be more human readable than name. + * You should use the NULL_IF_CONFIG_SMALL() macro to define it. + */ + const char *long_name; + enum AVMediaType type; + enum AVCodecID id; + /** + * Codec capabilities. + * see AV_CODEC_CAP_* + */ + int capabilities; + const AVRational *supported_framerates; ///< array of supported framerates, or NULL if any, array is terminated by {0,0} + const enum AVPixelFormat *pix_fmts; ///< array of supported pixel formats, or NULL if unknown, array is terminated by -1 + const int *supported_samplerates; ///< array of supported audio samplerates, or NULL if unknown, array is terminated by 0 + const enum AVSampleFormat *sample_fmts; ///< array of supported sample formats, or NULL if unknown, array is terminated by -1 + const uint64_t *channel_layouts; ///< array of support channel layouts, or NULL if unknown. array is terminated by 0 + uint8_t max_lowres; ///< maximum value for lowres supported by the decoder + const AVClass *priv_class; ///< AVClass for the private context + const AVProfile *profiles; ///< array of recognized profiles, or NULL if unknown, array is terminated by {FF_PROFILE_UNKNOWN} + + /** + * Group name of the codec implementation. + * This is a short symbolic name of the wrapper backing this codec. A + * wrapper uses some kind of external implementation for the codec, such + * as an external library, or a codec implementation provided by the OS or + * the hardware. + * If this field is NULL, this is a builtin, libavcodec native codec. + * If non-NULL, this will be the suffix in AVCodec.name in most cases + * (usually AVCodec.name will be of the form "_"). + */ + const char *wrapper_name; + + /***************************************************************** + * No fields below this line are part of the public API. They + * may not be used outside of libavcodec and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + int priv_data_size; + struct AVCodec *next; + /** + * @name Frame-level threading support functions + * @{ + */ + /** + * Copy necessary context variables from a previous thread context to the current one. + * If not defined, the next thread will start automatically; otherwise, the codec + * must call ff_thread_finish_setup(). + * + * dst and src will (rarely) point to the same context, in which case memcpy should be skipped. + */ + int (*update_thread_context)(struct AVCodecContext *dst, const struct AVCodecContext *src); + /** @} */ + + /** + * Private codec-specific defaults. + */ + const AVCodecDefault *defaults; + + /** + * Initialize codec static data, called from avcodec_register(). + * + * This is not intended for time consuming operations as it is + * run for every codec regardless of that codec being used. + */ + void (*init_static_data)(struct AVCodec *codec); + + int (*init)(struct AVCodecContext *); + int (*encode_sub)(struct AVCodecContext *, uint8_t *buf, int buf_size, + const struct AVSubtitle *sub); + /** + * Encode data to an AVPacket. + * + * @param avctx codec context + * @param avpkt output AVPacket (may contain a user-provided buffer) + * @param[in] frame AVFrame containing the raw data to be encoded + * @param[out] got_packet_ptr encoder sets to 0 or 1 to indicate that a + * non-empty packet was returned in avpkt. + * @return 0 on success, negative error code on failure + */ + int (*encode2)(struct AVCodecContext *avctx, struct AVPacket *avpkt, + const struct AVFrame *frame, int *got_packet_ptr); + int (*decode)(struct AVCodecContext *, void *outdata, int *outdata_size, struct AVPacket *avpkt); + int (*close)(struct AVCodecContext *); + /** + * Encode API with decoupled packet/frame dataflow. The API is the + * same as the avcodec_ prefixed APIs (avcodec_send_frame() etc.), except + * that: + * - never called if the codec is closed or the wrong type, + * - if AV_CODEC_CAP_DELAY is not set, drain frames are never sent, + * - only one drain frame is ever passed down, + */ + int (*send_frame)(struct AVCodecContext *avctx, const struct AVFrame *frame); + int (*receive_packet)(struct AVCodecContext *avctx, struct AVPacket *avpkt); + + /** + * Decode API with decoupled packet/frame dataflow. This function is called + * to get one output frame. It should call ff_decode_get_packet() to obtain + * input data. + */ + int (*receive_frame)(struct AVCodecContext *avctx, struct AVFrame *frame); + /** + * Flush buffers. + * Will be called when seeking + */ + void (*flush)(struct AVCodecContext *); + /** + * Internal codec capabilities. + * See FF_CODEC_CAP_* in internal.h + */ + int caps_internal; + + /** + * Decoding only, a comma-separated list of bitstream filters to apply to + * packets before decoding. + */ + const char *bsfs; + + /** + * Array of pointers to hardware configurations supported by the codec, + * or NULL if no hardware supported. The array is terminated by a NULL + * pointer. + * + * The user can only access this field via avcodec_get_hw_config(). + */ + const struct AVCodecHWConfigInternal **hw_configs; + + /** + * List of supported codec_tags, terminated by FF_CODEC_TAGS_END. + */ + const uint32_t *codec_tags; +} AVCodec; + +/** + * Iterate over all registered codecs. + * + * @param opaque a pointer where libavcodec will store the iteration state. Must + * point to NULL to start the iteration. + * + * @return the next registered codec or NULL when the iteration is + * finished + */ +const AVCodec *av_codec_iterate(void **opaque); + +/** + * Find a registered decoder with a matching codec ID. + * + * @param id AVCodecID of the requested decoder + * @return A decoder if one was found, NULL otherwise. + */ +AVCodec *avcodec_find_decoder(enum AVCodecID id); + +/** + * Find a registered decoder with the specified name. + * + * @param name name of the requested decoder + * @return A decoder if one was found, NULL otherwise. + */ +AVCodec *avcodec_find_decoder_by_name(const char *name); + +/** + * Find a registered encoder with a matching codec ID. + * + * @param id AVCodecID of the requested encoder + * @return An encoder if one was found, NULL otherwise. + */ +AVCodec *avcodec_find_encoder(enum AVCodecID id); + +/** + * Find a registered encoder with the specified name. + * + * @param name name of the requested encoder + * @return An encoder if one was found, NULL otherwise. + */ +AVCodec *avcodec_find_encoder_by_name(const char *name); +/** + * @return a non-zero number if codec is an encoder, zero otherwise + */ +int av_codec_is_encoder(const AVCodec *codec); + +/** + * @return a non-zero number if codec is a decoder, zero otherwise + */ +int av_codec_is_decoder(const AVCodec *codec); + +enum { + /** + * The codec supports this format via the hw_device_ctx interface. + * + * When selecting this format, AVCodecContext.hw_device_ctx should + * have been set to a device of the specified type before calling + * avcodec_open2(). + */ + AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX = 0x01, + /** + * The codec supports this format via the hw_frames_ctx interface. + * + * When selecting this format for a decoder, + * AVCodecContext.hw_frames_ctx should be set to a suitable frames + * context inside the get_format() callback. The frames context + * must have been created on a device of the specified type. + * + * When selecting this format for an encoder, + * AVCodecContext.hw_frames_ctx should be set to the context which + * will be used for the input frames before calling avcodec_open2(). + */ + AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX = 0x02, + /** + * The codec supports this format by some internal method. + * + * This format can be selected without any additional configuration - + * no device or frames context is required. + */ + AV_CODEC_HW_CONFIG_METHOD_INTERNAL = 0x04, + /** + * The codec supports this format by some ad-hoc method. + * + * Additional settings and/or function calls are required. See the + * codec-specific documentation for details. (Methods requiring + * this sort of configuration are deprecated and others should be + * used in preference.) + */ + AV_CODEC_HW_CONFIG_METHOD_AD_HOC = 0x08, +}; + +typedef struct AVCodecHWConfig { + /** + * For decoders, a hardware pixel format which that decoder may be + * able to decode to if suitable hardware is available. + * + * For encoders, a pixel format which the encoder may be able to + * accept. If set to AV_PIX_FMT_NONE, this applies to all pixel + * formats supported by the codec. + */ + enum AVPixelFormat pix_fmt; + /** + * Bit set of AV_CODEC_HW_CONFIG_METHOD_* flags, describing the possible + * setup methods which can be used with this configuration. + */ + int methods; + /** + * The device type associated with the configuration. + * + * Must be set for AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX and + * AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX, otherwise unused. + */ + enum AVHWDeviceType device_type; +} AVCodecHWConfig; + +/** + * Retrieve supported hardware configurations for a codec. + * + * Values of index from zero to some maximum return the indexed configuration + * descriptor; all other values return NULL. If the codec does not support + * any hardware configurations then it will always return NULL. + */ +const AVCodecHWConfig *avcodec_get_hw_config(const AVCodec *codec, int index); + +/** + * @} + */ + +#endif /* AVCODEC_CODEC_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/codec_desc.h b/ThirdParty/ffmpeg/arm/include/libavcodec/codec_desc.h new file mode 100644 index 000000000..126b52df4 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/codec_desc.h @@ -0,0 +1,128 @@ +/* + * Codec descriptors public API + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CODEC_DESC_H +#define AVCODEC_CODEC_DESC_H + +#include "libavutil/avutil.h" + +#include "codec_id.h" + +/** + * @addtogroup lavc_core + * @{ + */ + +/** + * This struct describes the properties of a single codec described by an + * AVCodecID. + * @see avcodec_descriptor_get() + */ +typedef struct AVCodecDescriptor { + enum AVCodecID id; + enum AVMediaType type; + /** + * Name of the codec described by this descriptor. It is non-empty and + * unique for each codec descriptor. It should contain alphanumeric + * characters and '_' only. + */ + const char *name; + /** + * A more descriptive name for this codec. May be NULL. + */ + const char *long_name; + /** + * Codec properties, a combination of AV_CODEC_PROP_* flags. + */ + int props; + /** + * MIME type(s) associated with the codec. + * May be NULL; if not, a NULL-terminated array of MIME types. + * The first item is always non-NULL and is the preferred MIME type. + */ + const char *const *mime_types; + /** + * If non-NULL, an array of profiles recognized for this codec. + * Terminated with FF_PROFILE_UNKNOWN. + */ + const struct AVProfile *profiles; +} AVCodecDescriptor; + +/** + * Codec uses only intra compression. + * Video and audio codecs only. + */ +#define AV_CODEC_PROP_INTRA_ONLY (1 << 0) +/** + * Codec supports lossy compression. Audio and video codecs only. + * @note a codec may support both lossy and lossless + * compression modes + */ +#define AV_CODEC_PROP_LOSSY (1 << 1) +/** + * Codec supports lossless compression. Audio and video codecs only. + */ +#define AV_CODEC_PROP_LOSSLESS (1 << 2) +/** + * Codec supports frame reordering. That is, the coded order (the order in which + * the encoded packets are output by the encoders / stored / input to the + * decoders) may be different from the presentation order of the corresponding + * frames. + * + * For codecs that do not have this property set, PTS and DTS should always be + * equal. + */ +#define AV_CODEC_PROP_REORDER (1 << 3) +/** + * Subtitle codec is bitmap based + * Decoded AVSubtitle data can be read from the AVSubtitleRect->pict field. + */ +#define AV_CODEC_PROP_BITMAP_SUB (1 << 16) +/** + * Subtitle codec is text based. + * Decoded AVSubtitle data can be read from the AVSubtitleRect->ass field. + */ +#define AV_CODEC_PROP_TEXT_SUB (1 << 17) + +/** + * @return descriptor for given codec ID or NULL if no descriptor exists. + */ +const AVCodecDescriptor *avcodec_descriptor_get(enum AVCodecID id); + +/** + * Iterate over all codec descriptors known to libavcodec. + * + * @param prev previous descriptor. NULL to get the first descriptor. + * + * @return next descriptor or NULL after the last descriptor + */ +const AVCodecDescriptor *avcodec_descriptor_next(const AVCodecDescriptor *prev); + +/** + * @return codec descriptor with the given name or NULL if no such descriptor + * exists. + */ +const AVCodecDescriptor *avcodec_descriptor_get_by_name(const char *name); + +/** + * @} + */ + +#endif // AVCODEC_CODEC_DESC_H diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/codec_id.h b/ThirdParty/ffmpeg/arm/include/libavcodec/codec_id.h new file mode 100644 index 000000000..d885962c9 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/codec_id.h @@ -0,0 +1,577 @@ +/* + * Codec IDs + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CODEC_ID_H +#define AVCODEC_CODEC_ID_H + +#include "libavutil/avutil.h" + +/** + * @addtogroup lavc_core + * @{ + */ + +/** + * Identify the syntax and semantics of the bitstream. + * The principle is roughly: + * Two decoders with the same ID can decode the same streams. + * Two encoders with the same ID can encode compatible streams. + * There may be slight deviations from the principle due to implementation + * details. + * + * If you add a codec ID to this list, add it so that + * 1. no value of an existing codec ID changes (that would break ABI), + * 2. it is as close as possible to similar codecs + * + * After adding new codec IDs, do not forget to add an entry to the codec + * descriptor list and bump libavcodec minor version. + */ +enum AVCodecID { + AV_CODEC_ID_NONE, + + /* video codecs */ + AV_CODEC_ID_MPEG1VIDEO, + AV_CODEC_ID_MPEG2VIDEO, ///< preferred ID for MPEG-1/2 video decoding + AV_CODEC_ID_H261, + AV_CODEC_ID_H263, + AV_CODEC_ID_RV10, + AV_CODEC_ID_RV20, + AV_CODEC_ID_MJPEG, + AV_CODEC_ID_MJPEGB, + AV_CODEC_ID_LJPEG, + AV_CODEC_ID_SP5X, + AV_CODEC_ID_JPEGLS, + AV_CODEC_ID_MPEG4, + AV_CODEC_ID_RAWVIDEO, + AV_CODEC_ID_MSMPEG4V1, + AV_CODEC_ID_MSMPEG4V2, + AV_CODEC_ID_MSMPEG4V3, + AV_CODEC_ID_WMV1, + AV_CODEC_ID_WMV2, + AV_CODEC_ID_H263P, + AV_CODEC_ID_H263I, + AV_CODEC_ID_FLV1, + AV_CODEC_ID_SVQ1, + AV_CODEC_ID_SVQ3, + AV_CODEC_ID_DVVIDEO, + AV_CODEC_ID_HUFFYUV, + AV_CODEC_ID_CYUV, + AV_CODEC_ID_H264, + AV_CODEC_ID_INDEO3, + AV_CODEC_ID_VP3, + AV_CODEC_ID_THEORA, + AV_CODEC_ID_ASV1, + AV_CODEC_ID_ASV2, + AV_CODEC_ID_FFV1, + AV_CODEC_ID_4XM, + AV_CODEC_ID_VCR1, + AV_CODEC_ID_CLJR, + AV_CODEC_ID_MDEC, + AV_CODEC_ID_ROQ, + AV_CODEC_ID_INTERPLAY_VIDEO, + AV_CODEC_ID_XAN_WC3, + AV_CODEC_ID_XAN_WC4, + AV_CODEC_ID_RPZA, + AV_CODEC_ID_CINEPAK, + AV_CODEC_ID_WS_VQA, + AV_CODEC_ID_MSRLE, + AV_CODEC_ID_MSVIDEO1, + AV_CODEC_ID_IDCIN, + AV_CODEC_ID_8BPS, + AV_CODEC_ID_SMC, + AV_CODEC_ID_FLIC, + AV_CODEC_ID_TRUEMOTION1, + AV_CODEC_ID_VMDVIDEO, + AV_CODEC_ID_MSZH, + AV_CODEC_ID_ZLIB, + AV_CODEC_ID_QTRLE, + AV_CODEC_ID_TSCC, + AV_CODEC_ID_ULTI, + AV_CODEC_ID_QDRAW, + AV_CODEC_ID_VIXL, + AV_CODEC_ID_QPEG, + AV_CODEC_ID_PNG, + AV_CODEC_ID_PPM, + AV_CODEC_ID_PBM, + AV_CODEC_ID_PGM, + AV_CODEC_ID_PGMYUV, + AV_CODEC_ID_PAM, + AV_CODEC_ID_FFVHUFF, + AV_CODEC_ID_RV30, + AV_CODEC_ID_RV40, + AV_CODEC_ID_VC1, + AV_CODEC_ID_WMV3, + AV_CODEC_ID_LOCO, + AV_CODEC_ID_WNV1, + AV_CODEC_ID_AASC, + AV_CODEC_ID_INDEO2, + AV_CODEC_ID_FRAPS, + AV_CODEC_ID_TRUEMOTION2, + AV_CODEC_ID_BMP, + AV_CODEC_ID_CSCD, + AV_CODEC_ID_MMVIDEO, + AV_CODEC_ID_ZMBV, + AV_CODEC_ID_AVS, + AV_CODEC_ID_SMACKVIDEO, + AV_CODEC_ID_NUV, + AV_CODEC_ID_KMVC, + AV_CODEC_ID_FLASHSV, + AV_CODEC_ID_CAVS, + AV_CODEC_ID_JPEG2000, + AV_CODEC_ID_VMNC, + AV_CODEC_ID_VP5, + AV_CODEC_ID_VP6, + AV_CODEC_ID_VP6F, + AV_CODEC_ID_TARGA, + AV_CODEC_ID_DSICINVIDEO, + AV_CODEC_ID_TIERTEXSEQVIDEO, + AV_CODEC_ID_TIFF, + AV_CODEC_ID_GIF, + AV_CODEC_ID_DXA, + AV_CODEC_ID_DNXHD, + AV_CODEC_ID_THP, + AV_CODEC_ID_SGI, + AV_CODEC_ID_C93, + AV_CODEC_ID_BETHSOFTVID, + AV_CODEC_ID_PTX, + AV_CODEC_ID_TXD, + AV_CODEC_ID_VP6A, + AV_CODEC_ID_AMV, + AV_CODEC_ID_VB, + AV_CODEC_ID_PCX, + AV_CODEC_ID_SUNRAST, + AV_CODEC_ID_INDEO4, + AV_CODEC_ID_INDEO5, + AV_CODEC_ID_MIMIC, + AV_CODEC_ID_RL2, + AV_CODEC_ID_ESCAPE124, + AV_CODEC_ID_DIRAC, + AV_CODEC_ID_BFI, + AV_CODEC_ID_CMV, + AV_CODEC_ID_MOTIONPIXELS, + AV_CODEC_ID_TGV, + AV_CODEC_ID_TGQ, + AV_CODEC_ID_TQI, + AV_CODEC_ID_AURA, + AV_CODEC_ID_AURA2, + AV_CODEC_ID_V210X, + AV_CODEC_ID_TMV, + AV_CODEC_ID_V210, + AV_CODEC_ID_DPX, + AV_CODEC_ID_MAD, + AV_CODEC_ID_FRWU, + AV_CODEC_ID_FLASHSV2, + AV_CODEC_ID_CDGRAPHICS, + AV_CODEC_ID_R210, + AV_CODEC_ID_ANM, + AV_CODEC_ID_BINKVIDEO, + AV_CODEC_ID_IFF_ILBM, +#define AV_CODEC_ID_IFF_BYTERUN1 AV_CODEC_ID_IFF_ILBM + AV_CODEC_ID_KGV1, + AV_CODEC_ID_YOP, + AV_CODEC_ID_VP8, + AV_CODEC_ID_PICTOR, + AV_CODEC_ID_ANSI, + AV_CODEC_ID_A64_MULTI, + AV_CODEC_ID_A64_MULTI5, + AV_CODEC_ID_R10K, + AV_CODEC_ID_MXPEG, + AV_CODEC_ID_LAGARITH, + AV_CODEC_ID_PRORES, + AV_CODEC_ID_JV, + AV_CODEC_ID_DFA, + AV_CODEC_ID_WMV3IMAGE, + AV_CODEC_ID_VC1IMAGE, + AV_CODEC_ID_UTVIDEO, + AV_CODEC_ID_BMV_VIDEO, + AV_CODEC_ID_VBLE, + AV_CODEC_ID_DXTORY, + AV_CODEC_ID_V410, + AV_CODEC_ID_XWD, + AV_CODEC_ID_CDXL, + AV_CODEC_ID_XBM, + AV_CODEC_ID_ZEROCODEC, + AV_CODEC_ID_MSS1, + AV_CODEC_ID_MSA1, + AV_CODEC_ID_TSCC2, + AV_CODEC_ID_MTS2, + AV_CODEC_ID_CLLC, + AV_CODEC_ID_MSS2, + AV_CODEC_ID_VP9, + AV_CODEC_ID_AIC, + AV_CODEC_ID_ESCAPE130, + AV_CODEC_ID_G2M, + AV_CODEC_ID_WEBP, + AV_CODEC_ID_HNM4_VIDEO, + AV_CODEC_ID_HEVC, +#define AV_CODEC_ID_H265 AV_CODEC_ID_HEVC + AV_CODEC_ID_FIC, + AV_CODEC_ID_ALIAS_PIX, + AV_CODEC_ID_BRENDER_PIX, + AV_CODEC_ID_PAF_VIDEO, + AV_CODEC_ID_EXR, + AV_CODEC_ID_VP7, + AV_CODEC_ID_SANM, + AV_CODEC_ID_SGIRLE, + AV_CODEC_ID_MVC1, + AV_CODEC_ID_MVC2, + AV_CODEC_ID_HQX, + AV_CODEC_ID_TDSC, + AV_CODEC_ID_HQ_HQA, + AV_CODEC_ID_HAP, + AV_CODEC_ID_DDS, + AV_CODEC_ID_DXV, + AV_CODEC_ID_SCREENPRESSO, + AV_CODEC_ID_RSCC, + AV_CODEC_ID_AVS2, + + AV_CODEC_ID_Y41P = 0x8000, + AV_CODEC_ID_AVRP, + AV_CODEC_ID_012V, + AV_CODEC_ID_AVUI, + AV_CODEC_ID_AYUV, + AV_CODEC_ID_TARGA_Y216, + AV_CODEC_ID_V308, + AV_CODEC_ID_V408, + AV_CODEC_ID_YUV4, + AV_CODEC_ID_AVRN, + AV_CODEC_ID_CPIA, + AV_CODEC_ID_XFACE, + AV_CODEC_ID_SNOW, + AV_CODEC_ID_SMVJPEG, + AV_CODEC_ID_APNG, + AV_CODEC_ID_DAALA, + AV_CODEC_ID_CFHD, + AV_CODEC_ID_TRUEMOTION2RT, + AV_CODEC_ID_M101, + AV_CODEC_ID_MAGICYUV, + AV_CODEC_ID_SHEERVIDEO, + AV_CODEC_ID_YLC, + AV_CODEC_ID_PSD, + AV_CODEC_ID_PIXLET, + AV_CODEC_ID_SPEEDHQ, + AV_CODEC_ID_FMVC, + AV_CODEC_ID_SCPR, + AV_CODEC_ID_CLEARVIDEO, + AV_CODEC_ID_XPM, + AV_CODEC_ID_AV1, + AV_CODEC_ID_BITPACKED, + AV_CODEC_ID_MSCC, + AV_CODEC_ID_SRGC, + AV_CODEC_ID_SVG, + AV_CODEC_ID_GDV, + AV_CODEC_ID_FITS, + AV_CODEC_ID_IMM4, + AV_CODEC_ID_PROSUMER, + AV_CODEC_ID_MWSC, + AV_CODEC_ID_WCMV, + AV_CODEC_ID_RASC, + AV_CODEC_ID_HYMT, + AV_CODEC_ID_ARBC, + AV_CODEC_ID_AGM, + AV_CODEC_ID_LSCR, + AV_CODEC_ID_VP4, + AV_CODEC_ID_IMM5, + AV_CODEC_ID_MVDV, + AV_CODEC_ID_MVHA, + AV_CODEC_ID_CDTOONS, + AV_CODEC_ID_MV30, + AV_CODEC_ID_NOTCHLC, + AV_CODEC_ID_PFM, + + /* various PCM "codecs" */ + AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs + AV_CODEC_ID_PCM_S16LE = 0x10000, + AV_CODEC_ID_PCM_S16BE, + AV_CODEC_ID_PCM_U16LE, + AV_CODEC_ID_PCM_U16BE, + AV_CODEC_ID_PCM_S8, + AV_CODEC_ID_PCM_U8, + AV_CODEC_ID_PCM_MULAW, + AV_CODEC_ID_PCM_ALAW, + AV_CODEC_ID_PCM_S32LE, + AV_CODEC_ID_PCM_S32BE, + AV_CODEC_ID_PCM_U32LE, + AV_CODEC_ID_PCM_U32BE, + AV_CODEC_ID_PCM_S24LE, + AV_CODEC_ID_PCM_S24BE, + AV_CODEC_ID_PCM_U24LE, + AV_CODEC_ID_PCM_U24BE, + AV_CODEC_ID_PCM_S24DAUD, + AV_CODEC_ID_PCM_ZORK, + AV_CODEC_ID_PCM_S16LE_PLANAR, + AV_CODEC_ID_PCM_DVD, + AV_CODEC_ID_PCM_F32BE, + AV_CODEC_ID_PCM_F32LE, + AV_CODEC_ID_PCM_F64BE, + AV_CODEC_ID_PCM_F64LE, + AV_CODEC_ID_PCM_BLURAY, + AV_CODEC_ID_PCM_LXF, + AV_CODEC_ID_S302M, + AV_CODEC_ID_PCM_S8_PLANAR, + AV_CODEC_ID_PCM_S24LE_PLANAR, + AV_CODEC_ID_PCM_S32LE_PLANAR, + AV_CODEC_ID_PCM_S16BE_PLANAR, + + AV_CODEC_ID_PCM_S64LE = 0x10800, + AV_CODEC_ID_PCM_S64BE, + AV_CODEC_ID_PCM_F16LE, + AV_CODEC_ID_PCM_F24LE, + AV_CODEC_ID_PCM_VIDC, + + /* various ADPCM codecs */ + AV_CODEC_ID_ADPCM_IMA_QT = 0x11000, + AV_CODEC_ID_ADPCM_IMA_WAV, + AV_CODEC_ID_ADPCM_IMA_DK3, + AV_CODEC_ID_ADPCM_IMA_DK4, + AV_CODEC_ID_ADPCM_IMA_WS, + AV_CODEC_ID_ADPCM_IMA_SMJPEG, + AV_CODEC_ID_ADPCM_MS, + AV_CODEC_ID_ADPCM_4XM, + AV_CODEC_ID_ADPCM_XA, + AV_CODEC_ID_ADPCM_ADX, + AV_CODEC_ID_ADPCM_EA, + AV_CODEC_ID_ADPCM_G726, + AV_CODEC_ID_ADPCM_CT, + AV_CODEC_ID_ADPCM_SWF, + AV_CODEC_ID_ADPCM_YAMAHA, + AV_CODEC_ID_ADPCM_SBPRO_4, + AV_CODEC_ID_ADPCM_SBPRO_3, + AV_CODEC_ID_ADPCM_SBPRO_2, + AV_CODEC_ID_ADPCM_THP, + AV_CODEC_ID_ADPCM_IMA_AMV, + AV_CODEC_ID_ADPCM_EA_R1, + AV_CODEC_ID_ADPCM_EA_R3, + AV_CODEC_ID_ADPCM_EA_R2, + AV_CODEC_ID_ADPCM_IMA_EA_SEAD, + AV_CODEC_ID_ADPCM_IMA_EA_EACS, + AV_CODEC_ID_ADPCM_EA_XAS, + AV_CODEC_ID_ADPCM_EA_MAXIS_XA, + AV_CODEC_ID_ADPCM_IMA_ISS, + AV_CODEC_ID_ADPCM_G722, + AV_CODEC_ID_ADPCM_IMA_APC, + AV_CODEC_ID_ADPCM_VIMA, + + AV_CODEC_ID_ADPCM_AFC = 0x11800, + AV_CODEC_ID_ADPCM_IMA_OKI, + AV_CODEC_ID_ADPCM_DTK, + AV_CODEC_ID_ADPCM_IMA_RAD, + AV_CODEC_ID_ADPCM_G726LE, + AV_CODEC_ID_ADPCM_THP_LE, + AV_CODEC_ID_ADPCM_PSX, + AV_CODEC_ID_ADPCM_AICA, + AV_CODEC_ID_ADPCM_IMA_DAT4, + AV_CODEC_ID_ADPCM_MTAF, + AV_CODEC_ID_ADPCM_AGM, + AV_CODEC_ID_ADPCM_ARGO, + AV_CODEC_ID_ADPCM_IMA_SSI, + AV_CODEC_ID_ADPCM_ZORK, + AV_CODEC_ID_ADPCM_IMA_APM, + AV_CODEC_ID_ADPCM_IMA_ALP, + AV_CODEC_ID_ADPCM_IMA_MTF, + AV_CODEC_ID_ADPCM_IMA_CUNNING, + + /* AMR */ + AV_CODEC_ID_AMR_NB = 0x12000, + AV_CODEC_ID_AMR_WB, + + /* RealAudio codecs*/ + AV_CODEC_ID_RA_144 = 0x13000, + AV_CODEC_ID_RA_288, + + /* various DPCM codecs */ + AV_CODEC_ID_ROQ_DPCM = 0x14000, + AV_CODEC_ID_INTERPLAY_DPCM, + AV_CODEC_ID_XAN_DPCM, + AV_CODEC_ID_SOL_DPCM, + + AV_CODEC_ID_SDX2_DPCM = 0x14800, + AV_CODEC_ID_GREMLIN_DPCM, + AV_CODEC_ID_DERF_DPCM, + + /* audio codecs */ + AV_CODEC_ID_MP2 = 0x15000, + AV_CODEC_ID_MP3, ///< preferred ID for decoding MPEG audio layer 1, 2 or 3 + AV_CODEC_ID_AAC, + AV_CODEC_ID_AC3, + AV_CODEC_ID_DTS, + AV_CODEC_ID_VORBIS, + AV_CODEC_ID_DVAUDIO, + AV_CODEC_ID_WMAV1, + AV_CODEC_ID_WMAV2, + AV_CODEC_ID_MACE3, + AV_CODEC_ID_MACE6, + AV_CODEC_ID_VMDAUDIO, + AV_CODEC_ID_FLAC, + AV_CODEC_ID_MP3ADU, + AV_CODEC_ID_MP3ON4, + AV_CODEC_ID_SHORTEN, + AV_CODEC_ID_ALAC, + AV_CODEC_ID_WESTWOOD_SND1, + AV_CODEC_ID_GSM, ///< as in Berlin toast format + AV_CODEC_ID_QDM2, + AV_CODEC_ID_COOK, + AV_CODEC_ID_TRUESPEECH, + AV_CODEC_ID_TTA, + AV_CODEC_ID_SMACKAUDIO, + AV_CODEC_ID_QCELP, + AV_CODEC_ID_WAVPACK, + AV_CODEC_ID_DSICINAUDIO, + AV_CODEC_ID_IMC, + AV_CODEC_ID_MUSEPACK7, + AV_CODEC_ID_MLP, + AV_CODEC_ID_GSM_MS, /* as found in WAV */ + AV_CODEC_ID_ATRAC3, + AV_CODEC_ID_APE, + AV_CODEC_ID_NELLYMOSER, + AV_CODEC_ID_MUSEPACK8, + AV_CODEC_ID_SPEEX, + AV_CODEC_ID_WMAVOICE, + AV_CODEC_ID_WMAPRO, + AV_CODEC_ID_WMALOSSLESS, + AV_CODEC_ID_ATRAC3P, + AV_CODEC_ID_EAC3, + AV_CODEC_ID_SIPR, + AV_CODEC_ID_MP1, + AV_CODEC_ID_TWINVQ, + AV_CODEC_ID_TRUEHD, + AV_CODEC_ID_MP4ALS, + AV_CODEC_ID_ATRAC1, + AV_CODEC_ID_BINKAUDIO_RDFT, + AV_CODEC_ID_BINKAUDIO_DCT, + AV_CODEC_ID_AAC_LATM, + AV_CODEC_ID_QDMC, + AV_CODEC_ID_CELT, + AV_CODEC_ID_G723_1, + AV_CODEC_ID_G729, + AV_CODEC_ID_8SVX_EXP, + AV_CODEC_ID_8SVX_FIB, + AV_CODEC_ID_BMV_AUDIO, + AV_CODEC_ID_RALF, + AV_CODEC_ID_IAC, + AV_CODEC_ID_ILBC, + AV_CODEC_ID_OPUS, + AV_CODEC_ID_COMFORT_NOISE, + AV_CODEC_ID_TAK, + AV_CODEC_ID_METASOUND, + AV_CODEC_ID_PAF_AUDIO, + AV_CODEC_ID_ON2AVC, + AV_CODEC_ID_DSS_SP, + AV_CODEC_ID_CODEC2, + + AV_CODEC_ID_FFWAVESYNTH = 0x15800, + AV_CODEC_ID_SONIC, + AV_CODEC_ID_SONIC_LS, + AV_CODEC_ID_EVRC, + AV_CODEC_ID_SMV, + AV_CODEC_ID_DSD_LSBF, + AV_CODEC_ID_DSD_MSBF, + AV_CODEC_ID_DSD_LSBF_PLANAR, + AV_CODEC_ID_DSD_MSBF_PLANAR, + AV_CODEC_ID_4GV, + AV_CODEC_ID_INTERPLAY_ACM, + AV_CODEC_ID_XMA1, + AV_CODEC_ID_XMA2, + AV_CODEC_ID_DST, + AV_CODEC_ID_ATRAC3AL, + AV_CODEC_ID_ATRAC3PAL, + AV_CODEC_ID_DOLBY_E, + AV_CODEC_ID_APTX, + AV_CODEC_ID_APTX_HD, + AV_CODEC_ID_SBC, + AV_CODEC_ID_ATRAC9, + AV_CODEC_ID_HCOM, + AV_CODEC_ID_ACELP_KELVIN, + AV_CODEC_ID_MPEGH_3D_AUDIO, + AV_CODEC_ID_SIREN, + AV_CODEC_ID_HCA, + + /* subtitle codecs */ + AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID pointing at the start of subtitle codecs. + AV_CODEC_ID_DVD_SUBTITLE = 0x17000, + AV_CODEC_ID_DVB_SUBTITLE, + AV_CODEC_ID_TEXT, ///< raw UTF-8 text + AV_CODEC_ID_XSUB, + AV_CODEC_ID_SSA, + AV_CODEC_ID_MOV_TEXT, + AV_CODEC_ID_HDMV_PGS_SUBTITLE, + AV_CODEC_ID_DVB_TELETEXT, + AV_CODEC_ID_SRT, + + AV_CODEC_ID_MICRODVD = 0x17800, + AV_CODEC_ID_EIA_608, + AV_CODEC_ID_JACOSUB, + AV_CODEC_ID_SAMI, + AV_CODEC_ID_REALTEXT, + AV_CODEC_ID_STL, + AV_CODEC_ID_SUBVIEWER1, + AV_CODEC_ID_SUBVIEWER, + AV_CODEC_ID_SUBRIP, + AV_CODEC_ID_WEBVTT, + AV_CODEC_ID_MPL2, + AV_CODEC_ID_VPLAYER, + AV_CODEC_ID_PJS, + AV_CODEC_ID_ASS, + AV_CODEC_ID_HDMV_TEXT_SUBTITLE, + AV_CODEC_ID_TTML, + AV_CODEC_ID_ARIB_CAPTION, + + /* other specific kind of codecs (generally used for attachments) */ + AV_CODEC_ID_FIRST_UNKNOWN = 0x18000, ///< A dummy ID pointing at the start of various fake codecs. + AV_CODEC_ID_TTF = 0x18000, + + AV_CODEC_ID_SCTE_35, ///< Contain timestamp estimated through PCR of program stream. + AV_CODEC_ID_EPG, + AV_CODEC_ID_BINTEXT = 0x18800, + AV_CODEC_ID_XBIN, + AV_CODEC_ID_IDF, + AV_CODEC_ID_OTF, + AV_CODEC_ID_SMPTE_KLV, + AV_CODEC_ID_DVD_NAV, + AV_CODEC_ID_TIMED_ID3, + AV_CODEC_ID_BIN_DATA, + + + AV_CODEC_ID_PROBE = 0x19000, ///< codec_id is not known (like AV_CODEC_ID_NONE) but lavf should attempt to identify it + + AV_CODEC_ID_MPEG2TS = 0x20000, /**< _FAKE_ codec to indicate a raw MPEG-2 TS + * stream (only used by libavformat) */ + AV_CODEC_ID_MPEG4SYSTEMS = 0x20001, /**< _FAKE_ codec to indicate a MPEG-4 Systems + * stream (only used by libavformat) */ + AV_CODEC_ID_FFMETADATA = 0x21000, ///< Dummy codec for streams containing only metadata information. + AV_CODEC_ID_WRAPPED_AVFRAME = 0x21001, ///< Passthrough codec, AVFrames wrapped in AVPacket +}; + +/** + * Get the type of the given codec. + */ +enum AVMediaType avcodec_get_type(enum AVCodecID codec_id); + +/** + * Get the name of a codec. + * @return a static string identifying the codec; never NULL + */ +const char *avcodec_get_name(enum AVCodecID id); + +/** + * @} + */ + +#endif // AVCODEC_CODEC_ID_H diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/codec_par.h b/ThirdParty/ffmpeg/arm/include/libavcodec/codec_par.h new file mode 100644 index 000000000..948758e23 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/codec_par.h @@ -0,0 +1,229 @@ +/* + * Codec parameters public API + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CODEC_PAR_H +#define AVCODEC_CODEC_PAR_H + +#include + +#include "libavutil/avutil.h" +#include "libavutil/rational.h" +#include "libavutil/pixfmt.h" + +#include "codec_id.h" + +/** + * @addtogroup lavc_core + */ + +enum AVFieldOrder { + AV_FIELD_UNKNOWN, + AV_FIELD_PROGRESSIVE, + AV_FIELD_TT, //< Top coded_first, top displayed first + AV_FIELD_BB, //< Bottom coded first, bottom displayed first + AV_FIELD_TB, //< Top coded first, bottom displayed first + AV_FIELD_BT, //< Bottom coded first, top displayed first +}; + +/** + * This struct describes the properties of an encoded stream. + * + * sizeof(AVCodecParameters) is not a part of the public ABI, this struct must + * be allocated with avcodec_parameters_alloc() and freed with + * avcodec_parameters_free(). + */ +typedef struct AVCodecParameters { + /** + * General type of the encoded data. + */ + enum AVMediaType codec_type; + /** + * Specific type of the encoded data (the codec used). + */ + enum AVCodecID codec_id; + /** + * Additional information about the codec (corresponds to the AVI FOURCC). + */ + uint32_t codec_tag; + + /** + * Extra binary data needed for initializing the decoder, codec-dependent. + * + * Must be allocated with av_malloc() and will be freed by + * avcodec_parameters_free(). The allocated size of extradata must be at + * least extradata_size + AV_INPUT_BUFFER_PADDING_SIZE, with the padding + * bytes zeroed. + */ + uint8_t *extradata; + /** + * Size of the extradata content in bytes. + */ + int extradata_size; + + /** + * - video: the pixel format, the value corresponds to enum AVPixelFormat. + * - audio: the sample format, the value corresponds to enum AVSampleFormat. + */ + int format; + + /** + * The average bitrate of the encoded data (in bits per second). + */ + int64_t bit_rate; + + /** + * The number of bits per sample in the codedwords. + * + * This is basically the bitrate per sample. It is mandatory for a bunch of + * formats to actually decode them. It's the number of bits for one sample in + * the actual coded bitstream. + * + * This could be for example 4 for ADPCM + * For PCM formats this matches bits_per_raw_sample + * Can be 0 + */ + int bits_per_coded_sample; + + /** + * This is the number of valid bits in each output sample. If the + * sample format has more bits, the least significant bits are additional + * padding bits, which are always 0. Use right shifts to reduce the sample + * to its actual size. For example, audio formats with 24 bit samples will + * have bits_per_raw_sample set to 24, and format set to AV_SAMPLE_FMT_S32. + * To get the original sample use "(int32_t)sample >> 8"." + * + * For ADPCM this might be 12 or 16 or similar + * Can be 0 + */ + int bits_per_raw_sample; + + /** + * Codec-specific bitstream restrictions that the stream conforms to. + */ + int profile; + int level; + + /** + * Video only. The dimensions of the video frame in pixels. + */ + int width; + int height; + + /** + * Video only. The aspect ratio (width / height) which a single pixel + * should have when displayed. + * + * When the aspect ratio is unknown / undefined, the numerator should be + * set to 0 (the denominator may have any value). + */ + AVRational sample_aspect_ratio; + + /** + * Video only. The order of the fields in interlaced video. + */ + enum AVFieldOrder field_order; + + /** + * Video only. Additional colorspace characteristics. + */ + enum AVColorRange color_range; + enum AVColorPrimaries color_primaries; + enum AVColorTransferCharacteristic color_trc; + enum AVColorSpace color_space; + enum AVChromaLocation chroma_location; + + /** + * Video only. Number of delayed frames. + */ + int video_delay; + + /** + * Audio only. The channel layout bitmask. May be 0 if the channel layout is + * unknown or unspecified, otherwise the number of bits set must be equal to + * the channels field. + */ + uint64_t channel_layout; + /** + * Audio only. The number of audio channels. + */ + int channels; + /** + * Audio only. The number of audio samples per second. + */ + int sample_rate; + /** + * Audio only. The number of bytes per coded audio frame, required by some + * formats. + * + * Corresponds to nBlockAlign in WAVEFORMATEX. + */ + int block_align; + /** + * Audio only. Audio frame size, if known. Required by some formats to be static. + */ + int frame_size; + + /** + * Audio only. The amount of padding (in samples) inserted by the encoder at + * the beginning of the audio. I.e. this number of leading decoded samples + * must be discarded by the caller to get the original audio without leading + * padding. + */ + int initial_padding; + /** + * Audio only. The amount of padding (in samples) appended by the encoder to + * the end of the audio. I.e. this number of decoded samples must be + * discarded by the caller from the end of the stream to get the original + * audio without any trailing padding. + */ + int trailing_padding; + /** + * Audio only. Number of samples to skip after a discontinuity. + */ + int seek_preroll; +} AVCodecParameters; + +/** + * Allocate a new AVCodecParameters and set its fields to default values + * (unknown/invalid/0). The returned struct must be freed with + * avcodec_parameters_free(). + */ +AVCodecParameters *avcodec_parameters_alloc(void); + +/** + * Free an AVCodecParameters instance and everything associated with it and + * write NULL to the supplied pointer. + */ +void avcodec_parameters_free(AVCodecParameters **par); + +/** + * Copy the contents of src to dst. Any allocated fields in dst are freed and + * replaced with newly allocated duplicates of the corresponding fields in src. + * + * @return >= 0 on success, a negative AVERROR code on failure. + */ +int avcodec_parameters_copy(AVCodecParameters *dst, const AVCodecParameters *src); + + +/** + * @} + */ + +#endif // AVCODEC_CODEC_PAR_H diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/d3d11va.h b/ThirdParty/ffmpeg/arm/include/libavcodec/d3d11va.h new file mode 100644 index 000000000..6816b6c1e --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/d3d11va.h @@ -0,0 +1,112 @@ +/* + * Direct3D11 HW acceleration + * + * copyright (c) 2009 Laurent Aimar + * copyright (c) 2015 Steve Lhomme + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_D3D11VA_H +#define AVCODEC_D3D11VA_H + +/** + * @file + * @ingroup lavc_codec_hwaccel_d3d11va + * Public libavcodec D3D11VA header. + */ + +#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0602 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0602 +#endif + +#include +#include + +/** + * @defgroup lavc_codec_hwaccel_d3d11va Direct3D11 + * @ingroup lavc_codec_hwaccel + * + * @{ + */ + +#define FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG 1 ///< Work around for Direct3D11 and old UVD/UVD+ ATI video cards +#define FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO 2 ///< Work around for Direct3D11 and old Intel GPUs with ClearVideo interface + +/** + * This structure is used to provides the necessary configurations and data + * to the Direct3D11 FFmpeg HWAccel implementation. + * + * The application must make it available as AVCodecContext.hwaccel_context. + * + * Use av_d3d11va_alloc_context() exclusively to allocate an AVD3D11VAContext. + */ +typedef struct AVD3D11VAContext { + /** + * D3D11 decoder object + */ + ID3D11VideoDecoder *decoder; + + /** + * D3D11 VideoContext + */ + ID3D11VideoContext *video_context; + + /** + * D3D11 configuration used to create the decoder + */ + D3D11_VIDEO_DECODER_CONFIG *cfg; + + /** + * The number of surface in the surface array + */ + unsigned surface_count; + + /** + * The array of Direct3D surfaces used to create the decoder + */ + ID3D11VideoDecoderOutputView **surface; + + /** + * A bit field configuring the workarounds needed for using the decoder + */ + uint64_t workaround; + + /** + * Private to the FFmpeg AVHWAccel implementation + */ + unsigned report_id; + + /** + * Mutex to access video_context + */ + HANDLE context_mutex; +} AVD3D11VAContext; + +/** + * Allocate an AVD3D11VAContext. + * + * @return Newly-allocated AVD3D11VAContext or NULL on failure. + */ +AVD3D11VAContext *av_d3d11va_alloc_context(void); + +/** + * @} + */ + +#endif /* AVCODEC_D3D11VA_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/dirac.h b/ThirdParty/ffmpeg/arm/include/libavcodec/dirac.h new file mode 100644 index 000000000..e6d9d346d --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/dirac.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2007 Marco Gerards + * Copyright (C) 2009 David Conrad + * Copyright (C) 2011 Jordi Ortiz + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_DIRAC_H +#define AVCODEC_DIRAC_H + +/** + * @file + * Interface to Dirac Decoder/Encoder + * @author Marco Gerards + * @author David Conrad + * @author Jordi Ortiz + */ + +#include "avcodec.h" + +/** + * The spec limits the number of wavelet decompositions to 4 for both + * level 1 (VC-2) and 128 (long-gop default). + * 5 decompositions is the maximum before >16-bit buffers are needed. + * Schroedinger allows this for DD 9,7 and 13,7 wavelets only, limiting + * the others to 4 decompositions (or 3 for the fidelity filter). + * + * We use this instead of MAX_DECOMPOSITIONS to save some memory. + */ +#define MAX_DWT_LEVELS 5 + +/** + * Parse code values: + * + * Dirac Specification -> + * 9.6.1 Table 9.1 + * + * VC-2 Specification -> + * 10.4.1 Table 10.1 + */ + +enum DiracParseCodes { + DIRAC_PCODE_SEQ_HEADER = 0x00, + DIRAC_PCODE_END_SEQ = 0x10, + DIRAC_PCODE_AUX = 0x20, + DIRAC_PCODE_PAD = 0x30, + DIRAC_PCODE_PICTURE_CODED = 0x08, + DIRAC_PCODE_PICTURE_RAW = 0x48, + DIRAC_PCODE_PICTURE_LOW_DEL = 0xC8, + DIRAC_PCODE_PICTURE_HQ = 0xE8, + DIRAC_PCODE_INTER_NOREF_CO1 = 0x0A, + DIRAC_PCODE_INTER_NOREF_CO2 = 0x09, + DIRAC_PCODE_INTER_REF_CO1 = 0x0D, + DIRAC_PCODE_INTER_REF_CO2 = 0x0E, + DIRAC_PCODE_INTRA_REF_CO = 0x0C, + DIRAC_PCODE_INTRA_REF_RAW = 0x4C, + DIRAC_PCODE_INTRA_REF_PICT = 0xCC, + DIRAC_PCODE_MAGIC = 0x42424344, +}; + +typedef struct DiracVersionInfo { + int major; + int minor; +} DiracVersionInfo; + +typedef struct AVDiracSeqHeader { + unsigned width; + unsigned height; + uint8_t chroma_format; ///< 0: 444 1: 422 2: 420 + + uint8_t interlaced; + uint8_t top_field_first; + + uint8_t frame_rate_index; ///< index into dirac_frame_rate[] + uint8_t aspect_ratio_index; ///< index into dirac_aspect_ratio[] + + uint16_t clean_width; + uint16_t clean_height; + uint16_t clean_left_offset; + uint16_t clean_right_offset; + + uint8_t pixel_range_index; ///< index into dirac_pixel_range_presets[] + uint8_t color_spec_index; ///< index into dirac_color_spec_presets[] + + int profile; + int level; + + AVRational framerate; + AVRational sample_aspect_ratio; + + enum AVPixelFormat pix_fmt; + enum AVColorRange color_range; + enum AVColorPrimaries color_primaries; + enum AVColorTransferCharacteristic color_trc; + enum AVColorSpace colorspace; + + DiracVersionInfo version; + int bit_depth; +} AVDiracSeqHeader; + +/** + * Parse a Dirac sequence header. + * + * @param dsh this function will allocate and fill an AVDiracSeqHeader struct + * and write it into this pointer. The caller must free it with + * av_free(). + * @param buf the data buffer + * @param buf_size the size of the data buffer in bytes + * @param log_ctx if non-NULL, this function will log errors here + * @return 0 on success, a negative AVERROR code on failure + */ +int av_dirac_parse_sequence_header(AVDiracSeqHeader **dsh, + const uint8_t *buf, size_t buf_size, + void *log_ctx); + +#endif /* AVCODEC_DIRAC_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/dv_profile.h b/ThirdParty/ffmpeg/arm/include/libavcodec/dv_profile.h new file mode 100644 index 000000000..9380a66f0 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/dv_profile.h @@ -0,0 +1,83 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_DV_PROFILE_H +#define AVCODEC_DV_PROFILE_H + +#include + +#include "libavutil/pixfmt.h" +#include "libavutil/rational.h" +#include "avcodec.h" + +/* minimum number of bytes to read from a DV stream in order to + * determine the profile */ +#define DV_PROFILE_BYTES (6 * 80) /* 6 DIF blocks */ + + +/* + * AVDVProfile is used to express the differences between various + * DV flavors. For now it's primarily used for differentiating + * 525/60 and 625/50, but the plans are to use it for various + * DV specs as well (e.g. SMPTE314M vs. IEC 61834). + */ +typedef struct AVDVProfile { + int dsf; /* value of the dsf in the DV header */ + int video_stype; /* stype for VAUX source pack */ + int frame_size; /* total size of one frame in bytes */ + int difseg_size; /* number of DIF segments per DIF channel */ + int n_difchan; /* number of DIF channels per frame */ + AVRational time_base; /* 1/framerate */ + int ltc_divisor; /* FPS from the LTS standpoint */ + int height; /* picture height in pixels */ + int width; /* picture width in pixels */ + AVRational sar[2]; /* sample aspect ratios for 4:3 and 16:9 */ + enum AVPixelFormat pix_fmt; /* picture pixel format */ + int bpm; /* blocks per macroblock */ + const uint8_t *block_sizes; /* AC block sizes, in bits */ + int audio_stride; /* size of audio_shuffle table */ + int audio_min_samples[3]; /* min amount of audio samples */ + /* for 48kHz, 44.1kHz and 32kHz */ + int audio_samples_dist[5]; /* how many samples are supposed to be */ + /* in each frame in a 5 frames window */ + const uint8_t (*audio_shuffle)[9]; /* PCM shuffling table */ +} AVDVProfile; + +/** + * Get a DV profile for the provided compressed frame. + * + * @param sys the profile used for the previous frame, may be NULL + * @param frame the compressed data buffer + * @param buf_size size of the buffer in bytes + * @return the DV profile for the supplied data or NULL on failure + */ +const AVDVProfile *av_dv_frame_profile(const AVDVProfile *sys, + const uint8_t *frame, unsigned buf_size); + +/** + * Get a DV profile for the provided stream parameters. + */ +const AVDVProfile *av_dv_codec_profile(int width, int height, enum AVPixelFormat pix_fmt); + +/** + * Get a DV profile for the provided stream parameters. + * The frame rate is used as a best-effort parameter. + */ +const AVDVProfile *av_dv_codec_profile2(int width, int height, enum AVPixelFormat pix_fmt, AVRational frame_rate); + +#endif /* AVCODEC_DV_PROFILE_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/dxva2.h b/ThirdParty/ffmpeg/arm/include/libavcodec/dxva2.h new file mode 100644 index 000000000..22c93992f --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/dxva2.h @@ -0,0 +1,93 @@ +/* + * DXVA2 HW acceleration + * + * copyright (c) 2009 Laurent Aimar + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_DXVA2_H +#define AVCODEC_DXVA2_H + +/** + * @file + * @ingroup lavc_codec_hwaccel_dxva2 + * Public libavcodec DXVA2 header. + */ + +#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0602 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0602 +#endif + +#include +#include +#include + +/** + * @defgroup lavc_codec_hwaccel_dxva2 DXVA2 + * @ingroup lavc_codec_hwaccel + * + * @{ + */ + +#define FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG 1 ///< Work around for DXVA2 and old UVD/UVD+ ATI video cards +#define FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO 2 ///< Work around for DXVA2 and old Intel GPUs with ClearVideo interface + +/** + * This structure is used to provides the necessary configurations and data + * to the DXVA2 FFmpeg HWAccel implementation. + * + * The application must make it available as AVCodecContext.hwaccel_context. + */ +struct dxva_context { + /** + * DXVA2 decoder object + */ + IDirectXVideoDecoder *decoder; + + /** + * DXVA2 configuration used to create the decoder + */ + const DXVA2_ConfigPictureDecode *cfg; + + /** + * The number of surface in the surface array + */ + unsigned surface_count; + + /** + * The array of Direct3D surfaces used to create the decoder + */ + LPDIRECT3DSURFACE9 *surface; + + /** + * A bit field configuring the workarounds needed for using the decoder + */ + uint64_t workaround; + + /** + * Private to the FFmpeg AVHWAccel implementation + */ + unsigned report_id; +}; + +/** + * @} + */ + +#endif /* AVCODEC_DXVA2_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/jni.h b/ThirdParty/ffmpeg/arm/include/libavcodec/jni.h new file mode 100644 index 000000000..dd99e9261 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/jni.h @@ -0,0 +1,46 @@ +/* + * JNI public API functions + * + * Copyright (c) 2015-2016 Matthieu Bouron + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_JNI_H +#define AVCODEC_JNI_H + +/* + * Manually set a Java virtual machine which will be used to retrieve the JNI + * environment. Once a Java VM is set it cannot be changed afterwards, meaning + * you can call multiple times av_jni_set_java_vm with the same Java VM pointer + * however it will error out if you try to set a different Java VM. + * + * @param vm Java virtual machine + * @param log_ctx context used for logging, can be NULL + * @return 0 on success, < 0 otherwise + */ +int av_jni_set_java_vm(void *vm, void *log_ctx); + +/* + * Get the Java virtual machine which has been set with av_jni_set_java_vm. + * + * @param vm Java virtual machine + * @return a pointer to the Java virtual machine + */ +void *av_jni_get_java_vm(void *log_ctx); + +#endif /* AVCODEC_JNI_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/mediacodec.h b/ThirdParty/ffmpeg/arm/include/libavcodec/mediacodec.h new file mode 100644 index 000000000..4c8545df0 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/mediacodec.h @@ -0,0 +1,101 @@ +/* + * Android MediaCodec public API + * + * Copyright (c) 2016 Matthieu Bouron + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_MEDIACODEC_H +#define AVCODEC_MEDIACODEC_H + +#include "libavcodec/avcodec.h" + +/** + * This structure holds a reference to a android/view/Surface object that will + * be used as output by the decoder. + * + */ +typedef struct AVMediaCodecContext { + + /** + * android/view/Surface object reference. + */ + void *surface; + +} AVMediaCodecContext; + +/** + * Allocate and initialize a MediaCodec context. + * + * When decoding with MediaCodec is finished, the caller must free the + * MediaCodec context with av_mediacodec_default_free. + * + * @return a pointer to a newly allocated AVMediaCodecContext on success, NULL otherwise + */ +AVMediaCodecContext *av_mediacodec_alloc_context(void); + +/** + * Convenience function that sets up the MediaCodec context. + * + * @param avctx codec context + * @param ctx MediaCodec context to initialize + * @param surface reference to an android/view/Surface + * @return 0 on success, < 0 otherwise + */ +int av_mediacodec_default_init(AVCodecContext *avctx, AVMediaCodecContext *ctx, void *surface); + +/** + * This function must be called to free the MediaCodec context initialized with + * av_mediacodec_default_init(). + * + * @param avctx codec context + */ +void av_mediacodec_default_free(AVCodecContext *avctx); + +/** + * Opaque structure representing a MediaCodec buffer to render. + */ +typedef struct MediaCodecBuffer AVMediaCodecBuffer; + +/** + * Release a MediaCodec buffer and render it to the surface that is associated + * with the decoder. This function should only be called once on a given + * buffer, once released the underlying buffer returns to the codec, thus + * subsequent calls to this function will have no effect. + * + * @param buffer the buffer to render + * @param render 1 to release and render the buffer to the surface or 0 to + * discard the buffer + * @return 0 on success, < 0 otherwise + */ +int av_mediacodec_release_buffer(AVMediaCodecBuffer *buffer, int render); + +/** + * Release a MediaCodec buffer and render it at the given time to the surface + * that is associated with the decoder. The timestamp must be within one second + * of the current java/lang/System#nanoTime() (which is implemented using + * CLOCK_MONOTONIC on Android). See the Android MediaCodec documentation + * of android/media/MediaCodec#releaseOutputBuffer(int,long) for more details. + * + * @param buffer the buffer to render + * @param time timestamp in nanoseconds of when to render the buffer + * @return 0 on success, < 0 otherwise + */ +int av_mediacodec_render_buffer_at_time(AVMediaCodecBuffer *buffer, int64_t time); + +#endif /* AVCODEC_MEDIACODEC_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/packet.h b/ThirdParty/ffmpeg/arm/include/libavcodec/packet.h new file mode 100644 index 000000000..41485f452 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/packet.h @@ -0,0 +1,722 @@ +/* + * AVPacket public API + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_PACKET_H +#define AVCODEC_PACKET_H + +#include +#include + +#include "libavutil/attributes.h" +#include "libavutil/buffer.h" +#include "libavutil/dict.h" +#include "libavutil/rational.h" + +#include "libavcodec/version.h" + +/** + * @defgroup lavc_packet AVPacket + * + * Types and functions for working with AVPacket. + * @{ + */ +enum AVPacketSideDataType { + /** + * An AV_PKT_DATA_PALETTE side data packet contains exactly AVPALETTE_SIZE + * bytes worth of palette. This side data signals that a new palette is + * present. + */ + AV_PKT_DATA_PALETTE, + + /** + * The AV_PKT_DATA_NEW_EXTRADATA is used to notify the codec or the format + * that the extradata buffer was changed and the receiving side should + * act upon it appropriately. The new extradata is embedded in the side + * data buffer and should be immediately used for processing the current + * frame or packet. + */ + AV_PKT_DATA_NEW_EXTRADATA, + + /** + * An AV_PKT_DATA_PARAM_CHANGE side data packet is laid out as follows: + * @code + * u32le param_flags + * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT) + * s32le channel_count + * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT) + * u64le channel_layout + * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE) + * s32le sample_rate + * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS) + * s32le width + * s32le height + * @endcode + */ + AV_PKT_DATA_PARAM_CHANGE, + + /** + * An AV_PKT_DATA_H263_MB_INFO side data packet contains a number of + * structures with info about macroblocks relevant to splitting the + * packet into smaller packets on macroblock edges (e.g. as for RFC 2190). + * That is, it does not necessarily contain info about all macroblocks, + * as long as the distance between macroblocks in the info is smaller + * than the target payload size. + * Each MB info structure is 12 bytes, and is laid out as follows: + * @code + * u32le bit offset from the start of the packet + * u8 current quantizer at the start of the macroblock + * u8 GOB number + * u16le macroblock address within the GOB + * u8 horizontal MV predictor + * u8 vertical MV predictor + * u8 horizontal MV predictor for block number 3 + * u8 vertical MV predictor for block number 3 + * @endcode + */ + AV_PKT_DATA_H263_MB_INFO, + + /** + * This side data should be associated with an audio stream and contains + * ReplayGain information in form of the AVReplayGain struct. + */ + AV_PKT_DATA_REPLAYGAIN, + + /** + * This side data contains a 3x3 transformation matrix describing an affine + * transformation that needs to be applied to the decoded video frames for + * correct presentation. + * + * See libavutil/display.h for a detailed description of the data. + */ + AV_PKT_DATA_DISPLAYMATRIX, + + /** + * This side data should be associated with a video stream and contains + * Stereoscopic 3D information in form of the AVStereo3D struct. + */ + AV_PKT_DATA_STEREO3D, + + /** + * This side data should be associated with an audio stream and corresponds + * to enum AVAudioServiceType. + */ + AV_PKT_DATA_AUDIO_SERVICE_TYPE, + + /** + * This side data contains quality related information from the encoder. + * @code + * u32le quality factor of the compressed frame. Allowed range is between 1 (good) and FF_LAMBDA_MAX (bad). + * u8 picture type + * u8 error count + * u16 reserved + * u64le[error count] sum of squared differences between encoder in and output + * @endcode + */ + AV_PKT_DATA_QUALITY_STATS, + + /** + * This side data contains an integer value representing the stream index + * of a "fallback" track. A fallback track indicates an alternate + * track to use when the current track can not be decoded for some reason. + * e.g. no decoder available for codec. + */ + AV_PKT_DATA_FALLBACK_TRACK, + + /** + * This side data corresponds to the AVCPBProperties struct. + */ + AV_PKT_DATA_CPB_PROPERTIES, + + /** + * Recommmends skipping the specified number of samples + * @code + * u32le number of samples to skip from start of this packet + * u32le number of samples to skip from end of this packet + * u8 reason for start skip + * u8 reason for end skip (0=padding silence, 1=convergence) + * @endcode + */ + AV_PKT_DATA_SKIP_SAMPLES, + + /** + * An AV_PKT_DATA_JP_DUALMONO side data packet indicates that + * the packet may contain "dual mono" audio specific to Japanese DTV + * and if it is true, recommends only the selected channel to be used. + * @code + * u8 selected channels (0=mail/left, 1=sub/right, 2=both) + * @endcode + */ + AV_PKT_DATA_JP_DUALMONO, + + /** + * A list of zero terminated key/value strings. There is no end marker for + * the list, so it is required to rely on the side data size to stop. + */ + AV_PKT_DATA_STRINGS_METADATA, + + /** + * Subtitle event position + * @code + * u32le x1 + * u32le y1 + * u32le x2 + * u32le y2 + * @endcode + */ + AV_PKT_DATA_SUBTITLE_POSITION, + + /** + * Data found in BlockAdditional element of matroska container. There is + * no end marker for the data, so it is required to rely on the side data + * size to recognize the end. 8 byte id (as found in BlockAddId) followed + * by data. + */ + AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, + + /** + * The optional first identifier line of a WebVTT cue. + */ + AV_PKT_DATA_WEBVTT_IDENTIFIER, + + /** + * The optional settings (rendering instructions) that immediately + * follow the timestamp specifier of a WebVTT cue. + */ + AV_PKT_DATA_WEBVTT_SETTINGS, + + /** + * A list of zero terminated key/value strings. There is no end marker for + * the list, so it is required to rely on the side data size to stop. This + * side data includes updated metadata which appeared in the stream. + */ + AV_PKT_DATA_METADATA_UPDATE, + + /** + * MPEGTS stream ID as uint8_t, this is required to pass the stream ID + * information from the demuxer to the corresponding muxer. + */ + AV_PKT_DATA_MPEGTS_STREAM_ID, + + /** + * Mastering display metadata (based on SMPTE-2086:2014). This metadata + * should be associated with a video stream and contains data in the form + * of the AVMasteringDisplayMetadata struct. + */ + AV_PKT_DATA_MASTERING_DISPLAY_METADATA, + + /** + * This side data should be associated with a video stream and corresponds + * to the AVSphericalMapping structure. + */ + AV_PKT_DATA_SPHERICAL, + + /** + * Content light level (based on CTA-861.3). This metadata should be + * associated with a video stream and contains data in the form of the + * AVContentLightMetadata struct. + */ + AV_PKT_DATA_CONTENT_LIGHT_LEVEL, + + /** + * ATSC A53 Part 4 Closed Captions. This metadata should be associated with + * a video stream. A53 CC bitstream is stored as uint8_t in AVPacketSideData.data. + * The number of bytes of CC data is AVPacketSideData.size. + */ + AV_PKT_DATA_A53_CC, + + /** + * This side data is encryption initialization data. + * The format is not part of ABI, use av_encryption_init_info_* methods to + * access. + */ + AV_PKT_DATA_ENCRYPTION_INIT_INFO, + + /** + * This side data contains encryption info for how to decrypt the packet. + * The format is not part of ABI, use av_encryption_info_* methods to access. + */ + AV_PKT_DATA_ENCRYPTION_INFO, + + /** + * Active Format Description data consisting of a single byte as specified + * in ETSI TS 101 154 using AVActiveFormatDescription enum. + */ + AV_PKT_DATA_AFD, + + /** + * Producer Reference Time data corresponding to the AVProducerReferenceTime struct, + * usually exported by some encoders (on demand through the prft flag set in the + * AVCodecContext export_side_data field). + */ + AV_PKT_DATA_PRFT, + + /** + * ICC profile data consisting of an opaque octet buffer following the + * format described by ISO 15076-1. + */ + AV_PKT_DATA_ICC_PROFILE, + + /** + * DOVI configuration + * ref: + * dolby-vision-bitstreams-within-the-iso-base-media-file-format-v2.1.2, section 2.2 + * dolby-vision-bitstreams-in-mpeg-2-transport-stream-multiplex-v1.2, section 3.3 + * Tags are stored in struct AVDOVIDecoderConfigurationRecord. + */ + AV_PKT_DATA_DOVI_CONF, + + /** + * The number of side data types. + * This is not part of the public API/ABI in the sense that it may + * change when new side data types are added. + * This must stay the last enum value. + * If its value becomes huge, some code using it + * needs to be updated as it assumes it to be smaller than other limits. + */ + AV_PKT_DATA_NB +}; + +#define AV_PKT_DATA_QUALITY_FACTOR AV_PKT_DATA_QUALITY_STATS //DEPRECATED + +typedef struct AVPacketSideData { + uint8_t *data; + int size; + enum AVPacketSideDataType type; +} AVPacketSideData; + +/** + * This structure stores compressed data. It is typically exported by demuxers + * and then passed as input to decoders, or received as output from encoders and + * then passed to muxers. + * + * For video, it should typically contain one compressed frame. For audio it may + * contain several compressed frames. Encoders are allowed to output empty + * packets, with no compressed data, containing only side data + * (e.g. to update some stream parameters at the end of encoding). + * + * AVPacket is one of the few structs in FFmpeg, whose size is a part of public + * ABI. Thus it may be allocated on stack and no new fields can be added to it + * without libavcodec and libavformat major bump. + * + * The semantics of data ownership depends on the buf field. + * If it is set, the packet data is dynamically allocated and is + * valid indefinitely until a call to av_packet_unref() reduces the + * reference count to 0. + * + * If the buf field is not set av_packet_ref() would make a copy instead + * of increasing the reference count. + * + * The side data is always allocated with av_malloc(), copied by + * av_packet_ref() and freed by av_packet_unref(). + * + * @see av_packet_ref + * @see av_packet_unref + */ +typedef struct AVPacket { + /** + * A reference to the reference-counted buffer where the packet data is + * stored. + * May be NULL, then the packet data is not reference-counted. + */ + AVBufferRef *buf; + /** + * Presentation timestamp in AVStream->time_base units; the time at which + * the decompressed packet will be presented to the user. + * Can be AV_NOPTS_VALUE if it is not stored in the file. + * pts MUST be larger or equal to dts as presentation cannot happen before + * decompression, unless one wants to view hex dumps. Some formats misuse + * the terms dts and pts/cts to mean something different. Such timestamps + * must be converted to true pts/dts before they are stored in AVPacket. + */ + int64_t pts; + /** + * Decompression timestamp in AVStream->time_base units; the time at which + * the packet is decompressed. + * Can be AV_NOPTS_VALUE if it is not stored in the file. + */ + int64_t dts; + uint8_t *data; + int size; + int stream_index; + /** + * A combination of AV_PKT_FLAG values + */ + int flags; + /** + * Additional packet data that can be provided by the container. + * Packet can contain several types of side information. + */ + AVPacketSideData *side_data; + int side_data_elems; + + /** + * Duration of this packet in AVStream->time_base units, 0 if unknown. + * Equals next_pts - this_pts in presentation order. + */ + int64_t duration; + + int64_t pos; ///< byte position in stream, -1 if unknown + +#if FF_API_CONVERGENCE_DURATION + /** + * @deprecated Same as the duration field, but as int64_t. This was required + * for Matroska subtitles, whose duration values could overflow when the + * duration field was still an int. + */ + attribute_deprecated + int64_t convergence_duration; +#endif +} AVPacket; + +#define AV_PKT_FLAG_KEY 0x0001 ///< The packet contains a keyframe +#define AV_PKT_FLAG_CORRUPT 0x0002 ///< The packet content is corrupted +/** + * Flag is used to discard packets which are required to maintain valid + * decoder state but are not required for output and should be dropped + * after decoding. + **/ +#define AV_PKT_FLAG_DISCARD 0x0004 +/** + * The packet comes from a trusted source. + * + * Otherwise-unsafe constructs such as arbitrary pointers to data + * outside the packet may be followed. + */ +#define AV_PKT_FLAG_TRUSTED 0x0008 +/** + * Flag is used to indicate packets that contain frames that can + * be discarded by the decoder. I.e. Non-reference frames. + */ +#define AV_PKT_FLAG_DISPOSABLE 0x0010 + +enum AVSideDataParamChangeFlags { + AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT = 0x0001, + AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT = 0x0002, + AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE = 0x0004, + AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS = 0x0008, +}; + +/** + * Allocate an AVPacket and set its fields to default values. The resulting + * struct must be freed using av_packet_free(). + * + * @return An AVPacket filled with default values or NULL on failure. + * + * @note this only allocates the AVPacket itself, not the data buffers. Those + * must be allocated through other means such as av_new_packet. + * + * @see av_new_packet + */ +AVPacket *av_packet_alloc(void); + +/** + * Create a new packet that references the same data as src. + * + * This is a shortcut for av_packet_alloc()+av_packet_ref(). + * + * @return newly created AVPacket on success, NULL on error. + * + * @see av_packet_alloc + * @see av_packet_ref + */ +AVPacket *av_packet_clone(const AVPacket *src); + +/** + * Free the packet, if the packet is reference counted, it will be + * unreferenced first. + * + * @param pkt packet to be freed. The pointer will be set to NULL. + * @note passing NULL is a no-op. + */ +void av_packet_free(AVPacket **pkt); + +/** + * Initialize optional fields of a packet with default values. + * + * Note, this does not touch the data and size members, which have to be + * initialized separately. + * + * @param pkt packet + */ +void av_init_packet(AVPacket *pkt); + +/** + * Allocate the payload of a packet and initialize its fields with + * default values. + * + * @param pkt packet + * @param size wanted payload size + * @return 0 if OK, AVERROR_xxx otherwise + */ +int av_new_packet(AVPacket *pkt, int size); + +/** + * Reduce packet size, correctly zeroing padding + * + * @param pkt packet + * @param size new size + */ +void av_shrink_packet(AVPacket *pkt, int size); + +/** + * Increase packet size, correctly zeroing padding + * + * @param pkt packet + * @param grow_by number of bytes by which to increase the size of the packet + */ +int av_grow_packet(AVPacket *pkt, int grow_by); + +/** + * Initialize a reference-counted packet from av_malloc()ed data. + * + * @param pkt packet to be initialized. This function will set the data, size, + * and buf fields, all others are left untouched. + * @param data Data allocated by av_malloc() to be used as packet data. If this + * function returns successfully, the data is owned by the underlying AVBuffer. + * The caller may not access the data through other means. + * @param size size of data in bytes, without the padding. I.e. the full buffer + * size is assumed to be size + AV_INPUT_BUFFER_PADDING_SIZE. + * + * @return 0 on success, a negative AVERROR on error + */ +int av_packet_from_data(AVPacket *pkt, uint8_t *data, int size); + +#if FF_API_AVPACKET_OLD_API +/** + * @warning This is a hack - the packet memory allocation stuff is broken. The + * packet is allocated if it was not really allocated. + * + * @deprecated Use av_packet_ref or av_packet_make_refcounted + */ +attribute_deprecated +int av_dup_packet(AVPacket *pkt); +/** + * Copy packet, including contents + * + * @return 0 on success, negative AVERROR on fail + * + * @deprecated Use av_packet_ref + */ +attribute_deprecated +int av_copy_packet(AVPacket *dst, const AVPacket *src); + +/** + * Copy packet side data + * + * @return 0 on success, negative AVERROR on fail + * + * @deprecated Use av_packet_copy_props + */ +attribute_deprecated +int av_copy_packet_side_data(AVPacket *dst, const AVPacket *src); + +/** + * Free a packet. + * + * @deprecated Use av_packet_unref + * + * @param pkt packet to free + */ +attribute_deprecated +void av_free_packet(AVPacket *pkt); +#endif +/** + * Allocate new information of a packet. + * + * @param pkt packet + * @param type side information type + * @param size side information size + * @return pointer to fresh allocated data or NULL otherwise + */ +uint8_t* av_packet_new_side_data(AVPacket *pkt, enum AVPacketSideDataType type, + int size); + +/** + * Wrap an existing array as a packet side data. + * + * @param pkt packet + * @param type side information type + * @param data the side data array. It must be allocated with the av_malloc() + * family of functions. The ownership of the data is transferred to + * pkt. + * @param size side information size + * @return a non-negative number on success, a negative AVERROR code on + * failure. On failure, the packet is unchanged and the data remains + * owned by the caller. + */ +int av_packet_add_side_data(AVPacket *pkt, enum AVPacketSideDataType type, + uint8_t *data, size_t size); + +/** + * Shrink the already allocated side data buffer + * + * @param pkt packet + * @param type side information type + * @param size new side information size + * @return 0 on success, < 0 on failure + */ +int av_packet_shrink_side_data(AVPacket *pkt, enum AVPacketSideDataType type, + int size); + +/** + * Get side information from packet. + * + * @param pkt packet + * @param type desired side information type + * @param size pointer for side information size to store (optional) + * @return pointer to data if present or NULL otherwise + */ +uint8_t* av_packet_get_side_data(const AVPacket *pkt, enum AVPacketSideDataType type, + int *size); + +#if FF_API_MERGE_SD_API +attribute_deprecated +int av_packet_merge_side_data(AVPacket *pkt); + +attribute_deprecated +int av_packet_split_side_data(AVPacket *pkt); +#endif + +const char *av_packet_side_data_name(enum AVPacketSideDataType type); + +/** + * Pack a dictionary for use in side_data. + * + * @param dict The dictionary to pack. + * @param size pointer to store the size of the returned data + * @return pointer to data if successful, NULL otherwise + */ +uint8_t *av_packet_pack_dictionary(AVDictionary *dict, int *size); +/** + * Unpack a dictionary from side_data. + * + * @param data data from side_data + * @param size size of the data + * @param dict the metadata storage dictionary + * @return 0 on success, < 0 on failure + */ +int av_packet_unpack_dictionary(const uint8_t *data, int size, AVDictionary **dict); + + +/** + * Convenience function to free all the side data stored. + * All the other fields stay untouched. + * + * @param pkt packet + */ +void av_packet_free_side_data(AVPacket *pkt); + +/** + * Setup a new reference to the data described by a given packet + * + * If src is reference-counted, setup dst as a new reference to the + * buffer in src. Otherwise allocate a new buffer in dst and copy the + * data from src into it. + * + * All the other fields are copied from src. + * + * @see av_packet_unref + * + * @param dst Destination packet. Will be completely overwritten. + * @param src Source packet + * + * @return 0 on success, a negative AVERROR on error. On error, dst + * will be blank (as if returned by av_packet_alloc()). + */ +int av_packet_ref(AVPacket *dst, const AVPacket *src); + +/** + * Wipe the packet. + * + * Unreference the buffer referenced by the packet and reset the + * remaining packet fields to their default values. + * + * @param pkt The packet to be unreferenced. + */ +void av_packet_unref(AVPacket *pkt); + +/** + * Move every field in src to dst and reset src. + * + * @see av_packet_unref + * + * @param src Source packet, will be reset + * @param dst Destination packet + */ +void av_packet_move_ref(AVPacket *dst, AVPacket *src); + +/** + * Copy only "properties" fields from src to dst. + * + * Properties for the purpose of this function are all the fields + * beside those related to the packet data (buf, data, size) + * + * @param dst Destination packet + * @param src Source packet + * + * @return 0 on success AVERROR on failure. + */ +int av_packet_copy_props(AVPacket *dst, const AVPacket *src); + +/** + * Ensure the data described by a given packet is reference counted. + * + * @note This function does not ensure that the reference will be writable. + * Use av_packet_make_writable instead for that purpose. + * + * @see av_packet_ref + * @see av_packet_make_writable + * + * @param pkt packet whose data should be made reference counted. + * + * @return 0 on success, a negative AVERROR on error. On failure, the + * packet is unchanged. + */ +int av_packet_make_refcounted(AVPacket *pkt); + +/** + * Create a writable reference for the data described by a given packet, + * avoiding data copy if possible. + * + * @param pkt Packet whose data should be made writable. + * + * @return 0 on success, a negative AVERROR on failure. On failure, the + * packet is unchanged. + */ +int av_packet_make_writable(AVPacket *pkt); + +/** + * Convert valid timing fields (timestamps / durations) in a packet from one + * timebase to another. Timestamps with unknown values (AV_NOPTS_VALUE) will be + * ignored. + * + * @param pkt packet on which the conversion will be performed + * @param tb_src source timebase, in which the timing fields in pkt are + * expressed + * @param tb_dst destination timebase, to which the timing fields will be + * converted + */ +void av_packet_rescale_ts(AVPacket *pkt, AVRational tb_src, AVRational tb_dst); + +/** + * @} + */ + +#endif // AVCODEC_PACKET_H diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/qsv.h b/ThirdParty/ffmpeg/arm/include/libavcodec/qsv.h new file mode 100644 index 000000000..b77158ec2 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/qsv.h @@ -0,0 +1,107 @@ +/* + * Intel MediaSDK QSV public API + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_QSV_H +#define AVCODEC_QSV_H + +#include + +#include "libavutil/buffer.h" + +/** + * This struct is used for communicating QSV parameters between libavcodec and + * the caller. It is managed by the caller and must be assigned to + * AVCodecContext.hwaccel_context. + * - decoding: hwaccel_context must be set on return from the get_format() + * callback + * - encoding: hwaccel_context must be set before avcodec_open2() + */ +typedef struct AVQSVContext { + /** + * If non-NULL, the session to use for encoding or decoding. + * Otherwise, libavcodec will try to create an internal session. + */ + mfxSession session; + + /** + * The IO pattern to use. + */ + int iopattern; + + /** + * Extra buffers to pass to encoder or decoder initialization. + */ + mfxExtBuffer **ext_buffers; + int nb_ext_buffers; + + /** + * Encoding only. If this field is set to non-zero by the caller, libavcodec + * will create an mfxExtOpaqueSurfaceAlloc extended buffer and pass it to + * the encoder initialization. This only makes sense if iopattern is also + * set to MFX_IOPATTERN_IN_OPAQUE_MEMORY. + * + * The number of allocated opaque surfaces will be the sum of the number + * required by the encoder and the user-provided value nb_opaque_surfaces. + * The array of the opaque surfaces will be exported to the caller through + * the opaque_surfaces field. + */ + int opaque_alloc; + + /** + * Encoding only, and only if opaque_alloc is set to non-zero. Before + * calling avcodec_open2(), the caller should set this field to the number + * of extra opaque surfaces to allocate beyond what is required by the + * encoder. + * + * On return from avcodec_open2(), this field will be set by libavcodec to + * the total number of allocated opaque surfaces. + */ + int nb_opaque_surfaces; + + /** + * Encoding only, and only if opaque_alloc is set to non-zero. On return + * from avcodec_open2(), this field will be used by libavcodec to export the + * array of the allocated opaque surfaces to the caller, so they can be + * passed to other parts of the pipeline. + * + * The buffer reference exported here is owned and managed by libavcodec, + * the callers should make their own reference with av_buffer_ref() and free + * it with av_buffer_unref() when it is no longer needed. + * + * The buffer data is an nb_opaque_surfaces-sized array of mfxFrameSurface1. + */ + AVBufferRef *opaque_surfaces; + + /** + * Encoding only, and only if opaque_alloc is set to non-zero. On return + * from avcodec_open2(), this field will be set to the surface type used in + * the opaque allocation request. + */ + int opaque_alloc_type; +} AVQSVContext; + +/** + * Allocate a new context. + * + * It must be freed by the caller with av_free(). + */ +AVQSVContext *av_qsv_alloc_context(void); + +#endif /* AVCODEC_QSV_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/vaapi.h b/ThirdParty/ffmpeg/arm/include/libavcodec/vaapi.h new file mode 100644 index 000000000..2cf7da588 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/vaapi.h @@ -0,0 +1,86 @@ +/* + * Video Acceleration API (shared data between FFmpeg and the video player) + * HW decode acceleration for MPEG-2, MPEG-4, H.264 and VC-1 + * + * Copyright (C) 2008-2009 Splitted-Desktop Systems + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VAAPI_H +#define AVCODEC_VAAPI_H + +/** + * @file + * @ingroup lavc_codec_hwaccel_vaapi + * Public libavcodec VA API header. + */ + +#include +#include "libavutil/attributes.h" +#include "version.h" + +#if FF_API_STRUCT_VAAPI_CONTEXT + +/** + * @defgroup lavc_codec_hwaccel_vaapi VA API Decoding + * @ingroup lavc_codec_hwaccel + * @{ + */ + +/** + * This structure is used to share data between the FFmpeg library and + * the client video application. + * This shall be zero-allocated and available as + * AVCodecContext.hwaccel_context. All user members can be set once + * during initialization or through each AVCodecContext.get_buffer() + * function call. In any case, they must be valid prior to calling + * decoding functions. + * + * Deprecated: use AVCodecContext.hw_frames_ctx instead. + */ +struct attribute_deprecated vaapi_context { + /** + * Window system dependent data + * + * - encoding: unused + * - decoding: Set by user + */ + void *display; + + /** + * Configuration ID + * + * - encoding: unused + * - decoding: Set by user + */ + uint32_t config_id; + + /** + * Context ID (video decode pipeline) + * + * - encoding: unused + * - decoding: Set by user + */ + uint32_t context_id; +}; + +/* @} */ + +#endif /* FF_API_STRUCT_VAAPI_CONTEXT */ + +#endif /* AVCODEC_VAAPI_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/vdpau.h b/ThirdParty/ffmpeg/arm/include/libavcodec/vdpau.h new file mode 100644 index 000000000..4d9994336 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/vdpau.h @@ -0,0 +1,176 @@ +/* + * The Video Decode and Presentation API for UNIX (VDPAU) is used for + * hardware-accelerated decoding of MPEG-1/2, H.264 and VC-1. + * + * Copyright (C) 2008 NVIDIA + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VDPAU_H +#define AVCODEC_VDPAU_H + +/** + * @file + * @ingroup lavc_codec_hwaccel_vdpau + * Public libavcodec VDPAU header. + */ + + +/** + * @defgroup lavc_codec_hwaccel_vdpau VDPAU Decoder and Renderer + * @ingroup lavc_codec_hwaccel + * + * VDPAU hardware acceleration has two modules + * - VDPAU decoding + * - VDPAU presentation + * + * The VDPAU decoding module parses all headers using FFmpeg + * parsing mechanisms and uses VDPAU for the actual decoding. + * + * As per the current implementation, the actual decoding + * and rendering (API calls) are done as part of the VDPAU + * presentation (vo_vdpau.c) module. + * + * @{ + */ + +#include + +#include "libavutil/avconfig.h" +#include "libavutil/attributes.h" + +#include "avcodec.h" +#include "version.h" + +struct AVCodecContext; +struct AVFrame; + +typedef int (*AVVDPAU_Render2)(struct AVCodecContext *, struct AVFrame *, + const VdpPictureInfo *, uint32_t, + const VdpBitstreamBuffer *); + +/** + * This structure is used to share data between the libavcodec library and + * the client video application. + * The user shall allocate the structure via the av_alloc_vdpau_hwaccel + * function and make it available as + * AVCodecContext.hwaccel_context. Members can be set by the user once + * during initialization or through each AVCodecContext.get_buffer() + * function call. In any case, they must be valid prior to calling + * decoding functions. + * + * The size of this structure is not a part of the public ABI and must not + * be used outside of libavcodec. Use av_vdpau_alloc_context() to allocate an + * AVVDPAUContext. + */ +typedef struct AVVDPAUContext { + /** + * VDPAU decoder handle + * + * Set by user. + */ + VdpDecoder decoder; + + /** + * VDPAU decoder render callback + * + * Set by the user. + */ + VdpDecoderRender *render; + + AVVDPAU_Render2 render2; +} AVVDPAUContext; + +/** + * @brief allocation function for AVVDPAUContext + * + * Allows extending the struct without breaking API/ABI + */ +AVVDPAUContext *av_alloc_vdpaucontext(void); + +AVVDPAU_Render2 av_vdpau_hwaccel_get_render2(const AVVDPAUContext *); +void av_vdpau_hwaccel_set_render2(AVVDPAUContext *, AVVDPAU_Render2); + +/** + * Associate a VDPAU device with a codec context for hardware acceleration. + * This function is meant to be called from the get_format() codec callback, + * or earlier. It can also be called after avcodec_flush_buffers() to change + * the underlying VDPAU device mid-stream (e.g. to recover from non-transparent + * display preemption). + * + * @note get_format() must return AV_PIX_FMT_VDPAU if this function completes + * successfully. + * + * @param avctx decoding context whose get_format() callback is invoked + * @param device VDPAU device handle to use for hardware acceleration + * @param get_proc_address VDPAU device driver + * @param flags zero of more OR'd AV_HWACCEL_FLAG_* flags + * + * @return 0 on success, an AVERROR code on failure. + */ +int av_vdpau_bind_context(AVCodecContext *avctx, VdpDevice device, + VdpGetProcAddress *get_proc_address, unsigned flags); + +/** + * Gets the parameters to create an adequate VDPAU video surface for the codec + * context using VDPAU hardware decoding acceleration. + * + * @note Behavior is undefined if the context was not successfully bound to a + * VDPAU device using av_vdpau_bind_context(). + * + * @param avctx the codec context being used for decoding the stream + * @param type storage space for the VDPAU video surface chroma type + * (or NULL to ignore) + * @param width storage space for the VDPAU video surface pixel width + * (or NULL to ignore) + * @param height storage space for the VDPAU video surface pixel height + * (or NULL to ignore) + * + * @return 0 on success, a negative AVERROR code on failure. + */ +int av_vdpau_get_surface_parameters(AVCodecContext *avctx, VdpChromaType *type, + uint32_t *width, uint32_t *height); + +/** + * Allocate an AVVDPAUContext. + * + * @return Newly-allocated AVVDPAUContext or NULL on failure. + */ +AVVDPAUContext *av_vdpau_alloc_context(void); + +#if FF_API_VDPAU_PROFILE +/** + * Get a decoder profile that should be used for initializing a VDPAU decoder. + * Should be called from the AVCodecContext.get_format() callback. + * + * @deprecated Use av_vdpau_bind_context() instead. + * + * @param avctx the codec context being used for decoding the stream + * @param profile a pointer into which the result will be written on success. + * The contents of profile are undefined if this function returns + * an error. + * + * @return 0 on success (non-negative), a negative AVERROR on failure. + */ +attribute_deprecated +int av_vdpau_get_profile(AVCodecContext *avctx, VdpDecoderProfile *profile); +#endif + +/* @}*/ + +#endif /* AVCODEC_VDPAU_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/version.h b/ThirdParty/ffmpeg/arm/include/libavcodec/version.h new file mode 100644 index 000000000..85fbe24dc --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/version.h @@ -0,0 +1,149 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VERSION_H +#define AVCODEC_VERSION_H + +/** + * @file + * @ingroup libavc + * Libavcodec version macros. + */ + +#include "libavutil/version.h" + +#define LIBAVCODEC_VERSION_MAJOR 58 +#define LIBAVCODEC_VERSION_MINOR 91 +#define LIBAVCODEC_VERSION_MICRO 100 + +#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ + LIBAVCODEC_VERSION_MINOR, \ + LIBAVCODEC_VERSION_MICRO) +#define LIBAVCODEC_VERSION AV_VERSION(LIBAVCODEC_VERSION_MAJOR, \ + LIBAVCODEC_VERSION_MINOR, \ + LIBAVCODEC_VERSION_MICRO) +#define LIBAVCODEC_BUILD LIBAVCODEC_VERSION_INT + +#define LIBAVCODEC_IDENT "Lavc" AV_STRINGIFY(LIBAVCODEC_VERSION) + +/** + * FF_API_* defines may be placed below to indicate public API that will be + * dropped at a future version bump. The defines themselves are not part of + * the public API and may change, break or disappear at any time. + * + * @note, when bumping the major version it is recommended to manually + * disable each FF_API_* in its own commit instead of disabling them all + * at once through the bump. This improves the git bisect-ability of the change. + */ + +#ifndef FF_API_LOWRES +#define FF_API_LOWRES (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_DEBUG_MV +#define FF_API_DEBUG_MV (LIBAVCODEC_VERSION_MAJOR < 58) +#endif +#ifndef FF_API_AVCTX_TIMEBASE +#define FF_API_AVCTX_TIMEBASE (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_CODED_FRAME +#define FF_API_CODED_FRAME (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_SIDEDATA_ONLY_PKT +#define FF_API_SIDEDATA_ONLY_PKT (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_VDPAU_PROFILE +#define FF_API_VDPAU_PROFILE (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_CONVERGENCE_DURATION +#define FF_API_CONVERGENCE_DURATION (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_AVPICTURE +#define FF_API_AVPICTURE (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_AVPACKET_OLD_API +#define FF_API_AVPACKET_OLD_API (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_RTP_CALLBACK +#define FF_API_RTP_CALLBACK (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_VBV_DELAY +#define FF_API_VBV_DELAY (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_CODER_TYPE +#define FF_API_CODER_TYPE (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_STAT_BITS +#define FF_API_STAT_BITS (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_PRIVATE_OPT +#define FF_API_PRIVATE_OPT (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_ASS_TIMING +#define FF_API_ASS_TIMING (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_OLD_BSF +#define FF_API_OLD_BSF (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_COPY_CONTEXT +#define FF_API_COPY_CONTEXT (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_GET_CONTEXT_DEFAULTS +#define FF_API_GET_CONTEXT_DEFAULTS (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_NVENC_OLD_NAME +#define FF_API_NVENC_OLD_NAME (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_STRUCT_VAAPI_CONTEXT +#define FF_API_STRUCT_VAAPI_CONTEXT (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_MERGE_SD_API +#define FF_API_MERGE_SD_API (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_TAG_STRING +#define FF_API_TAG_STRING (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_GETCHROMA +#define FF_API_GETCHROMA (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_CODEC_GET_SET +#define FF_API_CODEC_GET_SET (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_USER_VISIBLE_AVHWACCEL +#define FF_API_USER_VISIBLE_AVHWACCEL (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_LOCKMGR +#define FF_API_LOCKMGR (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_NEXT +#define FF_API_NEXT (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_UNSANITIZED_BITRATES +#define FF_API_UNSANITIZED_BITRATES (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_OPENH264_SLICE_MODE +#define FF_API_OPENH264_SLICE_MODE (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_OPENH264_CABAC +#define FF_API_OPENH264_CABAC (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_UNUSED_CODEC_CAPS +#define FF_API_UNUSED_CODEC_CAPS (LIBAVCODEC_VERSION_MAJOR < 59) +#endif + + +#endif /* AVCODEC_VERSION_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/videotoolbox.h b/ThirdParty/ffmpeg/arm/include/libavcodec/videotoolbox.h new file mode 100644 index 000000000..af2db0d58 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/videotoolbox.h @@ -0,0 +1,127 @@ +/* + * Videotoolbox hardware acceleration + * + * copyright (c) 2012 Sebastien Zwickert + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_VIDEOTOOLBOX_H +#define AVCODEC_VIDEOTOOLBOX_H + +/** + * @file + * @ingroup lavc_codec_hwaccel_videotoolbox + * Public libavcodec Videotoolbox header. + */ + +#include + +#define Picture QuickdrawPicture +#include +#undef Picture + +#include "libavcodec/avcodec.h" + +/** + * This struct holds all the information that needs to be passed + * between the caller and libavcodec for initializing Videotoolbox decoding. + * Its size is not a part of the public ABI, it must be allocated with + * av_videotoolbox_alloc_context() and freed with av_free(). + */ +typedef struct AVVideotoolboxContext { + /** + * Videotoolbox decompression session object. + * Created and freed the caller. + */ + VTDecompressionSessionRef session; + + /** + * The output callback that must be passed to the session. + * Set by av_videottoolbox_default_init() + */ + VTDecompressionOutputCallback output_callback; + + /** + * CVPixelBuffer Format Type that Videotoolbox will use for decoded frames. + * set by the caller. If this is set to 0, then no specific format is + * requested from the decoder, and its native format is output. + */ + OSType cv_pix_fmt_type; + + /** + * CoreMedia Format Description that Videotoolbox will use to create the decompression session. + * Set by the caller. + */ + CMVideoFormatDescriptionRef cm_fmt_desc; + + /** + * CoreMedia codec type that Videotoolbox will use to create the decompression session. + * Set by the caller. + */ + int cm_codec_type; +} AVVideotoolboxContext; + +/** + * Allocate and initialize a Videotoolbox context. + * + * This function should be called from the get_format() callback when the caller + * selects the AV_PIX_FMT_VIDETOOLBOX format. The caller must then create + * the decoder object (using the output callback provided by libavcodec) that + * will be used for Videotoolbox-accelerated decoding. + * + * When decoding with Videotoolbox is finished, the caller must destroy the decoder + * object and free the Videotoolbox context using av_free(). + * + * @return the newly allocated context or NULL on failure + */ +AVVideotoolboxContext *av_videotoolbox_alloc_context(void); + +/** + * This is a convenience function that creates and sets up the Videotoolbox context using + * an internal implementation. + * + * @param avctx the corresponding codec context + * + * @return >= 0 on success, a negative AVERROR code on failure + */ +int av_videotoolbox_default_init(AVCodecContext *avctx); + +/** + * This is a convenience function that creates and sets up the Videotoolbox context using + * an internal implementation. + * + * @param avctx the corresponding codec context + * @param vtctx the Videotoolbox context to use + * + * @return >= 0 on success, a negative AVERROR code on failure + */ +int av_videotoolbox_default_init2(AVCodecContext *avctx, AVVideotoolboxContext *vtctx); + +/** + * This function must be called to free the Videotoolbox context initialized with + * av_videotoolbox_default_init(). + * + * @param avctx the corresponding codec context + */ +void av_videotoolbox_default_free(AVCodecContext *avctx); + +/** + * @} + */ + +#endif /* AVCODEC_VIDEOTOOLBOX_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/vorbis_parser.h b/ThirdParty/ffmpeg/arm/include/libavcodec/vorbis_parser.h new file mode 100644 index 000000000..789932ac4 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/vorbis_parser.h @@ -0,0 +1,74 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * A public API for Vorbis parsing + * + * Determines the duration for each packet. + */ + +#ifndef AVCODEC_VORBIS_PARSER_H +#define AVCODEC_VORBIS_PARSER_H + +#include + +typedef struct AVVorbisParseContext AVVorbisParseContext; + +/** + * Allocate and initialize the Vorbis parser using headers in the extradata. + */ +AVVorbisParseContext *av_vorbis_parse_init(const uint8_t *extradata, + int extradata_size); + +/** + * Free the parser and everything associated with it. + */ +void av_vorbis_parse_free(AVVorbisParseContext **s); + +#define VORBIS_FLAG_HEADER 0x00000001 +#define VORBIS_FLAG_COMMENT 0x00000002 +#define VORBIS_FLAG_SETUP 0x00000004 + +/** + * Get the duration for a Vorbis packet. + * + * If @p flags is @c NULL, + * special frames are considered invalid. + * + * @param s Vorbis parser context + * @param buf buffer containing a Vorbis frame + * @param buf_size size of the buffer + * @param flags flags for special frames + */ +int av_vorbis_parse_frame_flags(AVVorbisParseContext *s, const uint8_t *buf, + int buf_size, int *flags); + +/** + * Get the duration for a Vorbis packet. + * + * @param s Vorbis parser context + * @param buf buffer containing a Vorbis frame + * @param buf_size size of the buffer + */ +int av_vorbis_parse_frame(AVVorbisParseContext *s, const uint8_t *buf, + int buf_size); + +void av_vorbis_parse_reset(AVVorbisParseContext *s); + +#endif /* AVCODEC_VORBIS_PARSER_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavcodec/xvmc.h b/ThirdParty/ffmpeg/arm/include/libavcodec/xvmc.h new file mode 100644 index 000000000..465ee78d6 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavcodec/xvmc.h @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2003 Ivan Kalvachev + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_XVMC_H +#define AVCODEC_XVMC_H + +/** + * @file + * @ingroup lavc_codec_hwaccel_xvmc + * Public libavcodec XvMC header. + */ + +#include + +#include "libavutil/attributes.h" +#include "version.h" +#include "avcodec.h" + +/** + * @defgroup lavc_codec_hwaccel_xvmc XvMC + * @ingroup lavc_codec_hwaccel + * + * @{ + */ + +#define AV_XVMC_ID 0x1DC711C0 /**< special value to ensure that regular pixel routines haven't corrupted the struct + the number is 1337 speak for the letters IDCT MCo (motion compensation) */ + +struct attribute_deprecated xvmc_pix_fmt { + /** The field contains the special constant value AV_XVMC_ID. + It is used as a test that the application correctly uses the API, + and that there is no corruption caused by pixel routines. + - application - set during initialization + - libavcodec - unchanged + */ + int xvmc_id; + + /** Pointer to the block array allocated by XvMCCreateBlocks(). + The array has to be freed by XvMCDestroyBlocks(). + Each group of 64 values represents one data block of differential + pixel information (in MoCo mode) or coefficients for IDCT. + - application - set the pointer during initialization + - libavcodec - fills coefficients/pixel data into the array + */ + short* data_blocks; + + /** Pointer to the macroblock description array allocated by + XvMCCreateMacroBlocks() and freed by XvMCDestroyMacroBlocks(). + - application - set the pointer during initialization + - libavcodec - fills description data into the array + */ + XvMCMacroBlock* mv_blocks; + + /** Number of macroblock descriptions that can be stored in the mv_blocks + array. + - application - set during initialization + - libavcodec - unchanged + */ + int allocated_mv_blocks; + + /** Number of blocks that can be stored at once in the data_blocks array. + - application - set during initialization + - libavcodec - unchanged + */ + int allocated_data_blocks; + + /** Indicate that the hardware would interpret data_blocks as IDCT + coefficients and perform IDCT on them. + - application - set during initialization + - libavcodec - unchanged + */ + int idct; + + /** In MoCo mode it indicates that intra macroblocks are assumed to be in + unsigned format; same as the XVMC_INTRA_UNSIGNED flag. + - application - set during initialization + - libavcodec - unchanged + */ + int unsigned_intra; + + /** Pointer to the surface allocated by XvMCCreateSurface(). + It has to be freed by XvMCDestroySurface() on application exit. + It identifies the frame and its state on the video hardware. + - application - set during initialization + - libavcodec - unchanged + */ + XvMCSurface* p_surface; + +/** Set by the decoder before calling ff_draw_horiz_band(), + needed by the XvMCRenderSurface function. */ +//@{ + /** Pointer to the surface used as past reference + - application - unchanged + - libavcodec - set + */ + XvMCSurface* p_past_surface; + + /** Pointer to the surface used as future reference + - application - unchanged + - libavcodec - set + */ + XvMCSurface* p_future_surface; + + /** top/bottom field or frame + - application - unchanged + - libavcodec - set + */ + unsigned int picture_structure; + + /** XVMC_SECOND_FIELD - 1st or 2nd field in the sequence + - application - unchanged + - libavcodec - set + */ + unsigned int flags; +//}@ + + /** Number of macroblock descriptions in the mv_blocks array + that have already been passed to the hardware. + - application - zeroes it on get_buffer(). + A successful ff_draw_horiz_band() may increment it + with filled_mb_block_num or zero both. + - libavcodec - unchanged + */ + int start_mv_blocks_num; + + /** Number of new macroblock descriptions in the mv_blocks array (after + start_mv_blocks_num) that are filled by libavcodec and have to be + passed to the hardware. + - application - zeroes it on get_buffer() or after successful + ff_draw_horiz_band(). + - libavcodec - increment with one of each stored MB + */ + int filled_mv_blocks_num; + + /** Number of the next free data block; one data block consists of + 64 short values in the data_blocks array. + All blocks before this one have already been claimed by placing their + position into the corresponding block description structure field, + that are part of the mv_blocks array. + - application - zeroes it on get_buffer(). + A successful ff_draw_horiz_band() may zero it together + with start_mb_blocks_num. + - libavcodec - each decoded macroblock increases it by the number + of coded blocks it contains. + */ + int next_free_data_block_num; +}; + +/** + * @} + */ + +#endif /* AVCODEC_XVMC_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavformat/avformat.h b/ThirdParty/ffmpeg/arm/include/libavformat/avformat.h new file mode 100644 index 000000000..21c282a10 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavformat/avformat.h @@ -0,0 +1,3093 @@ +/* + * copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_AVFORMAT_H +#define AVFORMAT_AVFORMAT_H + +/** + * @file + * @ingroup libavf + * Main libavformat public API header + */ + +/** + * @defgroup libavf libavformat + * I/O and Muxing/Demuxing Library + * + * Libavformat (lavf) is a library for dealing with various media container + * formats. Its main two purposes are demuxing - i.e. splitting a media file + * into component streams, and the reverse process of muxing - writing supplied + * data in a specified container format. It also has an @ref lavf_io + * "I/O module" which supports a number of protocols for accessing the data (e.g. + * file, tcp, http and others). + * Unless you are absolutely sure you won't use libavformat's network + * capabilities, you should also call avformat_network_init(). + * + * A supported input format is described by an AVInputFormat struct, conversely + * an output format is described by AVOutputFormat. You can iterate over all + * input/output formats using the av_demuxer_iterate / av_muxer_iterate() functions. + * The protocols layer is not part of the public API, so you can only get the names + * of supported protocols with the avio_enum_protocols() function. + * + * Main lavf structure used for both muxing and demuxing is AVFormatContext, + * which exports all information about the file being read or written. As with + * most Libavformat structures, its size is not part of public ABI, so it cannot be + * allocated on stack or directly with av_malloc(). To create an + * AVFormatContext, use avformat_alloc_context() (some functions, like + * avformat_open_input() might do that for you). + * + * Most importantly an AVFormatContext contains: + * @li the @ref AVFormatContext.iformat "input" or @ref AVFormatContext.oformat + * "output" format. It is either autodetected or set by user for input; + * always set by user for output. + * @li an @ref AVFormatContext.streams "array" of AVStreams, which describe all + * elementary streams stored in the file. AVStreams are typically referred to + * using their index in this array. + * @li an @ref AVFormatContext.pb "I/O context". It is either opened by lavf or + * set by user for input, always set by user for output (unless you are dealing + * with an AVFMT_NOFILE format). + * + * @section lavf_options Passing options to (de)muxers + * It is possible to configure lavf muxers and demuxers using the @ref avoptions + * mechanism. Generic (format-independent) libavformat options are provided by + * AVFormatContext, they can be examined from a user program by calling + * av_opt_next() / av_opt_find() on an allocated AVFormatContext (or its AVClass + * from avformat_get_class()). Private (format-specific) options are provided by + * AVFormatContext.priv_data if and only if AVInputFormat.priv_class / + * AVOutputFormat.priv_class of the corresponding format struct is non-NULL. + * Further options may be provided by the @ref AVFormatContext.pb "I/O context", + * if its AVClass is non-NULL, and the protocols layer. See the discussion on + * nesting in @ref avoptions documentation to learn how to access those. + * + * @section urls + * URL strings in libavformat are made of a scheme/protocol, a ':', and a + * scheme specific string. URLs without a scheme and ':' used for local files + * are supported but deprecated. "file:" should be used for local files. + * + * It is important that the scheme string is not taken from untrusted + * sources without checks. + * + * Note that some schemes/protocols are quite powerful, allowing access to + * both local and remote files, parts of them, concatenations of them, local + * audio and video devices and so on. + * + * @{ + * + * @defgroup lavf_decoding Demuxing + * @{ + * Demuxers read a media file and split it into chunks of data (@em packets). A + * @ref AVPacket "packet" contains one or more encoded frames which belongs to a + * single elementary stream. In the lavf API this process is represented by the + * avformat_open_input() function for opening a file, av_read_frame() for + * reading a single packet and finally avformat_close_input(), which does the + * cleanup. + * + * @section lavf_decoding_open Opening a media file + * The minimum information required to open a file is its URL, which + * is passed to avformat_open_input(), as in the following code: + * @code + * const char *url = "file:in.mp3"; + * AVFormatContext *s = NULL; + * int ret = avformat_open_input(&s, url, NULL, NULL); + * if (ret < 0) + * abort(); + * @endcode + * The above code attempts to allocate an AVFormatContext, open the + * specified file (autodetecting the format) and read the header, exporting the + * information stored there into s. Some formats do not have a header or do not + * store enough information there, so it is recommended that you call the + * avformat_find_stream_info() function which tries to read and decode a few + * frames to find missing information. + * + * In some cases you might want to preallocate an AVFormatContext yourself with + * avformat_alloc_context() and do some tweaking on it before passing it to + * avformat_open_input(). One such case is when you want to use custom functions + * for reading input data instead of lavf internal I/O layer. + * To do that, create your own AVIOContext with avio_alloc_context(), passing + * your reading callbacks to it. Then set the @em pb field of your + * AVFormatContext to newly created AVIOContext. + * + * Since the format of the opened file is in general not known until after + * avformat_open_input() has returned, it is not possible to set demuxer private + * options on a preallocated context. Instead, the options should be passed to + * avformat_open_input() wrapped in an AVDictionary: + * @code + * AVDictionary *options = NULL; + * av_dict_set(&options, "video_size", "640x480", 0); + * av_dict_set(&options, "pixel_format", "rgb24", 0); + * + * if (avformat_open_input(&s, url, NULL, &options) < 0) + * abort(); + * av_dict_free(&options); + * @endcode + * This code passes the private options 'video_size' and 'pixel_format' to the + * demuxer. They would be necessary for e.g. the rawvideo demuxer, since it + * cannot know how to interpret raw video data otherwise. If the format turns + * out to be something different than raw video, those options will not be + * recognized by the demuxer and therefore will not be applied. Such unrecognized + * options are then returned in the options dictionary (recognized options are + * consumed). The calling program can handle such unrecognized options as it + * wishes, e.g. + * @code + * AVDictionaryEntry *e; + * if (e = av_dict_get(options, "", NULL, AV_DICT_IGNORE_SUFFIX)) { + * fprintf(stderr, "Option %s not recognized by the demuxer.\n", e->key); + * abort(); + * } + * @endcode + * + * After you have finished reading the file, you must close it with + * avformat_close_input(). It will free everything associated with the file. + * + * @section lavf_decoding_read Reading from an opened file + * Reading data from an opened AVFormatContext is done by repeatedly calling + * av_read_frame() on it. Each call, if successful, will return an AVPacket + * containing encoded data for one AVStream, identified by + * AVPacket.stream_index. This packet may be passed straight into the libavcodec + * decoding functions avcodec_send_packet() or avcodec_decode_subtitle2() if the + * caller wishes to decode the data. + * + * AVPacket.pts, AVPacket.dts and AVPacket.duration timing information will be + * set if known. They may also be unset (i.e. AV_NOPTS_VALUE for + * pts/dts, 0 for duration) if the stream does not provide them. The timing + * information will be in AVStream.time_base units, i.e. it has to be + * multiplied by the timebase to convert them to seconds. + * + * A packet returned by av_read_frame() is always reference-counted, + * i.e. AVPacket.buf is set and the user may keep it indefinitely. + * The packet must be freed with av_packet_unref() when it is no + * longer needed. + * + * @section lavf_decoding_seek Seeking + * @} + * + * @defgroup lavf_encoding Muxing + * @{ + * Muxers take encoded data in the form of @ref AVPacket "AVPackets" and write + * it into files or other output bytestreams in the specified container format. + * + * The main API functions for muxing are avformat_write_header() for writing the + * file header, av_write_frame() / av_interleaved_write_frame() for writing the + * packets and av_write_trailer() for finalizing the file. + * + * At the beginning of the muxing process, the caller must first call + * avformat_alloc_context() to create a muxing context. The caller then sets up + * the muxer by filling the various fields in this context: + * + * - The @ref AVFormatContext.oformat "oformat" field must be set to select the + * muxer that will be used. + * - Unless the format is of the AVFMT_NOFILE type, the @ref AVFormatContext.pb + * "pb" field must be set to an opened IO context, either returned from + * avio_open2() or a custom one. + * - Unless the format is of the AVFMT_NOSTREAMS type, at least one stream must + * be created with the avformat_new_stream() function. The caller should fill + * the @ref AVStream.codecpar "stream codec parameters" information, such as the + * codec @ref AVCodecParameters.codec_type "type", @ref AVCodecParameters.codec_id + * "id" and other parameters (e.g. width / height, the pixel or sample format, + * etc.) as known. The @ref AVStream.time_base "stream timebase" should + * be set to the timebase that the caller desires to use for this stream (note + * that the timebase actually used by the muxer can be different, as will be + * described later). + * - It is advised to manually initialize only the relevant fields in + * AVCodecParameters, rather than using @ref avcodec_parameters_copy() during + * remuxing: there is no guarantee that the codec context values remain valid + * for both input and output format contexts. + * - The caller may fill in additional information, such as @ref + * AVFormatContext.metadata "global" or @ref AVStream.metadata "per-stream" + * metadata, @ref AVFormatContext.chapters "chapters", @ref + * AVFormatContext.programs "programs", etc. as described in the + * AVFormatContext documentation. Whether such information will actually be + * stored in the output depends on what the container format and the muxer + * support. + * + * When the muxing context is fully set up, the caller must call + * avformat_write_header() to initialize the muxer internals and write the file + * header. Whether anything actually is written to the IO context at this step + * depends on the muxer, but this function must always be called. Any muxer + * private options must be passed in the options parameter to this function. + * + * The data is then sent to the muxer by repeatedly calling av_write_frame() or + * av_interleaved_write_frame() (consult those functions' documentation for + * discussion on the difference between them; only one of them may be used with + * a single muxing context, they should not be mixed). Do note that the timing + * information on the packets sent to the muxer must be in the corresponding + * AVStream's timebase. That timebase is set by the muxer (in the + * avformat_write_header() step) and may be different from the timebase + * requested by the caller. + * + * Once all the data has been written, the caller must call av_write_trailer() + * to flush any buffered packets and finalize the output file, then close the IO + * context (if any) and finally free the muxing context with + * avformat_free_context(). + * @} + * + * @defgroup lavf_io I/O Read/Write + * @{ + * @section lavf_io_dirlist Directory listing + * The directory listing API makes it possible to list files on remote servers. + * + * Some of possible use cases: + * - an "open file" dialog to choose files from a remote location, + * - a recursive media finder providing a player with an ability to play all + * files from a given directory. + * + * @subsection lavf_io_dirlist_open Opening a directory + * At first, a directory needs to be opened by calling avio_open_dir() + * supplied with a URL and, optionally, ::AVDictionary containing + * protocol-specific parameters. The function returns zero or positive + * integer and allocates AVIODirContext on success. + * + * @code + * AVIODirContext *ctx = NULL; + * if (avio_open_dir(&ctx, "smb://example.com/some_dir", NULL) < 0) { + * fprintf(stderr, "Cannot open directory.\n"); + * abort(); + * } + * @endcode + * + * This code tries to open a sample directory using smb protocol without + * any additional parameters. + * + * @subsection lavf_io_dirlist_read Reading entries + * Each directory's entry (i.e. file, another directory, anything else + * within ::AVIODirEntryType) is represented by AVIODirEntry. + * Reading consecutive entries from an opened AVIODirContext is done by + * repeatedly calling avio_read_dir() on it. Each call returns zero or + * positive integer if successful. Reading can be stopped right after the + * NULL entry has been read -- it means there are no entries left to be + * read. The following code reads all entries from a directory associated + * with ctx and prints their names to standard output. + * @code + * AVIODirEntry *entry = NULL; + * for (;;) { + * if (avio_read_dir(ctx, &entry) < 0) { + * fprintf(stderr, "Cannot list directory.\n"); + * abort(); + * } + * if (!entry) + * break; + * printf("%s\n", entry->name); + * avio_free_directory_entry(&entry); + * } + * @endcode + * @} + * + * @defgroup lavf_codec Demuxers + * @{ + * @defgroup lavf_codec_native Native Demuxers + * @{ + * @} + * @defgroup lavf_codec_wrappers External library wrappers + * @{ + * @} + * @} + * @defgroup lavf_protos I/O Protocols + * @{ + * @} + * @defgroup lavf_internal Internal + * @{ + * @} + * @} + */ + +#include +#include /* FILE */ +#include "libavcodec/avcodec.h" +#include "libavutil/dict.h" +#include "libavutil/log.h" + +#include "avio.h" +#include "libavformat/version.h" + +struct AVFormatContext; + +struct AVDeviceInfoList; +struct AVDeviceCapabilitiesQuery; + +/** + * @defgroup metadata_api Public Metadata API + * @{ + * @ingroup libavf + * The metadata API allows libavformat to export metadata tags to a client + * application when demuxing. Conversely it allows a client application to + * set metadata when muxing. + * + * Metadata is exported or set as pairs of key/value strings in the 'metadata' + * fields of the AVFormatContext, AVStream, AVChapter and AVProgram structs + * using the @ref lavu_dict "AVDictionary" API. Like all strings in FFmpeg, + * metadata is assumed to be UTF-8 encoded Unicode. Note that metadata + * exported by demuxers isn't checked to be valid UTF-8 in most cases. + * + * Important concepts to keep in mind: + * - Keys are unique; there can never be 2 tags with the same key. This is + * also meant semantically, i.e., a demuxer should not knowingly produce + * several keys that are literally different but semantically identical. + * E.g., key=Author5, key=Author6. In this example, all authors must be + * placed in the same tag. + * - Metadata is flat, not hierarchical; there are no subtags. If you + * want to store, e.g., the email address of the child of producer Alice + * and actor Bob, that could have key=alice_and_bobs_childs_email_address. + * - Several modifiers can be applied to the tag name. This is done by + * appending a dash character ('-') and the modifier name in the order + * they appear in the list below -- e.g. foo-eng-sort, not foo-sort-eng. + * - language -- a tag whose value is localized for a particular language + * is appended with the ISO 639-2/B 3-letter language code. + * For example: Author-ger=Michael, Author-eng=Mike + * The original/default language is in the unqualified "Author" tag. + * A demuxer should set a default if it sets any translated tag. + * - sorting -- a modified version of a tag that should be used for + * sorting will have '-sort' appended. E.g. artist="The Beatles", + * artist-sort="Beatles, The". + * - Some protocols and demuxers support metadata updates. After a successful + * call to av_read_packet(), AVFormatContext.event_flags or AVStream.event_flags + * will be updated to indicate if metadata changed. In order to detect metadata + * changes on a stream, you need to loop through all streams in the AVFormatContext + * and check their individual event_flags. + * + * - Demuxers attempt to export metadata in a generic format, however tags + * with no generic equivalents are left as they are stored in the container. + * Follows a list of generic tag names: + * + @verbatim + album -- name of the set this work belongs to + album_artist -- main creator of the set/album, if different from artist. + e.g. "Various Artists" for compilation albums. + artist -- main creator of the work + comment -- any additional description of the file. + composer -- who composed the work, if different from artist. + copyright -- name of copyright holder. + creation_time-- date when the file was created, preferably in ISO 8601. + date -- date when the work was created, preferably in ISO 8601. + disc -- number of a subset, e.g. disc in a multi-disc collection. + encoder -- name/settings of the software/hardware that produced the file. + encoded_by -- person/group who created the file. + filename -- original name of the file. + genre -- . + language -- main language in which the work is performed, preferably + in ISO 639-2 format. Multiple languages can be specified by + separating them with commas. + performer -- artist who performed the work, if different from artist. + E.g for "Also sprach Zarathustra", artist would be "Richard + Strauss" and performer "London Philharmonic Orchestra". + publisher -- name of the label/publisher. + service_name -- name of the service in broadcasting (channel name). + service_provider -- name of the service provider in broadcasting. + title -- name of the work. + track -- number of this work in the set, can be in form current/total. + variant_bitrate -- the total bitrate of the bitrate variant that the current stream is part of + @endverbatim + * + * Look in the examples section for an application example how to use the Metadata API. + * + * @} + */ + +/* packet functions */ + + +/** + * Allocate and read the payload of a packet and initialize its + * fields with default values. + * + * @param s associated IO context + * @param pkt packet + * @param size desired payload size + * @return >0 (read size) if OK, AVERROR_xxx otherwise + */ +int av_get_packet(AVIOContext *s, AVPacket *pkt, int size); + + +/** + * Read data and append it to the current content of the AVPacket. + * If pkt->size is 0 this is identical to av_get_packet. + * Note that this uses av_grow_packet and thus involves a realloc + * which is inefficient. Thus this function should only be used + * when there is no reasonable way to know (an upper bound of) + * the final size. + * + * @param s associated IO context + * @param pkt packet + * @param size amount of data to read + * @return >0 (read size) if OK, AVERROR_xxx otherwise, previous data + * will not be lost even if an error occurs. + */ +int av_append_packet(AVIOContext *s, AVPacket *pkt, int size); + +/*************************************************/ +/* input/output formats */ + +struct AVCodecTag; + +/** + * This structure contains the data a format has to probe a file. + */ +typedef struct AVProbeData { + const char *filename; + unsigned char *buf; /**< Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero. */ + int buf_size; /**< Size of buf except extra allocated bytes */ + const char *mime_type; /**< mime_type, when known. */ +} AVProbeData; + +#define AVPROBE_SCORE_RETRY (AVPROBE_SCORE_MAX/4) +#define AVPROBE_SCORE_STREAM_RETRY (AVPROBE_SCORE_MAX/4-1) + +#define AVPROBE_SCORE_EXTENSION 50 ///< score for file extension +#define AVPROBE_SCORE_MIME 75 ///< score for file mime type +#define AVPROBE_SCORE_MAX 100 ///< maximum score + +#define AVPROBE_PADDING_SIZE 32 ///< extra allocated bytes at the end of the probe buffer + +/// Demuxer will use avio_open, no opened file should be provided by the caller. +#define AVFMT_NOFILE 0x0001 +#define AVFMT_NEEDNUMBER 0x0002 /**< Needs '%d' in filename. */ +#define AVFMT_SHOW_IDS 0x0008 /**< Show format stream IDs numbers. */ +#define AVFMT_GLOBALHEADER 0x0040 /**< Format wants global header. */ +#define AVFMT_NOTIMESTAMPS 0x0080 /**< Format does not need / have any timestamps. */ +#define AVFMT_GENERIC_INDEX 0x0100 /**< Use generic index building code. */ +#define AVFMT_TS_DISCONT 0x0200 /**< Format allows timestamp discontinuities. Note, muxers always require valid (monotone) timestamps */ +#define AVFMT_VARIABLE_FPS 0x0400 /**< Format allows variable fps. */ +#define AVFMT_NODIMENSIONS 0x0800 /**< Format does not need width/height */ +#define AVFMT_NOSTREAMS 0x1000 /**< Format does not require any streams */ +#define AVFMT_NOBINSEARCH 0x2000 /**< Format does not allow to fall back on binary search via read_timestamp */ +#define AVFMT_NOGENSEARCH 0x4000 /**< Format does not allow to fall back on generic search */ +#define AVFMT_NO_BYTE_SEEK 0x8000 /**< Format does not allow seeking by bytes */ +#define AVFMT_ALLOW_FLUSH 0x10000 /**< Format allows flushing. If not set, the muxer will not receive a NULL packet in the write_packet function. */ +#define AVFMT_TS_NONSTRICT 0x20000 /**< Format does not require strictly + increasing timestamps, but they must + still be monotonic */ +#define AVFMT_TS_NEGATIVE 0x40000 /**< Format allows muxing negative + timestamps. If not set the timestamp + will be shifted in av_write_frame and + av_interleaved_write_frame so they + start from 0. + The user or muxer can override this through + AVFormatContext.avoid_negative_ts + */ + +#define AVFMT_SEEK_TO_PTS 0x4000000 /**< Seeking is based on PTS */ + +/** + * @addtogroup lavf_encoding + * @{ + */ +typedef struct AVOutputFormat { + const char *name; + /** + * Descriptive name for the format, meant to be more human-readable + * than name. You should use the NULL_IF_CONFIG_SMALL() macro + * to define it. + */ + const char *long_name; + const char *mime_type; + const char *extensions; /**< comma-separated filename extensions */ + /* output support */ + enum AVCodecID audio_codec; /**< default audio codec */ + enum AVCodecID video_codec; /**< default video codec */ + enum AVCodecID subtitle_codec; /**< default subtitle codec */ + /** + * can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, + * AVFMT_GLOBALHEADER, AVFMT_NOTIMESTAMPS, AVFMT_VARIABLE_FPS, + * AVFMT_NODIMENSIONS, AVFMT_NOSTREAMS, AVFMT_ALLOW_FLUSH, + * AVFMT_TS_NONSTRICT, AVFMT_TS_NEGATIVE + */ + int flags; + + /** + * List of supported codec_id-codec_tag pairs, ordered by "better + * choice first". The arrays are all terminated by AV_CODEC_ID_NONE. + */ + const struct AVCodecTag * const *codec_tag; + + + const AVClass *priv_class; ///< AVClass for the private context + + /***************************************************************** + * No fields below this line are part of the public API. They + * may not be used outside of libavformat and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + /** + * The ff_const59 define is not part of the public API and will + * be removed without further warning. + */ +#if FF_API_AVIOFORMAT +#define ff_const59 +#else +#define ff_const59 const +#endif + ff_const59 struct AVOutputFormat *next; + /** + * size of private data so that it can be allocated in the wrapper + */ + int priv_data_size; + + int (*write_header)(struct AVFormatContext *); + /** + * Write a packet. If AVFMT_ALLOW_FLUSH is set in flags, + * pkt can be NULL in order to flush data buffered in the muxer. + * When flushing, return 0 if there still is more data to flush, + * or 1 if everything was flushed and there is no more buffered + * data. + */ + int (*write_packet)(struct AVFormatContext *, AVPacket *pkt); + int (*write_trailer)(struct AVFormatContext *); + /** + * A format-specific function for interleavement. + * If unset, packets will be interleaved by dts. + */ + int (*interleave_packet)(struct AVFormatContext *, AVPacket *out, + AVPacket *in, int flush); + /** + * Test if the given codec can be stored in this container. + * + * @return 1 if the codec is supported, 0 if it is not. + * A negative number if unknown. + * MKTAG('A', 'P', 'I', 'C') if the codec is only supported as AV_DISPOSITION_ATTACHED_PIC + */ + int (*query_codec)(enum AVCodecID id, int std_compliance); + + void (*get_output_timestamp)(struct AVFormatContext *s, int stream, + int64_t *dts, int64_t *wall); + /** + * Allows sending messages from application to device. + */ + int (*control_message)(struct AVFormatContext *s, int type, + void *data, size_t data_size); + + /** + * Write an uncoded AVFrame. + * + * See av_write_uncoded_frame() for details. + * + * The library will free *frame afterwards, but the muxer can prevent it + * by setting the pointer to NULL. + */ + int (*write_uncoded_frame)(struct AVFormatContext *, int stream_index, + AVFrame **frame, unsigned flags); + /** + * Returns device list with it properties. + * @see avdevice_list_devices() for more details. + */ + int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list); + /** + * Initialize device capabilities submodule. + * @see avdevice_capabilities_create() for more details. + */ + int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps); + /** + * Free device capabilities submodule. + * @see avdevice_capabilities_free() for more details. + */ + int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps); + enum AVCodecID data_codec; /**< default data codec */ + /** + * Initialize format. May allocate data here, and set any AVFormatContext or + * AVStream parameters that need to be set before packets are sent. + * This method must not write output. + * + * Return 0 if streams were fully configured, 1 if not, negative AVERROR on failure + * + * Any allocations made here must be freed in deinit(). + */ + int (*init)(struct AVFormatContext *); + /** + * Deinitialize format. If present, this is called whenever the muxer is being + * destroyed, regardless of whether or not the header has been written. + * + * If a trailer is being written, this is called after write_trailer(). + * + * This is called if init() fails as well. + */ + void (*deinit)(struct AVFormatContext *); + /** + * Set up any necessary bitstream filtering and extract any extra data needed + * for the global header. + * Return 0 if more packets from this stream must be checked; 1 if not. + */ + int (*check_bitstream)(struct AVFormatContext *, const AVPacket *pkt); +} AVOutputFormat; +/** + * @} + */ + +/** + * @addtogroup lavf_decoding + * @{ + */ +typedef struct AVInputFormat { + /** + * A comma separated list of short names for the format. New names + * may be appended with a minor bump. + */ + const char *name; + + /** + * Descriptive name for the format, meant to be more human-readable + * than name. You should use the NULL_IF_CONFIG_SMALL() macro + * to define it. + */ + const char *long_name; + + /** + * Can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_SHOW_IDS, + * AVFMT_NOTIMESTAMPS, AVFMT_GENERIC_INDEX, AVFMT_TS_DISCONT, AVFMT_NOBINSEARCH, + * AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK, AVFMT_SEEK_TO_PTS. + */ + int flags; + + /** + * If extensions are defined, then no probe is done. You should + * usually not use extension format guessing because it is not + * reliable enough + */ + const char *extensions; + + const struct AVCodecTag * const *codec_tag; + + const AVClass *priv_class; ///< AVClass for the private context + + /** + * Comma-separated list of mime types. + * It is used check for matching mime types while probing. + * @see av_probe_input_format2 + */ + const char *mime_type; + + /***************************************************************** + * No fields below this line are part of the public API. They + * may not be used outside of libavformat and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + ff_const59 struct AVInputFormat *next; + + /** + * Raw demuxers store their codec ID here. + */ + int raw_codec_id; + + /** + * Size of private data so that it can be allocated in the wrapper. + */ + int priv_data_size; + + /** + * Tell if a given file has a chance of being parsed as this format. + * The buffer provided is guaranteed to be AVPROBE_PADDING_SIZE bytes + * big so you do not have to check for that unless you need more. + */ + int (*read_probe)(const AVProbeData *); + + /** + * Read the format header and initialize the AVFormatContext + * structure. Return 0 if OK. 'avformat_new_stream' should be + * called to create new streams. + */ + int (*read_header)(struct AVFormatContext *); + + /** + * Read one packet and put it in 'pkt'. pts and flags are also + * set. 'avformat_new_stream' can be called only if the flag + * AVFMTCTX_NOHEADER is used and only in the calling thread (not in a + * background thread). + * @return 0 on success, < 0 on error. + * Upon returning an error, pkt must be unreferenced by the caller. + */ + int (*read_packet)(struct AVFormatContext *, AVPacket *pkt); + + /** + * Close the stream. The AVFormatContext and AVStreams are not + * freed by this function + */ + int (*read_close)(struct AVFormatContext *); + + /** + * Seek to a given timestamp relative to the frames in + * stream component stream_index. + * @param stream_index Must not be -1. + * @param flags Selects which direction should be preferred if no exact + * match is available. + * @return >= 0 on success (but not necessarily the new offset) + */ + int (*read_seek)(struct AVFormatContext *, + int stream_index, int64_t timestamp, int flags); + + /** + * Get the next timestamp in stream[stream_index].time_base units. + * @return the timestamp or AV_NOPTS_VALUE if an error occurred + */ + int64_t (*read_timestamp)(struct AVFormatContext *s, int stream_index, + int64_t *pos, int64_t pos_limit); + + /** + * Start/resume playing - only meaningful if using a network-based format + * (RTSP). + */ + int (*read_play)(struct AVFormatContext *); + + /** + * Pause playing - only meaningful if using a network-based format + * (RTSP). + */ + int (*read_pause)(struct AVFormatContext *); + + /** + * Seek to timestamp ts. + * Seeking will be done so that the point from which all active streams + * can be presented successfully will be closest to ts and within min/max_ts. + * Active streams are all streams that have AVStream.discard < AVDISCARD_ALL. + */ + int (*read_seek2)(struct AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags); + + /** + * Returns device list with it properties. + * @see avdevice_list_devices() for more details. + */ + int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list); + + /** + * Initialize device capabilities submodule. + * @see avdevice_capabilities_create() for more details. + */ + int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps); + + /** + * Free device capabilities submodule. + * @see avdevice_capabilities_free() for more details. + */ + int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps); +} AVInputFormat; +/** + * @} + */ + +enum AVStreamParseType { + AVSTREAM_PARSE_NONE, + AVSTREAM_PARSE_FULL, /**< full parsing and repack */ + AVSTREAM_PARSE_HEADERS, /**< Only parse headers, do not repack. */ + AVSTREAM_PARSE_TIMESTAMPS, /**< full parsing and interpolation of timestamps for frames not starting on a packet boundary */ + AVSTREAM_PARSE_FULL_ONCE, /**< full parsing and repack of the first frame only, only implemented for H.264 currently */ + AVSTREAM_PARSE_FULL_RAW, /**< full parsing and repack with timestamp and position generation by parser for raw + this assumes that each packet in the file contains no demuxer level headers and + just codec level data, otherwise position generation would fail */ +}; + +typedef struct AVIndexEntry { + int64_t pos; + int64_t timestamp; /**< + * Timestamp in AVStream.time_base units, preferably the time from which on correctly decoded frames are available + * when seeking to this entry. That means preferable PTS on keyframe based formats. + * But demuxers can choose to store a different timestamp, if it is more convenient for the implementation or nothing better + * is known + */ +#define AVINDEX_KEYFRAME 0x0001 +#define AVINDEX_DISCARD_FRAME 0x0002 /** + * Flag is used to indicate which frame should be discarded after decoding. + */ + int flags:2; + int size:30; //Yeah, trying to keep the size of this small to reduce memory requirements (it is 24 vs. 32 bytes due to possible 8-byte alignment). + int min_distance; /**< Minimum distance between this and the previous keyframe, used to avoid unneeded searching. */ +} AVIndexEntry; + +#define AV_DISPOSITION_DEFAULT 0x0001 +#define AV_DISPOSITION_DUB 0x0002 +#define AV_DISPOSITION_ORIGINAL 0x0004 +#define AV_DISPOSITION_COMMENT 0x0008 +#define AV_DISPOSITION_LYRICS 0x0010 +#define AV_DISPOSITION_KARAOKE 0x0020 + +/** + * Track should be used during playback by default. + * Useful for subtitle track that should be displayed + * even when user did not explicitly ask for subtitles. + */ +#define AV_DISPOSITION_FORCED 0x0040 +#define AV_DISPOSITION_HEARING_IMPAIRED 0x0080 /**< stream for hearing impaired audiences */ +#define AV_DISPOSITION_VISUAL_IMPAIRED 0x0100 /**< stream for visual impaired audiences */ +#define AV_DISPOSITION_CLEAN_EFFECTS 0x0200 /**< stream without voice */ +/** + * The stream is stored in the file as an attached picture/"cover art" (e.g. + * APIC frame in ID3v2). The first (usually only) packet associated with it + * will be returned among the first few packets read from the file unless + * seeking takes place. It can also be accessed at any time in + * AVStream.attached_pic. + */ +#define AV_DISPOSITION_ATTACHED_PIC 0x0400 +/** + * The stream is sparse, and contains thumbnail images, often corresponding + * to chapter markers. Only ever used with AV_DISPOSITION_ATTACHED_PIC. + */ +#define AV_DISPOSITION_TIMED_THUMBNAILS 0x0800 + +typedef struct AVStreamInternal AVStreamInternal; + +/** + * To specify text track kind (different from subtitles default). + */ +#define AV_DISPOSITION_CAPTIONS 0x10000 +#define AV_DISPOSITION_DESCRIPTIONS 0x20000 +#define AV_DISPOSITION_METADATA 0x40000 +#define AV_DISPOSITION_DEPENDENT 0x80000 ///< dependent audio stream (mix_type=0 in mpegts) +#define AV_DISPOSITION_STILL_IMAGE 0x100000 ///< still images in video stream (still_picture_flag=1 in mpegts) + +/** + * Options for behavior on timestamp wrap detection. + */ +#define AV_PTS_WRAP_IGNORE 0 ///< ignore the wrap +#define AV_PTS_WRAP_ADD_OFFSET 1 ///< add the format specific offset on wrap detection +#define AV_PTS_WRAP_SUB_OFFSET -1 ///< subtract the format specific offset on wrap detection + +/** + * Stream structure. + * New fields can be added to the end with minor version bumps. + * Removal, reordering and changes to existing fields require a major + * version bump. + * sizeof(AVStream) must not be used outside libav*. + */ +typedef struct AVStream { + int index; /**< stream index in AVFormatContext */ + /** + * Format-specific stream ID. + * decoding: set by libavformat + * encoding: set by the user, replaced by libavformat if left unset + */ + int id; +#if FF_API_LAVF_AVCTX + /** + * @deprecated use the codecpar struct instead + */ + attribute_deprecated + AVCodecContext *codec; +#endif + void *priv_data; + + /** + * This is the fundamental unit of time (in seconds) in terms + * of which frame timestamps are represented. + * + * decoding: set by libavformat + * encoding: May be set by the caller before avformat_write_header() to + * provide a hint to the muxer about the desired timebase. In + * avformat_write_header(), the muxer will overwrite this field + * with the timebase that will actually be used for the timestamps + * written into the file (which may or may not be related to the + * user-provided one, depending on the format). + */ + AVRational time_base; + + /** + * Decoding: pts of the first frame of the stream in presentation order, in stream time base. + * Only set this if you are absolutely 100% sure that the value you set + * it to really is the pts of the first frame. + * This may be undefined (AV_NOPTS_VALUE). + * @note The ASF header does NOT contain a correct start_time the ASF + * demuxer must NOT set this. + */ + int64_t start_time; + + /** + * Decoding: duration of the stream, in stream time base. + * If a source file does not specify a duration, but does specify + * a bitrate, this value will be estimated from bitrate and file size. + * + * Encoding: May be set by the caller before avformat_write_header() to + * provide a hint to the muxer about the estimated duration. + */ + int64_t duration; + + int64_t nb_frames; ///< number of frames in this stream if known or 0 + + int disposition; /**< AV_DISPOSITION_* bit field */ + + enum AVDiscard discard; ///< Selects which packets can be discarded at will and do not need to be demuxed. + + /** + * sample aspect ratio (0 if unknown) + * - encoding: Set by user. + * - decoding: Set by libavformat. + */ + AVRational sample_aspect_ratio; + + AVDictionary *metadata; + + /** + * Average framerate + * + * - demuxing: May be set by libavformat when creating the stream or in + * avformat_find_stream_info(). + * - muxing: May be set by the caller before avformat_write_header(). + */ + AVRational avg_frame_rate; + + /** + * For streams with AV_DISPOSITION_ATTACHED_PIC disposition, this packet + * will contain the attached picture. + * + * decoding: set by libavformat, must not be modified by the caller. + * encoding: unused + */ + AVPacket attached_pic; + + /** + * An array of side data that applies to the whole stream (i.e. the + * container does not allow it to change between packets). + * + * There may be no overlap between the side data in this array and side data + * in the packets. I.e. a given side data is either exported by the muxer + * (demuxing) / set by the caller (muxing) in this array, then it never + * appears in the packets, or the side data is exported / sent through + * the packets (always in the first packet where the value becomes known or + * changes), then it does not appear in this array. + * + * - demuxing: Set by libavformat when the stream is created. + * - muxing: May be set by the caller before avformat_write_header(). + * + * Freed by libavformat in avformat_free_context(). + * + * @see av_format_inject_global_side_data() + */ + AVPacketSideData *side_data; + /** + * The number of elements in the AVStream.side_data array. + */ + int nb_side_data; + + /** + * Flags for the user to detect events happening on the stream. Flags must + * be cleared by the user once the event has been handled. + * A combination of AVSTREAM_EVENT_FLAG_*. + */ + int event_flags; +#define AVSTREAM_EVENT_FLAG_METADATA_UPDATED 0x0001 ///< The call resulted in updated metadata. + + /** + * Real base framerate of the stream. + * This is the lowest framerate with which all timestamps can be + * represented accurately (it is the least common multiple of all + * framerates in the stream). Note, this value is just a guess! + * For example, if the time base is 1/90000 and all frames have either + * approximately 3600 or 1800 timer ticks, then r_frame_rate will be 50/1. + */ + AVRational r_frame_rate; + +#if FF_API_LAVF_FFSERVER + /** + * String containing pairs of key and values describing recommended encoder configuration. + * Pairs are separated by ','. + * Keys are separated from values by '='. + * + * @deprecated unused + */ + attribute_deprecated + char *recommended_encoder_configuration; +#endif + + /** + * Codec parameters associated with this stream. Allocated and freed by + * libavformat in avformat_new_stream() and avformat_free_context() + * respectively. + * + * - demuxing: filled by libavformat on stream creation or in + * avformat_find_stream_info() + * - muxing: filled by the caller before avformat_write_header() + */ + AVCodecParameters *codecpar; + + /***************************************************************** + * All fields below this line are not part of the public API. They + * may not be used outside of libavformat and can be changed and + * removed at will. + * Internal note: be aware that physically removing these fields + * will break ABI. Replace removed fields with dummy fields, and + * add new fields to AVStreamInternal. + ***************************************************************** + */ + +#define MAX_STD_TIMEBASES (30*12+30+3+6) + /** + * Stream information used internally by avformat_find_stream_info() + */ + struct { + int64_t last_dts; + int64_t duration_gcd; + int duration_count; + int64_t rfps_duration_sum; + double (*duration_error)[2][MAX_STD_TIMEBASES]; + int64_t codec_info_duration; + int64_t codec_info_duration_fields; + int frame_delay_evidence; + + /** + * 0 -> decoder has not been searched for yet. + * >0 -> decoder found + * <0 -> decoder with codec_id == -found_decoder has not been found + */ + int found_decoder; + + int64_t last_duration; + + /** + * Those are used for average framerate estimation. + */ + int64_t fps_first_dts; + int fps_first_dts_idx; + int64_t fps_last_dts; + int fps_last_dts_idx; + + } *info; + + int pts_wrap_bits; /**< number of bits in pts (used for wrapping control) */ + + // Timestamp generation support: + /** + * Timestamp corresponding to the last dts sync point. + * + * Initialized when AVCodecParserContext.dts_sync_point >= 0 and + * a DTS is received from the underlying container. Otherwise set to + * AV_NOPTS_VALUE by default. + */ + int64_t first_dts; + int64_t cur_dts; + int64_t last_IP_pts; + int last_IP_duration; + + /** + * Number of packets to buffer for codec probing + */ + int probe_packets; + + /** + * Number of frames that have been demuxed during avformat_find_stream_info() + */ + int codec_info_nb_frames; + + /* av_read_frame() support */ + enum AVStreamParseType need_parsing; + struct AVCodecParserContext *parser; + + /** + * last packet in packet_buffer for this stream when muxing. + */ + struct AVPacketList *last_in_packet_buffer; + AVProbeData probe_data; +#define MAX_REORDER_DELAY 16 + int64_t pts_buffer[MAX_REORDER_DELAY+1]; + + AVIndexEntry *index_entries; /**< Only used if the format does not + support seeking natively. */ + int nb_index_entries; + unsigned int index_entries_allocated_size; + + /** + * Stream Identifier + * This is the MPEG-TS stream identifier +1 + * 0 means unknown + */ + int stream_identifier; + + /** + * Details of the MPEG-TS program which created this stream. + */ + int program_num; + int pmt_version; + int pmt_stream_idx; + + int64_t interleaver_chunk_size; + int64_t interleaver_chunk_duration; + + /** + * stream probing state + * -1 -> probing finished + * 0 -> no probing requested + * rest -> perform probing with request_probe being the minimum score to accept. + */ + int request_probe; + /** + * Indicates that everything up to the next keyframe + * should be discarded. + */ + int skip_to_keyframe; + + /** + * Number of samples to skip at the start of the frame decoded from the next packet. + */ + int skip_samples; + + /** + * If not 0, the number of samples that should be skipped from the start of + * the stream (the samples are removed from packets with pts==0, which also + * assumes negative timestamps do not happen). + * Intended for use with formats such as mp3 with ad-hoc gapless audio + * support. + */ + int64_t start_skip_samples; + + /** + * If not 0, the first audio sample that should be discarded from the stream. + * This is broken by design (needs global sample count), but can't be + * avoided for broken by design formats such as mp3 with ad-hoc gapless + * audio support. + */ + int64_t first_discard_sample; + + /** + * The sample after last sample that is intended to be discarded after + * first_discard_sample. Works on frame boundaries only. Used to prevent + * early EOF if the gapless info is broken (considered concatenated mp3s). + */ + int64_t last_discard_sample; + + /** + * Number of internally decoded frames, used internally in libavformat, do not access + * its lifetime differs from info which is why it is not in that structure. + */ + int nb_decoded_frames; + + /** + * Timestamp offset added to timestamps before muxing + */ + int64_t mux_ts_offset; + + /** + * Internal data to check for wrapping of the time stamp + */ + int64_t pts_wrap_reference; + + /** + * Options for behavior, when a wrap is detected. + * + * Defined by AV_PTS_WRAP_ values. + * + * If correction is enabled, there are two possibilities: + * If the first time stamp is near the wrap point, the wrap offset + * will be subtracted, which will create negative time stamps. + * Otherwise the offset will be added. + */ + int pts_wrap_behavior; + + /** + * Internal data to prevent doing update_initial_durations() twice + */ + int update_initial_durations_done; + + /** + * Internal data to generate dts from pts + */ + int64_t pts_reorder_error[MAX_REORDER_DELAY+1]; + uint8_t pts_reorder_error_count[MAX_REORDER_DELAY+1]; + + /** + * Internal data to analyze DTS and detect faulty mpeg streams + */ + int64_t last_dts_for_order_check; + uint8_t dts_ordered; + uint8_t dts_misordered; + + /** + * Internal data to inject global side data + */ + int inject_global_side_data; + + /** + * display aspect ratio (0 if unknown) + * - encoding: unused + * - decoding: Set by libavformat to calculate sample_aspect_ratio internally + */ + AVRational display_aspect_ratio; + + /** + * An opaque field for libavformat internal usage. + * Must not be accessed in any way by callers. + */ + AVStreamInternal *internal; +} AVStream; + +#if FF_API_FORMAT_GET_SET +/** + * Accessors for some AVStream fields. These used to be provided for ABI + * compatibility, and do not need to be used anymore. + */ +attribute_deprecated +AVRational av_stream_get_r_frame_rate(const AVStream *s); +attribute_deprecated +void av_stream_set_r_frame_rate(AVStream *s, AVRational r); +#if FF_API_LAVF_FFSERVER +attribute_deprecated +char* av_stream_get_recommended_encoder_configuration(const AVStream *s); +attribute_deprecated +void av_stream_set_recommended_encoder_configuration(AVStream *s, char *configuration); +#endif +#endif + +struct AVCodecParserContext *av_stream_get_parser(const AVStream *s); + +/** + * Returns the pts of the last muxed packet + its duration + * + * the retuned value is undefined when used with a demuxer. + */ +int64_t av_stream_get_end_pts(const AVStream *st); + +#define AV_PROGRAM_RUNNING 1 + +/** + * New fields can be added to the end with minor version bumps. + * Removal, reordering and changes to existing fields require a major + * version bump. + * sizeof(AVProgram) must not be used outside libav*. + */ +typedef struct AVProgram { + int id; + int flags; + enum AVDiscard discard; ///< selects which program to discard and which to feed to the caller + unsigned int *stream_index; + unsigned int nb_stream_indexes; + AVDictionary *metadata; + + int program_num; + int pmt_pid; + int pcr_pid; + int pmt_version; + + /***************************************************************** + * All fields below this line are not part of the public API. They + * may not be used outside of libavformat and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** + */ + int64_t start_time; + int64_t end_time; + + int64_t pts_wrap_reference; ///< reference dts for wrap detection + int pts_wrap_behavior; ///< behavior on wrap detection +} AVProgram; + +#define AVFMTCTX_NOHEADER 0x0001 /**< signal that no header is present + (streams are added dynamically) */ +#define AVFMTCTX_UNSEEKABLE 0x0002 /**< signal that the stream is definitely + not seekable, and attempts to call the + seek function will fail. For some + network protocols (e.g. HLS), this can + change dynamically at runtime. */ + +typedef struct AVChapter { + int id; ///< unique ID to identify the chapter + AVRational time_base; ///< time base in which the start/end timestamps are specified + int64_t start, end; ///< chapter start/end time in time_base units + AVDictionary *metadata; +} AVChapter; + + +/** + * Callback used by devices to communicate with application. + */ +typedef int (*av_format_control_message)(struct AVFormatContext *s, int type, + void *data, size_t data_size); + +typedef int (*AVOpenCallback)(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options); + +/** + * The duration of a video can be estimated through various ways, and this enum can be used + * to know how the duration was estimated. + */ +enum AVDurationEstimationMethod { + AVFMT_DURATION_FROM_PTS, ///< Duration accurately estimated from PTSes + AVFMT_DURATION_FROM_STREAM, ///< Duration estimated from a stream with a known duration + AVFMT_DURATION_FROM_BITRATE ///< Duration estimated from bitrate (less accurate) +}; + +typedef struct AVFormatInternal AVFormatInternal; + +/** + * Format I/O context. + * New fields can be added to the end with minor version bumps. + * Removal, reordering and changes to existing fields require a major + * version bump. + * sizeof(AVFormatContext) must not be used outside libav*, use + * avformat_alloc_context() to create an AVFormatContext. + * + * Fields can be accessed through AVOptions (av_opt*), + * the name string used matches the associated command line parameter name and + * can be found in libavformat/options_table.h. + * The AVOption/command line parameter names differ in some cases from the C + * structure field names for historic reasons or brevity. + */ +typedef struct AVFormatContext { + /** + * A class for logging and @ref avoptions. Set by avformat_alloc_context(). + * Exports (de)muxer private options if they exist. + */ + const AVClass *av_class; + + /** + * The input container format. + * + * Demuxing only, set by avformat_open_input(). + */ + ff_const59 struct AVInputFormat *iformat; + + /** + * The output container format. + * + * Muxing only, must be set by the caller before avformat_write_header(). + */ + ff_const59 struct AVOutputFormat *oformat; + + /** + * Format private data. This is an AVOptions-enabled struct + * if and only if iformat/oformat.priv_class is not NULL. + * + * - muxing: set by avformat_write_header() + * - demuxing: set by avformat_open_input() + */ + void *priv_data; + + /** + * I/O context. + * + * - demuxing: either set by the user before avformat_open_input() (then + * the user must close it manually) or set by avformat_open_input(). + * - muxing: set by the user before avformat_write_header(). The caller must + * take care of closing / freeing the IO context. + * + * Do NOT set this field if AVFMT_NOFILE flag is set in + * iformat/oformat.flags. In such a case, the (de)muxer will handle + * I/O in some other way and this field will be NULL. + */ + AVIOContext *pb; + + /* stream info */ + /** + * Flags signalling stream properties. A combination of AVFMTCTX_*. + * Set by libavformat. + */ + int ctx_flags; + + /** + * Number of elements in AVFormatContext.streams. + * + * Set by avformat_new_stream(), must not be modified by any other code. + */ + unsigned int nb_streams; + /** + * A list of all streams in the file. New streams are created with + * avformat_new_stream(). + * + * - demuxing: streams are created by libavformat in avformat_open_input(). + * If AVFMTCTX_NOHEADER is set in ctx_flags, then new streams may also + * appear in av_read_frame(). + * - muxing: streams are created by the user before avformat_write_header(). + * + * Freed by libavformat in avformat_free_context(). + */ + AVStream **streams; + +#if FF_API_FORMAT_FILENAME + /** + * input or output filename + * + * - demuxing: set by avformat_open_input() + * - muxing: may be set by the caller before avformat_write_header() + * + * @deprecated Use url instead. + */ + attribute_deprecated + char filename[1024]; +#endif + + /** + * input or output URL. Unlike the old filename field, this field has no + * length restriction. + * + * - demuxing: set by avformat_open_input(), initialized to an empty + * string if url parameter was NULL in avformat_open_input(). + * - muxing: may be set by the caller before calling avformat_write_header() + * (or avformat_init_output() if that is called first) to a string + * which is freeable by av_free(). Set to an empty string if it + * was NULL in avformat_init_output(). + * + * Freed by libavformat in avformat_free_context(). + */ + char *url; + + /** + * Position of the first frame of the component, in + * AV_TIME_BASE fractional seconds. NEVER set this value directly: + * It is deduced from the AVStream values. + * + * Demuxing only, set by libavformat. + */ + int64_t start_time; + + /** + * Duration of the stream, in AV_TIME_BASE fractional + * seconds. Only set this value if you know none of the individual stream + * durations and also do not set any of them. This is deduced from the + * AVStream values if not set. + * + * Demuxing only, set by libavformat. + */ + int64_t duration; + + /** + * Total stream bitrate in bit/s, 0 if not + * available. Never set it directly if the file_size and the + * duration are known as FFmpeg can compute it automatically. + */ + int64_t bit_rate; + + unsigned int packet_size; + int max_delay; + + /** + * Flags modifying the (de)muxer behaviour. A combination of AVFMT_FLAG_*. + * Set by the user before avformat_open_input() / avformat_write_header(). + */ + int flags; +#define AVFMT_FLAG_GENPTS 0x0001 ///< Generate missing pts even if it requires parsing future frames. +#define AVFMT_FLAG_IGNIDX 0x0002 ///< Ignore index. +#define AVFMT_FLAG_NONBLOCK 0x0004 ///< Do not block when reading packets from input. +#define AVFMT_FLAG_IGNDTS 0x0008 ///< Ignore DTS on frames that contain both DTS & PTS +#define AVFMT_FLAG_NOFILLIN 0x0010 ///< Do not infer any values from other values, just return what is stored in the container +#define AVFMT_FLAG_NOPARSE 0x0020 ///< Do not use AVParsers, you also must set AVFMT_FLAG_NOFILLIN as the fillin code works on frames and no parsing -> no frames. Also seeking to frames can not work if parsing to find frame boundaries has been disabled +#define AVFMT_FLAG_NOBUFFER 0x0040 ///< Do not buffer frames when possible +#define AVFMT_FLAG_CUSTOM_IO 0x0080 ///< The caller has supplied a custom AVIOContext, don't avio_close() it. +#define AVFMT_FLAG_DISCARD_CORRUPT 0x0100 ///< Discard frames marked corrupted +#define AVFMT_FLAG_FLUSH_PACKETS 0x0200 ///< Flush the AVIOContext every packet. +/** + * When muxing, try to avoid writing any random/volatile data to the output. + * This includes any random IDs, real-time timestamps/dates, muxer version, etc. + * + * This flag is mainly intended for testing. + */ +#define AVFMT_FLAG_BITEXACT 0x0400 +#if FF_API_LAVF_MP4A_LATM +#define AVFMT_FLAG_MP4A_LATM 0x8000 ///< Deprecated, does nothing. +#endif +#define AVFMT_FLAG_SORT_DTS 0x10000 ///< try to interleave outputted packets by dts (using this flag can slow demuxing down) +#define AVFMT_FLAG_PRIV_OPT 0x20000 ///< Enable use of private options by delaying codec open (this could be made default once all code is converted) +#if FF_API_LAVF_KEEPSIDE_FLAG +#define AVFMT_FLAG_KEEP_SIDE_DATA 0x40000 ///< Deprecated, does nothing. +#endif +#define AVFMT_FLAG_FAST_SEEK 0x80000 ///< Enable fast, but inaccurate seeks for some formats +#define AVFMT_FLAG_SHORTEST 0x100000 ///< Stop muxing when the shortest stream stops. +#define AVFMT_FLAG_AUTO_BSF 0x200000 ///< Add bitstream filters as requested by the muxer + + /** + * Maximum size of the data read from input for determining + * the input container format. + * Demuxing only, set by the caller before avformat_open_input(). + */ + int64_t probesize; + + /** + * Maximum duration (in AV_TIME_BASE units) of the data read + * from input in avformat_find_stream_info(). + * Demuxing only, set by the caller before avformat_find_stream_info(). + * Can be set to 0 to let avformat choose using a heuristic. + */ + int64_t max_analyze_duration; + + const uint8_t *key; + int keylen; + + unsigned int nb_programs; + AVProgram **programs; + + /** + * Forced video codec_id. + * Demuxing: Set by user. + */ + enum AVCodecID video_codec_id; + + /** + * Forced audio codec_id. + * Demuxing: Set by user. + */ + enum AVCodecID audio_codec_id; + + /** + * Forced subtitle codec_id. + * Demuxing: Set by user. + */ + enum AVCodecID subtitle_codec_id; + + /** + * Maximum amount of memory in bytes to use for the index of each stream. + * If the index exceeds this size, entries will be discarded as + * needed to maintain a smaller size. This can lead to slower or less + * accurate seeking (depends on demuxer). + * Demuxers for which a full in-memory index is mandatory will ignore + * this. + * - muxing: unused + * - demuxing: set by user + */ + unsigned int max_index_size; + + /** + * Maximum amount of memory in bytes to use for buffering frames + * obtained from realtime capture devices. + */ + unsigned int max_picture_buffer; + + /** + * Number of chapters in AVChapter array. + * When muxing, chapters are normally written in the file header, + * so nb_chapters should normally be initialized before write_header + * is called. Some muxers (e.g. mov and mkv) can also write chapters + * in the trailer. To write chapters in the trailer, nb_chapters + * must be zero when write_header is called and non-zero when + * write_trailer is called. + * - muxing: set by user + * - demuxing: set by libavformat + */ + unsigned int nb_chapters; + AVChapter **chapters; + + /** + * Metadata that applies to the whole file. + * + * - demuxing: set by libavformat in avformat_open_input() + * - muxing: may be set by the caller before avformat_write_header() + * + * Freed by libavformat in avformat_free_context(). + */ + AVDictionary *metadata; + + /** + * Start time of the stream in real world time, in microseconds + * since the Unix epoch (00:00 1st January 1970). That is, pts=0 in the + * stream was captured at this real world time. + * - muxing: Set by the caller before avformat_write_header(). If set to + * either 0 or AV_NOPTS_VALUE, then the current wall-time will + * be used. + * - demuxing: Set by libavformat. AV_NOPTS_VALUE if unknown. Note that + * the value may become known after some number of frames + * have been received. + */ + int64_t start_time_realtime; + + /** + * The number of frames used for determining the framerate in + * avformat_find_stream_info(). + * Demuxing only, set by the caller before avformat_find_stream_info(). + */ + int fps_probe_size; + + /** + * Error recognition; higher values will detect more errors but may + * misdetect some more or less valid parts as errors. + * Demuxing only, set by the caller before avformat_open_input(). + */ + int error_recognition; + + /** + * Custom interrupt callbacks for the I/O layer. + * + * demuxing: set by the user before avformat_open_input(). + * muxing: set by the user before avformat_write_header() + * (mainly useful for AVFMT_NOFILE formats). The callback + * should also be passed to avio_open2() if it's used to + * open the file. + */ + AVIOInterruptCB interrupt_callback; + + /** + * Flags to enable debugging. + */ + int debug; +#define FF_FDEBUG_TS 0x0001 + + /** + * Maximum buffering duration for interleaving. + * + * To ensure all the streams are interleaved correctly, + * av_interleaved_write_frame() will wait until it has at least one packet + * for each stream before actually writing any packets to the output file. + * When some streams are "sparse" (i.e. there are large gaps between + * successive packets), this can result in excessive buffering. + * + * This field specifies the maximum difference between the timestamps of the + * first and the last packet in the muxing queue, above which libavformat + * will output a packet regardless of whether it has queued a packet for all + * the streams. + * + * Muxing only, set by the caller before avformat_write_header(). + */ + int64_t max_interleave_delta; + + /** + * Allow non-standard and experimental extension + * @see AVCodecContext.strict_std_compliance + */ + int strict_std_compliance; + + /** + * Flags for the user to detect events happening on the file. Flags must + * be cleared by the user once the event has been handled. + * A combination of AVFMT_EVENT_FLAG_*. + */ + int event_flags; +#define AVFMT_EVENT_FLAG_METADATA_UPDATED 0x0001 ///< The call resulted in updated metadata. + + /** + * Maximum number of packets to read while waiting for the first timestamp. + * Decoding only. + */ + int max_ts_probe; + + /** + * Avoid negative timestamps during muxing. + * Any value of the AVFMT_AVOID_NEG_TS_* constants. + * Note, this only works when using av_interleaved_write_frame. (interleave_packet_per_dts is in use) + * - muxing: Set by user + * - demuxing: unused + */ + int avoid_negative_ts; +#define AVFMT_AVOID_NEG_TS_AUTO -1 ///< Enabled when required by target format +#define AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE 1 ///< Shift timestamps so they are non negative +#define AVFMT_AVOID_NEG_TS_MAKE_ZERO 2 ///< Shift timestamps so that they start at 0 + + /** + * Transport stream id. + * This will be moved into demuxer private options. Thus no API/ABI compatibility + */ + int ts_id; + + /** + * Audio preload in microseconds. + * Note, not all formats support this and unpredictable things may happen if it is used when not supported. + * - encoding: Set by user + * - decoding: unused + */ + int audio_preload; + + /** + * Max chunk time in microseconds. + * Note, not all formats support this and unpredictable things may happen if it is used when not supported. + * - encoding: Set by user + * - decoding: unused + */ + int max_chunk_duration; + + /** + * Max chunk size in bytes + * Note, not all formats support this and unpredictable things may happen if it is used when not supported. + * - encoding: Set by user + * - decoding: unused + */ + int max_chunk_size; + + /** + * forces the use of wallclock timestamps as pts/dts of packets + * This has undefined results in the presence of B frames. + * - encoding: unused + * - decoding: Set by user + */ + int use_wallclock_as_timestamps; + + /** + * avio flags, used to force AVIO_FLAG_DIRECT. + * - encoding: unused + * - decoding: Set by user + */ + int avio_flags; + + /** + * The duration field can be estimated through various ways, and this field can be used + * to know how the duration was estimated. + * - encoding: unused + * - decoding: Read by user + */ + enum AVDurationEstimationMethod duration_estimation_method; + + /** + * Skip initial bytes when opening stream + * - encoding: unused + * - decoding: Set by user + */ + int64_t skip_initial_bytes; + + /** + * Correct single timestamp overflows + * - encoding: unused + * - decoding: Set by user + */ + unsigned int correct_ts_overflow; + + /** + * Force seeking to any (also non key) frames. + * - encoding: unused + * - decoding: Set by user + */ + int seek2any; + + /** + * Flush the I/O context after each packet. + * - encoding: Set by user + * - decoding: unused + */ + int flush_packets; + + /** + * format probing score. + * The maximal score is AVPROBE_SCORE_MAX, its set when the demuxer probes + * the format. + * - encoding: unused + * - decoding: set by avformat, read by user + */ + int probe_score; + + /** + * number of bytes to read maximally to identify format. + * - encoding: unused + * - decoding: set by user + */ + int format_probesize; + + /** + * ',' separated list of allowed decoders. + * If NULL then all are allowed + * - encoding: unused + * - decoding: set by user + */ + char *codec_whitelist; + + /** + * ',' separated list of allowed demuxers. + * If NULL then all are allowed + * - encoding: unused + * - decoding: set by user + */ + char *format_whitelist; + + /** + * An opaque field for libavformat internal usage. + * Must not be accessed in any way by callers. + */ + AVFormatInternal *internal; + + /** + * IO repositioned flag. + * This is set by avformat when the underlaying IO context read pointer + * is repositioned, for example when doing byte based seeking. + * Demuxers can use the flag to detect such changes. + */ + int io_repositioned; + + /** + * Forced video codec. + * This allows forcing a specific decoder, even when there are multiple with + * the same codec_id. + * Demuxing: Set by user + */ + AVCodec *video_codec; + + /** + * Forced audio codec. + * This allows forcing a specific decoder, even when there are multiple with + * the same codec_id. + * Demuxing: Set by user + */ + AVCodec *audio_codec; + + /** + * Forced subtitle codec. + * This allows forcing a specific decoder, even when there are multiple with + * the same codec_id. + * Demuxing: Set by user + */ + AVCodec *subtitle_codec; + + /** + * Forced data codec. + * This allows forcing a specific decoder, even when there are multiple with + * the same codec_id. + * Demuxing: Set by user + */ + AVCodec *data_codec; + + /** + * Number of bytes to be written as padding in a metadata header. + * Demuxing: Unused. + * Muxing: Set by user via av_format_set_metadata_header_padding. + */ + int metadata_header_padding; + + /** + * User data. + * This is a place for some private data of the user. + */ + void *opaque; + + /** + * Callback used by devices to communicate with application. + */ + av_format_control_message control_message_cb; + + /** + * Output timestamp offset, in microseconds. + * Muxing: set by user + */ + int64_t output_ts_offset; + + /** + * dump format separator. + * can be ", " or "\n " or anything else + * - muxing: Set by user. + * - demuxing: Set by user. + */ + uint8_t *dump_separator; + + /** + * Forced Data codec_id. + * Demuxing: Set by user. + */ + enum AVCodecID data_codec_id; + +#if FF_API_OLD_OPEN_CALLBACKS + /** + * Called to open further IO contexts when needed for demuxing. + * + * This can be set by the user application to perform security checks on + * the URLs before opening them. + * The function should behave like avio_open2(), AVFormatContext is provided + * as contextual information and to reach AVFormatContext.opaque. + * + * If NULL then some simple checks are used together with avio_open2(). + * + * Must not be accessed directly from outside avformat. + * @See av_format_set_open_cb() + * + * Demuxing: Set by user. + * + * @deprecated Use io_open and io_close. + */ + attribute_deprecated + int (*open_cb)(struct AVFormatContext *s, AVIOContext **p, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options); +#endif + + /** + * ',' separated list of allowed protocols. + * - encoding: unused + * - decoding: set by user + */ + char *protocol_whitelist; + + /** + * A callback for opening new IO streams. + * + * Whenever a muxer or a demuxer needs to open an IO stream (typically from + * avformat_open_input() for demuxers, but for certain formats can happen at + * other times as well), it will call this callback to obtain an IO context. + * + * @param s the format context + * @param pb on success, the newly opened IO context should be returned here + * @param url the url to open + * @param flags a combination of AVIO_FLAG_* + * @param options a dictionary of additional options, with the same + * semantics as in avio_open2() + * @return 0 on success, a negative AVERROR code on failure + * + * @note Certain muxers and demuxers do nesting, i.e. they open one or more + * additional internal format contexts. Thus the AVFormatContext pointer + * passed to this callback may be different from the one facing the caller. + * It will, however, have the same 'opaque' field. + */ + int (*io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url, + int flags, AVDictionary **options); + + /** + * A callback for closing the streams opened with AVFormatContext.io_open(). + */ + void (*io_close)(struct AVFormatContext *s, AVIOContext *pb); + + /** + * ',' separated list of disallowed protocols. + * - encoding: unused + * - decoding: set by user + */ + char *protocol_blacklist; + + /** + * The maximum number of streams. + * - encoding: unused + * - decoding: set by user + */ + int max_streams; + + /** + * Skip duration calcuation in estimate_timings_from_pts. + * - encoding: unused + * - decoding: set by user + */ + int skip_estimate_duration_from_pts; + + /** + * Maximum number of packets that can be probed + * - encoding: unused + * - decoding: set by user + */ + int max_probe_packets; +} AVFormatContext; + +#if FF_API_FORMAT_GET_SET +/** + * Accessors for some AVFormatContext fields. These used to be provided for ABI + * compatibility, and do not need to be used anymore. + */ +attribute_deprecated +int av_format_get_probe_score(const AVFormatContext *s); +attribute_deprecated +AVCodec * av_format_get_video_codec(const AVFormatContext *s); +attribute_deprecated +void av_format_set_video_codec(AVFormatContext *s, AVCodec *c); +attribute_deprecated +AVCodec * av_format_get_audio_codec(const AVFormatContext *s); +attribute_deprecated +void av_format_set_audio_codec(AVFormatContext *s, AVCodec *c); +attribute_deprecated +AVCodec * av_format_get_subtitle_codec(const AVFormatContext *s); +attribute_deprecated +void av_format_set_subtitle_codec(AVFormatContext *s, AVCodec *c); +attribute_deprecated +AVCodec * av_format_get_data_codec(const AVFormatContext *s); +attribute_deprecated +void av_format_set_data_codec(AVFormatContext *s, AVCodec *c); +attribute_deprecated +int av_format_get_metadata_header_padding(const AVFormatContext *s); +attribute_deprecated +void av_format_set_metadata_header_padding(AVFormatContext *s, int c); +attribute_deprecated +void * av_format_get_opaque(const AVFormatContext *s); +attribute_deprecated +void av_format_set_opaque(AVFormatContext *s, void *opaque); +attribute_deprecated +av_format_control_message av_format_get_control_message_cb(const AVFormatContext *s); +attribute_deprecated +void av_format_set_control_message_cb(AVFormatContext *s, av_format_control_message callback); +#if FF_API_OLD_OPEN_CALLBACKS +attribute_deprecated AVOpenCallback av_format_get_open_cb(const AVFormatContext *s); +attribute_deprecated void av_format_set_open_cb(AVFormatContext *s, AVOpenCallback callback); +#endif +#endif + +/** + * This function will cause global side data to be injected in the next packet + * of each stream as well as after any subsequent seek. + */ +void av_format_inject_global_side_data(AVFormatContext *s); + +/** + * Returns the method used to set ctx->duration. + * + * @return AVFMT_DURATION_FROM_PTS, AVFMT_DURATION_FROM_STREAM, or AVFMT_DURATION_FROM_BITRATE. + */ +enum AVDurationEstimationMethod av_fmt_ctx_get_duration_estimation_method(const AVFormatContext* ctx); + +typedef struct AVPacketList { + AVPacket pkt; + struct AVPacketList *next; +} AVPacketList; + + +/** + * @defgroup lavf_core Core functions + * @ingroup libavf + * + * Functions for querying libavformat capabilities, allocating core structures, + * etc. + * @{ + */ + +/** + * Return the LIBAVFORMAT_VERSION_INT constant. + */ +unsigned avformat_version(void); + +/** + * Return the libavformat build-time configuration. + */ +const char *avformat_configuration(void); + +/** + * Return the libavformat license. + */ +const char *avformat_license(void); + +#if FF_API_NEXT +/** + * Initialize libavformat and register all the muxers, demuxers and + * protocols. If you do not call this function, then you can select + * exactly which formats you want to support. + * + * @see av_register_input_format() + * @see av_register_output_format() + */ +attribute_deprecated +void av_register_all(void); + +attribute_deprecated +void av_register_input_format(AVInputFormat *format); +attribute_deprecated +void av_register_output_format(AVOutputFormat *format); +#endif + +/** + * Do global initialization of network libraries. This is optional, + * and not recommended anymore. + * + * This functions only exists to work around thread-safety issues + * with older GnuTLS or OpenSSL libraries. If libavformat is linked + * to newer versions of those libraries, or if you do not use them, + * calling this function is unnecessary. Otherwise, you need to call + * this function before any other threads using them are started. + * + * This function will be deprecated once support for older GnuTLS and + * OpenSSL libraries is removed, and this function has no purpose + * anymore. + */ +int avformat_network_init(void); + +/** + * Undo the initialization done by avformat_network_init. Call it only + * once for each time you called avformat_network_init. + */ +int avformat_network_deinit(void); + +#if FF_API_NEXT +/** + * If f is NULL, returns the first registered input format, + * if f is non-NULL, returns the next registered input format after f + * or NULL if f is the last one. + */ +attribute_deprecated +AVInputFormat *av_iformat_next(const AVInputFormat *f); + +/** + * If f is NULL, returns the first registered output format, + * if f is non-NULL, returns the next registered output format after f + * or NULL if f is the last one. + */ +attribute_deprecated +AVOutputFormat *av_oformat_next(const AVOutputFormat *f); +#endif + +/** + * Iterate over all registered muxers. + * + * @param opaque a pointer where libavformat will store the iteration state. Must + * point to NULL to start the iteration. + * + * @return the next registered muxer or NULL when the iteration is + * finished + */ +const AVOutputFormat *av_muxer_iterate(void **opaque); + +/** + * Iterate over all registered demuxers. + * + * @param opaque a pointer where libavformat will store the iteration state. Must + * point to NULL to start the iteration. + * + * @return the next registered demuxer or NULL when the iteration is + * finished + */ +const AVInputFormat *av_demuxer_iterate(void **opaque); + +/** + * Allocate an AVFormatContext. + * avformat_free_context() can be used to free the context and everything + * allocated by the framework within it. + */ +AVFormatContext *avformat_alloc_context(void); + +/** + * Free an AVFormatContext and all its streams. + * @param s context to free + */ +void avformat_free_context(AVFormatContext *s); + +/** + * Get the AVClass for AVFormatContext. It can be used in combination with + * AV_OPT_SEARCH_FAKE_OBJ for examining options. + * + * @see av_opt_find(). + */ +const AVClass *avformat_get_class(void); + +/** + * Add a new stream to a media file. + * + * When demuxing, it is called by the demuxer in read_header(). If the + * flag AVFMTCTX_NOHEADER is set in s.ctx_flags, then it may also + * be called in read_packet(). + * + * When muxing, should be called by the user before avformat_write_header(). + * + * User is required to call avcodec_close() and avformat_free_context() to + * clean up the allocation by avformat_new_stream(). + * + * @param s media file handle + * @param c If non-NULL, the AVCodecContext corresponding to the new stream + * will be initialized to use this codec. This is needed for e.g. codec-specific + * defaults to be set, so codec should be provided if it is known. + * + * @return newly created stream or NULL on error. + */ +AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c); + +/** + * Wrap an existing array as stream side data. + * + * @param st stream + * @param type side information type + * @param data the side data array. It must be allocated with the av_malloc() + * family of functions. The ownership of the data is transferred to + * st. + * @param size side information size + * @return zero on success, a negative AVERROR code on failure. On failure, + * the stream is unchanged and the data remains owned by the caller. + */ +int av_stream_add_side_data(AVStream *st, enum AVPacketSideDataType type, + uint8_t *data, size_t size); + +/** + * Allocate new information from stream. + * + * @param stream stream + * @param type desired side information type + * @param size side information size + * @return pointer to fresh allocated data or NULL otherwise + */ +uint8_t *av_stream_new_side_data(AVStream *stream, + enum AVPacketSideDataType type, int size); +/** + * Get side information from stream. + * + * @param stream stream + * @param type desired side information type + * @param size pointer for side information size to store (optional) + * @return pointer to data if present or NULL otherwise + */ +uint8_t *av_stream_get_side_data(const AVStream *stream, + enum AVPacketSideDataType type, int *size); + +AVProgram *av_new_program(AVFormatContext *s, int id); + +/** + * @} + */ + + +/** + * Allocate an AVFormatContext for an output format. + * avformat_free_context() can be used to free the context and + * everything allocated by the framework within it. + * + * @param *ctx is set to the created format context, or to NULL in + * case of failure + * @param oformat format to use for allocating the context, if NULL + * format_name and filename are used instead + * @param format_name the name of output format to use for allocating the + * context, if NULL filename is used instead + * @param filename the name of the filename to use for allocating the + * context, may be NULL + * @return >= 0 in case of success, a negative AVERROR code in case of + * failure + */ +int avformat_alloc_output_context2(AVFormatContext **ctx, ff_const59 AVOutputFormat *oformat, + const char *format_name, const char *filename); + +/** + * @addtogroup lavf_decoding + * @{ + */ + +/** + * Find AVInputFormat based on the short name of the input format. + */ +ff_const59 AVInputFormat *av_find_input_format(const char *short_name); + +/** + * Guess the file format. + * + * @param pd data to be probed + * @param is_opened Whether the file is already opened; determines whether + * demuxers with or without AVFMT_NOFILE are probed. + */ +ff_const59 AVInputFormat *av_probe_input_format(ff_const59 AVProbeData *pd, int is_opened); + +/** + * Guess the file format. + * + * @param pd data to be probed + * @param is_opened Whether the file is already opened; determines whether + * demuxers with or without AVFMT_NOFILE are probed. + * @param score_max A probe score larger that this is required to accept a + * detection, the variable is set to the actual detection + * score afterwards. + * If the score is <= AVPROBE_SCORE_MAX / 4 it is recommended + * to retry with a larger probe buffer. + */ +ff_const59 AVInputFormat *av_probe_input_format2(ff_const59 AVProbeData *pd, int is_opened, int *score_max); + +/** + * Guess the file format. + * + * @param is_opened Whether the file is already opened; determines whether + * demuxers with or without AVFMT_NOFILE are probed. + * @param score_ret The score of the best detection. + */ +ff_const59 AVInputFormat *av_probe_input_format3(ff_const59 AVProbeData *pd, int is_opened, int *score_ret); + +/** + * Probe a bytestream to determine the input format. Each time a probe returns + * with a score that is too low, the probe buffer size is increased and another + * attempt is made. When the maximum probe size is reached, the input format + * with the highest score is returned. + * + * @param pb the bytestream to probe + * @param fmt the input format is put here + * @param url the url of the stream + * @param logctx the log context + * @param offset the offset within the bytestream to probe from + * @param max_probe_size the maximum probe buffer size (zero for default) + * @return the score in case of success, a negative value corresponding to an + * the maximal score is AVPROBE_SCORE_MAX + * AVERROR code otherwise + */ +int av_probe_input_buffer2(AVIOContext *pb, ff_const59 AVInputFormat **fmt, + const char *url, void *logctx, + unsigned int offset, unsigned int max_probe_size); + +/** + * Like av_probe_input_buffer2() but returns 0 on success + */ +int av_probe_input_buffer(AVIOContext *pb, ff_const59 AVInputFormat **fmt, + const char *url, void *logctx, + unsigned int offset, unsigned int max_probe_size); + +/** + * Open an input stream and read the header. The codecs are not opened. + * The stream must be closed with avformat_close_input(). + * + * @param ps Pointer to user-supplied AVFormatContext (allocated by avformat_alloc_context). + * May be a pointer to NULL, in which case an AVFormatContext is allocated by this + * function and written into ps. + * Note that a user-supplied AVFormatContext will be freed on failure. + * @param url URL of the stream to open. + * @param fmt If non-NULL, this parameter forces a specific input format. + * Otherwise the format is autodetected. + * @param options A dictionary filled with AVFormatContext and demuxer-private options. + * On return this parameter will be destroyed and replaced with a dict containing + * options that were not found. May be NULL. + * + * @return 0 on success, a negative AVERROR on failure. + * + * @note If you want to use custom IO, preallocate the format context and set its pb field. + */ +int avformat_open_input(AVFormatContext **ps, const char *url, ff_const59 AVInputFormat *fmt, AVDictionary **options); + +attribute_deprecated +int av_demuxer_open(AVFormatContext *ic); + +/** + * Read packets of a media file to get stream information. This + * is useful for file formats with no headers such as MPEG. This + * function also computes the real framerate in case of MPEG-2 repeat + * frame mode. + * The logical file position is not changed by this function; + * examined packets may be buffered for later processing. + * + * @param ic media file handle + * @param options If non-NULL, an ic.nb_streams long array of pointers to + * dictionaries, where i-th member contains options for + * codec corresponding to i-th stream. + * On return each dictionary will be filled with options that were not found. + * @return >=0 if OK, AVERROR_xxx on error + * + * @note this function isn't guaranteed to open all the codecs, so + * options being non-empty at return is a perfectly normal behavior. + * + * @todo Let the user decide somehow what information is needed so that + * we do not waste time getting stuff the user does not need. + */ +int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options); + +/** + * Find the programs which belong to a given stream. + * + * @param ic media file handle + * @param last the last found program, the search will start after this + * program, or from the beginning if it is NULL + * @param s stream index + * @return the next program which belongs to s, NULL if no program is found or + * the last program is not among the programs of ic. + */ +AVProgram *av_find_program_from_stream(AVFormatContext *ic, AVProgram *last, int s); + +void av_program_add_stream_index(AVFormatContext *ac, int progid, unsigned int idx); + +/** + * Find the "best" stream in the file. + * The best stream is determined according to various heuristics as the most + * likely to be what the user expects. + * If the decoder parameter is non-NULL, av_find_best_stream will find the + * default decoder for the stream's codec; streams for which no decoder can + * be found are ignored. + * + * @param ic media file handle + * @param type stream type: video, audio, subtitles, etc. + * @param wanted_stream_nb user-requested stream number, + * or -1 for automatic selection + * @param related_stream try to find a stream related (eg. in the same + * program) to this one, or -1 if none + * @param decoder_ret if non-NULL, returns the decoder for the + * selected stream + * @param flags flags; none are currently defined + * @return the non-negative stream number in case of success, + * AVERROR_STREAM_NOT_FOUND if no stream with the requested type + * could be found, + * AVERROR_DECODER_NOT_FOUND if streams were found but no decoder + * @note If av_find_best_stream returns successfully and decoder_ret is not + * NULL, then *decoder_ret is guaranteed to be set to a valid AVCodec. + */ +int av_find_best_stream(AVFormatContext *ic, + enum AVMediaType type, + int wanted_stream_nb, + int related_stream, + AVCodec **decoder_ret, + int flags); + +/** + * Return the next frame of a stream. + * This function returns what is stored in the file, and does not validate + * that what is there are valid frames for the decoder. It will split what is + * stored in the file into frames and return one for each call. It will not + * omit invalid data between valid frames so as to give the decoder the maximum + * information possible for decoding. + * + * On success, the returned packet is reference-counted (pkt->buf is set) and + * valid indefinitely. The packet must be freed with av_packet_unref() when + * it is no longer needed. For video, the packet contains exactly one frame. + * For audio, it contains an integer number of frames if each frame has + * a known fixed size (e.g. PCM or ADPCM data). If the audio frames have + * a variable size (e.g. MPEG audio), then it contains one frame. + * + * pkt->pts, pkt->dts and pkt->duration are always set to correct + * values in AVStream.time_base units (and guessed if the format cannot + * provide them). pkt->pts can be AV_NOPTS_VALUE if the video format + * has B-frames, so it is better to rely on pkt->dts if you do not + * decompress the payload. + * + * @return 0 if OK, < 0 on error or end of file. On error, pkt will be blank + * (as if it came from av_packet_alloc()). + * + * @note pkt will be initialized, so it may be uninitialized, but it must not + * contain data that needs to be freed. + */ +int av_read_frame(AVFormatContext *s, AVPacket *pkt); + +/** + * Seek to the keyframe at timestamp. + * 'timestamp' in 'stream_index'. + * + * @param s media file handle + * @param stream_index If stream_index is (-1), a default + * stream is selected, and timestamp is automatically converted + * from AV_TIME_BASE units to the stream specific time_base. + * @param timestamp Timestamp in AVStream.time_base units + * or, if no stream is specified, in AV_TIME_BASE units. + * @param flags flags which select direction and seeking mode + * @return >= 0 on success + */ +int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, + int flags); + +/** + * Seek to timestamp ts. + * Seeking will be done so that the point from which all active streams + * can be presented successfully will be closest to ts and within min/max_ts. + * Active streams are all streams that have AVStream.discard < AVDISCARD_ALL. + * + * If flags contain AVSEEK_FLAG_BYTE, then all timestamps are in bytes and + * are the file position (this may not be supported by all demuxers). + * If flags contain AVSEEK_FLAG_FRAME, then all timestamps are in frames + * in the stream with stream_index (this may not be supported by all demuxers). + * Otherwise all timestamps are in units of the stream selected by stream_index + * or if stream_index is -1, in AV_TIME_BASE units. + * If flags contain AVSEEK_FLAG_ANY, then non-keyframes are treated as + * keyframes (this may not be supported by all demuxers). + * If flags contain AVSEEK_FLAG_BACKWARD, it is ignored. + * + * @param s media file handle + * @param stream_index index of the stream which is used as time base reference + * @param min_ts smallest acceptable timestamp + * @param ts target timestamp + * @param max_ts largest acceptable timestamp + * @param flags flags + * @return >=0 on success, error code otherwise + * + * @note This is part of the new seek API which is still under construction. + */ +int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags); + +/** + * Discard all internally buffered data. This can be useful when dealing with + * discontinuities in the byte stream. Generally works only with formats that + * can resync. This includes headerless formats like MPEG-TS/TS but should also + * work with NUT, Ogg and in a limited way AVI for example. + * + * The set of streams, the detected duration, stream parameters and codecs do + * not change when calling this function. If you want a complete reset, it's + * better to open a new AVFormatContext. + * + * This does not flush the AVIOContext (s->pb). If necessary, call + * avio_flush(s->pb) before calling this function. + * + * @param s media file handle + * @return >=0 on success, error code otherwise + */ +int avformat_flush(AVFormatContext *s); + +/** + * Start playing a network-based stream (e.g. RTSP stream) at the + * current position. + */ +int av_read_play(AVFormatContext *s); + +/** + * Pause a network-based stream (e.g. RTSP stream). + * + * Use av_read_play() to resume it. + */ +int av_read_pause(AVFormatContext *s); + +/** + * Close an opened input AVFormatContext. Free it and all its contents + * and set *s to NULL. + */ +void avformat_close_input(AVFormatContext **s); +/** + * @} + */ + +#define AVSEEK_FLAG_BACKWARD 1 ///< seek backward +#define AVSEEK_FLAG_BYTE 2 ///< seeking based on position in bytes +#define AVSEEK_FLAG_ANY 4 ///< seek to any frame, even non-keyframes +#define AVSEEK_FLAG_FRAME 8 ///< seeking based on frame number + +/** + * @addtogroup lavf_encoding + * @{ + */ + +#define AVSTREAM_INIT_IN_WRITE_HEADER 0 ///< stream parameters initialized in avformat_write_header +#define AVSTREAM_INIT_IN_INIT_OUTPUT 1 ///< stream parameters initialized in avformat_init_output + +/** + * Allocate the stream private data and write the stream header to + * an output media file. + * + * @param s Media file handle, must be allocated with avformat_alloc_context(). + * Its oformat field must be set to the desired output format; + * Its pb field must be set to an already opened AVIOContext. + * @param options An AVDictionary filled with AVFormatContext and muxer-private options. + * On return this parameter will be destroyed and replaced with a dict containing + * options that were not found. May be NULL. + * + * @return AVSTREAM_INIT_IN_WRITE_HEADER on success if the codec had not already been fully initialized in avformat_init, + * AVSTREAM_INIT_IN_INIT_OUTPUT on success if the codec had already been fully initialized in avformat_init, + * negative AVERROR on failure. + * + * @see av_opt_find, av_dict_set, avio_open, av_oformat_next, avformat_init_output. + */ +av_warn_unused_result +int avformat_write_header(AVFormatContext *s, AVDictionary **options); + +/** + * Allocate the stream private data and initialize the codec, but do not write the header. + * May optionally be used before avformat_write_header to initialize stream parameters + * before actually writing the header. + * If using this function, do not pass the same options to avformat_write_header. + * + * @param s Media file handle, must be allocated with avformat_alloc_context(). + * Its oformat field must be set to the desired output format; + * Its pb field must be set to an already opened AVIOContext. + * @param options An AVDictionary filled with AVFormatContext and muxer-private options. + * On return this parameter will be destroyed and replaced with a dict containing + * options that were not found. May be NULL. + * + * @return AVSTREAM_INIT_IN_WRITE_HEADER on success if the codec requires avformat_write_header to fully initialize, + * AVSTREAM_INIT_IN_INIT_OUTPUT on success if the codec has been fully initialized, + * negative AVERROR on failure. + * + * @see av_opt_find, av_dict_set, avio_open, av_oformat_next, avformat_write_header. + */ +av_warn_unused_result +int avformat_init_output(AVFormatContext *s, AVDictionary **options); + +/** + * Write a packet to an output media file. + * + * This function passes the packet directly to the muxer, without any buffering + * or reordering. The caller is responsible for correctly interleaving the + * packets if the format requires it. Callers that want libavformat to handle + * the interleaving should call av_interleaved_write_frame() instead of this + * function. + * + * @param s media file handle + * @param pkt The packet containing the data to be written. Note that unlike + * av_interleaved_write_frame(), this function does not take + * ownership of the packet passed to it (though some muxers may make + * an internal reference to the input packet). + *
    + * This parameter can be NULL (at any time, not just at the end), in + * order to immediately flush data buffered within the muxer, for + * muxers that buffer up data internally before writing it to the + * output. + *
    + * Packet's @ref AVPacket.stream_index "stream_index" field must be + * set to the index of the corresponding stream in @ref + * AVFormatContext.streams "s->streams". + *
    + * The timestamps (@ref AVPacket.pts "pts", @ref AVPacket.dts "dts") + * must be set to correct values in the stream's timebase (unless the + * output format is flagged with the AVFMT_NOTIMESTAMPS flag, then + * they can be set to AV_NOPTS_VALUE). + * The dts for subsequent packets passed to this function must be strictly + * increasing when compared in their respective timebases (unless the + * output format is flagged with the AVFMT_TS_NONSTRICT, then they + * merely have to be nondecreasing). @ref AVPacket.duration + * "duration") should also be set if known. + * @return < 0 on error, = 0 if OK, 1 if flushed and there is no more data to flush + * + * @see av_interleaved_write_frame() + */ +int av_write_frame(AVFormatContext *s, AVPacket *pkt); + +/** + * Write a packet to an output media file ensuring correct interleaving. + * + * This function will buffer the packets internally as needed to make sure the + * packets in the output file are properly interleaved in the order of + * increasing dts. Callers doing their own interleaving should call + * av_write_frame() instead of this function. + * + * Using this function instead of av_write_frame() can give muxers advance + * knowledge of future packets, improving e.g. the behaviour of the mp4 + * muxer for VFR content in fragmenting mode. + * + * @param s media file handle + * @param pkt The packet containing the data to be written. + *
    + * If the packet is reference-counted, this function will take + * ownership of this reference and unreference it later when it sees + * fit. + * The caller must not access the data through this reference after + * this function returns. If the packet is not reference-counted, + * libavformat will make a copy. + *
    + * This parameter can be NULL (at any time, not just at the end), to + * flush the interleaving queues. + *
    + * Packet's @ref AVPacket.stream_index "stream_index" field must be + * set to the index of the corresponding stream in @ref + * AVFormatContext.streams "s->streams". + *
    + * The timestamps (@ref AVPacket.pts "pts", @ref AVPacket.dts "dts") + * must be set to correct values in the stream's timebase (unless the + * output format is flagged with the AVFMT_NOTIMESTAMPS flag, then + * they can be set to AV_NOPTS_VALUE). + * The dts for subsequent packets in one stream must be strictly + * increasing (unless the output format is flagged with the + * AVFMT_TS_NONSTRICT, then they merely have to be nondecreasing). + * @ref AVPacket.duration "duration") should also be set if known. + * + * @return 0 on success, a negative AVERROR on error. Libavformat will always + * take care of freeing the packet, even if this function fails. + * + * @see av_write_frame(), AVFormatContext.max_interleave_delta + */ +int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt); + +/** + * Write an uncoded frame to an output media file. + * + * The frame must be correctly interleaved according to the container + * specification; if not, av_interleaved_write_uncoded_frame() must be used. + * + * See av_interleaved_write_uncoded_frame() for details. + */ +int av_write_uncoded_frame(AVFormatContext *s, int stream_index, + AVFrame *frame); + +/** + * Write an uncoded frame to an output media file. + * + * If the muxer supports it, this function makes it possible to write an AVFrame + * structure directly, without encoding it into a packet. + * It is mostly useful for devices and similar special muxers that use raw + * video or PCM data and will not serialize it into a byte stream. + * + * To test whether it is possible to use it with a given muxer and stream, + * use av_write_uncoded_frame_query(). + * + * The caller gives up ownership of the frame and must not access it + * afterwards. + * + * @return >=0 for success, a negative code on error + */ +int av_interleaved_write_uncoded_frame(AVFormatContext *s, int stream_index, + AVFrame *frame); + +/** + * Test whether a muxer supports uncoded frame. + * + * @return >=0 if an uncoded frame can be written to that muxer and stream, + * <0 if not + */ +int av_write_uncoded_frame_query(AVFormatContext *s, int stream_index); + +/** + * Write the stream trailer to an output media file and free the + * file private data. + * + * May only be called after a successful call to avformat_write_header. + * + * @param s media file handle + * @return 0 if OK, AVERROR_xxx on error + */ +int av_write_trailer(AVFormatContext *s); + +/** + * Return the output format in the list of registered output formats + * which best matches the provided parameters, or return NULL if + * there is no match. + * + * @param short_name if non-NULL checks if short_name matches with the + * names of the registered formats + * @param filename if non-NULL checks if filename terminates with the + * extensions of the registered formats + * @param mime_type if non-NULL checks if mime_type matches with the + * MIME type of the registered formats + */ +ff_const59 AVOutputFormat *av_guess_format(const char *short_name, + const char *filename, + const char *mime_type); + +/** + * Guess the codec ID based upon muxer and filename. + */ +enum AVCodecID av_guess_codec(ff_const59 AVOutputFormat *fmt, const char *short_name, + const char *filename, const char *mime_type, + enum AVMediaType type); + +/** + * Get timing information for the data currently output. + * The exact meaning of "currently output" depends on the format. + * It is mostly relevant for devices that have an internal buffer and/or + * work in real time. + * @param s media file handle + * @param stream stream in the media file + * @param[out] dts DTS of the last packet output for the stream, in stream + * time_base units + * @param[out] wall absolute time when that packet whas output, + * in microsecond + * @return 0 if OK, AVERROR(ENOSYS) if the format does not support it + * Note: some formats or devices may not allow to measure dts and wall + * atomically. + */ +int av_get_output_timestamp(struct AVFormatContext *s, int stream, + int64_t *dts, int64_t *wall); + + +/** + * @} + */ + + +/** + * @defgroup lavf_misc Utility functions + * @ingroup libavf + * @{ + * + * Miscellaneous utility functions related to both muxing and demuxing + * (or neither). + */ + +/** + * Send a nice hexadecimal dump of a buffer to the specified file stream. + * + * @param f The file stream pointer where the dump should be sent to. + * @param buf buffer + * @param size buffer size + * + * @see av_hex_dump_log, av_pkt_dump2, av_pkt_dump_log2 + */ +void av_hex_dump(FILE *f, const uint8_t *buf, int size); + +/** + * Send a nice hexadecimal dump of a buffer to the log. + * + * @param avcl A pointer to an arbitrary struct of which the first field is a + * pointer to an AVClass struct. + * @param level The importance level of the message, lower values signifying + * higher importance. + * @param buf buffer + * @param size buffer size + * + * @see av_hex_dump, av_pkt_dump2, av_pkt_dump_log2 + */ +void av_hex_dump_log(void *avcl, int level, const uint8_t *buf, int size); + +/** + * Send a nice dump of a packet to the specified file stream. + * + * @param f The file stream pointer where the dump should be sent to. + * @param pkt packet to dump + * @param dump_payload True if the payload must be displayed, too. + * @param st AVStream that the packet belongs to + */ +void av_pkt_dump2(FILE *f, const AVPacket *pkt, int dump_payload, const AVStream *st); + + +/** + * Send a nice dump of a packet to the log. + * + * @param avcl A pointer to an arbitrary struct of which the first field is a + * pointer to an AVClass struct. + * @param level The importance level of the message, lower values signifying + * higher importance. + * @param pkt packet to dump + * @param dump_payload True if the payload must be displayed, too. + * @param st AVStream that the packet belongs to + */ +void av_pkt_dump_log2(void *avcl, int level, const AVPacket *pkt, int dump_payload, + const AVStream *st); + +/** + * Get the AVCodecID for the given codec tag tag. + * If no codec id is found returns AV_CODEC_ID_NONE. + * + * @param tags list of supported codec_id-codec_tag pairs, as stored + * in AVInputFormat.codec_tag and AVOutputFormat.codec_tag + * @param tag codec tag to match to a codec ID + */ +enum AVCodecID av_codec_get_id(const struct AVCodecTag * const *tags, unsigned int tag); + +/** + * Get the codec tag for the given codec id id. + * If no codec tag is found returns 0. + * + * @param tags list of supported codec_id-codec_tag pairs, as stored + * in AVInputFormat.codec_tag and AVOutputFormat.codec_tag + * @param id codec ID to match to a codec tag + */ +unsigned int av_codec_get_tag(const struct AVCodecTag * const *tags, enum AVCodecID id); + +/** + * Get the codec tag for the given codec id. + * + * @param tags list of supported codec_id - codec_tag pairs, as stored + * in AVInputFormat.codec_tag and AVOutputFormat.codec_tag + * @param id codec id that should be searched for in the list + * @param tag A pointer to the found tag + * @return 0 if id was not found in tags, > 0 if it was found + */ +int av_codec_get_tag2(const struct AVCodecTag * const *tags, enum AVCodecID id, + unsigned int *tag); + +int av_find_default_stream_index(AVFormatContext *s); + +/** + * Get the index for a specific timestamp. + * + * @param st stream that the timestamp belongs to + * @param timestamp timestamp to retrieve the index for + * @param flags if AVSEEK_FLAG_BACKWARD then the returned index will correspond + * to the timestamp which is <= the requested one, if backward + * is 0, then it will be >= + * if AVSEEK_FLAG_ANY seek to any frame, only keyframes otherwise + * @return < 0 if no such timestamp could be found + */ +int av_index_search_timestamp(AVStream *st, int64_t timestamp, int flags); + +/** + * Add an index entry into a sorted list. Update the entry if the list + * already contains it. + * + * @param timestamp timestamp in the time base of the given stream + */ +int av_add_index_entry(AVStream *st, int64_t pos, int64_t timestamp, + int size, int distance, int flags); + + +/** + * Split a URL string into components. + * + * The pointers to buffers for storing individual components may be null, + * in order to ignore that component. Buffers for components not found are + * set to empty strings. If the port is not found, it is set to a negative + * value. + * + * @param proto the buffer for the protocol + * @param proto_size the size of the proto buffer + * @param authorization the buffer for the authorization + * @param authorization_size the size of the authorization buffer + * @param hostname the buffer for the host name + * @param hostname_size the size of the hostname buffer + * @param port_ptr a pointer to store the port number in + * @param path the buffer for the path + * @param path_size the size of the path buffer + * @param url the URL to split + */ +void av_url_split(char *proto, int proto_size, + char *authorization, int authorization_size, + char *hostname, int hostname_size, + int *port_ptr, + char *path, int path_size, + const char *url); + + +/** + * Print detailed information about the input or output format, such as + * duration, bitrate, streams, container, programs, metadata, side data, + * codec and time base. + * + * @param ic the context to analyze + * @param index index of the stream to dump information about + * @param url the URL to print, such as source or destination file + * @param is_output Select whether the specified context is an input(0) or output(1) + */ +void av_dump_format(AVFormatContext *ic, + int index, + const char *url, + int is_output); + + +#define AV_FRAME_FILENAME_FLAGS_MULTIPLE 1 ///< Allow multiple %d + +/** + * Return in 'buf' the path with '%d' replaced by a number. + * + * Also handles the '%0nd' format where 'n' is the total number + * of digits and '%%'. + * + * @param buf destination buffer + * @param buf_size destination buffer size + * @param path numbered sequence string + * @param number frame number + * @param flags AV_FRAME_FILENAME_FLAGS_* + * @return 0 if OK, -1 on format error + */ +int av_get_frame_filename2(char *buf, int buf_size, + const char *path, int number, int flags); + +int av_get_frame_filename(char *buf, int buf_size, + const char *path, int number); + +/** + * Check whether filename actually is a numbered sequence generator. + * + * @param filename possible numbered sequence string + * @return 1 if a valid numbered sequence string, 0 otherwise + */ +int av_filename_number_test(const char *filename); + +/** + * Generate an SDP for an RTP session. + * + * Note, this overwrites the id values of AVStreams in the muxer contexts + * for getting unique dynamic payload types. + * + * @param ac array of AVFormatContexts describing the RTP streams. If the + * array is composed by only one context, such context can contain + * multiple AVStreams (one AVStream per RTP stream). Otherwise, + * all the contexts in the array (an AVCodecContext per RTP stream) + * must contain only one AVStream. + * @param n_files number of AVCodecContexts contained in ac + * @param buf buffer where the SDP will be stored (must be allocated by + * the caller) + * @param size the size of the buffer + * @return 0 if OK, AVERROR_xxx on error + */ +int av_sdp_create(AVFormatContext *ac[], int n_files, char *buf, int size); + +/** + * Return a positive value if the given filename has one of the given + * extensions, 0 otherwise. + * + * @param filename file name to check against the given extensions + * @param extensions a comma-separated list of filename extensions + */ +int av_match_ext(const char *filename, const char *extensions); + +/** + * Test if the given container can store a codec. + * + * @param ofmt container to check for compatibility + * @param codec_id codec to potentially store in container + * @param std_compliance standards compliance level, one of FF_COMPLIANCE_* + * + * @return 1 if codec with ID codec_id can be stored in ofmt, 0 if it cannot. + * A negative number if this information is not available. + */ +int avformat_query_codec(const AVOutputFormat *ofmt, enum AVCodecID codec_id, + int std_compliance); + +/** + * @defgroup riff_fourcc RIFF FourCCs + * @{ + * Get the tables mapping RIFF FourCCs to libavcodec AVCodecIDs. The tables are + * meant to be passed to av_codec_get_id()/av_codec_get_tag() as in the + * following code: + * @code + * uint32_t tag = MKTAG('H', '2', '6', '4'); + * const struct AVCodecTag *table[] = { avformat_get_riff_video_tags(), 0 }; + * enum AVCodecID id = av_codec_get_id(table, tag); + * @endcode + */ +/** + * @return the table mapping RIFF FourCCs for video to libavcodec AVCodecID. + */ +const struct AVCodecTag *avformat_get_riff_video_tags(void); +/** + * @return the table mapping RIFF FourCCs for audio to AVCodecID. + */ +const struct AVCodecTag *avformat_get_riff_audio_tags(void); +/** + * @return the table mapping MOV FourCCs for video to libavcodec AVCodecID. + */ +const struct AVCodecTag *avformat_get_mov_video_tags(void); +/** + * @return the table mapping MOV FourCCs for audio to AVCodecID. + */ +const struct AVCodecTag *avformat_get_mov_audio_tags(void); + +/** + * @} + */ + +/** + * Guess the sample aspect ratio of a frame, based on both the stream and the + * frame aspect ratio. + * + * Since the frame aspect ratio is set by the codec but the stream aspect ratio + * is set by the demuxer, these two may not be equal. This function tries to + * return the value that you should use if you would like to display the frame. + * + * Basic logic is to use the stream aspect ratio if it is set to something sane + * otherwise use the frame aspect ratio. This way a container setting, which is + * usually easy to modify can override the coded value in the frames. + * + * @param format the format context which the stream is part of + * @param stream the stream which the frame is part of + * @param frame the frame with the aspect ratio to be determined + * @return the guessed (valid) sample_aspect_ratio, 0/1 if no idea + */ +AVRational av_guess_sample_aspect_ratio(AVFormatContext *format, AVStream *stream, AVFrame *frame); + +/** + * Guess the frame rate, based on both the container and codec information. + * + * @param ctx the format context which the stream is part of + * @param stream the stream which the frame is part of + * @param frame the frame for which the frame rate should be determined, may be NULL + * @return the guessed (valid) frame rate, 0/1 if no idea + */ +AVRational av_guess_frame_rate(AVFormatContext *ctx, AVStream *stream, AVFrame *frame); + +/** + * Check if the stream st contained in s is matched by the stream specifier + * spec. + * + * See the "stream specifiers" chapter in the documentation for the syntax + * of spec. + * + * @return >0 if st is matched by spec; + * 0 if st is not matched by spec; + * AVERROR code if spec is invalid + * + * @note A stream specifier can match several streams in the format. + */ +int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st, + const char *spec); + +int avformat_queue_attached_pictures(AVFormatContext *s); + +#if FF_API_OLD_BSF +/** + * Apply a list of bitstream filters to a packet. + * + * @param codec AVCodecContext, usually from an AVStream + * @param pkt the packet to apply filters to. If, on success, the returned + * packet has size == 0 and side_data_elems == 0, it indicates that + * the packet should be dropped + * @param bsfc a NULL-terminated list of filters to apply + * @return >=0 on success; + * AVERROR code on failure + */ +attribute_deprecated +int av_apply_bitstream_filters(AVCodecContext *codec, AVPacket *pkt, + AVBitStreamFilterContext *bsfc); +#endif + +enum AVTimebaseSource { + AVFMT_TBCF_AUTO = -1, + AVFMT_TBCF_DECODER, + AVFMT_TBCF_DEMUXER, +#if FF_API_R_FRAME_RATE + AVFMT_TBCF_R_FRAMERATE, +#endif +}; + +/** + * Transfer internal timing information from one stream to another. + * + * This function is useful when doing stream copy. + * + * @param ofmt target output format for ost + * @param ost output stream which needs timings copy and adjustments + * @param ist reference input stream to copy timings from + * @param copy_tb define from where the stream codec timebase needs to be imported + */ +int avformat_transfer_internal_stream_timing_info(const AVOutputFormat *ofmt, + AVStream *ost, const AVStream *ist, + enum AVTimebaseSource copy_tb); + +/** + * Get the internal codec timebase from a stream. + * + * @param st input stream to extract the timebase from + */ +AVRational av_stream_get_codec_timebase(const AVStream *st); + +/** + * @} + */ + +#endif /* AVFORMAT_AVFORMAT_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavformat/avio.h b/ThirdParty/ffmpeg/arm/include/libavformat/avio.h new file mode 100644 index 000000000..d022820a6 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavformat/avio.h @@ -0,0 +1,888 @@ +/* + * copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef AVFORMAT_AVIO_H +#define AVFORMAT_AVIO_H + +/** + * @file + * @ingroup lavf_io + * Buffered I/O operations + */ + +#include + +#include "libavutil/common.h" +#include "libavutil/dict.h" +#include "libavutil/log.h" + +#include "libavformat/version.h" + +/** + * Seeking works like for a local file. + */ +#define AVIO_SEEKABLE_NORMAL (1 << 0) + +/** + * Seeking by timestamp with avio_seek_time() is possible. + */ +#define AVIO_SEEKABLE_TIME (1 << 1) + +/** + * Callback for checking whether to abort blocking functions. + * AVERROR_EXIT is returned in this case by the interrupted + * function. During blocking operations, callback is called with + * opaque as parameter. If the callback returns 1, the + * blocking operation will be aborted. + * + * No members can be added to this struct without a major bump, if + * new elements have been added after this struct in AVFormatContext + * or AVIOContext. + */ +typedef struct AVIOInterruptCB { + int (*callback)(void*); + void *opaque; +} AVIOInterruptCB; + +/** + * Directory entry types. + */ +enum AVIODirEntryType { + AVIO_ENTRY_UNKNOWN, + AVIO_ENTRY_BLOCK_DEVICE, + AVIO_ENTRY_CHARACTER_DEVICE, + AVIO_ENTRY_DIRECTORY, + AVIO_ENTRY_NAMED_PIPE, + AVIO_ENTRY_SYMBOLIC_LINK, + AVIO_ENTRY_SOCKET, + AVIO_ENTRY_FILE, + AVIO_ENTRY_SERVER, + AVIO_ENTRY_SHARE, + AVIO_ENTRY_WORKGROUP, +}; + +/** + * Describes single entry of the directory. + * + * Only name and type fields are guaranteed be set. + * Rest of fields are protocol or/and platform dependent and might be unknown. + */ +typedef struct AVIODirEntry { + char *name; /**< Filename */ + int type; /**< Type of the entry */ + int utf8; /**< Set to 1 when name is encoded with UTF-8, 0 otherwise. + Name can be encoded with UTF-8 even though 0 is set. */ + int64_t size; /**< File size in bytes, -1 if unknown. */ + int64_t modification_timestamp; /**< Time of last modification in microseconds since unix + epoch, -1 if unknown. */ + int64_t access_timestamp; /**< Time of last access in microseconds since unix epoch, + -1 if unknown. */ + int64_t status_change_timestamp; /**< Time of last status change in microseconds since unix + epoch, -1 if unknown. */ + int64_t user_id; /**< User ID of owner, -1 if unknown. */ + int64_t group_id; /**< Group ID of owner, -1 if unknown. */ + int64_t filemode; /**< Unix file mode, -1 if unknown. */ +} AVIODirEntry; + +typedef struct AVIODirContext { + struct URLContext *url_context; +} AVIODirContext; + +/** + * Different data types that can be returned via the AVIO + * write_data_type callback. + */ +enum AVIODataMarkerType { + /** + * Header data; this needs to be present for the stream to be decodeable. + */ + AVIO_DATA_MARKER_HEADER, + /** + * A point in the output bytestream where a decoder can start decoding + * (i.e. a keyframe). A demuxer/decoder given the data flagged with + * AVIO_DATA_MARKER_HEADER, followed by any AVIO_DATA_MARKER_SYNC_POINT, + * should give decodeable results. + */ + AVIO_DATA_MARKER_SYNC_POINT, + /** + * A point in the output bytestream where a demuxer can start parsing + * (for non self synchronizing bytestream formats). That is, any + * non-keyframe packet start point. + */ + AVIO_DATA_MARKER_BOUNDARY_POINT, + /** + * This is any, unlabelled data. It can either be a muxer not marking + * any positions at all, it can be an actual boundary/sync point + * that the muxer chooses not to mark, or a later part of a packet/fragment + * that is cut into multiple write callbacks due to limited IO buffer size. + */ + AVIO_DATA_MARKER_UNKNOWN, + /** + * Trailer data, which doesn't contain actual content, but only for + * finalizing the output file. + */ + AVIO_DATA_MARKER_TRAILER, + /** + * A point in the output bytestream where the underlying AVIOContext might + * flush the buffer depending on latency or buffering requirements. Typically + * means the end of a packet. + */ + AVIO_DATA_MARKER_FLUSH_POINT, +}; + +/** + * Bytestream IO Context. + * New fields can be added to the end with minor version bumps. + * Removal, reordering and changes to existing fields require a major + * version bump. + * sizeof(AVIOContext) must not be used outside libav*. + * + * @note None of the function pointers in AVIOContext should be called + * directly, they should only be set by the client application + * when implementing custom I/O. Normally these are set to the + * function pointers specified in avio_alloc_context() + */ +typedef struct AVIOContext { + /** + * A class for private options. + * + * If this AVIOContext is created by avio_open2(), av_class is set and + * passes the options down to protocols. + * + * If this AVIOContext is manually allocated, then av_class may be set by + * the caller. + * + * warning -- this field can be NULL, be sure to not pass this AVIOContext + * to any av_opt_* functions in that case. + */ + const AVClass *av_class; + + /* + * The following shows the relationship between buffer, buf_ptr, + * buf_ptr_max, buf_end, buf_size, and pos, when reading and when writing + * (since AVIOContext is used for both): + * + ********************************************************************************** + * READING + ********************************************************************************** + * + * | buffer_size | + * |---------------------------------------| + * | | + * + * buffer buf_ptr buf_end + * +---------------+-----------------------+ + * |/ / / / / / / /|/ / / / / / /| | + * read buffer: |/ / consumed / | to be read /| | + * |/ / / / / / / /|/ / / / / / /| | + * +---------------+-----------------------+ + * + * pos + * +-------------------------------------------+-----------------+ + * input file: | | | + * +-------------------------------------------+-----------------+ + * + * + ********************************************************************************** + * WRITING + ********************************************************************************** + * + * | buffer_size | + * |--------------------------------------| + * | | + * + * buf_ptr_max + * buffer (buf_ptr) buf_end + * +-----------------------+--------------+ + * |/ / / / / / / / / / / /| | + * write buffer: | / / to be flushed / / | | + * |/ / / / / / / / / / / /| | + * +-----------------------+--------------+ + * buf_ptr can be in this + * due to a backward seek + * + * pos + * +-------------+----------------------------------------------+ + * output file: | | | + * +-------------+----------------------------------------------+ + * + */ + unsigned char *buffer; /**< Start of the buffer. */ + int buffer_size; /**< Maximum buffer size */ + unsigned char *buf_ptr; /**< Current position in the buffer */ + unsigned char *buf_end; /**< End of the data, may be less than + buffer+buffer_size if the read function returned + less data than requested, e.g. for streams where + no more data has been received yet. */ + void *opaque; /**< A private pointer, passed to the read/write/seek/... + functions. */ + int (*read_packet)(void *opaque, uint8_t *buf, int buf_size); + int (*write_packet)(void *opaque, uint8_t *buf, int buf_size); + int64_t (*seek)(void *opaque, int64_t offset, int whence); + int64_t pos; /**< position in the file of the current buffer */ + int eof_reached; /**< true if was unable to read due to error or eof */ + int write_flag; /**< true if open for writing */ + int max_packet_size; + unsigned long checksum; + unsigned char *checksum_ptr; + unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size); + int error; /**< contains the error code or 0 if no error happened */ + /** + * Pause or resume playback for network streaming protocols - e.g. MMS. + */ + int (*read_pause)(void *opaque, int pause); + /** + * Seek to a given timestamp in stream with the specified stream_index. + * Needed for some network streaming protocols which don't support seeking + * to byte position. + */ + int64_t (*read_seek)(void *opaque, int stream_index, + int64_t timestamp, int flags); + /** + * A combination of AVIO_SEEKABLE_ flags or 0 when the stream is not seekable. + */ + int seekable; + + /** + * max filesize, used to limit allocations + * This field is internal to libavformat and access from outside is not allowed. + */ + int64_t maxsize; + + /** + * avio_read and avio_write should if possible be satisfied directly + * instead of going through a buffer, and avio_seek will always + * call the underlying seek function directly. + */ + int direct; + + /** + * Bytes read statistic + * This field is internal to libavformat and access from outside is not allowed. + */ + int64_t bytes_read; + + /** + * seek statistic + * This field is internal to libavformat and access from outside is not allowed. + */ + int seek_count; + + /** + * writeout statistic + * This field is internal to libavformat and access from outside is not allowed. + */ + int writeout_count; + + /** + * Original buffer size + * used internally after probing and ensure seekback to reset the buffer size + * This field is internal to libavformat and access from outside is not allowed. + */ + int orig_buffer_size; + + /** + * Threshold to favor readahead over seek. + * This is current internal only, do not use from outside. + */ + int short_seek_threshold; + + /** + * ',' separated list of allowed protocols. + */ + const char *protocol_whitelist; + + /** + * ',' separated list of disallowed protocols. + */ + const char *protocol_blacklist; + + /** + * A callback that is used instead of write_packet. + */ + int (*write_data_type)(void *opaque, uint8_t *buf, int buf_size, + enum AVIODataMarkerType type, int64_t time); + /** + * If set, don't call write_data_type separately for AVIO_DATA_MARKER_BOUNDARY_POINT, + * but ignore them and treat them as AVIO_DATA_MARKER_UNKNOWN (to avoid needlessly + * small chunks of data returned from the callback). + */ + int ignore_boundary_point; + + /** + * Internal, not meant to be used from outside of AVIOContext. + */ + enum AVIODataMarkerType current_type; + int64_t last_time; + + /** + * A callback that is used instead of short_seek_threshold. + * This is current internal only, do not use from outside. + */ + int (*short_seek_get)(void *opaque); + + int64_t written; + + /** + * Maximum reached position before a backward seek in the write buffer, + * used keeping track of already written data for a later flush. + */ + unsigned char *buf_ptr_max; + + /** + * Try to buffer at least this amount of data before flushing it + */ + int min_packet_size; +} AVIOContext; + +/** + * Return the name of the protocol that will handle the passed URL. + * + * NULL is returned if no protocol could be found for the given URL. + * + * @return Name of the protocol or NULL. + */ +const char *avio_find_protocol_name(const char *url); + +/** + * Return AVIO_FLAG_* access flags corresponding to the access permissions + * of the resource in url, or a negative value corresponding to an + * AVERROR code in case of failure. The returned access flags are + * masked by the value in flags. + * + * @note This function is intrinsically unsafe, in the sense that the + * checked resource may change its existence or permission status from + * one call to another. Thus you should not trust the returned value, + * unless you are sure that no other processes are accessing the + * checked resource. + */ +int avio_check(const char *url, int flags); + +/** + * Move or rename a resource. + * + * @note url_src and url_dst should share the same protocol and authority. + * + * @param url_src url to resource to be moved + * @param url_dst new url to resource if the operation succeeded + * @return >=0 on success or negative on error. + */ +int avpriv_io_move(const char *url_src, const char *url_dst); + +/** + * Delete a resource. + * + * @param url resource to be deleted. + * @return >=0 on success or negative on error. + */ +int avpriv_io_delete(const char *url); + +/** + * Open directory for reading. + * + * @param s directory read context. Pointer to a NULL pointer must be passed. + * @param url directory to be listed. + * @param options A dictionary filled with protocol-private options. On return + * this parameter will be destroyed and replaced with a dictionary + * containing options that were not found. May be NULL. + * @return >=0 on success or negative on error. + */ +int avio_open_dir(AVIODirContext **s, const char *url, AVDictionary **options); + +/** + * Get next directory entry. + * + * Returned entry must be freed with avio_free_directory_entry(). In particular + * it may outlive AVIODirContext. + * + * @param s directory read context. + * @param[out] next next entry or NULL when no more entries. + * @return >=0 on success or negative on error. End of list is not considered an + * error. + */ +int avio_read_dir(AVIODirContext *s, AVIODirEntry **next); + +/** + * Close directory. + * + * @note Entries created using avio_read_dir() are not deleted and must be + * freeded with avio_free_directory_entry(). + * + * @param s directory read context. + * @return >=0 on success or negative on error. + */ +int avio_close_dir(AVIODirContext **s); + +/** + * Free entry allocated by avio_read_dir(). + * + * @param entry entry to be freed. + */ +void avio_free_directory_entry(AVIODirEntry **entry); + +/** + * Allocate and initialize an AVIOContext for buffered I/O. It must be later + * freed with avio_context_free(). + * + * @param buffer Memory block for input/output operations via AVIOContext. + * The buffer must be allocated with av_malloc() and friends. + * It may be freed and replaced with a new buffer by libavformat. + * AVIOContext.buffer holds the buffer currently in use, + * which must be later freed with av_free(). + * @param buffer_size The buffer size is very important for performance. + * For protocols with fixed blocksize it should be set to this blocksize. + * For others a typical size is a cache page, e.g. 4kb. + * @param write_flag Set to 1 if the buffer should be writable, 0 otherwise. + * @param opaque An opaque pointer to user-specific data. + * @param read_packet A function for refilling the buffer, may be NULL. + * For stream protocols, must never return 0 but rather + * a proper AVERROR code. + * @param write_packet A function for writing the buffer contents, may be NULL. + * The function may not change the input buffers content. + * @param seek A function for seeking to specified byte position, may be NULL. + * + * @return Allocated AVIOContext or NULL on failure. + */ +AVIOContext *avio_alloc_context( + unsigned char *buffer, + int buffer_size, + int write_flag, + void *opaque, + int (*read_packet)(void *opaque, uint8_t *buf, int buf_size), + int (*write_packet)(void *opaque, uint8_t *buf, int buf_size), + int64_t (*seek)(void *opaque, int64_t offset, int whence)); + +/** + * Free the supplied IO context and everything associated with it. + * + * @param s Double pointer to the IO context. This function will write NULL + * into s. + */ +void avio_context_free(AVIOContext **s); + +void avio_w8(AVIOContext *s, int b); +void avio_write(AVIOContext *s, const unsigned char *buf, int size); +void avio_wl64(AVIOContext *s, uint64_t val); +void avio_wb64(AVIOContext *s, uint64_t val); +void avio_wl32(AVIOContext *s, unsigned int val); +void avio_wb32(AVIOContext *s, unsigned int val); +void avio_wl24(AVIOContext *s, unsigned int val); +void avio_wb24(AVIOContext *s, unsigned int val); +void avio_wl16(AVIOContext *s, unsigned int val); +void avio_wb16(AVIOContext *s, unsigned int val); + +/** + * Write a NULL-terminated string. + * @return number of bytes written. + */ +int avio_put_str(AVIOContext *s, const char *str); + +/** + * Convert an UTF-8 string to UTF-16LE and write it. + * @param s the AVIOContext + * @param str NULL-terminated UTF-8 string + * + * @return number of bytes written. + */ +int avio_put_str16le(AVIOContext *s, const char *str); + +/** + * Convert an UTF-8 string to UTF-16BE and write it. + * @param s the AVIOContext + * @param str NULL-terminated UTF-8 string + * + * @return number of bytes written. + */ +int avio_put_str16be(AVIOContext *s, const char *str); + +/** + * Mark the written bytestream as a specific type. + * + * Zero-length ranges are omitted from the output. + * + * @param time the stream time the current bytestream pos corresponds to + * (in AV_TIME_BASE units), or AV_NOPTS_VALUE if unknown or not + * applicable + * @param type the kind of data written starting at the current pos + */ +void avio_write_marker(AVIOContext *s, int64_t time, enum AVIODataMarkerType type); + +/** + * ORing this as the "whence" parameter to a seek function causes it to + * return the filesize without seeking anywhere. Supporting this is optional. + * If it is not supported then the seek function will return <0. + */ +#define AVSEEK_SIZE 0x10000 + +/** + * Passing this flag as the "whence" parameter to a seek function causes it to + * seek by any means (like reopening and linear reading) or other normally unreasonable + * means that can be extremely slow. + * This may be ignored by the seek code. + */ +#define AVSEEK_FORCE 0x20000 + +/** + * fseek() equivalent for AVIOContext. + * @return new position or AVERROR. + */ +int64_t avio_seek(AVIOContext *s, int64_t offset, int whence); + +/** + * Skip given number of bytes forward + * @return new position or AVERROR. + */ +int64_t avio_skip(AVIOContext *s, int64_t offset); + +/** + * ftell() equivalent for AVIOContext. + * @return position or AVERROR. + */ +static av_always_inline int64_t avio_tell(AVIOContext *s) +{ + return avio_seek(s, 0, SEEK_CUR); +} + +/** + * Get the filesize. + * @return filesize or AVERROR + */ +int64_t avio_size(AVIOContext *s); + +/** + * Similar to feof() but also returns nonzero on read errors. + * @return non zero if and only if at end of file or a read error happened when reading. + */ +int avio_feof(AVIOContext *s); + +/** + * Writes a formatted string to the context. + * @return number of bytes written, < 0 on error. + */ +int avio_printf(AVIOContext *s, const char *fmt, ...) av_printf_format(2, 3); + +/** + * Write a NULL terminated array of strings to the context. + * Usually you don't need to use this function directly but its macro wrapper, + * avio_print. + */ +void avio_print_string_array(AVIOContext *s, const char *strings[]); + +/** + * Write strings (const char *) to the context. + * This is a convenience macro around avio_print_string_array and it + * automatically creates the string array from the variable argument list. + * For simple string concatenations this function is more performant than using + * avio_printf since it does not need a temporary buffer. + */ +#define avio_print(s, ...) \ + avio_print_string_array(s, (const char*[]){__VA_ARGS__, NULL}) + +/** + * Force flushing of buffered data. + * + * For write streams, force the buffered data to be immediately written to the output, + * without to wait to fill the internal buffer. + * + * For read streams, discard all currently buffered data, and advance the + * reported file position to that of the underlying stream. This does not + * read new data, and does not perform any seeks. + */ +void avio_flush(AVIOContext *s); + +/** + * Read size bytes from AVIOContext into buf. + * @return number of bytes read or AVERROR + */ +int avio_read(AVIOContext *s, unsigned char *buf, int size); + +/** + * Read size bytes from AVIOContext into buf. Unlike avio_read(), this is allowed + * to read fewer bytes than requested. The missing bytes can be read in the next + * call. This always tries to read at least 1 byte. + * Useful to reduce latency in certain cases. + * @return number of bytes read or AVERROR + */ +int avio_read_partial(AVIOContext *s, unsigned char *buf, int size); + +/** + * @name Functions for reading from AVIOContext + * @{ + * + * @note return 0 if EOF, so you cannot use it if EOF handling is + * necessary + */ +int avio_r8 (AVIOContext *s); +unsigned int avio_rl16(AVIOContext *s); +unsigned int avio_rl24(AVIOContext *s); +unsigned int avio_rl32(AVIOContext *s); +uint64_t avio_rl64(AVIOContext *s); +unsigned int avio_rb16(AVIOContext *s); +unsigned int avio_rb24(AVIOContext *s); +unsigned int avio_rb32(AVIOContext *s); +uint64_t avio_rb64(AVIOContext *s); +/** + * @} + */ + +/** + * Read a string from pb into buf. The reading will terminate when either + * a NULL character was encountered, maxlen bytes have been read, or nothing + * more can be read from pb. The result is guaranteed to be NULL-terminated, it + * will be truncated if buf is too small. + * Note that the string is not interpreted or validated in any way, it + * might get truncated in the middle of a sequence for multi-byte encodings. + * + * @return number of bytes read (is always <= maxlen). + * If reading ends on EOF or error, the return value will be one more than + * bytes actually read. + */ +int avio_get_str(AVIOContext *pb, int maxlen, char *buf, int buflen); + +/** + * Read a UTF-16 string from pb and convert it to UTF-8. + * The reading will terminate when either a null or invalid character was + * encountered or maxlen bytes have been read. + * @return number of bytes read (is always <= maxlen) + */ +int avio_get_str16le(AVIOContext *pb, int maxlen, char *buf, int buflen); +int avio_get_str16be(AVIOContext *pb, int maxlen, char *buf, int buflen); + + +/** + * @name URL open modes + * The flags argument to avio_open must be one of the following + * constants, optionally ORed with other flags. + * @{ + */ +#define AVIO_FLAG_READ 1 /**< read-only */ +#define AVIO_FLAG_WRITE 2 /**< write-only */ +#define AVIO_FLAG_READ_WRITE (AVIO_FLAG_READ|AVIO_FLAG_WRITE) /**< read-write pseudo flag */ +/** + * @} + */ + +/** + * Use non-blocking mode. + * If this flag is set, operations on the context will return + * AVERROR(EAGAIN) if they can not be performed immediately. + * If this flag is not set, operations on the context will never return + * AVERROR(EAGAIN). + * Note that this flag does not affect the opening/connecting of the + * context. Connecting a protocol will always block if necessary (e.g. on + * network protocols) but never hang (e.g. on busy devices). + * Warning: non-blocking protocols is work-in-progress; this flag may be + * silently ignored. + */ +#define AVIO_FLAG_NONBLOCK 8 + +/** + * Use direct mode. + * avio_read and avio_write should if possible be satisfied directly + * instead of going through a buffer, and avio_seek will always + * call the underlying seek function directly. + */ +#define AVIO_FLAG_DIRECT 0x8000 + +/** + * Create and initialize a AVIOContext for accessing the + * resource indicated by url. + * @note When the resource indicated by url has been opened in + * read+write mode, the AVIOContext can be used only for writing. + * + * @param s Used to return the pointer to the created AVIOContext. + * In case of failure the pointed to value is set to NULL. + * @param url resource to access + * @param flags flags which control how the resource indicated by url + * is to be opened + * @return >= 0 in case of success, a negative value corresponding to an + * AVERROR code in case of failure + */ +int avio_open(AVIOContext **s, const char *url, int flags); + +/** + * Create and initialize a AVIOContext for accessing the + * resource indicated by url. + * @note When the resource indicated by url has been opened in + * read+write mode, the AVIOContext can be used only for writing. + * + * @param s Used to return the pointer to the created AVIOContext. + * In case of failure the pointed to value is set to NULL. + * @param url resource to access + * @param flags flags which control how the resource indicated by url + * is to be opened + * @param int_cb an interrupt callback to be used at the protocols level + * @param options A dictionary filled with protocol-private options. On return + * this parameter will be destroyed and replaced with a dict containing options + * that were not found. May be NULL. + * @return >= 0 in case of success, a negative value corresponding to an + * AVERROR code in case of failure + */ +int avio_open2(AVIOContext **s, const char *url, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options); + +/** + * Close the resource accessed by the AVIOContext s and free it. + * This function can only be used if s was opened by avio_open(). + * + * The internal buffer is automatically flushed before closing the + * resource. + * + * @return 0 on success, an AVERROR < 0 on error. + * @see avio_closep + */ +int avio_close(AVIOContext *s); + +/** + * Close the resource accessed by the AVIOContext *s, free it + * and set the pointer pointing to it to NULL. + * This function can only be used if s was opened by avio_open(). + * + * The internal buffer is automatically flushed before closing the + * resource. + * + * @return 0 on success, an AVERROR < 0 on error. + * @see avio_close + */ +int avio_closep(AVIOContext **s); + + +/** + * Open a write only memory stream. + * + * @param s new IO context + * @return zero if no error. + */ +int avio_open_dyn_buf(AVIOContext **s); + +/** + * Return the written size and a pointer to the buffer. + * The AVIOContext stream is left intact. + * The buffer must NOT be freed. + * No padding is added to the buffer. + * + * @param s IO context + * @param pbuffer pointer to a byte buffer + * @return the length of the byte buffer + */ +int avio_get_dyn_buf(AVIOContext *s, uint8_t **pbuffer); + +/** + * Return the written size and a pointer to the buffer. The buffer + * must be freed with av_free(). + * Padding of AV_INPUT_BUFFER_PADDING_SIZE is added to the buffer. + * + * @param s IO context + * @param pbuffer pointer to a byte buffer + * @return the length of the byte buffer + */ +int avio_close_dyn_buf(AVIOContext *s, uint8_t **pbuffer); + +/** + * Iterate through names of available protocols. + * + * @param opaque A private pointer representing current protocol. + * It must be a pointer to NULL on first iteration and will + * be updated by successive calls to avio_enum_protocols. + * @param output If set to 1, iterate over output protocols, + * otherwise over input protocols. + * + * @return A static string containing the name of current protocol or NULL + */ +const char *avio_enum_protocols(void **opaque, int output); + +/** + * Get AVClass by names of available protocols. + * + * @return A AVClass of input protocol name or NULL + */ +const AVClass *avio_protocol_get_class(const char *name); + +/** + * Pause and resume playing - only meaningful if using a network streaming + * protocol (e.g. MMS). + * + * @param h IO context from which to call the read_pause function pointer + * @param pause 1 for pause, 0 for resume + */ +int avio_pause(AVIOContext *h, int pause); + +/** + * Seek to a given timestamp relative to some component stream. + * Only meaningful if using a network streaming protocol (e.g. MMS.). + * + * @param h IO context from which to call the seek function pointers + * @param stream_index The stream index that the timestamp is relative to. + * If stream_index is (-1) the timestamp should be in AV_TIME_BASE + * units from the beginning of the presentation. + * If a stream_index >= 0 is used and the protocol does not support + * seeking based on component streams, the call will fail. + * @param timestamp timestamp in AVStream.time_base units + * or if there is no stream specified then in AV_TIME_BASE units. + * @param flags Optional combination of AVSEEK_FLAG_BACKWARD, AVSEEK_FLAG_BYTE + * and AVSEEK_FLAG_ANY. The protocol may silently ignore + * AVSEEK_FLAG_BACKWARD and AVSEEK_FLAG_ANY, but AVSEEK_FLAG_BYTE will + * fail if used and not supported. + * @return >= 0 on success + * @see AVInputFormat::read_seek + */ +int64_t avio_seek_time(AVIOContext *h, int stream_index, + int64_t timestamp, int flags); + +/* Avoid a warning. The header can not be included because it breaks c++. */ +struct AVBPrint; + +/** + * Read contents of h into print buffer, up to max_size bytes, or up to EOF. + * + * @return 0 for success (max_size bytes read or EOF reached), negative error + * code otherwise + */ +int avio_read_to_bprint(AVIOContext *h, struct AVBPrint *pb, size_t max_size); + +/** + * Accept and allocate a client context on a server context. + * @param s the server context + * @param c the client context, must be unallocated + * @return >= 0 on success or a negative value corresponding + * to an AVERROR on failure + */ +int avio_accept(AVIOContext *s, AVIOContext **c); + +/** + * Perform one step of the protocol handshake to accept a new client. + * This function must be called on a client returned by avio_accept() before + * using it as a read/write context. + * It is separate from avio_accept() because it may block. + * A step of the handshake is defined by places where the application may + * decide to change the proceedings. + * For example, on a protocol with a request header and a reply header, each + * one can constitute a step because the application may use the parameters + * from the request to change parameters in the reply; or each individual + * chunk of the request can constitute a step. + * If the handshake is already finished, avio_handshake() does nothing and + * returns 0 immediately. + * + * @param c the client context to perform the handshake on + * @return 0 on a complete and successful handshake + * > 0 if the handshake progressed, but is not complete + * < 0 for an AVERROR code + */ +int avio_handshake(AVIOContext *c); +#endif /* AVFORMAT_AVIO_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavformat/version.h b/ThirdParty/ffmpeg/arm/include/libavformat/version.h new file mode 100644 index 000000000..13c8a615a --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavformat/version.h @@ -0,0 +1,114 @@ +/* + * Version macros. + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_VERSION_H +#define AVFORMAT_VERSION_H + +/** + * @file + * @ingroup libavf + * Libavformat version macros + */ + +#include "libavutil/version.h" + +// Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium) +// Also please add any ticket numbers that you believe might be affected here +#define LIBAVFORMAT_VERSION_MAJOR 58 +#define LIBAVFORMAT_VERSION_MINOR 45 +#define LIBAVFORMAT_VERSION_MICRO 100 + +#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ + LIBAVFORMAT_VERSION_MINOR, \ + LIBAVFORMAT_VERSION_MICRO) +#define LIBAVFORMAT_VERSION AV_VERSION(LIBAVFORMAT_VERSION_MAJOR, \ + LIBAVFORMAT_VERSION_MINOR, \ + LIBAVFORMAT_VERSION_MICRO) +#define LIBAVFORMAT_BUILD LIBAVFORMAT_VERSION_INT + +#define LIBAVFORMAT_IDENT "Lavf" AV_STRINGIFY(LIBAVFORMAT_VERSION) + +/** + * FF_API_* defines may be placed below to indicate public API that will be + * dropped at a future version bump. The defines themselves are not part of + * the public API and may change, break or disappear at any time. + * + * @note, when bumping the major version it is recommended to manually + * disable each FF_API_* in its own commit instead of disabling them all + * at once through the bump. This improves the git bisect-ability of the change. + * + */ +#ifndef FF_API_COMPUTE_PKT_FIELDS2 +#define FF_API_COMPUTE_PKT_FIELDS2 (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_OLD_OPEN_CALLBACKS +#define FF_API_OLD_OPEN_CALLBACKS (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_LAVF_AVCTX +#define FF_API_LAVF_AVCTX (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_HTTP_USER_AGENT +#define FF_API_HTTP_USER_AGENT (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_HLS_WRAP +#define FF_API_HLS_WRAP (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_HLS_USE_LOCALTIME +#define FF_API_HLS_USE_LOCALTIME (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_LAVF_KEEPSIDE_FLAG +#define FF_API_LAVF_KEEPSIDE_FLAG (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_OLD_ROTATE_API +#define FF_API_OLD_ROTATE_API (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_FORMAT_GET_SET +#define FF_API_FORMAT_GET_SET (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_OLD_AVIO_EOF_0 +#define FF_API_OLD_AVIO_EOF_0 (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_LAVF_FFSERVER +#define FF_API_LAVF_FFSERVER (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_FORMAT_FILENAME +#define FF_API_FORMAT_FILENAME (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_OLD_RTSP_OPTIONS +#define FF_API_OLD_RTSP_OPTIONS (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_NEXT +#define FF_API_NEXT (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_DASH_MIN_SEG_DURATION +#define FF_API_DASH_MIN_SEG_DURATION (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_LAVF_MP4A_LATM +#define FF_API_LAVF_MP4A_LATM (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_AVIOFORMAT +#define FF_API_AVIOFORMAT (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif + + +#ifndef FF_API_R_FRAME_RATE +#define FF_API_R_FRAME_RATE 1 +#endif +#endif /* AVFORMAT_VERSION_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/adler32.h b/ThirdParty/ffmpeg/arm/include/libavutil/adler32.h new file mode 100644 index 000000000..a1f035b73 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/adler32.h @@ -0,0 +1,60 @@ +/* + * copyright (c) 2006 Mans Rullgard + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_adler32 + * Public header for Adler-32 hash function implementation. + */ + +#ifndef AVUTIL_ADLER32_H +#define AVUTIL_ADLER32_H + +#include +#include "attributes.h" + +/** + * @defgroup lavu_adler32 Adler-32 + * @ingroup lavu_hash + * Adler-32 hash function implementation. + * + * @{ + */ + +/** + * Calculate the Adler32 checksum of a buffer. + * + * Passing the return value to a subsequent av_adler32_update() call + * allows the checksum of multiple buffers to be calculated as though + * they were concatenated. + * + * @param adler initial checksum value + * @param buf pointer to input buffer + * @param len size of input buffer + * @return updated checksum + */ +unsigned long av_adler32_update(unsigned long adler, const uint8_t *buf, + unsigned int len) av_pure; + +/** + * @} + */ + +#endif /* AVUTIL_ADLER32_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/aes.h b/ThirdParty/ffmpeg/arm/include/libavutil/aes.h new file mode 100644 index 000000000..09efbda10 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/aes.h @@ -0,0 +1,65 @@ +/* + * copyright (c) 2007 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_AES_H +#define AVUTIL_AES_H + +#include + +#include "attributes.h" +#include "version.h" + +/** + * @defgroup lavu_aes AES + * @ingroup lavu_crypto + * @{ + */ + +extern const int av_aes_size; + +struct AVAES; + +/** + * Allocate an AVAES context. + */ +struct AVAES *av_aes_alloc(void); + +/** + * Initialize an AVAES context. + * @param key_bits 128, 192 or 256 + * @param decrypt 0 for encryption, 1 for decryption + */ +int av_aes_init(struct AVAES *a, const uint8_t *key, int key_bits, int decrypt); + +/** + * Encrypt or decrypt a buffer using a previously initialized context. + * @param count number of 16 byte blocks + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param iv initialization vector for CBC mode, if NULL then ECB will be used + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_aes_crypt(struct AVAES *a, uint8_t *dst, const uint8_t *src, int count, uint8_t *iv, int decrypt); + +/** + * @} + */ + +#endif /* AVUTIL_AES_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/aes_ctr.h b/ThirdParty/ffmpeg/arm/include/libavutil/aes_ctr.h new file mode 100644 index 000000000..e4aae126a --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/aes_ctr.h @@ -0,0 +1,88 @@ +/* + * AES-CTR cipher + * Copyright (c) 2015 Eran Kornblau + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_AES_CTR_H +#define AVUTIL_AES_CTR_H + +#include + +#include "attributes.h" +#include "version.h" + +#define AES_CTR_KEY_SIZE (16) +#define AES_CTR_IV_SIZE (8) + +struct AVAESCTR; + +/** + * Allocate an AVAESCTR context. + */ +struct AVAESCTR *av_aes_ctr_alloc(void); + +/** + * Initialize an AVAESCTR context. + * @param key encryption key, must have a length of AES_CTR_KEY_SIZE + */ +int av_aes_ctr_init(struct AVAESCTR *a, const uint8_t *key); + +/** + * Release an AVAESCTR context. + */ +void av_aes_ctr_free(struct AVAESCTR *a); + +/** + * Process a buffer using a previously initialized context. + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param size the size of src and dst + */ +void av_aes_ctr_crypt(struct AVAESCTR *a, uint8_t *dst, const uint8_t *src, int size); + +/** + * Get the current iv + */ +const uint8_t* av_aes_ctr_get_iv(struct AVAESCTR *a); + +/** + * Generate a random iv + */ +void av_aes_ctr_set_random_iv(struct AVAESCTR *a); + +/** + * Forcefully change the 8-byte iv + */ +void av_aes_ctr_set_iv(struct AVAESCTR *a, const uint8_t* iv); + +/** + * Forcefully change the "full" 16-byte iv, including the counter + */ +void av_aes_ctr_set_full_iv(struct AVAESCTR *a, const uint8_t* iv); + +/** + * Increment the top 64 bit of the iv (performed after each frame) + */ +void av_aes_ctr_increment_iv(struct AVAESCTR *a); + +/** + * @} + */ + +#endif /* AVUTIL_AES_CTR_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/attributes.h b/ThirdParty/ffmpeg/arm/include/libavutil/attributes.h new file mode 100644 index 000000000..5cb9fe345 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/attributes.h @@ -0,0 +1,173 @@ +/* + * copyright (c) 2006 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Macro definitions for various function/variable attributes + */ + +#ifndef AVUTIL_ATTRIBUTES_H +#define AVUTIL_ATTRIBUTES_H + +#ifdef __GNUC__ +# define AV_GCC_VERSION_AT_LEAST(x,y) (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y)) +# define AV_GCC_VERSION_AT_MOST(x,y) (__GNUC__ < (x) || __GNUC__ == (x) && __GNUC_MINOR__ <= (y)) +#else +# define AV_GCC_VERSION_AT_LEAST(x,y) 0 +# define AV_GCC_VERSION_AT_MOST(x,y) 0 +#endif + +#ifdef __has_builtin +# define AV_HAS_BUILTIN(x) __has_builtin(x) +#else +# define AV_HAS_BUILTIN(x) 0 +#endif + +#ifndef av_always_inline +#if AV_GCC_VERSION_AT_LEAST(3,1) +# define av_always_inline __attribute__((always_inline)) inline +#elif defined(_MSC_VER) +# define av_always_inline __forceinline +#else +# define av_always_inline inline +#endif +#endif + +#ifndef av_extern_inline +#if defined(__ICL) && __ICL >= 1210 || defined(__GNUC_STDC_INLINE__) +# define av_extern_inline extern inline +#else +# define av_extern_inline inline +#endif +#endif + +#if AV_GCC_VERSION_AT_LEAST(3,4) +# define av_warn_unused_result __attribute__((warn_unused_result)) +#else +# define av_warn_unused_result +#endif + +#if AV_GCC_VERSION_AT_LEAST(3,1) +# define av_noinline __attribute__((noinline)) +#elif defined(_MSC_VER) +# define av_noinline __declspec(noinline) +#else +# define av_noinline +#endif + +#if AV_GCC_VERSION_AT_LEAST(3,1) || defined(__clang__) +# define av_pure __attribute__((pure)) +#else +# define av_pure +#endif + +#if AV_GCC_VERSION_AT_LEAST(2,6) || defined(__clang__) +# define av_const __attribute__((const)) +#else +# define av_const +#endif + +#if AV_GCC_VERSION_AT_LEAST(4,3) || defined(__clang__) +# define av_cold __attribute__((cold)) +#else +# define av_cold +#endif + +#if AV_GCC_VERSION_AT_LEAST(4,1) && !defined(__llvm__) +# define av_flatten __attribute__((flatten)) +#else +# define av_flatten +#endif + +#if AV_GCC_VERSION_AT_LEAST(3,1) +# define attribute_deprecated __attribute__((deprecated)) +#elif defined(_MSC_VER) +# define attribute_deprecated __declspec(deprecated) +#else +# define attribute_deprecated +#endif + +/** + * Disable warnings about deprecated features + * This is useful for sections of code kept for backward compatibility and + * scheduled for removal. + */ +#ifndef AV_NOWARN_DEPRECATED +#if AV_GCC_VERSION_AT_LEAST(4,6) +# define AV_NOWARN_DEPRECATED(code) \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \ + code \ + _Pragma("GCC diagnostic pop") +#elif defined(_MSC_VER) +# define AV_NOWARN_DEPRECATED(code) \ + __pragma(warning(push)) \ + __pragma(warning(disable : 4996)) \ + code; \ + __pragma(warning(pop)) +#else +# define AV_NOWARN_DEPRECATED(code) code +#endif +#endif + +#if defined(__GNUC__) || defined(__clang__) +# define av_unused __attribute__((unused)) +#else +# define av_unused +#endif + +/** + * Mark a variable as used and prevent the compiler from optimizing it + * away. This is useful for variables accessed only from inline + * assembler without the compiler being aware. + */ +#if AV_GCC_VERSION_AT_LEAST(3,1) || defined(__clang__) +# define av_used __attribute__((used)) +#else +# define av_used +#endif + +#if AV_GCC_VERSION_AT_LEAST(3,3) || defined(__clang__) +# define av_alias __attribute__((may_alias)) +#else +# define av_alias +#endif + +#if (defined(__GNUC__) || defined(__clang__)) && !defined(__INTEL_COMPILER) +# define av_uninit(x) x=x +#else +# define av_uninit(x) x +#endif + +#if defined(__GNUC__) || defined(__clang__) +# define av_builtin_constant_p __builtin_constant_p +# define av_printf_format(fmtpos, attrpos) __attribute__((__format__(__printf__, fmtpos, attrpos))) +#else +# define av_builtin_constant_p(x) 0 +# define av_printf_format(fmtpos, attrpos) +#endif + +#if AV_GCC_VERSION_AT_LEAST(2,5) || defined(__clang__) +# define av_noreturn __attribute__((noreturn)) +#else +# define av_noreturn +#endif + +#endif /* AVUTIL_ATTRIBUTES_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/audio_fifo.h b/ThirdParty/ffmpeg/arm/include/libavutil/audio_fifo.h new file mode 100644 index 000000000..d8a9194a8 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/audio_fifo.h @@ -0,0 +1,187 @@ +/* + * Audio FIFO + * Copyright (c) 2012 Justin Ruggles + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Audio FIFO Buffer + */ + +#ifndef AVUTIL_AUDIO_FIFO_H +#define AVUTIL_AUDIO_FIFO_H + +#include "avutil.h" +#include "fifo.h" +#include "samplefmt.h" + +/** + * @addtogroup lavu_audio + * @{ + * + * @defgroup lavu_audiofifo Audio FIFO Buffer + * @{ + */ + +/** + * Context for an Audio FIFO Buffer. + * + * - Operates at the sample level rather than the byte level. + * - Supports multiple channels with either planar or packed sample format. + * - Automatic reallocation when writing to a full buffer. + */ +typedef struct AVAudioFifo AVAudioFifo; + +/** + * Free an AVAudioFifo. + * + * @param af AVAudioFifo to free + */ +void av_audio_fifo_free(AVAudioFifo *af); + +/** + * Allocate an AVAudioFifo. + * + * @param sample_fmt sample format + * @param channels number of channels + * @param nb_samples initial allocation size, in samples + * @return newly allocated AVAudioFifo, or NULL on error + */ +AVAudioFifo *av_audio_fifo_alloc(enum AVSampleFormat sample_fmt, int channels, + int nb_samples); + +/** + * Reallocate an AVAudioFifo. + * + * @param af AVAudioFifo to reallocate + * @param nb_samples new allocation size, in samples + * @return 0 if OK, or negative AVERROR code on failure + */ +av_warn_unused_result +int av_audio_fifo_realloc(AVAudioFifo *af, int nb_samples); + +/** + * Write data to an AVAudioFifo. + * + * The AVAudioFifo will be reallocated automatically if the available space + * is less than nb_samples. + * + * @see enum AVSampleFormat + * The documentation for AVSampleFormat describes the data layout. + * + * @param af AVAudioFifo to write to + * @param data audio data plane pointers + * @param nb_samples number of samples to write + * @return number of samples actually written, or negative AVERROR + * code on failure. If successful, the number of samples + * actually written will always be nb_samples. + */ +int av_audio_fifo_write(AVAudioFifo *af, void **data, int nb_samples); + +/** + * Peek data from an AVAudioFifo. + * + * @see enum AVSampleFormat + * The documentation for AVSampleFormat describes the data layout. + * + * @param af AVAudioFifo to read from + * @param data audio data plane pointers + * @param nb_samples number of samples to peek + * @return number of samples actually peek, or negative AVERROR code + * on failure. The number of samples actually peek will not + * be greater than nb_samples, and will only be less than + * nb_samples if av_audio_fifo_size is less than nb_samples. + */ +int av_audio_fifo_peek(AVAudioFifo *af, void **data, int nb_samples); + +/** + * Peek data from an AVAudioFifo. + * + * @see enum AVSampleFormat + * The documentation for AVSampleFormat describes the data layout. + * + * @param af AVAudioFifo to read from + * @param data audio data plane pointers + * @param nb_samples number of samples to peek + * @param offset offset from current read position + * @return number of samples actually peek, or negative AVERROR code + * on failure. The number of samples actually peek will not + * be greater than nb_samples, and will only be less than + * nb_samples if av_audio_fifo_size is less than nb_samples. + */ +int av_audio_fifo_peek_at(AVAudioFifo *af, void **data, int nb_samples, int offset); + +/** + * Read data from an AVAudioFifo. + * + * @see enum AVSampleFormat + * The documentation for AVSampleFormat describes the data layout. + * + * @param af AVAudioFifo to read from + * @param data audio data plane pointers + * @param nb_samples number of samples to read + * @return number of samples actually read, or negative AVERROR code + * on failure. The number of samples actually read will not + * be greater than nb_samples, and will only be less than + * nb_samples if av_audio_fifo_size is less than nb_samples. + */ +int av_audio_fifo_read(AVAudioFifo *af, void **data, int nb_samples); + +/** + * Drain data from an AVAudioFifo. + * + * Removes the data without reading it. + * + * @param af AVAudioFifo to drain + * @param nb_samples number of samples to drain + * @return 0 if OK, or negative AVERROR code on failure + */ +int av_audio_fifo_drain(AVAudioFifo *af, int nb_samples); + +/** + * Reset the AVAudioFifo buffer. + * + * This empties all data in the buffer. + * + * @param af AVAudioFifo to reset + */ +void av_audio_fifo_reset(AVAudioFifo *af); + +/** + * Get the current number of samples in the AVAudioFifo available for reading. + * + * @param af the AVAudioFifo to query + * @return number of samples available for reading + */ +int av_audio_fifo_size(AVAudioFifo *af); + +/** + * Get the current number of samples in the AVAudioFifo available for writing. + * + * @param af the AVAudioFifo to query + * @return number of samples available for writing + */ +int av_audio_fifo_space(AVAudioFifo *af); + +/** + * @} + * @} + */ + +#endif /* AVUTIL_AUDIO_FIFO_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/avassert.h b/ThirdParty/ffmpeg/arm/include/libavutil/avassert.h new file mode 100644 index 000000000..9abeadea4 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/avassert.h @@ -0,0 +1,75 @@ +/* + * copyright (c) 2010 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * simple assert() macros that are a bit more flexible than ISO C assert(). + * @author Michael Niedermayer + */ + +#ifndef AVUTIL_AVASSERT_H +#define AVUTIL_AVASSERT_H + +#include +#include "avutil.h" +#include "log.h" + +/** + * assert() equivalent, that is always enabled. + */ +#define av_assert0(cond) do { \ + if (!(cond)) { \ + av_log(NULL, AV_LOG_PANIC, "Assertion %s failed at %s:%d\n", \ + AV_STRINGIFY(cond), __FILE__, __LINE__); \ + abort(); \ + } \ +} while (0) + + +/** + * assert() equivalent, that does not lie in speed critical code. + * These asserts() thus can be enabled without fearing speed loss. + */ +#if defined(ASSERT_LEVEL) && ASSERT_LEVEL > 0 +#define av_assert1(cond) av_assert0(cond) +#else +#define av_assert1(cond) ((void)0) +#endif + + +/** + * assert() equivalent, that does lie in speed critical code. + */ +#if defined(ASSERT_LEVEL) && ASSERT_LEVEL > 1 +#define av_assert2(cond) av_assert0(cond) +#define av_assert2_fpu() av_assert0_fpu() +#else +#define av_assert2(cond) ((void)0) +#define av_assert2_fpu() ((void)0) +#endif + +/** + * Assert that floating point operations can be executed. + * + * This will av_assert0() that the cpu is not in MMX state on X86 + */ +void av_assert0_fpu(void); + +#endif /* AVUTIL_AVASSERT_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/avconfig.h b/ThirdParty/ffmpeg/arm/include/libavutil/avconfig.h new file mode 100644 index 000000000..c289fbb55 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/avconfig.h @@ -0,0 +1,6 @@ +/* Generated by ffmpeg configure */ +#ifndef AVUTIL_AVCONFIG_H +#define AVUTIL_AVCONFIG_H +#define AV_HAVE_BIGENDIAN 0 +#define AV_HAVE_FAST_UNALIGNED 1 +#endif /* AVUTIL_AVCONFIG_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/avstring.h b/ThirdParty/ffmpeg/arm/include/libavutil/avstring.h new file mode 100644 index 000000000..274335cfb --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/avstring.h @@ -0,0 +1,418 @@ +/* + * Copyright (c) 2007 Mans Rullgard + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_AVSTRING_H +#define AVUTIL_AVSTRING_H + +#include +#include +#include "attributes.h" + +/** + * @addtogroup lavu_string + * @{ + */ + +/** + * Return non-zero if pfx is a prefix of str. If it is, *ptr is set to + * the address of the first character in str after the prefix. + * + * @param str input string + * @param pfx prefix to test + * @param ptr updated if the prefix is matched inside str + * @return non-zero if the prefix matches, zero otherwise + */ +int av_strstart(const char *str, const char *pfx, const char **ptr); + +/** + * Return non-zero if pfx is a prefix of str independent of case. If + * it is, *ptr is set to the address of the first character in str + * after the prefix. + * + * @param str input string + * @param pfx prefix to test + * @param ptr updated if the prefix is matched inside str + * @return non-zero if the prefix matches, zero otherwise + */ +int av_stristart(const char *str, const char *pfx, const char **ptr); + +/** + * Locate the first case-independent occurrence in the string haystack + * of the string needle. A zero-length string needle is considered to + * match at the start of haystack. + * + * This function is a case-insensitive version of the standard strstr(). + * + * @param haystack string to search in + * @param needle string to search for + * @return pointer to the located match within haystack + * or a null pointer if no match + */ +char *av_stristr(const char *haystack, const char *needle); + +/** + * Locate the first occurrence of the string needle in the string haystack + * where not more than hay_length characters are searched. A zero-length + * string needle is considered to match at the start of haystack. + * + * This function is a length-limited version of the standard strstr(). + * + * @param haystack string to search in + * @param needle string to search for + * @param hay_length length of string to search in + * @return pointer to the located match within haystack + * or a null pointer if no match + */ +char *av_strnstr(const char *haystack, const char *needle, size_t hay_length); + +/** + * Copy the string src to dst, but no more than size - 1 bytes, and + * null-terminate dst. + * + * This function is the same as BSD strlcpy(). + * + * @param dst destination buffer + * @param src source string + * @param size size of destination buffer + * @return the length of src + * + * @warning since the return value is the length of src, src absolutely + * _must_ be a properly 0-terminated string, otherwise this will read beyond + * the end of the buffer and possibly crash. + */ +size_t av_strlcpy(char *dst, const char *src, size_t size); + +/** + * Append the string src to the string dst, but to a total length of + * no more than size - 1 bytes, and null-terminate dst. + * + * This function is similar to BSD strlcat(), but differs when + * size <= strlen(dst). + * + * @param dst destination buffer + * @param src source string + * @param size size of destination buffer + * @return the total length of src and dst + * + * @warning since the return value use the length of src and dst, these + * absolutely _must_ be a properly 0-terminated strings, otherwise this + * will read beyond the end of the buffer and possibly crash. + */ +size_t av_strlcat(char *dst, const char *src, size_t size); + +/** + * Append output to a string, according to a format. Never write out of + * the destination buffer, and always put a terminating 0 within + * the buffer. + * @param dst destination buffer (string to which the output is + * appended) + * @param size total size of the destination buffer + * @param fmt printf-compatible format string, specifying how the + * following parameters are used + * @return the length of the string that would have been generated + * if enough space had been available + */ +size_t av_strlcatf(char *dst, size_t size, const char *fmt, ...) av_printf_format(3, 4); + +/** + * Get the count of continuous non zero chars starting from the beginning. + * + * @param len maximum number of characters to check in the string, that + * is the maximum value which is returned by the function + */ +static inline size_t av_strnlen(const char *s, size_t len) +{ + size_t i; + for (i = 0; i < len && s[i]; i++) + ; + return i; +} + +/** + * Print arguments following specified format into a large enough auto + * allocated buffer. It is similar to GNU asprintf(). + * @param fmt printf-compatible format string, specifying how the + * following parameters are used. + * @return the allocated string + * @note You have to free the string yourself with av_free(). + */ +char *av_asprintf(const char *fmt, ...) av_printf_format(1, 2); + +/** + * Convert a number to an av_malloced string. + */ +char *av_d2str(double d); + +/** + * Unescape the given string until a non escaped terminating char, + * and return the token corresponding to the unescaped string. + * + * The normal \ and ' escaping is supported. Leading and trailing + * whitespaces are removed, unless they are escaped with '\' or are + * enclosed between ''. + * + * @param buf the buffer to parse, buf will be updated to point to the + * terminating char + * @param term a 0-terminated list of terminating chars + * @return the malloced unescaped string, which must be av_freed by + * the user, NULL in case of allocation failure + */ +char *av_get_token(const char **buf, const char *term); + +/** + * Split the string into several tokens which can be accessed by + * successive calls to av_strtok(). + * + * A token is defined as a sequence of characters not belonging to the + * set specified in delim. + * + * On the first call to av_strtok(), s should point to the string to + * parse, and the value of saveptr is ignored. In subsequent calls, s + * should be NULL, and saveptr should be unchanged since the previous + * call. + * + * This function is similar to strtok_r() defined in POSIX.1. + * + * @param s the string to parse, may be NULL + * @param delim 0-terminated list of token delimiters, must be non-NULL + * @param saveptr user-provided pointer which points to stored + * information necessary for av_strtok() to continue scanning the same + * string. saveptr is updated to point to the next character after the + * first delimiter found, or to NULL if the string was terminated + * @return the found token, or NULL when no token is found + */ +char *av_strtok(char *s, const char *delim, char **saveptr); + +/** + * Locale-independent conversion of ASCII isdigit. + */ +static inline av_const int av_isdigit(int c) +{ + return c >= '0' && c <= '9'; +} + +/** + * Locale-independent conversion of ASCII isgraph. + */ +static inline av_const int av_isgraph(int c) +{ + return c > 32 && c < 127; +} + +/** + * Locale-independent conversion of ASCII isspace. + */ +static inline av_const int av_isspace(int c) +{ + return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || + c == '\v'; +} + +/** + * Locale-independent conversion of ASCII characters to uppercase. + */ +static inline av_const int av_toupper(int c) +{ + if (c >= 'a' && c <= 'z') + c ^= 0x20; + return c; +} + +/** + * Locale-independent conversion of ASCII characters to lowercase. + */ +static inline av_const int av_tolower(int c) +{ + if (c >= 'A' && c <= 'Z') + c ^= 0x20; + return c; +} + +/** + * Locale-independent conversion of ASCII isxdigit. + */ +static inline av_const int av_isxdigit(int c) +{ + c = av_tolower(c); + return av_isdigit(c) || (c >= 'a' && c <= 'f'); +} + +/** + * Locale-independent case-insensitive compare. + * @note This means only ASCII-range characters are case-insensitive + */ +int av_strcasecmp(const char *a, const char *b); + +/** + * Locale-independent case-insensitive compare. + * @note This means only ASCII-range characters are case-insensitive + */ +int av_strncasecmp(const char *a, const char *b, size_t n); + +/** + * Locale-independent strings replace. + * @note This means only ASCII-range characters are replace + */ +char *av_strireplace(const char *str, const char *from, const char *to); + +/** + * Thread safe basename. + * @param path the string to parse, on DOS both \ and / are considered separators. + * @return pointer to the basename substring. + * If path does not contain a slash, the function returns a copy of path. + * If path is a NULL pointer or points to an empty string, a pointer + * to a string "." is returned. + */ +const char *av_basename(const char *path); + +/** + * Thread safe dirname. + * @param path the string to parse, on DOS both \ and / are considered separators. + * @return A pointer to a string that's the parent directory of path. + * If path is a NULL pointer or points to an empty string, a pointer + * to a string "." is returned. + * @note the function may modify the contents of the path, so copies should be passed. + */ +const char *av_dirname(char *path); + +/** + * Match instances of a name in a comma-separated list of names. + * List entries are checked from the start to the end of the names list, + * the first match ends further processing. If an entry prefixed with '-' + * matches, then 0 is returned. The "ALL" list entry is considered to + * match all names. + * + * @param name Name to look for. + * @param names List of names. + * @return 1 on match, 0 otherwise. + */ +int av_match_name(const char *name, const char *names); + +/** + * Append path component to the existing path. + * Path separator '/' is placed between when needed. + * Resulting string have to be freed with av_free(). + * @param path base path + * @param component component to be appended + * @return new path or NULL on error. + */ +char *av_append_path_component(const char *path, const char *component); + +enum AVEscapeMode { + AV_ESCAPE_MODE_AUTO, ///< Use auto-selected escaping mode. + AV_ESCAPE_MODE_BACKSLASH, ///< Use backslash escaping. + AV_ESCAPE_MODE_QUOTE, ///< Use single-quote escaping. +}; + +/** + * Consider spaces special and escape them even in the middle of the + * string. + * + * This is equivalent to adding the whitespace characters to the special + * characters lists, except it is guaranteed to use the exact same list + * of whitespace characters as the rest of libavutil. + */ +#define AV_ESCAPE_FLAG_WHITESPACE (1 << 0) + +/** + * Escape only specified special characters. + * Without this flag, escape also any characters that may be considered + * special by av_get_token(), such as the single quote. + */ +#define AV_ESCAPE_FLAG_STRICT (1 << 1) + +/** + * Escape string in src, and put the escaped string in an allocated + * string in *dst, which must be freed with av_free(). + * + * @param dst pointer where an allocated string is put + * @param src string to escape, must be non-NULL + * @param special_chars string containing the special characters which + * need to be escaped, can be NULL + * @param mode escape mode to employ, see AV_ESCAPE_MODE_* macros. + * Any unknown value for mode will be considered equivalent to + * AV_ESCAPE_MODE_BACKSLASH, but this behaviour can change without + * notice. + * @param flags flags which control how to escape, see AV_ESCAPE_FLAG_ macros + * @return the length of the allocated string, or a negative error code in case of error + * @see av_bprint_escape() + */ +av_warn_unused_result +int av_escape(char **dst, const char *src, const char *special_chars, + enum AVEscapeMode mode, int flags); + +#define AV_UTF8_FLAG_ACCEPT_INVALID_BIG_CODES 1 ///< accept codepoints over 0x10FFFF +#define AV_UTF8_FLAG_ACCEPT_NON_CHARACTERS 2 ///< accept non-characters - 0xFFFE and 0xFFFF +#define AV_UTF8_FLAG_ACCEPT_SURROGATES 4 ///< accept UTF-16 surrogates codes +#define AV_UTF8_FLAG_EXCLUDE_XML_INVALID_CONTROL_CODES 8 ///< exclude control codes not accepted by XML + +#define AV_UTF8_FLAG_ACCEPT_ALL \ + AV_UTF8_FLAG_ACCEPT_INVALID_BIG_CODES|AV_UTF8_FLAG_ACCEPT_NON_CHARACTERS|AV_UTF8_FLAG_ACCEPT_SURROGATES + +/** + * Read and decode a single UTF-8 code point (character) from the + * buffer in *buf, and update *buf to point to the next byte to + * decode. + * + * In case of an invalid byte sequence, the pointer will be updated to + * the next byte after the invalid sequence and the function will + * return an error code. + * + * Depending on the specified flags, the function will also fail in + * case the decoded code point does not belong to a valid range. + * + * @note For speed-relevant code a carefully implemented use of + * GET_UTF8() may be preferred. + * + * @param codep pointer used to return the parsed code in case of success. + * The value in *codep is set even in case the range check fails. + * @param bufp pointer to the address the first byte of the sequence + * to decode, updated by the function to point to the + * byte next after the decoded sequence + * @param buf_end pointer to the end of the buffer, points to the next + * byte past the last in the buffer. This is used to + * avoid buffer overreads (in case of an unfinished + * UTF-8 sequence towards the end of the buffer). + * @param flags a collection of AV_UTF8_FLAG_* flags + * @return >= 0 in case a sequence was successfully read, a negative + * value in case of invalid sequence + */ +av_warn_unused_result +int av_utf8_decode(int32_t *codep, const uint8_t **bufp, const uint8_t *buf_end, + unsigned int flags); + +/** + * Check if a name is in a list. + * @returns 0 if not found, or the 1 based index where it has been found in the + * list. + */ +int av_match_list(const char *name, const char *list, char separator); + +/** + * See libc sscanf manual for more information. + * Locale-independent sscanf implementation. + */ +int av_sscanf(const char *string, const char *format, ...); + +/** + * @} + */ + +#endif /* AVUTIL_AVSTRING_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/avutil.h b/ThirdParty/ffmpeg/arm/include/libavutil/avutil.h new file mode 100644 index 000000000..4d633156d --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/avutil.h @@ -0,0 +1,365 @@ +/* + * copyright (c) 2006 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_AVUTIL_H +#define AVUTIL_AVUTIL_H + +/** + * @file + * @ingroup lavu + * Convenience header that includes @ref lavu "libavutil"'s core. + */ + +/** + * @mainpage + * + * @section ffmpeg_intro Introduction + * + * This document describes the usage of the different libraries + * provided by FFmpeg. + * + * @li @ref libavc "libavcodec" encoding/decoding library + * @li @ref lavfi "libavfilter" graph-based frame editing library + * @li @ref libavf "libavformat" I/O and muxing/demuxing library + * @li @ref lavd "libavdevice" special devices muxing/demuxing library + * @li @ref lavu "libavutil" common utility library + * @li @ref lswr "libswresample" audio resampling, format conversion and mixing + * @li @ref lpp "libpostproc" post processing library + * @li @ref libsws "libswscale" color conversion and scaling library + * + * @section ffmpeg_versioning Versioning and compatibility + * + * Each of the FFmpeg libraries contains a version.h header, which defines a + * major, minor and micro version number with the + * LIBRARYNAME_VERSION_{MAJOR,MINOR,MICRO} macros. The major version + * number is incremented with backward incompatible changes - e.g. removing + * parts of the public API, reordering public struct members, etc. The minor + * version number is incremented for backward compatible API changes or major + * new features - e.g. adding a new public function or a new decoder. The micro + * version number is incremented for smaller changes that a calling program + * might still want to check for - e.g. changing behavior in a previously + * unspecified situation. + * + * FFmpeg guarantees backward API and ABI compatibility for each library as long + * as its major version number is unchanged. This means that no public symbols + * will be removed or renamed. Types and names of the public struct members and + * values of public macros and enums will remain the same (unless they were + * explicitly declared as not part of the public API). Documented behavior will + * not change. + * + * In other words, any correct program that works with a given FFmpeg snapshot + * should work just as well without any changes with any later snapshot with the + * same major versions. This applies to both rebuilding the program against new + * FFmpeg versions or to replacing the dynamic FFmpeg libraries that a program + * links against. + * + * However, new public symbols may be added and new members may be appended to + * public structs whose size is not part of public ABI (most public structs in + * FFmpeg). New macros and enum values may be added. Behavior in undocumented + * situations may change slightly (and be documented). All those are accompanied + * by an entry in doc/APIchanges and incrementing either the minor or micro + * version number. + */ + +/** + * @defgroup lavu libavutil + * Common code shared across all FFmpeg libraries. + * + * @note + * libavutil is designed to be modular. In most cases, in order to use the + * functions provided by one component of libavutil you must explicitly include + * the specific header containing that feature. If you are only using + * media-related components, you could simply include libavutil/avutil.h, which + * brings in most of the "core" components. + * + * @{ + * + * @defgroup lavu_crypto Crypto and Hashing + * + * @{ + * @} + * + * @defgroup lavu_math Mathematics + * @{ + * + * @} + * + * @defgroup lavu_string String Manipulation + * + * @{ + * + * @} + * + * @defgroup lavu_mem Memory Management + * + * @{ + * + * @} + * + * @defgroup lavu_data Data Structures + * @{ + * + * @} + * + * @defgroup lavu_video Video related + * + * @{ + * + * @} + * + * @defgroup lavu_audio Audio related + * + * @{ + * + * @} + * + * @defgroup lavu_error Error Codes + * + * @{ + * + * @} + * + * @defgroup lavu_log Logging Facility + * + * @{ + * + * @} + * + * @defgroup lavu_misc Other + * + * @{ + * + * @defgroup preproc_misc Preprocessor String Macros + * + * @{ + * + * @} + * + * @defgroup version_utils Library Version Macros + * + * @{ + * + * @} + */ + + +/** + * @addtogroup lavu_ver + * @{ + */ + +/** + * Return the LIBAVUTIL_VERSION_INT constant. + */ +unsigned avutil_version(void); + +/** + * Return an informative version string. This usually is the actual release + * version number or a git commit description. This string has no fixed format + * and can change any time. It should never be parsed by code. + */ +const char *av_version_info(void); + +/** + * Return the libavutil build-time configuration. + */ +const char *avutil_configuration(void); + +/** + * Return the libavutil license. + */ +const char *avutil_license(void); + +/** + * @} + */ + +/** + * @addtogroup lavu_media Media Type + * @brief Media Type + */ + +enum AVMediaType { + AVMEDIA_TYPE_UNKNOWN = -1, ///< Usually treated as AVMEDIA_TYPE_DATA + AVMEDIA_TYPE_VIDEO, + AVMEDIA_TYPE_AUDIO, + AVMEDIA_TYPE_DATA, ///< Opaque data information usually continuous + AVMEDIA_TYPE_SUBTITLE, + AVMEDIA_TYPE_ATTACHMENT, ///< Opaque data information usually sparse + AVMEDIA_TYPE_NB +}; + +/** + * Return a string describing the media_type enum, NULL if media_type + * is unknown. + */ +const char *av_get_media_type_string(enum AVMediaType media_type); + +/** + * @defgroup lavu_const Constants + * @{ + * + * @defgroup lavu_enc Encoding specific + * + * @note those definition should move to avcodec + * @{ + */ + +#define FF_LAMBDA_SHIFT 7 +#define FF_LAMBDA_SCALE (1< + +/** + * @defgroup lavu_base64 Base64 + * @ingroup lavu_crypto + * @{ + */ + +/** + * Decode a base64-encoded string. + * + * @param out buffer for decoded data + * @param in null-terminated input string + * @param out_size size in bytes of the out buffer, must be at + * least 3/4 of the length of in, that is AV_BASE64_DECODE_SIZE(strlen(in)) + * @return number of bytes written, or a negative value in case of + * invalid input + */ +int av_base64_decode(uint8_t *out, const char *in, int out_size); + +/** + * Calculate the output size in bytes needed to decode a base64 string + * with length x to a data buffer. + */ +#define AV_BASE64_DECODE_SIZE(x) ((x) * 3LL / 4) + +/** + * Encode data to base64 and null-terminate. + * + * @param out buffer for encoded data + * @param out_size size in bytes of the out buffer (including the + * null terminator), must be at least AV_BASE64_SIZE(in_size) + * @param in input buffer containing the data to encode + * @param in_size size in bytes of the in buffer + * @return out or NULL in case of error + */ +char *av_base64_encode(char *out, int out_size, const uint8_t *in, int in_size); + +/** + * Calculate the output size needed to base64-encode x bytes to a + * null-terminated string. + */ +#define AV_BASE64_SIZE(x) (((x)+2) / 3 * 4 + 1) + + /** + * @} + */ + +#endif /* AVUTIL_BASE64_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/blowfish.h b/ThirdParty/ffmpeg/arm/include/libavutil/blowfish.h new file mode 100644 index 000000000..9e289a40d --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/blowfish.h @@ -0,0 +1,82 @@ +/* + * Blowfish algorithm + * Copyright (c) 2012 Samuel Pitoiset + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_BLOWFISH_H +#define AVUTIL_BLOWFISH_H + +#include + +/** + * @defgroup lavu_blowfish Blowfish + * @ingroup lavu_crypto + * @{ + */ + +#define AV_BF_ROUNDS 16 + +typedef struct AVBlowfish { + uint32_t p[AV_BF_ROUNDS + 2]; + uint32_t s[4][256]; +} AVBlowfish; + +/** + * Allocate an AVBlowfish context. + */ +AVBlowfish *av_blowfish_alloc(void); + +/** + * Initialize an AVBlowfish context. + * + * @param ctx an AVBlowfish context + * @param key a key + * @param key_len length of the key + */ +void av_blowfish_init(struct AVBlowfish *ctx, const uint8_t *key, int key_len); + +/** + * Encrypt or decrypt a buffer using a previously initialized context. + * + * @param ctx an AVBlowfish context + * @param xl left four bytes halves of input to be encrypted + * @param xr right four bytes halves of input to be encrypted + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_blowfish_crypt_ecb(struct AVBlowfish *ctx, uint32_t *xl, uint32_t *xr, + int decrypt); + +/** + * Encrypt or decrypt a buffer using a previously initialized context. + * + * @param ctx an AVBlowfish context + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param count number of 8 byte blocks + * @param iv initialization vector for CBC mode, if NULL ECB will be used + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_blowfish_crypt(struct AVBlowfish *ctx, uint8_t *dst, const uint8_t *src, + int count, uint8_t *iv, int decrypt); + +/** + * @} + */ + +#endif /* AVUTIL_BLOWFISH_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/bprint.h b/ThirdParty/ffmpeg/arm/include/libavutil/bprint.h new file mode 100644 index 000000000..c09b1ac1e --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/bprint.h @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2012 Nicolas George + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_BPRINT_H +#define AVUTIL_BPRINT_H + +#include + +#include "attributes.h" +#include "avstring.h" + +/** + * Define a structure with extra padding to a fixed size + * This helps ensuring binary compatibility with future versions. + */ + +#define FF_PAD_STRUCTURE(name, size, ...) \ +struct ff_pad_helper_##name { __VA_ARGS__ }; \ +typedef struct name { \ + __VA_ARGS__ \ + char reserved_padding[size - sizeof(struct ff_pad_helper_##name)]; \ +} name; + +/** + * Buffer to print data progressively + * + * The string buffer grows as necessary and is always 0-terminated. + * The content of the string is never accessed, and thus is + * encoding-agnostic and can even hold binary data. + * + * Small buffers are kept in the structure itself, and thus require no + * memory allocation at all (unless the contents of the buffer is needed + * after the structure goes out of scope). This is almost as lightweight as + * declaring a local "char buf[512]". + * + * The length of the string can go beyond the allocated size: the buffer is + * then truncated, but the functions still keep account of the actual total + * length. + * + * In other words, buf->len can be greater than buf->size and records the + * total length of what would have been to the buffer if there had been + * enough memory. + * + * Append operations do not need to be tested for failure: if a memory + * allocation fails, data stop being appended to the buffer, but the length + * is still updated. This situation can be tested with + * av_bprint_is_complete(). + * + * The size_max field determines several possible behaviours: + * + * size_max = -1 (= UINT_MAX) or any large value will let the buffer be + * reallocated as necessary, with an amortized linear cost. + * + * size_max = 0 prevents writing anything to the buffer: only the total + * length is computed. The write operations can then possibly be repeated in + * a buffer with exactly the necessary size + * (using size_init = size_max = len + 1). + * + * size_max = 1 is automatically replaced by the exact size available in the + * structure itself, thus ensuring no dynamic memory allocation. The + * internal buffer is large enough to hold a reasonable paragraph of text, + * such as the current paragraph. + */ + +FF_PAD_STRUCTURE(AVBPrint, 1024, + char *str; /**< string so far */ + unsigned len; /**< length so far */ + unsigned size; /**< allocated memory */ + unsigned size_max; /**< maximum allocated memory */ + char reserved_internal_buffer[1]; +) + +/** + * Convenience macros for special values for av_bprint_init() size_max + * parameter. + */ +#define AV_BPRINT_SIZE_UNLIMITED ((unsigned)-1) +#define AV_BPRINT_SIZE_AUTOMATIC 1 +#define AV_BPRINT_SIZE_COUNT_ONLY 0 + +/** + * Init a print buffer. + * + * @param buf buffer to init + * @param size_init initial size (including the final 0) + * @param size_max maximum size; + * 0 means do not write anything, just count the length; + * 1 is replaced by the maximum value for automatic storage; + * any large value means that the internal buffer will be + * reallocated as needed up to that limit; -1 is converted to + * UINT_MAX, the largest limit possible. + * Check also AV_BPRINT_SIZE_* macros. + */ +void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max); + +/** + * Init a print buffer using a pre-existing buffer. + * + * The buffer will not be reallocated. + * + * @param buf buffer structure to init + * @param buffer byte buffer to use for the string data + * @param size size of buffer + */ +void av_bprint_init_for_buffer(AVBPrint *buf, char *buffer, unsigned size); + +/** + * Append a formatted string to a print buffer. + */ +void av_bprintf(AVBPrint *buf, const char *fmt, ...) av_printf_format(2, 3); + +/** + * Append a formatted string to a print buffer. + */ +void av_vbprintf(AVBPrint *buf, const char *fmt, va_list vl_arg); + +/** + * Append char c n times to a print buffer. + */ +void av_bprint_chars(AVBPrint *buf, char c, unsigned n); + +/** + * Append data to a print buffer. + * + * param buf bprint buffer to use + * param data pointer to data + * param size size of data + */ +void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size); + +struct tm; +/** + * Append a formatted date and time to a print buffer. + * + * param buf bprint buffer to use + * param fmt date and time format string, see strftime() + * param tm broken-down time structure to translate + * + * @note due to poor design of the standard strftime function, it may + * produce poor results if the format string expands to a very long text and + * the bprint buffer is near the limit stated by the size_max option. + */ +void av_bprint_strftime(AVBPrint *buf, const char *fmt, const struct tm *tm); + +/** + * Allocate bytes in the buffer for external use. + * + * @param[in] buf buffer structure + * @param[in] size required size + * @param[out] mem pointer to the memory area + * @param[out] actual_size size of the memory area after allocation; + * can be larger or smaller than size + */ +void av_bprint_get_buffer(AVBPrint *buf, unsigned size, + unsigned char **mem, unsigned *actual_size); + +/** + * Reset the string to "" but keep internal allocated data. + */ +void av_bprint_clear(AVBPrint *buf); + +/** + * Test if the print buffer is complete (not truncated). + * + * It may have been truncated due to a memory allocation failure + * or the size_max limit (compare size and size_max if necessary). + */ +static inline int av_bprint_is_complete(const AVBPrint *buf) +{ + return buf->len < buf->size; +} + +/** + * Finalize a print buffer. + * + * The print buffer can no longer be used afterwards, + * but the len and size fields are still valid. + * + * @arg[out] ret_str if not NULL, used to return a permanent copy of the + * buffer contents, or NULL if memory allocation fails; + * if NULL, the buffer is discarded and freed + * @return 0 for success or error code (probably AVERROR(ENOMEM)) + */ +int av_bprint_finalize(AVBPrint *buf, char **ret_str); + +/** + * Escape the content in src and append it to dstbuf. + * + * @param dstbuf already inited destination bprint buffer + * @param src string containing the text to escape + * @param special_chars string containing the special characters which + * need to be escaped, can be NULL + * @param mode escape mode to employ, see AV_ESCAPE_MODE_* macros. + * Any unknown value for mode will be considered equivalent to + * AV_ESCAPE_MODE_BACKSLASH, but this behaviour can change without + * notice. + * @param flags flags which control how to escape, see AV_ESCAPE_FLAG_* macros + */ +void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars, + enum AVEscapeMode mode, int flags); + +#endif /* AVUTIL_BPRINT_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/bswap.h b/ThirdParty/ffmpeg/arm/include/libavutil/bswap.h new file mode 100644 index 000000000..91cb79538 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/bswap.h @@ -0,0 +1,109 @@ +/* + * copyright (c) 2006 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * byte swapping routines + */ + +#ifndef AVUTIL_BSWAP_H +#define AVUTIL_BSWAP_H + +#include +#include "libavutil/avconfig.h" +#include "attributes.h" + +#ifdef HAVE_AV_CONFIG_H + +#include "config.h" + +#if ARCH_AARCH64 +# include "aarch64/bswap.h" +#elif ARCH_ARM +# include "arm/bswap.h" +#elif ARCH_AVR32 +# include "avr32/bswap.h" +#elif ARCH_SH4 +# include "sh4/bswap.h" +#elif ARCH_X86 +# include "x86/bswap.h" +#endif + +#endif /* HAVE_AV_CONFIG_H */ + +#define AV_BSWAP16C(x) (((x) << 8 & 0xff00) | ((x) >> 8 & 0x00ff)) +#define AV_BSWAP32C(x) (AV_BSWAP16C(x) << 16 | AV_BSWAP16C((x) >> 16)) +#define AV_BSWAP64C(x) (AV_BSWAP32C(x) << 32 | AV_BSWAP32C((x) >> 32)) + +#define AV_BSWAPC(s, x) AV_BSWAP##s##C(x) + +#ifndef av_bswap16 +static av_always_inline av_const uint16_t av_bswap16(uint16_t x) +{ + x= (x>>8) | (x<<8); + return x; +} +#endif + +#ifndef av_bswap32 +static av_always_inline av_const uint32_t av_bswap32(uint32_t x) +{ + return AV_BSWAP32C(x); +} +#endif + +#ifndef av_bswap64 +static inline uint64_t av_const av_bswap64(uint64_t x) +{ + return (uint64_t)av_bswap32(x) << 32 | av_bswap32(x >> 32); +} +#endif + +// be2ne ... big-endian to native-endian +// le2ne ... little-endian to native-endian + +#if AV_HAVE_BIGENDIAN +#define av_be2ne16(x) (x) +#define av_be2ne32(x) (x) +#define av_be2ne64(x) (x) +#define av_le2ne16(x) av_bswap16(x) +#define av_le2ne32(x) av_bswap32(x) +#define av_le2ne64(x) av_bswap64(x) +#define AV_BE2NEC(s, x) (x) +#define AV_LE2NEC(s, x) AV_BSWAPC(s, x) +#else +#define av_be2ne16(x) av_bswap16(x) +#define av_be2ne32(x) av_bswap32(x) +#define av_be2ne64(x) av_bswap64(x) +#define av_le2ne16(x) (x) +#define av_le2ne32(x) (x) +#define av_le2ne64(x) (x) +#define AV_BE2NEC(s, x) AV_BSWAPC(s, x) +#define AV_LE2NEC(s, x) (x) +#endif + +#define AV_BE2NE16C(x) AV_BE2NEC(16, x) +#define AV_BE2NE32C(x) AV_BE2NEC(32, x) +#define AV_BE2NE64C(x) AV_BE2NEC(64, x) +#define AV_LE2NE16C(x) AV_LE2NEC(16, x) +#define AV_LE2NE32C(x) AV_LE2NEC(32, x) +#define AV_LE2NE64C(x) AV_LE2NEC(64, x) + +#endif /* AVUTIL_BSWAP_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/buffer.h b/ThirdParty/ffmpeg/arm/include/libavutil/buffer.h new file mode 100644 index 000000000..c0f3f6cc9 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/buffer.h @@ -0,0 +1,305 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_buffer + * refcounted data buffer API + */ + +#ifndef AVUTIL_BUFFER_H +#define AVUTIL_BUFFER_H + +#include + +/** + * @defgroup lavu_buffer AVBuffer + * @ingroup lavu_data + * + * @{ + * AVBuffer is an API for reference-counted data buffers. + * + * There are two core objects in this API -- AVBuffer and AVBufferRef. AVBuffer + * represents the data buffer itself; it is opaque and not meant to be accessed + * by the caller directly, but only through AVBufferRef. However, the caller may + * e.g. compare two AVBuffer pointers to check whether two different references + * are describing the same data buffer. AVBufferRef represents a single + * reference to an AVBuffer and it is the object that may be manipulated by the + * caller directly. + * + * There are two functions provided for creating a new AVBuffer with a single + * reference -- av_buffer_alloc() to just allocate a new buffer, and + * av_buffer_create() to wrap an existing array in an AVBuffer. From an existing + * reference, additional references may be created with av_buffer_ref(). + * Use av_buffer_unref() to free a reference (this will automatically free the + * data once all the references are freed). + * + * The convention throughout this API and the rest of FFmpeg is such that the + * buffer is considered writable if there exists only one reference to it (and + * it has not been marked as read-only). The av_buffer_is_writable() function is + * provided to check whether this is true and av_buffer_make_writable() will + * automatically create a new writable buffer when necessary. + * Of course nothing prevents the calling code from violating this convention, + * however that is safe only when all the existing references are under its + * control. + * + * @note Referencing and unreferencing the buffers is thread-safe and thus + * may be done from multiple threads simultaneously without any need for + * additional locking. + * + * @note Two different references to the same buffer can point to different + * parts of the buffer (i.e. their AVBufferRef.data will not be equal). + */ + +/** + * A reference counted buffer type. It is opaque and is meant to be used through + * references (AVBufferRef). + */ +typedef struct AVBuffer AVBuffer; + +/** + * A reference to a data buffer. + * + * The size of this struct is not a part of the public ABI and it is not meant + * to be allocated directly. + */ +typedef struct AVBufferRef { + AVBuffer *buffer; + + /** + * The data buffer. It is considered writable if and only if + * this is the only reference to the buffer, in which case + * av_buffer_is_writable() returns 1. + */ + uint8_t *data; + /** + * Size of data in bytes. + */ + int size; +} AVBufferRef; + +/** + * Allocate an AVBuffer of the given size using av_malloc(). + * + * @return an AVBufferRef of given size or NULL when out of memory + */ +AVBufferRef *av_buffer_alloc(int size); + +/** + * Same as av_buffer_alloc(), except the returned buffer will be initialized + * to zero. + */ +AVBufferRef *av_buffer_allocz(int size); + +/** + * Always treat the buffer as read-only, even when it has only one + * reference. + */ +#define AV_BUFFER_FLAG_READONLY (1 << 0) + +/** + * Create an AVBuffer from an existing array. + * + * If this function is successful, data is owned by the AVBuffer. The caller may + * only access data through the returned AVBufferRef and references derived from + * it. + * If this function fails, data is left untouched. + * @param data data array + * @param size size of data in bytes + * @param free a callback for freeing this buffer's data + * @param opaque parameter to be got for processing or passed to free + * @param flags a combination of AV_BUFFER_FLAG_* + * + * @return an AVBufferRef referring to data on success, NULL on failure. + */ +AVBufferRef *av_buffer_create(uint8_t *data, int size, + void (*free)(void *opaque, uint8_t *data), + void *opaque, int flags); + +/** + * Default free callback, which calls av_free() on the buffer data. + * This function is meant to be passed to av_buffer_create(), not called + * directly. + */ +void av_buffer_default_free(void *opaque, uint8_t *data); + +/** + * Create a new reference to an AVBuffer. + * + * @return a new AVBufferRef referring to the same AVBuffer as buf or NULL on + * failure. + */ +AVBufferRef *av_buffer_ref(AVBufferRef *buf); + +/** + * Free a given reference and automatically free the buffer if there are no more + * references to it. + * + * @param buf the reference to be freed. The pointer is set to NULL on return. + */ +void av_buffer_unref(AVBufferRef **buf); + +/** + * @return 1 if the caller may write to the data referred to by buf (which is + * true if and only if buf is the only reference to the underlying AVBuffer). + * Return 0 otherwise. + * A positive answer is valid until av_buffer_ref() is called on buf. + */ +int av_buffer_is_writable(const AVBufferRef *buf); + +/** + * @return the opaque parameter set by av_buffer_create. + */ +void *av_buffer_get_opaque(const AVBufferRef *buf); + +int av_buffer_get_ref_count(const AVBufferRef *buf); + +/** + * Create a writable reference from a given buffer reference, avoiding data copy + * if possible. + * + * @param buf buffer reference to make writable. On success, buf is either left + * untouched, or it is unreferenced and a new writable AVBufferRef is + * written in its place. On failure, buf is left untouched. + * @return 0 on success, a negative AVERROR on failure. + */ +int av_buffer_make_writable(AVBufferRef **buf); + +/** + * Reallocate a given buffer. + * + * @param buf a buffer reference to reallocate. On success, buf will be + * unreferenced and a new reference with the required size will be + * written in its place. On failure buf will be left untouched. *buf + * may be NULL, then a new buffer is allocated. + * @param size required new buffer size. + * @return 0 on success, a negative AVERROR on failure. + * + * @note the buffer is actually reallocated with av_realloc() only if it was + * initially allocated through av_buffer_realloc(NULL) and there is only one + * reference to it (i.e. the one passed to this function). In all other cases + * a new buffer is allocated and the data is copied. + */ +int av_buffer_realloc(AVBufferRef **buf, int size); + +/** + * @} + */ + +/** + * @defgroup lavu_bufferpool AVBufferPool + * @ingroup lavu_data + * + * @{ + * AVBufferPool is an API for a lock-free thread-safe pool of AVBuffers. + * + * Frequently allocating and freeing large buffers may be slow. AVBufferPool is + * meant to solve this in cases when the caller needs a set of buffers of the + * same size (the most obvious use case being buffers for raw video or audio + * frames). + * + * At the beginning, the user must call av_buffer_pool_init() to create the + * buffer pool. Then whenever a buffer is needed, call av_buffer_pool_get() to + * get a reference to a new buffer, similar to av_buffer_alloc(). This new + * reference works in all aspects the same way as the one created by + * av_buffer_alloc(). However, when the last reference to this buffer is + * unreferenced, it is returned to the pool instead of being freed and will be + * reused for subsequent av_buffer_pool_get() calls. + * + * When the caller is done with the pool and no longer needs to allocate any new + * buffers, av_buffer_pool_uninit() must be called to mark the pool as freeable. + * Once all the buffers are released, it will automatically be freed. + * + * Allocating and releasing buffers with this API is thread-safe as long as + * either the default alloc callback is used, or the user-supplied one is + * thread-safe. + */ + +/** + * The buffer pool. This structure is opaque and not meant to be accessed + * directly. It is allocated with av_buffer_pool_init() and freed with + * av_buffer_pool_uninit(). + */ +typedef struct AVBufferPool AVBufferPool; + +/** + * Allocate and initialize a buffer pool. + * + * @param size size of each buffer in this pool + * @param alloc a function that will be used to allocate new buffers when the + * pool is empty. May be NULL, then the default allocator will be used + * (av_buffer_alloc()). + * @return newly created buffer pool on success, NULL on error. + */ +AVBufferPool *av_buffer_pool_init(int size, AVBufferRef* (*alloc)(int size)); + +/** + * Allocate and initialize a buffer pool with a more complex allocator. + * + * @param size size of each buffer in this pool + * @param opaque arbitrary user data used by the allocator + * @param alloc a function that will be used to allocate new buffers when the + * pool is empty. May be NULL, then the default allocator will be + * used (av_buffer_alloc()). + * @param pool_free a function that will be called immediately before the pool + * is freed. I.e. after av_buffer_pool_uninit() is called + * by the caller and all the frames are returned to the pool + * and freed. It is intended to uninitialize the user opaque + * data. May be NULL. + * @return newly created buffer pool on success, NULL on error. + */ +AVBufferPool *av_buffer_pool_init2(int size, void *opaque, + AVBufferRef* (*alloc)(void *opaque, int size), + void (*pool_free)(void *opaque)); + +/** + * Mark the pool as being available for freeing. It will actually be freed only + * once all the allocated buffers associated with the pool are released. Thus it + * is safe to call this function while some of the allocated buffers are still + * in use. + * + * @param pool pointer to the pool to be freed. It will be set to NULL. + */ +void av_buffer_pool_uninit(AVBufferPool **pool); + +/** + * Allocate a new AVBuffer, reusing an old buffer from the pool when available. + * This function may be called simultaneously from multiple threads. + * + * @return a reference to the new buffer on success, NULL on error. + */ +AVBufferRef *av_buffer_pool_get(AVBufferPool *pool); + +/** + * Query the original opaque parameter of an allocated buffer in the pool. + * + * @param ref a buffer reference to a buffer returned by av_buffer_pool_get. + * @return the opaque parameter set by the buffer allocator function of the + * buffer pool. + * + * @note the opaque parameter of ref is used by the buffer pool implementation, + * therefore you have to use this function to access the original opaque + * parameter of an allocated buffer. + */ +void *av_buffer_pool_buffer_get_opaque(AVBufferRef *ref); + +/** + * @} + */ + +#endif /* AVUTIL_BUFFER_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/camellia.h b/ThirdParty/ffmpeg/arm/include/libavutil/camellia.h new file mode 100644 index 000000000..e674c9b9a --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/camellia.h @@ -0,0 +1,70 @@ +/* + * An implementation of the CAMELLIA algorithm as mentioned in RFC3713 + * Copyright (c) 2014 Supraja Meedinti + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_CAMELLIA_H +#define AVUTIL_CAMELLIA_H + +#include + + +/** + * @file + * @brief Public header for libavutil CAMELLIA algorithm + * @defgroup lavu_camellia CAMELLIA + * @ingroup lavu_crypto + * @{ + */ + +extern const int av_camellia_size; + +struct AVCAMELLIA; + +/** + * Allocate an AVCAMELLIA context + * To free the struct: av_free(ptr) + */ +struct AVCAMELLIA *av_camellia_alloc(void); + +/** + * Initialize an AVCAMELLIA context. + * + * @param ctx an AVCAMELLIA context + * @param key a key of 16, 24, 32 bytes used for encryption/decryption + * @param key_bits number of keybits: possible are 128, 192, 256 + */ +int av_camellia_init(struct AVCAMELLIA *ctx, const uint8_t *key, int key_bits); + +/** + * Encrypt or decrypt a buffer using a previously initialized context + * + * @param ctx an AVCAMELLIA context + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param count number of 16 byte blocks + * @paran iv initialization vector for CBC mode, NULL for ECB mode + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_camellia_crypt(struct AVCAMELLIA *ctx, uint8_t *dst, const uint8_t *src, int count, uint8_t* iv, int decrypt); + +/** + * @} + */ +#endif /* AVUTIL_CAMELLIA_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/cast5.h b/ThirdParty/ffmpeg/arm/include/libavutil/cast5.h new file mode 100644 index 000000000..ad5b347e6 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/cast5.h @@ -0,0 +1,80 @@ +/* + * An implementation of the CAST128 algorithm as mentioned in RFC2144 + * Copyright (c) 2014 Supraja Meedinti + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_CAST5_H +#define AVUTIL_CAST5_H + +#include + + +/** + * @file + * @brief Public header for libavutil CAST5 algorithm + * @defgroup lavu_cast5 CAST5 + * @ingroup lavu_crypto + * @{ + */ + +extern const int av_cast5_size; + +struct AVCAST5; + +/** + * Allocate an AVCAST5 context + * To free the struct: av_free(ptr) + */ +struct AVCAST5 *av_cast5_alloc(void); +/** + * Initialize an AVCAST5 context. + * + * @param ctx an AVCAST5 context + * @param key a key of 5,6,...16 bytes used for encryption/decryption + * @param key_bits number of keybits: possible are 40,48,...,128 + * @return 0 on success, less than 0 on failure + */ +int av_cast5_init(struct AVCAST5 *ctx, const uint8_t *key, int key_bits); + +/** + * Encrypt or decrypt a buffer using a previously initialized context, ECB mode only + * + * @param ctx an AVCAST5 context + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param count number of 8 byte blocks + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_cast5_crypt(struct AVCAST5 *ctx, uint8_t *dst, const uint8_t *src, int count, int decrypt); + +/** + * Encrypt or decrypt a buffer using a previously initialized context + * + * @param ctx an AVCAST5 context + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param count number of 8 byte blocks + * @param iv initialization vector for CBC mode, NULL for ECB mode + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_cast5_crypt2(struct AVCAST5 *ctx, uint8_t *dst, const uint8_t *src, int count, uint8_t *iv, int decrypt); +/** + * @} + */ +#endif /* AVUTIL_CAST5_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/channel_layout.h b/ThirdParty/ffmpeg/arm/include/libavutil/channel_layout.h new file mode 100644 index 000000000..50bb8f03c --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/channel_layout.h @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2006 Michael Niedermayer + * Copyright (c) 2008 Peter Ross + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_CHANNEL_LAYOUT_H +#define AVUTIL_CHANNEL_LAYOUT_H + +#include + +/** + * @file + * audio channel layout utility functions + */ + +/** + * @addtogroup lavu_audio + * @{ + */ + +/** + * @defgroup channel_masks Audio channel masks + * + * A channel layout is a 64-bits integer with a bit set for every channel. + * The number of bits set must be equal to the number of channels. + * The value 0 means that the channel layout is not known. + * @note this data structure is not powerful enough to handle channels + * combinations that have the same channel multiple times, such as + * dual-mono. + * + * @{ + */ +#define AV_CH_FRONT_LEFT 0x00000001 +#define AV_CH_FRONT_RIGHT 0x00000002 +#define AV_CH_FRONT_CENTER 0x00000004 +#define AV_CH_LOW_FREQUENCY 0x00000008 +#define AV_CH_BACK_LEFT 0x00000010 +#define AV_CH_BACK_RIGHT 0x00000020 +#define AV_CH_FRONT_LEFT_OF_CENTER 0x00000040 +#define AV_CH_FRONT_RIGHT_OF_CENTER 0x00000080 +#define AV_CH_BACK_CENTER 0x00000100 +#define AV_CH_SIDE_LEFT 0x00000200 +#define AV_CH_SIDE_RIGHT 0x00000400 +#define AV_CH_TOP_CENTER 0x00000800 +#define AV_CH_TOP_FRONT_LEFT 0x00001000 +#define AV_CH_TOP_FRONT_CENTER 0x00002000 +#define AV_CH_TOP_FRONT_RIGHT 0x00004000 +#define AV_CH_TOP_BACK_LEFT 0x00008000 +#define AV_CH_TOP_BACK_CENTER 0x00010000 +#define AV_CH_TOP_BACK_RIGHT 0x00020000 +#define AV_CH_STEREO_LEFT 0x20000000 ///< Stereo downmix. +#define AV_CH_STEREO_RIGHT 0x40000000 ///< See AV_CH_STEREO_LEFT. +#define AV_CH_WIDE_LEFT 0x0000000080000000ULL +#define AV_CH_WIDE_RIGHT 0x0000000100000000ULL +#define AV_CH_SURROUND_DIRECT_LEFT 0x0000000200000000ULL +#define AV_CH_SURROUND_DIRECT_RIGHT 0x0000000400000000ULL +#define AV_CH_LOW_FREQUENCY_2 0x0000000800000000ULL + +/** Channel mask value used for AVCodecContext.request_channel_layout + to indicate that the user requests the channel order of the decoder output + to be the native codec channel order. */ +#define AV_CH_LAYOUT_NATIVE 0x8000000000000000ULL + +/** + * @} + * @defgroup channel_mask_c Audio channel layouts + * @{ + * */ +#define AV_CH_LAYOUT_MONO (AV_CH_FRONT_CENTER) +#define AV_CH_LAYOUT_STEREO (AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT) +#define AV_CH_LAYOUT_2POINT1 (AV_CH_LAYOUT_STEREO|AV_CH_LOW_FREQUENCY) +#define AV_CH_LAYOUT_2_1 (AV_CH_LAYOUT_STEREO|AV_CH_BACK_CENTER) +#define AV_CH_LAYOUT_SURROUND (AV_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER) +#define AV_CH_LAYOUT_3POINT1 (AV_CH_LAYOUT_SURROUND|AV_CH_LOW_FREQUENCY) +#define AV_CH_LAYOUT_4POINT0 (AV_CH_LAYOUT_SURROUND|AV_CH_BACK_CENTER) +#define AV_CH_LAYOUT_4POINT1 (AV_CH_LAYOUT_4POINT0|AV_CH_LOW_FREQUENCY) +#define AV_CH_LAYOUT_2_2 (AV_CH_LAYOUT_STEREO|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT) +#define AV_CH_LAYOUT_QUAD (AV_CH_LAYOUT_STEREO|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) +#define AV_CH_LAYOUT_5POINT0 (AV_CH_LAYOUT_SURROUND|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT) +#define AV_CH_LAYOUT_5POINT1 (AV_CH_LAYOUT_5POINT0|AV_CH_LOW_FREQUENCY) +#define AV_CH_LAYOUT_5POINT0_BACK (AV_CH_LAYOUT_SURROUND|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) +#define AV_CH_LAYOUT_5POINT1_BACK (AV_CH_LAYOUT_5POINT0_BACK|AV_CH_LOW_FREQUENCY) +#define AV_CH_LAYOUT_6POINT0 (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_CENTER) +#define AV_CH_LAYOUT_6POINT0_FRONT (AV_CH_LAYOUT_2_2|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER) +#define AV_CH_LAYOUT_HEXAGONAL (AV_CH_LAYOUT_5POINT0_BACK|AV_CH_BACK_CENTER) +#define AV_CH_LAYOUT_6POINT1 (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_CENTER) +#define AV_CH_LAYOUT_6POINT1_BACK (AV_CH_LAYOUT_5POINT1_BACK|AV_CH_BACK_CENTER) +#define AV_CH_LAYOUT_6POINT1_FRONT (AV_CH_LAYOUT_6POINT0_FRONT|AV_CH_LOW_FREQUENCY) +#define AV_CH_LAYOUT_7POINT0 (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) +#define AV_CH_LAYOUT_7POINT0_FRONT (AV_CH_LAYOUT_5POINT0|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER) +#define AV_CH_LAYOUT_7POINT1 (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) +#define AV_CH_LAYOUT_7POINT1_WIDE (AV_CH_LAYOUT_5POINT1|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER) +#define AV_CH_LAYOUT_7POINT1_WIDE_BACK (AV_CH_LAYOUT_5POINT1_BACK|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER) +#define AV_CH_LAYOUT_OCTAGONAL (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_CENTER|AV_CH_BACK_RIGHT) +#define AV_CH_LAYOUT_HEXADECAGONAL (AV_CH_LAYOUT_OCTAGONAL|AV_CH_WIDE_LEFT|AV_CH_WIDE_RIGHT|AV_CH_TOP_BACK_LEFT|AV_CH_TOP_BACK_RIGHT|AV_CH_TOP_BACK_CENTER|AV_CH_TOP_FRONT_CENTER|AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_FRONT_RIGHT) +#define AV_CH_LAYOUT_STEREO_DOWNMIX (AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT) + +enum AVMatrixEncoding { + AV_MATRIX_ENCODING_NONE, + AV_MATRIX_ENCODING_DOLBY, + AV_MATRIX_ENCODING_DPLII, + AV_MATRIX_ENCODING_DPLIIX, + AV_MATRIX_ENCODING_DPLIIZ, + AV_MATRIX_ENCODING_DOLBYEX, + AV_MATRIX_ENCODING_DOLBYHEADPHONE, + AV_MATRIX_ENCODING_NB +}; + +/** + * Return a channel layout id that matches name, or 0 if no match is found. + * + * name can be one or several of the following notations, + * separated by '+' or '|': + * - the name of an usual channel layout (mono, stereo, 4.0, quad, 5.0, + * 5.0(side), 5.1, 5.1(side), 7.1, 7.1(wide), downmix); + * - the name of a single channel (FL, FR, FC, LFE, BL, BR, FLC, FRC, BC, + * SL, SR, TC, TFL, TFC, TFR, TBL, TBC, TBR, DL, DR); + * - a number of channels, in decimal, followed by 'c', yielding + * the default channel layout for that number of channels (@see + * av_get_default_channel_layout); + * - a channel layout mask, in hexadecimal starting with "0x" (see the + * AV_CH_* macros). + * + * Example: "stereo+FC" = "2c+FC" = "2c+1c" = "0x7" + */ +uint64_t av_get_channel_layout(const char *name); + +/** + * Return a channel layout and the number of channels based on the specified name. + * + * This function is similar to (@see av_get_channel_layout), but can also parse + * unknown channel layout specifications. + * + * @param[in] name channel layout specification string + * @param[out] channel_layout parsed channel layout (0 if unknown) + * @param[out] nb_channels number of channels + * + * @return 0 on success, AVERROR(EINVAL) if the parsing fails. + */ +int av_get_extended_channel_layout(const char *name, uint64_t* channel_layout, int* nb_channels); + +/** + * Return a description of a channel layout. + * If nb_channels is <= 0, it is guessed from the channel_layout. + * + * @param buf put here the string containing the channel layout + * @param buf_size size in bytes of the buffer + */ +void av_get_channel_layout_string(char *buf, int buf_size, int nb_channels, uint64_t channel_layout); + +struct AVBPrint; +/** + * Append a description of a channel layout to a bprint buffer. + */ +void av_bprint_channel_layout(struct AVBPrint *bp, int nb_channels, uint64_t channel_layout); + +/** + * Return the number of channels in the channel layout. + */ +int av_get_channel_layout_nb_channels(uint64_t channel_layout); + +/** + * Return default channel layout for a given number of channels. + */ +int64_t av_get_default_channel_layout(int nb_channels); + +/** + * Get the index of a channel in channel_layout. + * + * @param channel a channel layout describing exactly one channel which must be + * present in channel_layout. + * + * @return index of channel in channel_layout on success, a negative AVERROR + * on error. + */ +int av_get_channel_layout_channel_index(uint64_t channel_layout, + uint64_t channel); + +/** + * Get the channel with the given index in channel_layout. + */ +uint64_t av_channel_layout_extract_channel(uint64_t channel_layout, int index); + +/** + * Get the name of a given channel. + * + * @return channel name on success, NULL on error. + */ +const char *av_get_channel_name(uint64_t channel); + +/** + * Get the description of a given channel. + * + * @param channel a channel layout with a single channel + * @return channel description on success, NULL on error + */ +const char *av_get_channel_description(uint64_t channel); + +/** + * Get the value and name of a standard channel layout. + * + * @param[in] index index in an internal list, starting at 0 + * @param[out] layout channel layout mask + * @param[out] name name of the layout + * @return 0 if the layout exists, + * <0 if index is beyond the limits + */ +int av_get_standard_channel_layout(unsigned index, uint64_t *layout, + const char **name); + +/** + * @} + * @} + */ + +#endif /* AVUTIL_CHANNEL_LAYOUT_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/common.h b/ThirdParty/ffmpeg/arm/include/libavutil/common.h new file mode 100644 index 000000000..2777cea9f --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/common.h @@ -0,0 +1,608 @@ +/* + * copyright (c) 2006 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * common internal and external API header + */ + +#ifndef AVUTIL_COMMON_H +#define AVUTIL_COMMON_H + +#if defined(__cplusplus) && !defined(__STDC_CONSTANT_MACROS) && !defined(UINT64_C) +#error missing -D__STDC_CONSTANT_MACROS / #define __STDC_CONSTANT_MACROS +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "attributes.h" +#include "macros.h" +#include "version.h" +#include "libavutil/avconfig.h" + +#if AV_HAVE_BIGENDIAN +# define AV_NE(be, le) (be) +#else +# define AV_NE(be, le) (le) +#endif + +//rounded division & shift +#define RSHIFT(a,b) ((a) > 0 ? ((a) + ((1<<(b))>>1))>>(b) : ((a) + ((1<<(b))>>1)-1)>>(b)) +/* assume b>0 */ +#define ROUNDED_DIV(a,b) (((a)>=0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b)) +/* Fast a/(1<=0 and b>=0 */ +#define AV_CEIL_RSHIFT(a,b) (!av_builtin_constant_p(b) ? -((-(a)) >> (b)) \ + : ((a) + (1<<(b)) - 1) >> (b)) +/* Backwards compat. */ +#define FF_CEIL_RSHIFT AV_CEIL_RSHIFT + +#define FFUDIV(a,b) (((a)>0 ?(a):(a)-(b)+1) / (b)) +#define FFUMOD(a,b) ((a)-(b)*FFUDIV(a,b)) + +/** + * Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they + * are not representable as absolute values of their type. This is the same + * as with *abs() + * @see FFNABS() + */ +#define FFABS(a) ((a) >= 0 ? (a) : (-(a))) +#define FFSIGN(a) ((a) > 0 ? 1 : -1) + +/** + * Negative Absolute value. + * this works for all integers of all types. + * As with many macros, this evaluates its argument twice, it thus must not have + * a sideeffect, that is FFNABS(x++) has undefined behavior. + */ +#define FFNABS(a) ((a) <= 0 ? (a) : (-(a))) + +/** + * Comparator. + * For two numerical expressions x and y, gives 1 if x > y, -1 if x < y, and 0 + * if x == y. This is useful for instance in a qsort comparator callback. + * Furthermore, compilers are able to optimize this to branchless code, and + * there is no risk of overflow with signed types. + * As with many macros, this evaluates its argument multiple times, it thus + * must not have a side-effect. + */ +#define FFDIFFSIGN(x,y) (((x)>(y)) - ((x)<(y))) + +#define FFMAX(a,b) ((a) > (b) ? (a) : (b)) +#define FFMAX3(a,b,c) FFMAX(FFMAX(a,b),c) +#define FFMIN(a,b) ((a) > (b) ? (b) : (a)) +#define FFMIN3(a,b,c) FFMIN(FFMIN(a,b),c) + +#define FFSWAP(type,a,b) do{type SWAP_tmp= b; b= a; a= SWAP_tmp;}while(0) +#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0])) + +/* misc math functions */ + +#ifdef HAVE_AV_CONFIG_H +# include "config.h" +# include "intmath.h" +#endif + +/* Pull in unguarded fallback defines at the end of this file. */ +#include "common.h" + +#ifndef av_log2 +av_const int av_log2(unsigned v); +#endif + +#ifndef av_log2_16bit +av_const int av_log2_16bit(unsigned v); +#endif + +/** + * Clip a signed integer value into the amin-amax range. + * @param a value to clip + * @param amin minimum value of the clip range + * @param amax maximum value of the clip range + * @return clipped value + */ +static av_always_inline av_const int av_clip_c(int a, int amin, int amax) +{ +#if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 + if (amin > amax) abort(); +#endif + if (a < amin) return amin; + else if (a > amax) return amax; + else return a; +} + +/** + * Clip a signed 64bit integer value into the amin-amax range. + * @param a value to clip + * @param amin minimum value of the clip range + * @param amax maximum value of the clip range + * @return clipped value + */ +static av_always_inline av_const int64_t av_clip64_c(int64_t a, int64_t amin, int64_t amax) +{ +#if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 + if (amin > amax) abort(); +#endif + if (a < amin) return amin; + else if (a > amax) return amax; + else return a; +} + +/** + * Clip a signed integer value into the 0-255 range. + * @param a value to clip + * @return clipped value + */ +static av_always_inline av_const uint8_t av_clip_uint8_c(int a) +{ + if (a&(~0xFF)) return (~a)>>31; + else return a; +} + +/** + * Clip a signed integer value into the -128,127 range. + * @param a value to clip + * @return clipped value + */ +static av_always_inline av_const int8_t av_clip_int8_c(int a) +{ + if ((a+0x80U) & ~0xFF) return (a>>31) ^ 0x7F; + else return a; +} + +/** + * Clip a signed integer value into the 0-65535 range. + * @param a value to clip + * @return clipped value + */ +static av_always_inline av_const uint16_t av_clip_uint16_c(int a) +{ + if (a&(~0xFFFF)) return (~a)>>31; + else return a; +} + +/** + * Clip a signed integer value into the -32768,32767 range. + * @param a value to clip + * @return clipped value + */ +static av_always_inline av_const int16_t av_clip_int16_c(int a) +{ + if ((a+0x8000U) & ~0xFFFF) return (a>>31) ^ 0x7FFF; + else return a; +} + +/** + * Clip a signed 64-bit integer value into the -2147483648,2147483647 range. + * @param a value to clip + * @return clipped value + */ +static av_always_inline av_const int32_t av_clipl_int32_c(int64_t a) +{ + if ((a+0x80000000u) & ~UINT64_C(0xFFFFFFFF)) return (int32_t)((a>>63) ^ 0x7FFFFFFF); + else return (int32_t)a; +} + +/** + * Clip a signed integer into the -(2^p),(2^p-1) range. + * @param a value to clip + * @param p bit position to clip at + * @return clipped value + */ +static av_always_inline av_const int av_clip_intp2_c(int a, int p) +{ + if (((unsigned)a + (1 << p)) & ~((2 << p) - 1)) + return (a >> 31) ^ ((1 << p) - 1); + else + return a; +} + +/** + * Clip a signed integer to an unsigned power of two range. + * @param a value to clip + * @param p bit position to clip at + * @return clipped value + */ +static av_always_inline av_const unsigned av_clip_uintp2_c(int a, int p) +{ + if (a & ~((1<> 31 & ((1<= 0 && a >= INT64_MAX - b) + return INT64_MAX; + if (b <= 0 && a <= INT64_MIN - b) + return INT64_MIN; + return a + b; +#endif +} + +/** + * Subtract two signed 64-bit values with saturation. + * + * @param a one value + * @param b another value + * @return difference with signed saturation + */ +static av_always_inline int64_t av_sat_sub64_c(int64_t a, int64_t b) { +#if (!defined(__INTEL_COMPILER) && AV_GCC_VERSION_AT_LEAST(5,1)) || AV_HAS_BUILTIN(__builtin_sub_overflow) + int64_t tmp; + return !__builtin_sub_overflow(a, b, &tmp) ? tmp : (tmp < 0 ? INT64_MAX : INT64_MIN); +#else + if (b <= 0 && a >= INT64_MAX + b) + return INT64_MAX; + if (b >= 0 && a <= INT64_MIN + b) + return INT64_MIN; + return a - b; +#endif +} + +/** + * Clip a float value into the amin-amax range. + * @param a value to clip + * @param amin minimum value of the clip range + * @param amax maximum value of the clip range + * @return clipped value + */ +static av_always_inline av_const float av_clipf_c(float a, float amin, float amax) +{ +#if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 + if (amin > amax) abort(); +#endif + if (a < amin) return amin; + else if (a > amax) return amax; + else return a; +} + +/** + * Clip a double value into the amin-amax range. + * @param a value to clip + * @param amin minimum value of the clip range + * @param amax maximum value of the clip range + * @return clipped value + */ +static av_always_inline av_const double av_clipd_c(double a, double amin, double amax) +{ +#if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2 + if (amin > amax) abort(); +#endif + if (a < amin) return amin; + else if (a > amax) return amax; + else return a; +} + +/** Compute ceil(log2(x)). + * @param x value used to compute ceil(log2(x)) + * @return computed ceiling of log2(x) + */ +static av_always_inline av_const int av_ceil_log2_c(int x) +{ + return av_log2((x - 1) << 1); +} + +/** + * Count number of bits set to one in x + * @param x value to count bits of + * @return the number of bits set to one in x + */ +static av_always_inline av_const int av_popcount_c(uint32_t x) +{ + x -= (x >> 1) & 0x55555555; + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + x = (x + (x >> 4)) & 0x0F0F0F0F; + x += x >> 8; + return (x + (x >> 16)) & 0x3F; +} + +/** + * Count number of bits set to one in x + * @param x value to count bits of + * @return the number of bits set to one in x + */ +static av_always_inline av_const int av_popcount64_c(uint64_t x) +{ + return av_popcount((uint32_t)x) + av_popcount((uint32_t)(x >> 32)); +} + +static av_always_inline av_const int av_parity_c(uint32_t v) +{ + return av_popcount(v) & 1; +} + +#define MKTAG(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((unsigned)(d) << 24)) +#define MKBETAG(a,b,c,d) ((d) | ((c) << 8) | ((b) << 16) | ((unsigned)(a) << 24)) + +/** + * Convert a UTF-8 character (up to 4 bytes) to its 32-bit UCS-4 encoded form. + * + * @param val Output value, must be an lvalue of type uint32_t. + * @param GET_BYTE Expression reading one byte from the input. + * Evaluated up to 7 times (4 for the currently + * assigned Unicode range). With a memory buffer + * input, this could be *ptr++, or if you want to make sure + * that *ptr stops at the end of a NULL terminated string then + * *ptr ? *ptr++ : 0 + * @param ERROR Expression to be evaluated on invalid input, + * typically a goto statement. + * + * @warning ERROR should not contain a loop control statement which + * could interact with the internal while loop, and should force an + * exit from the macro code (e.g. through a goto or a return) in order + * to prevent undefined results. + */ +#define GET_UTF8(val, GET_BYTE, ERROR)\ + val= (GET_BYTE);\ + {\ + uint32_t top = (val & 128) >> 1;\ + if ((val & 0xc0) == 0x80 || val >= 0xFE)\ + {ERROR}\ + while (val & top) {\ + unsigned int tmp = (GET_BYTE) - 128;\ + if(tmp>>6)\ + {ERROR}\ + val= (val<<6) + tmp;\ + top <<= 5;\ + }\ + val &= (top << 1) - 1;\ + } + +/** + * Convert a UTF-16 character (2 or 4 bytes) to its 32-bit UCS-4 encoded form. + * + * @param val Output value, must be an lvalue of type uint32_t. + * @param GET_16BIT Expression returning two bytes of UTF-16 data converted + * to native byte order. Evaluated one or two times. + * @param ERROR Expression to be evaluated on invalid input, + * typically a goto statement. + */ +#define GET_UTF16(val, GET_16BIT, ERROR)\ + val = (GET_16BIT);\ + {\ + unsigned int hi = val - 0xD800;\ + if (hi < 0x800) {\ + val = (GET_16BIT) - 0xDC00;\ + if (val > 0x3FFU || hi > 0x3FFU)\ + {ERROR}\ + val += (hi<<10) + 0x10000;\ + }\ + }\ + +/** + * @def PUT_UTF8(val, tmp, PUT_BYTE) + * Convert a 32-bit Unicode character to its UTF-8 encoded form (up to 4 bytes long). + * @param val is an input-only argument and should be of type uint32_t. It holds + * a UCS-4 encoded Unicode character that is to be converted to UTF-8. If + * val is given as a function it is executed only once. + * @param tmp is a temporary variable and should be of type uint8_t. It + * represents an intermediate value during conversion that is to be + * output by PUT_BYTE. + * @param PUT_BYTE writes the converted UTF-8 bytes to any proper destination. + * It could be a function or a statement, and uses tmp as the input byte. + * For example, PUT_BYTE could be "*output++ = tmp;" PUT_BYTE will be + * executed up to 4 times for values in the valid UTF-8 range and up to + * 7 times in the general case, depending on the length of the converted + * Unicode character. + */ +#define PUT_UTF8(val, tmp, PUT_BYTE)\ + {\ + int bytes, shift;\ + uint32_t in = val;\ + if (in < 0x80) {\ + tmp = in;\ + PUT_BYTE\ + } else {\ + bytes = (av_log2(in) + 4) / 5;\ + shift = (bytes - 1) * 6;\ + tmp = (256 - (256 >> bytes)) | (in >> shift);\ + PUT_BYTE\ + while (shift >= 6) {\ + shift -= 6;\ + tmp = 0x80 | ((in >> shift) & 0x3f);\ + PUT_BYTE\ + }\ + }\ + } + +/** + * @def PUT_UTF16(val, tmp, PUT_16BIT) + * Convert a 32-bit Unicode character to its UTF-16 encoded form (2 or 4 bytes). + * @param val is an input-only argument and should be of type uint32_t. It holds + * a UCS-4 encoded Unicode character that is to be converted to UTF-16. If + * val is given as a function it is executed only once. + * @param tmp is a temporary variable and should be of type uint16_t. It + * represents an intermediate value during conversion that is to be + * output by PUT_16BIT. + * @param PUT_16BIT writes the converted UTF-16 data to any proper destination + * in desired endianness. It could be a function or a statement, and uses tmp + * as the input byte. For example, PUT_BYTE could be "*output++ = tmp;" + * PUT_BYTE will be executed 1 or 2 times depending on input character. + */ +#define PUT_UTF16(val, tmp, PUT_16BIT)\ + {\ + uint32_t in = val;\ + if (in < 0x10000) {\ + tmp = in;\ + PUT_16BIT\ + } else {\ + tmp = 0xD800 | ((in - 0x10000) >> 10);\ + PUT_16BIT\ + tmp = 0xDC00 | ((in - 0x10000) & 0x3FF);\ + PUT_16BIT\ + }\ + }\ + + + +#include "mem.h" + +#ifdef HAVE_AV_CONFIG_H +# include "internal.h" +#endif /* HAVE_AV_CONFIG_H */ + +#endif /* AVUTIL_COMMON_H */ + +/* + * The following definitions are outside the multiple inclusion guard + * to ensure they are immediately available in intmath.h. + */ + +#ifndef av_ceil_log2 +# define av_ceil_log2 av_ceil_log2_c +#endif +#ifndef av_clip +# define av_clip av_clip_c +#endif +#ifndef av_clip64 +# define av_clip64 av_clip64_c +#endif +#ifndef av_clip_uint8 +# define av_clip_uint8 av_clip_uint8_c +#endif +#ifndef av_clip_int8 +# define av_clip_int8 av_clip_int8_c +#endif +#ifndef av_clip_uint16 +# define av_clip_uint16 av_clip_uint16_c +#endif +#ifndef av_clip_int16 +# define av_clip_int16 av_clip_int16_c +#endif +#ifndef av_clipl_int32 +# define av_clipl_int32 av_clipl_int32_c +#endif +#ifndef av_clip_intp2 +# define av_clip_intp2 av_clip_intp2_c +#endif +#ifndef av_clip_uintp2 +# define av_clip_uintp2 av_clip_uintp2_c +#endif +#ifndef av_mod_uintp2 +# define av_mod_uintp2 av_mod_uintp2_c +#endif +#ifndef av_sat_add32 +# define av_sat_add32 av_sat_add32_c +#endif +#ifndef av_sat_dadd32 +# define av_sat_dadd32 av_sat_dadd32_c +#endif +#ifndef av_sat_sub32 +# define av_sat_sub32 av_sat_sub32_c +#endif +#ifndef av_sat_dsub32 +# define av_sat_dsub32 av_sat_dsub32_c +#endif +#ifndef av_sat_add64 +# define av_sat_add64 av_sat_add64_c +#endif +#ifndef av_sat_sub64 +# define av_sat_sub64 av_sat_sub64_c +#endif +#ifndef av_clipf +# define av_clipf av_clipf_c +#endif +#ifndef av_clipd +# define av_clipd av_clipd_c +#endif +#ifndef av_popcount +# define av_popcount av_popcount_c +#endif +#ifndef av_popcount64 +# define av_popcount64 av_popcount64_c +#endif +#ifndef av_parity +# define av_parity av_parity_c +#endif diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/cpu.h b/ThirdParty/ffmpeg/arm/include/libavutil/cpu.h new file mode 100644 index 000000000..8bb9eb606 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/cpu.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_CPU_H +#define AVUTIL_CPU_H + +#include + +#include "attributes.h" + +#define AV_CPU_FLAG_FORCE 0x80000000 /* force usage of selected flags (OR) */ + + /* lower 16 bits - CPU features */ +#define AV_CPU_FLAG_MMX 0x0001 ///< standard MMX +#define AV_CPU_FLAG_MMXEXT 0x0002 ///< SSE integer functions or AMD MMX ext +#define AV_CPU_FLAG_MMX2 0x0002 ///< SSE integer functions or AMD MMX ext +#define AV_CPU_FLAG_3DNOW 0x0004 ///< AMD 3DNOW +#define AV_CPU_FLAG_SSE 0x0008 ///< SSE functions +#define AV_CPU_FLAG_SSE2 0x0010 ///< PIV SSE2 functions +#define AV_CPU_FLAG_SSE2SLOW 0x40000000 ///< SSE2 supported, but usually not faster + ///< than regular MMX/SSE (e.g. Core1) +#define AV_CPU_FLAG_3DNOWEXT 0x0020 ///< AMD 3DNowExt +#define AV_CPU_FLAG_SSE3 0x0040 ///< Prescott SSE3 functions +#define AV_CPU_FLAG_SSE3SLOW 0x20000000 ///< SSE3 supported, but usually not faster + ///< than regular MMX/SSE (e.g. Core1) +#define AV_CPU_FLAG_SSSE3 0x0080 ///< Conroe SSSE3 functions +#define AV_CPU_FLAG_SSSE3SLOW 0x4000000 ///< SSSE3 supported, but usually not faster +#define AV_CPU_FLAG_ATOM 0x10000000 ///< Atom processor, some SSSE3 instructions are slower +#define AV_CPU_FLAG_SSE4 0x0100 ///< Penryn SSE4.1 functions +#define AV_CPU_FLAG_SSE42 0x0200 ///< Nehalem SSE4.2 functions +#define AV_CPU_FLAG_AESNI 0x80000 ///< Advanced Encryption Standard functions +#define AV_CPU_FLAG_AVX 0x4000 ///< AVX functions: requires OS support even if YMM registers aren't used +#define AV_CPU_FLAG_AVXSLOW 0x8000000 ///< AVX supported, but slow when using YMM registers (e.g. Bulldozer) +#define AV_CPU_FLAG_XOP 0x0400 ///< Bulldozer XOP functions +#define AV_CPU_FLAG_FMA4 0x0800 ///< Bulldozer FMA4 functions +#define AV_CPU_FLAG_CMOV 0x1000 ///< supports cmov instruction +#define AV_CPU_FLAG_AVX2 0x8000 ///< AVX2 functions: requires OS support even if YMM registers aren't used +#define AV_CPU_FLAG_FMA3 0x10000 ///< Haswell FMA3 functions +#define AV_CPU_FLAG_BMI1 0x20000 ///< Bit Manipulation Instruction Set 1 +#define AV_CPU_FLAG_BMI2 0x40000 ///< Bit Manipulation Instruction Set 2 +#define AV_CPU_FLAG_AVX512 0x100000 ///< AVX-512 functions: requires OS support even if YMM/ZMM registers aren't used + +#define AV_CPU_FLAG_ALTIVEC 0x0001 ///< standard +#define AV_CPU_FLAG_VSX 0x0002 ///< ISA 2.06 +#define AV_CPU_FLAG_POWER8 0x0004 ///< ISA 2.07 + +#define AV_CPU_FLAG_ARMV5TE (1 << 0) +#define AV_CPU_FLAG_ARMV6 (1 << 1) +#define AV_CPU_FLAG_ARMV6T2 (1 << 2) +#define AV_CPU_FLAG_VFP (1 << 3) +#define AV_CPU_FLAG_VFPV3 (1 << 4) +#define AV_CPU_FLAG_NEON (1 << 5) +#define AV_CPU_FLAG_ARMV8 (1 << 6) +#define AV_CPU_FLAG_VFP_VM (1 << 7) ///< VFPv2 vector mode, deprecated in ARMv7-A and unavailable in various CPUs implementations +#define AV_CPU_FLAG_SETEND (1 <<16) + +/** + * Return the flags which specify extensions supported by the CPU. + * The returned value is affected by av_force_cpu_flags() if that was used + * before. So av_get_cpu_flags() can easily be used in an application to + * detect the enabled cpu flags. + */ +int av_get_cpu_flags(void); + +/** + * Disables cpu detection and forces the specified flags. + * -1 is a special case that disables forcing of specific flags. + */ +void av_force_cpu_flags(int flags); + +/** + * Set a mask on flags returned by av_get_cpu_flags(). + * This function is mainly useful for testing. + * Please use av_force_cpu_flags() and av_get_cpu_flags() instead which are more flexible + */ +attribute_deprecated void av_set_cpu_flags_mask(int mask); + +/** + * Parse CPU flags from a string. + * + * The returned flags contain the specified flags as well as related unspecified flags. + * + * This function exists only for compatibility with libav. + * Please use av_parse_cpu_caps() when possible. + * @return a combination of AV_CPU_* flags, negative on error. + */ +attribute_deprecated +int av_parse_cpu_flags(const char *s); + +/** + * Parse CPU caps from a string and update the given AV_CPU_* flags based on that. + * + * @return negative on error. + */ +int av_parse_cpu_caps(unsigned *flags, const char *s); + +/** + * @return the number of logical CPU cores present. + */ +int av_cpu_count(void); + +/** + * Get the maximum data alignment that may be required by FFmpeg. + * + * Note that this is affected by the build configuration and the CPU flags mask, + * so e.g. if the CPU supports AVX, but libavutil has been built with + * --disable-avx or the AV_CPU_FLAG_AVX flag has been disabled through + * av_set_cpu_flags_mask(), then this function will behave as if AVX is not + * present. + */ +size_t av_cpu_max_align(void); + +#endif /* AVUTIL_CPU_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/crc.h b/ThirdParty/ffmpeg/arm/include/libavutil/crc.h new file mode 100644 index 000000000..47e22b4c7 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/crc.h @@ -0,0 +1,100 @@ +/* + * copyright (c) 2006 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_crc32 + * Public header for CRC hash function implementation. + */ + +#ifndef AVUTIL_CRC_H +#define AVUTIL_CRC_H + +#include +#include +#include "attributes.h" +#include "version.h" + +/** + * @defgroup lavu_crc32 CRC + * @ingroup lavu_hash + * CRC (Cyclic Redundancy Check) hash function implementation. + * + * This module supports numerous CRC polynomials, in addition to the most + * widely used CRC-32-IEEE. See @ref AVCRCId for a list of available + * polynomials. + * + * @{ + */ + +typedef uint32_t AVCRC; + +typedef enum { + AV_CRC_8_ATM, + AV_CRC_16_ANSI, + AV_CRC_16_CCITT, + AV_CRC_32_IEEE, + AV_CRC_32_IEEE_LE, /*< reversed bitorder version of AV_CRC_32_IEEE */ + AV_CRC_16_ANSI_LE, /*< reversed bitorder version of AV_CRC_16_ANSI */ + AV_CRC_24_IEEE, + AV_CRC_8_EBU, + AV_CRC_MAX, /*< Not part of public API! Do not use outside libavutil. */ +}AVCRCId; + +/** + * Initialize a CRC table. + * @param ctx must be an array of size sizeof(AVCRC)*257 or sizeof(AVCRC)*1024 + * @param le If 1, the lowest bit represents the coefficient for the highest + * exponent of the corresponding polynomial (both for poly and + * actual CRC). + * If 0, you must swap the CRC parameter and the result of av_crc + * if you need the standard representation (can be simplified in + * most cases to e.g. bswap16): + * av_bswap32(crc << (32-bits)) + * @param bits number of bits for the CRC + * @param poly generator polynomial without the x**bits coefficient, in the + * representation as specified by le + * @param ctx_size size of ctx in bytes + * @return <0 on failure + */ +int av_crc_init(AVCRC *ctx, int le, int bits, uint32_t poly, int ctx_size); + +/** + * Get an initialized standard CRC table. + * @param crc_id ID of a standard CRC + * @return a pointer to the CRC table or NULL on failure + */ +const AVCRC *av_crc_get_table(AVCRCId crc_id); + +/** + * Calculate the CRC of a block. + * @param crc CRC of previous blocks if any or initial value for CRC + * @return CRC updated with the data from the given block + * + * @see av_crc_init() "le" parameter + */ +uint32_t av_crc(const AVCRC *ctx, uint32_t crc, + const uint8_t *buffer, size_t length) av_pure; + +/** + * @} + */ + +#endif /* AVUTIL_CRC_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/des.h b/ThirdParty/ffmpeg/arm/include/libavutil/des.h new file mode 100644 index 000000000..4cf11f5bc --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/des.h @@ -0,0 +1,77 @@ +/* + * DES encryption/decryption + * Copyright (c) 2007 Reimar Doeffinger + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_DES_H +#define AVUTIL_DES_H + +#include + +/** + * @defgroup lavu_des DES + * @ingroup lavu_crypto + * @{ + */ + +typedef struct AVDES { + uint64_t round_keys[3][16]; + int triple_des; +} AVDES; + +/** + * Allocate an AVDES context. + */ +AVDES *av_des_alloc(void); + +/** + * @brief Initializes an AVDES context. + * + * @param key_bits must be 64 or 192 + * @param decrypt 0 for encryption/CBC-MAC, 1 for decryption + * @return zero on success, negative value otherwise + */ +int av_des_init(struct AVDES *d, const uint8_t *key, int key_bits, int decrypt); + +/** + * @brief Encrypts / decrypts using the DES algorithm. + * + * @param count number of 8 byte blocks + * @param dst destination array, can be equal to src, must be 8-byte aligned + * @param src source array, can be equal to dst, must be 8-byte aligned, may be NULL + * @param iv initialization vector for CBC mode, if NULL then ECB will be used, + * must be 8-byte aligned + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_des_crypt(struct AVDES *d, uint8_t *dst, const uint8_t *src, int count, uint8_t *iv, int decrypt); + +/** + * @brief Calculates CBC-MAC using the DES algorithm. + * + * @param count number of 8 byte blocks + * @param dst destination array, can be equal to src, must be 8-byte aligned + * @param src source array, can be equal to dst, must be 8-byte aligned, may be NULL + */ +void av_des_mac(struct AVDES *d, uint8_t *dst, const uint8_t *src, int count); + +/** + * @} + */ + +#endif /* AVUTIL_DES_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/dict.h b/ThirdParty/ffmpeg/arm/include/libavutil/dict.h new file mode 100644 index 000000000..118f1f00e --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/dict.h @@ -0,0 +1,200 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Public dictionary API. + * @deprecated + * AVDictionary is provided for compatibility with libav. It is both in + * implementation as well as API inefficient. It does not scale and is + * extremely slow with large dictionaries. + * It is recommended that new code uses our tree container from tree.c/h + * where applicable, which uses AVL trees to achieve O(log n) performance. + */ + +#ifndef AVUTIL_DICT_H +#define AVUTIL_DICT_H + +#include + +#include "version.h" + +/** + * @addtogroup lavu_dict AVDictionary + * @ingroup lavu_data + * + * @brief Simple key:value store + * + * @{ + * Dictionaries are used for storing key:value pairs. To create + * an AVDictionary, simply pass an address of a NULL pointer to + * av_dict_set(). NULL can be used as an empty dictionary wherever + * a pointer to an AVDictionary is required. + * Use av_dict_get() to retrieve an entry or iterate over all + * entries and finally av_dict_free() to free the dictionary + * and all its contents. + * + @code + AVDictionary *d = NULL; // "create" an empty dictionary + AVDictionaryEntry *t = NULL; + + av_dict_set(&d, "foo", "bar", 0); // add an entry + + char *k = av_strdup("key"); // if your strings are already allocated, + char *v = av_strdup("value"); // you can avoid copying them like this + av_dict_set(&d, k, v, AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL); + + while (t = av_dict_get(d, "", t, AV_DICT_IGNORE_SUFFIX)) { + <....> // iterate over all entries in d + } + av_dict_free(&d); + @endcode + */ + +#define AV_DICT_MATCH_CASE 1 /**< Only get an entry with exact-case key match. Only relevant in av_dict_get(). */ +#define AV_DICT_IGNORE_SUFFIX 2 /**< Return first entry in a dictionary whose first part corresponds to the search key, + ignoring the suffix of the found key string. Only relevant in av_dict_get(). */ +#define AV_DICT_DONT_STRDUP_KEY 4 /**< Take ownership of a key that's been + allocated with av_malloc() or another memory allocation function. */ +#define AV_DICT_DONT_STRDUP_VAL 8 /**< Take ownership of a value that's been + allocated with av_malloc() or another memory allocation function. */ +#define AV_DICT_DONT_OVERWRITE 16 ///< Don't overwrite existing entries. +#define AV_DICT_APPEND 32 /**< If the entry already exists, append to it. Note that no + delimiter is added, the strings are simply concatenated. */ +#define AV_DICT_MULTIKEY 64 /**< Allow to store several equal keys in the dictionary */ + +typedef struct AVDictionaryEntry { + char *key; + char *value; +} AVDictionaryEntry; + +typedef struct AVDictionary AVDictionary; + +/** + * Get a dictionary entry with matching key. + * + * The returned entry key or value must not be changed, or it will + * cause undefined behavior. + * + * To iterate through all the dictionary entries, you can set the matching key + * to the null string "" and set the AV_DICT_IGNORE_SUFFIX flag. + * + * @param prev Set to the previous matching element to find the next. + * If set to NULL the first matching element is returned. + * @param key matching key + * @param flags a collection of AV_DICT_* flags controlling how the entry is retrieved + * @return found entry or NULL in case no matching entry was found in the dictionary + */ +AVDictionaryEntry *av_dict_get(const AVDictionary *m, const char *key, + const AVDictionaryEntry *prev, int flags); + +/** + * Get number of entries in dictionary. + * + * @param m dictionary + * @return number of entries in dictionary + */ +int av_dict_count(const AVDictionary *m); + +/** + * Set the given entry in *pm, overwriting an existing entry. + * + * Note: If AV_DICT_DONT_STRDUP_KEY or AV_DICT_DONT_STRDUP_VAL is set, + * these arguments will be freed on error. + * + * Warning: Adding a new entry to a dictionary invalidates all existing entries + * previously returned with av_dict_get. + * + * @param pm pointer to a pointer to a dictionary struct. If *pm is NULL + * a dictionary struct is allocated and put in *pm. + * @param key entry key to add to *pm (will either be av_strduped or added as a new key depending on flags) + * @param value entry value to add to *pm (will be av_strduped or added as a new key depending on flags). + * Passing a NULL value will cause an existing entry to be deleted. + * @return >= 0 on success otherwise an error code <0 + */ +int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags); + +/** + * Convenience wrapper for av_dict_set that converts the value to a string + * and stores it. + * + * Note: If AV_DICT_DONT_STRDUP_KEY is set, key will be freed on error. + */ +int av_dict_set_int(AVDictionary **pm, const char *key, int64_t value, int flags); + +/** + * Parse the key/value pairs list and add the parsed entries to a dictionary. + * + * In case of failure, all the successfully set entries are stored in + * *pm. You may need to manually free the created dictionary. + * + * @param key_val_sep a 0-terminated list of characters used to separate + * key from value + * @param pairs_sep a 0-terminated list of characters used to separate + * two pairs from each other + * @param flags flags to use when adding to dictionary. + * AV_DICT_DONT_STRDUP_KEY and AV_DICT_DONT_STRDUP_VAL + * are ignored since the key/value tokens will always + * be duplicated. + * @return 0 on success, negative AVERROR code on failure + */ +int av_dict_parse_string(AVDictionary **pm, const char *str, + const char *key_val_sep, const char *pairs_sep, + int flags); + +/** + * Copy entries from one AVDictionary struct into another. + * @param dst pointer to a pointer to a AVDictionary struct. If *dst is NULL, + * this function will allocate a struct for you and put it in *dst + * @param src pointer to source AVDictionary struct + * @param flags flags to use when setting entries in *dst + * @note metadata is read using the AV_DICT_IGNORE_SUFFIX flag + * @return 0 on success, negative AVERROR code on failure. If dst was allocated + * by this function, callers should free the associated memory. + */ +int av_dict_copy(AVDictionary **dst, const AVDictionary *src, int flags); + +/** + * Free all the memory allocated for an AVDictionary struct + * and all keys and values. + */ +void av_dict_free(AVDictionary **m); + +/** + * Get dictionary entries as a string. + * + * Create a string containing dictionary's entries. + * Such string may be passed back to av_dict_parse_string(). + * @note String is escaped with backslashes ('\'). + * + * @param[in] m dictionary + * @param[out] buffer Pointer to buffer that will be allocated with string containg entries. + * Buffer must be freed by the caller when is no longer needed. + * @param[in] key_val_sep character used to separate key from value + * @param[in] pairs_sep character used to separate two pairs from each other + * @return >= 0 on success, negative on error + * @warning Separators cannot be neither '\\' nor '\0'. They also cannot be the same. + */ +int av_dict_get_string(const AVDictionary *m, char **buffer, + const char key_val_sep, const char pairs_sep); + +/** + * @} + */ + +#endif /* AVUTIL_DICT_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/display.h b/ThirdParty/ffmpeg/arm/include/libavutil/display.h new file mode 100644 index 000000000..515adad79 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/display.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2014 Vittorio Giovara + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Display matrix + */ + +#ifndef AVUTIL_DISPLAY_H +#define AVUTIL_DISPLAY_H + +#include +#include "common.h" + +/** + * @addtogroup lavu_video + * @{ + * + * @defgroup lavu_video_display Display transformation matrix functions + * @{ + */ + +/** + * @addtogroup lavu_video_display + * The display transformation matrix specifies an affine transformation that + * should be applied to video frames for correct presentation. It is compatible + * with the matrices stored in the ISO/IEC 14496-12 container format. + * + * The data is a 3x3 matrix represented as a 9-element array: + * + * @code{.unparsed} + * | a b u | + * (a, b, u, c, d, v, x, y, w) -> | c d v | + * | x y w | + * @endcode + * + * All numbers are stored in native endianness, as 16.16 fixed-point values, + * except for u, v and w, which are stored as 2.30 fixed-point values. + * + * The transformation maps a point (p, q) in the source (pre-transformation) + * frame to the point (p', q') in the destination (post-transformation) frame as + * follows: + * + * @code{.unparsed} + * | a b u | + * (p, q, 1) . | c d v | = z * (p', q', 1) + * | x y w | + * @endcode + * + * The transformation can also be more explicitly written in components as + * follows: + * + * @code{.unparsed} + * p' = (a * p + c * q + x) / z; + * q' = (b * p + d * q + y) / z; + * z = u * p + v * q + w + * @endcode + */ + +/** + * Extract the rotation component of the transformation matrix. + * + * @param matrix the transformation matrix + * @return the angle (in degrees) by which the transformation rotates the frame + * counterclockwise. The angle will be in range [-180.0, 180.0], + * or NaN if the matrix is singular. + * + * @note floating point numbers are inherently inexact, so callers are + * recommended to round the return value to nearest integer before use. + */ +double av_display_rotation_get(const int32_t matrix[9]); + +/** + * Initialize a transformation matrix describing a pure counterclockwise + * rotation by the specified angle (in degrees). + * + * @param matrix an allocated transformation matrix (will be fully overwritten + * by this function) + * @param angle rotation angle in degrees. + */ +void av_display_rotation_set(int32_t matrix[9], double angle); + +/** + * Flip the input matrix horizontally and/or vertically. + * + * @param matrix an allocated transformation matrix + * @param hflip whether the matrix should be flipped horizontally + * @param vflip whether the matrix should be flipped vertically + */ +void av_display_matrix_flip(int32_t matrix[9], int hflip, int vflip); + +/** + * @} + * @} + */ + +#endif /* AVUTIL_DISPLAY_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/dovi_meta.h b/ThirdParty/ffmpeg/arm/include/libavutil/dovi_meta.h new file mode 100644 index 000000000..299911d43 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/dovi_meta.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020 Vacing Fang + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * DOVI configuration + */ + + +#ifndef AVUTIL_DOVI_META_H +#define AVUTIL_DOVI_META_H + +#include +#include + +/* + * DOVI configuration + * ref: dolby-vision-bitstreams-within-the-iso-base-media-file-format-v2.1.2 + dolby-vision-bitstreams-in-mpeg-2-transport-stream-multiplex-v1.2 + * @code + * uint8_t dv_version_major, the major version number that the stream complies with + * uint8_t dv_version_minor, the minor version number that the stream complies with + * uint8_t dv_profile, the Dolby Vision profile + * uint8_t dv_level, the Dolby Vision level + * uint8_t rpu_present_flag + * uint8_t el_present_flag + * uint8_t bl_present_flag + * uint8_t dv_bl_signal_compatibility_id + * @endcode + * + * @note The struct must be allocated with av_dovi_alloc() and + * its size is not a part of the public ABI. + */ +typedef struct AVDOVIDecoderConfigurationRecord { + uint8_t dv_version_major; + uint8_t dv_version_minor; + uint8_t dv_profile; + uint8_t dv_level; + uint8_t rpu_present_flag; + uint8_t el_present_flag; + uint8_t bl_present_flag; + uint8_t dv_bl_signal_compatibility_id; +} AVDOVIDecoderConfigurationRecord; + +/** + * Allocate a AVDOVIDecoderConfigurationRecord structure and initialize its + * fields to default values. + * + * @return the newly allocated struct or NULL on failure + */ +AVDOVIDecoderConfigurationRecord *av_dovi_alloc(size_t *size); + +#endif /* AVUTIL_DOVI_META_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/downmix_info.h b/ThirdParty/ffmpeg/arm/include/libavutil/downmix_info.h new file mode 100644 index 000000000..221cf5bf9 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/downmix_info.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2014 Tim Walker + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_DOWNMIX_INFO_H +#define AVUTIL_DOWNMIX_INFO_H + +#include "frame.h" + +/** + * @file + * audio downmix medatata + */ + +/** + * @addtogroup lavu_audio + * @{ + */ + +/** + * @defgroup downmix_info Audio downmix metadata + * @{ + */ + +/** + * Possible downmix types. + */ +enum AVDownmixType { + AV_DOWNMIX_TYPE_UNKNOWN, /**< Not indicated. */ + AV_DOWNMIX_TYPE_LORO, /**< Lo/Ro 2-channel downmix (Stereo). */ + AV_DOWNMIX_TYPE_LTRT, /**< Lt/Rt 2-channel downmix, Dolby Surround compatible. */ + AV_DOWNMIX_TYPE_DPLII, /**< Lt/Rt 2-channel downmix, Dolby Pro Logic II compatible. */ + AV_DOWNMIX_TYPE_NB /**< Number of downmix types. Not part of ABI. */ +}; + +/** + * This structure describes optional metadata relevant to a downmix procedure. + * + * All fields are set by the decoder to the value indicated in the audio + * bitstream (if present), or to a "sane" default otherwise. + */ +typedef struct AVDownmixInfo { + /** + * Type of downmix preferred by the mastering engineer. + */ + enum AVDownmixType preferred_downmix_type; + + /** + * Absolute scale factor representing the nominal level of the center + * channel during a regular downmix. + */ + double center_mix_level; + + /** + * Absolute scale factor representing the nominal level of the center + * channel during an Lt/Rt compatible downmix. + */ + double center_mix_level_ltrt; + + /** + * Absolute scale factor representing the nominal level of the surround + * channels during a regular downmix. + */ + double surround_mix_level; + + /** + * Absolute scale factor representing the nominal level of the surround + * channels during an Lt/Rt compatible downmix. + */ + double surround_mix_level_ltrt; + + /** + * Absolute scale factor representing the level at which the LFE data is + * mixed into L/R channels during downmixing. + */ + double lfe_mix_level; +} AVDownmixInfo; + +/** + * Get a frame's AV_FRAME_DATA_DOWNMIX_INFO side data for editing. + * + * If the side data is absent, it is created and added to the frame. + * + * @param frame the frame for which the side data is to be obtained or created + * + * @return the AVDownmixInfo structure to be edited by the caller, or NULL if + * the structure cannot be allocated. + */ +AVDownmixInfo *av_downmix_info_update_side_data(AVFrame *frame); + +/** + * @} + */ + +/** + * @} + */ + +#endif /* AVUTIL_DOWNMIX_INFO_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/encryption_info.h b/ThirdParty/ffmpeg/arm/include/libavutil/encryption_info.h new file mode 100644 index 000000000..8fe7ebfe4 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/encryption_info.h @@ -0,0 +1,205 @@ +/** + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_ENCRYPTION_INFO_H +#define AVUTIL_ENCRYPTION_INFO_H + +#include +#include + +typedef struct AVSubsampleEncryptionInfo { + /** The number of bytes that are clear. */ + unsigned int bytes_of_clear_data; + + /** + * The number of bytes that are protected. If using pattern encryption, + * the pattern applies to only the protected bytes; if not using pattern + * encryption, all these bytes are encrypted. + */ + unsigned int bytes_of_protected_data; +} AVSubsampleEncryptionInfo; + +/** + * This describes encryption info for a packet. This contains frame-specific + * info for how to decrypt the packet before passing it to the decoder. + * + * The size of this struct is not part of the public ABI. + */ +typedef struct AVEncryptionInfo { + /** The fourcc encryption scheme, in big-endian byte order. */ + uint32_t scheme; + + /** + * Only used for pattern encryption. This is the number of 16-byte blocks + * that are encrypted. + */ + uint32_t crypt_byte_block; + + /** + * Only used for pattern encryption. This is the number of 16-byte blocks + * that are clear. + */ + uint32_t skip_byte_block; + + /** + * The ID of the key used to encrypt the packet. This should always be + * 16 bytes long, but may be changed in the future. + */ + uint8_t *key_id; + uint32_t key_id_size; + + /** + * The initialization vector. This may have been zero-filled to be the + * correct block size. This should always be 16 bytes long, but may be + * changed in the future. + */ + uint8_t *iv; + uint32_t iv_size; + + /** + * An array of subsample encryption info specifying how parts of the sample + * are encrypted. If there are no subsamples, then the whole sample is + * encrypted. + */ + AVSubsampleEncryptionInfo *subsamples; + uint32_t subsample_count; +} AVEncryptionInfo; + +/** + * This describes info used to initialize an encryption key system. + * + * The size of this struct is not part of the public ABI. + */ +typedef struct AVEncryptionInitInfo { + /** + * A unique identifier for the key system this is for, can be NULL if it + * is not known. This should always be 16 bytes, but may change in the + * future. + */ + uint8_t* system_id; + uint32_t system_id_size; + + /** + * An array of key IDs this initialization data is for. All IDs are the + * same length. Can be NULL if there are no known key IDs. + */ + uint8_t** key_ids; + /** The number of key IDs. */ + uint32_t num_key_ids; + /** + * The number of bytes in each key ID. This should always be 16, but may + * change in the future. + */ + uint32_t key_id_size; + + /** + * Key-system specific initialization data. This data is copied directly + * from the file and the format depends on the specific key system. This + * can be NULL if there is no initialization data; in that case, there + * will be at least one key ID. + */ + uint8_t* data; + uint32_t data_size; + + /** + * An optional pointer to the next initialization info in the list. + */ + struct AVEncryptionInitInfo *next; +} AVEncryptionInitInfo; + +/** + * Allocates an AVEncryptionInfo structure and sub-pointers to hold the given + * number of subsamples. This will allocate pointers for the key ID, IV, + * and subsample entries, set the size members, and zero-initialize the rest. + * + * @param subsample_count The number of subsamples. + * @param key_id_size The number of bytes in the key ID, should be 16. + * @param iv_size The number of bytes in the IV, should be 16. + * + * @return The new AVEncryptionInfo structure, or NULL on error. + */ +AVEncryptionInfo *av_encryption_info_alloc(uint32_t subsample_count, uint32_t key_id_size, uint32_t iv_size); + +/** + * Allocates an AVEncryptionInfo structure with a copy of the given data. + * @return The new AVEncryptionInfo structure, or NULL on error. + */ +AVEncryptionInfo *av_encryption_info_clone(const AVEncryptionInfo *info); + +/** + * Frees the given encryption info object. This MUST NOT be used to free the + * side-data data pointer, that should use normal side-data methods. + */ +void av_encryption_info_free(AVEncryptionInfo *info); + +/** + * Creates a copy of the AVEncryptionInfo that is contained in the given side + * data. The resulting object should be passed to av_encryption_info_free() + * when done. + * + * @return The new AVEncryptionInfo structure, or NULL on error. + */ +AVEncryptionInfo *av_encryption_info_get_side_data(const uint8_t *side_data, size_t side_data_size); + +/** + * Allocates and initializes side data that holds a copy of the given encryption + * info. The resulting pointer should be either freed using av_free or given + * to av_packet_add_side_data(). + * + * @return The new side-data pointer, or NULL. + */ +uint8_t *av_encryption_info_add_side_data( + const AVEncryptionInfo *info, size_t *side_data_size); + + +/** + * Allocates an AVEncryptionInitInfo structure and sub-pointers to hold the + * given sizes. This will allocate pointers and set all the fields. + * + * @return The new AVEncryptionInitInfo structure, or NULL on error. + */ +AVEncryptionInitInfo *av_encryption_init_info_alloc( + uint32_t system_id_size, uint32_t num_key_ids, uint32_t key_id_size, uint32_t data_size); + +/** + * Frees the given encryption init info object. This MUST NOT be used to free + * the side-data data pointer, that should use normal side-data methods. + */ +void av_encryption_init_info_free(AVEncryptionInitInfo* info); + +/** + * Creates a copy of the AVEncryptionInitInfo that is contained in the given + * side data. The resulting object should be passed to + * av_encryption_init_info_free() when done. + * + * @return The new AVEncryptionInitInfo structure, or NULL on error. + */ +AVEncryptionInitInfo *av_encryption_init_info_get_side_data( + const uint8_t* side_data, size_t side_data_size); + +/** + * Allocates and initializes side data that holds a copy of the given encryption + * init info. The resulting pointer should be either freed using av_free or + * given to av_packet_add_side_data(). + * + * @return The new side-data pointer, or NULL. + */ +uint8_t *av_encryption_init_info_add_side_data( + const AVEncryptionInitInfo *info, size_t *side_data_size); + +#endif /* AVUTIL_ENCRYPTION_INFO_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/error.h b/ThirdParty/ffmpeg/arm/include/libavutil/error.h new file mode 100644 index 000000000..71df4da35 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/error.h @@ -0,0 +1,126 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * error code definitions + */ + +#ifndef AVUTIL_ERROR_H +#define AVUTIL_ERROR_H + +#include +#include + +/** + * @addtogroup lavu_error + * + * @{ + */ + + +/* error handling */ +#if EDOM > 0 +#define AVERROR(e) (-(e)) ///< Returns a negative error code from a POSIX error code, to return from library functions. +#define AVUNERROR(e) (-(e)) ///< Returns a POSIX error code from a library function error return value. +#else +/* Some platforms have E* and errno already negated. */ +#define AVERROR(e) (e) +#define AVUNERROR(e) (e) +#endif + +#define FFERRTAG(a, b, c, d) (-(int)MKTAG(a, b, c, d)) + +#define AVERROR_BSF_NOT_FOUND FFERRTAG(0xF8,'B','S','F') ///< Bitstream filter not found +#define AVERROR_BUG FFERRTAG( 'B','U','G','!') ///< Internal bug, also see AVERROR_BUG2 +#define AVERROR_BUFFER_TOO_SMALL FFERRTAG( 'B','U','F','S') ///< Buffer too small +#define AVERROR_DECODER_NOT_FOUND FFERRTAG(0xF8,'D','E','C') ///< Decoder not found +#define AVERROR_DEMUXER_NOT_FOUND FFERRTAG(0xF8,'D','E','M') ///< Demuxer not found +#define AVERROR_ENCODER_NOT_FOUND FFERRTAG(0xF8,'E','N','C') ///< Encoder not found +#define AVERROR_EOF FFERRTAG( 'E','O','F',' ') ///< End of file +#define AVERROR_EXIT FFERRTAG( 'E','X','I','T') ///< Immediate exit was requested; the called function should not be restarted +#define AVERROR_EXTERNAL FFERRTAG( 'E','X','T',' ') ///< Generic error in an external library +#define AVERROR_FILTER_NOT_FOUND FFERRTAG(0xF8,'F','I','L') ///< Filter not found +#define AVERROR_INVALIDDATA FFERRTAG( 'I','N','D','A') ///< Invalid data found when processing input +#define AVERROR_MUXER_NOT_FOUND FFERRTAG(0xF8,'M','U','X') ///< Muxer not found +#define AVERROR_OPTION_NOT_FOUND FFERRTAG(0xF8,'O','P','T') ///< Option not found +#define AVERROR_PATCHWELCOME FFERRTAG( 'P','A','W','E') ///< Not yet implemented in FFmpeg, patches welcome +#define AVERROR_PROTOCOL_NOT_FOUND FFERRTAG(0xF8,'P','R','O') ///< Protocol not found + +#define AVERROR_STREAM_NOT_FOUND FFERRTAG(0xF8,'S','T','R') ///< Stream not found +/** + * This is semantically identical to AVERROR_BUG + * it has been introduced in Libav after our AVERROR_BUG and with a modified value. + */ +#define AVERROR_BUG2 FFERRTAG( 'B','U','G',' ') +#define AVERROR_UNKNOWN FFERRTAG( 'U','N','K','N') ///< Unknown error, typically from an external library +#define AVERROR_EXPERIMENTAL (-0x2bb2afa8) ///< Requested feature is flagged experimental. Set strict_std_compliance if you really want to use it. +#define AVERROR_INPUT_CHANGED (-0x636e6701) ///< Input changed between calls. Reconfiguration is required. (can be OR-ed with AVERROR_OUTPUT_CHANGED) +#define AVERROR_OUTPUT_CHANGED (-0x636e6702) ///< Output changed between calls. Reconfiguration is required. (can be OR-ed with AVERROR_INPUT_CHANGED) +/* HTTP & RTSP errors */ +#define AVERROR_HTTP_BAD_REQUEST FFERRTAG(0xF8,'4','0','0') +#define AVERROR_HTTP_UNAUTHORIZED FFERRTAG(0xF8,'4','0','1') +#define AVERROR_HTTP_FORBIDDEN FFERRTAG(0xF8,'4','0','3') +#define AVERROR_HTTP_NOT_FOUND FFERRTAG(0xF8,'4','0','4') +#define AVERROR_HTTP_OTHER_4XX FFERRTAG(0xF8,'4','X','X') +#define AVERROR_HTTP_SERVER_ERROR FFERRTAG(0xF8,'5','X','X') + +#define AV_ERROR_MAX_STRING_SIZE 64 + +/** + * Put a description of the AVERROR code errnum in errbuf. + * In case of failure the global variable errno is set to indicate the + * error. Even in case of failure av_strerror() will print a generic + * error message indicating the errnum provided to errbuf. + * + * @param errnum error code to describe + * @param errbuf buffer to which description is written + * @param errbuf_size the size in bytes of errbuf + * @return 0 on success, a negative value if a description for errnum + * cannot be found + */ +int av_strerror(int errnum, char *errbuf, size_t errbuf_size); + +/** + * Fill the provided buffer with a string containing an error string + * corresponding to the AVERROR code errnum. + * + * @param errbuf a buffer + * @param errbuf_size size in bytes of errbuf + * @param errnum error code to describe + * @return the buffer in input, filled with the error description + * @see av_strerror() + */ +static inline char *av_make_error_string(char *errbuf, size_t errbuf_size, int errnum) +{ + av_strerror(errnum, errbuf, errbuf_size); + return errbuf; +} + +/** + * Convenience macro, the return value should be used only directly in + * function arguments but never stand-alone. + */ +#define av_err2str(errnum) \ + av_make_error_string((char[AV_ERROR_MAX_STRING_SIZE]){0}, AV_ERROR_MAX_STRING_SIZE, errnum) + +/** + * @} + */ + +#endif /* AVUTIL_ERROR_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/eval.h b/ThirdParty/ffmpeg/arm/include/libavutil/eval.h new file mode 100644 index 000000000..068c62cda --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/eval.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2002 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * simple arithmetic expression evaluator + */ + +#ifndef AVUTIL_EVAL_H +#define AVUTIL_EVAL_H + +#include "avutil.h" + +typedef struct AVExpr AVExpr; + +/** + * Parse and evaluate an expression. + * Note, this is significantly slower than av_expr_eval(). + * + * @param res a pointer to a double where is put the result value of + * the expression, or NAN in case of error + * @param s expression as a zero terminated string, for example "1+2^3+5*5+sin(2/3)" + * @param const_names NULL terminated array of zero terminated strings of constant identifiers, for example {"PI", "E", 0} + * @param const_values a zero terminated array of values for the identifiers from const_names + * @param func1_names NULL terminated array of zero terminated strings of funcs1 identifiers + * @param funcs1 NULL terminated array of function pointers for functions which take 1 argument + * @param func2_names NULL terminated array of zero terminated strings of funcs2 identifiers + * @param funcs2 NULL terminated array of function pointers for functions which take 2 arguments + * @param opaque a pointer which will be passed to all functions from funcs1 and funcs2 + * @param log_ctx parent logging context + * @return >= 0 in case of success, a negative value corresponding to an + * AVERROR code otherwise + */ +int av_expr_parse_and_eval(double *res, const char *s, + const char * const *const_names, const double *const_values, + const char * const *func1_names, double (* const *funcs1)(void *, double), + const char * const *func2_names, double (* const *funcs2)(void *, double, double), + void *opaque, int log_offset, void *log_ctx); + +/** + * Parse an expression. + * + * @param expr a pointer where is put an AVExpr containing the parsed + * value in case of successful parsing, or NULL otherwise. + * The pointed to AVExpr must be freed with av_expr_free() by the user + * when it is not needed anymore. + * @param s expression as a zero terminated string, for example "1+2^3+5*5+sin(2/3)" + * @param const_names NULL terminated array of zero terminated strings of constant identifiers, for example {"PI", "E", 0} + * @param func1_names NULL terminated array of zero terminated strings of funcs1 identifiers + * @param funcs1 NULL terminated array of function pointers for functions which take 1 argument + * @param func2_names NULL terminated array of zero terminated strings of funcs2 identifiers + * @param funcs2 NULL terminated array of function pointers for functions which take 2 arguments + * @param log_ctx parent logging context + * @return >= 0 in case of success, a negative value corresponding to an + * AVERROR code otherwise + */ +int av_expr_parse(AVExpr **expr, const char *s, + const char * const *const_names, + const char * const *func1_names, double (* const *funcs1)(void *, double), + const char * const *func2_names, double (* const *funcs2)(void *, double, double), + int log_offset, void *log_ctx); + +/** + * Evaluate a previously parsed expression. + * + * @param const_values a zero terminated array of values for the identifiers from av_expr_parse() const_names + * @param opaque a pointer which will be passed to all functions from funcs1 and funcs2 + * @return the value of the expression + */ +double av_expr_eval(AVExpr *e, const double *const_values, void *opaque); + +/** + * Track the presence of variables and their number of occurrences in a parsed expression + * + * @param counter a zero-initialized array where the count of each variable will be stored + * @param size size of array + * @return 0 on success, a negative value indicates that no expression or array was passed + * or size was zero + */ +int av_expr_count_vars(AVExpr *e, unsigned *counter, int size); + +/** + * Track the presence of user provided functions and their number of occurrences + * in a parsed expression. + * + * @param counter a zero-initialized array where the count of each function will be stored + * if you passed 5 functions with 2 arguments to av_expr_parse() + * then for arg=2 this will use upto 5 entries. + * @param size size of array + * @param arg number of arguments the counted functions have + * @return 0 on success, a negative value indicates that no expression or array was passed + * or size was zero + */ +int av_expr_count_func(AVExpr *e, unsigned *counter, int size, int arg); + +/** + * Free a parsed expression previously created with av_expr_parse(). + */ +void av_expr_free(AVExpr *e); + +/** + * Parse the string in numstr and return its value as a double. If + * the string is empty, contains only whitespaces, or does not contain + * an initial substring that has the expected syntax for a + * floating-point number, no conversion is performed. In this case, + * returns a value of zero and the value returned in tail is the value + * of numstr. + * + * @param numstr a string representing a number, may contain one of + * the International System number postfixes, for example 'K', 'M', + * 'G'. If 'i' is appended after the postfix, powers of 2 are used + * instead of powers of 10. The 'B' postfix multiplies the value by + * 8, and can be appended after another postfix or used alone. This + * allows using for example 'KB', 'MiB', 'G' and 'B' as postfix. + * @param tail if non-NULL puts here the pointer to the char next + * after the last parsed character + */ +double av_strtod(const char *numstr, char **tail); + +#endif /* AVUTIL_EVAL_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/ffversion.h b/ThirdParty/ffmpeg/arm/include/libavutil/ffversion.h new file mode 100644 index 000000000..117d49d34 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/ffversion.h @@ -0,0 +1,5 @@ +/* Automatically generated by version.sh, do not manually edit! */ +#ifndef AVUTIL_FFVERSION_H +#define AVUTIL_FFVERSION_H +#define FFMPEG_VERSION "4.3" +#endif /* AVUTIL_FFVERSION_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/fifo.h b/ThirdParty/ffmpeg/arm/include/libavutil/fifo.h new file mode 100644 index 000000000..dc7bc6f0d --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/fifo.h @@ -0,0 +1,179 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * a very simple circular buffer FIFO implementation + */ + +#ifndef AVUTIL_FIFO_H +#define AVUTIL_FIFO_H + +#include +#include "avutil.h" +#include "attributes.h" + +typedef struct AVFifoBuffer { + uint8_t *buffer; + uint8_t *rptr, *wptr, *end; + uint32_t rndx, wndx; +} AVFifoBuffer; + +/** + * Initialize an AVFifoBuffer. + * @param size of FIFO + * @return AVFifoBuffer or NULL in case of memory allocation failure + */ +AVFifoBuffer *av_fifo_alloc(unsigned int size); + +/** + * Initialize an AVFifoBuffer. + * @param nmemb number of elements + * @param size size of the single element + * @return AVFifoBuffer or NULL in case of memory allocation failure + */ +AVFifoBuffer *av_fifo_alloc_array(size_t nmemb, size_t size); + +/** + * Free an AVFifoBuffer. + * @param f AVFifoBuffer to free + */ +void av_fifo_free(AVFifoBuffer *f); + +/** + * Free an AVFifoBuffer and reset pointer to NULL. + * @param f AVFifoBuffer to free + */ +void av_fifo_freep(AVFifoBuffer **f); + +/** + * Reset the AVFifoBuffer to the state right after av_fifo_alloc, in particular it is emptied. + * @param f AVFifoBuffer to reset + */ +void av_fifo_reset(AVFifoBuffer *f); + +/** + * Return the amount of data in bytes in the AVFifoBuffer, that is the + * amount of data you can read from it. + * @param f AVFifoBuffer to read from + * @return size + */ +int av_fifo_size(const AVFifoBuffer *f); + +/** + * Return the amount of space in bytes in the AVFifoBuffer, that is the + * amount of data you can write into it. + * @param f AVFifoBuffer to write into + * @return size + */ +int av_fifo_space(const AVFifoBuffer *f); + +/** + * Feed data at specific position from an AVFifoBuffer to a user-supplied callback. + * Similar as av_fifo_gereric_read but without discarding data. + * @param f AVFifoBuffer to read from + * @param offset offset from current read position + * @param buf_size number of bytes to read + * @param func generic read function + * @param dest data destination + */ +int av_fifo_generic_peek_at(AVFifoBuffer *f, void *dest, int offset, int buf_size, void (*func)(void*, void*, int)); + +/** + * Feed data from an AVFifoBuffer to a user-supplied callback. + * Similar as av_fifo_gereric_read but without discarding data. + * @param f AVFifoBuffer to read from + * @param buf_size number of bytes to read + * @param func generic read function + * @param dest data destination + */ +int av_fifo_generic_peek(AVFifoBuffer *f, void *dest, int buf_size, void (*func)(void*, void*, int)); + +/** + * Feed data from an AVFifoBuffer to a user-supplied callback. + * @param f AVFifoBuffer to read from + * @param buf_size number of bytes to read + * @param func generic read function + * @param dest data destination + */ +int av_fifo_generic_read(AVFifoBuffer *f, void *dest, int buf_size, void (*func)(void*, void*, int)); + +/** + * Feed data from a user-supplied callback to an AVFifoBuffer. + * @param f AVFifoBuffer to write to + * @param src data source; non-const since it may be used as a + * modifiable context by the function defined in func + * @param size number of bytes to write + * @param func generic write function; the first parameter is src, + * the second is dest_buf, the third is dest_buf_size. + * func must return the number of bytes written to dest_buf, or <= 0 to + * indicate no more data available to write. + * If func is NULL, src is interpreted as a simple byte array for source data. + * @return the number of bytes written to the FIFO + */ +int av_fifo_generic_write(AVFifoBuffer *f, void *src, int size, int (*func)(void*, void*, int)); + +/** + * Resize an AVFifoBuffer. + * In case of reallocation failure, the old FIFO is kept unchanged. + * + * @param f AVFifoBuffer to resize + * @param size new AVFifoBuffer size in bytes + * @return <0 for failure, >=0 otherwise + */ +int av_fifo_realloc2(AVFifoBuffer *f, unsigned int size); + +/** + * Enlarge an AVFifoBuffer. + * In case of reallocation failure, the old FIFO is kept unchanged. + * The new fifo size may be larger than the requested size. + * + * @param f AVFifoBuffer to resize + * @param additional_space the amount of space in bytes to allocate in addition to av_fifo_size() + * @return <0 for failure, >=0 otherwise + */ +int av_fifo_grow(AVFifoBuffer *f, unsigned int additional_space); + +/** + * Read and discard the specified amount of data from an AVFifoBuffer. + * @param f AVFifoBuffer to read from + * @param size amount of data to read in bytes + */ +void av_fifo_drain(AVFifoBuffer *f, int size); + +/** + * Return a pointer to the data stored in a FIFO buffer at a certain offset. + * The FIFO buffer is not modified. + * + * @param f AVFifoBuffer to peek at, f must be non-NULL + * @param offs an offset in bytes, its absolute value must be less + * than the used buffer size or the returned pointer will + * point outside to the buffer data. + * The used buffer size can be checked with av_fifo_size(). + */ +static inline uint8_t *av_fifo_peek2(const AVFifoBuffer *f, int offs) +{ + uint8_t *ptr = f->rptr + offs; + if (ptr >= f->end) + ptr = f->buffer + (ptr - f->end); + else if (ptr < f->buffer) + ptr = f->end - (f->buffer - ptr); + return ptr; +} + +#endif /* AVUTIL_FIFO_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/file.h b/ThirdParty/ffmpeg/arm/include/libavutil/file.h new file mode 100644 index 000000000..3ef4a6022 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/file.h @@ -0,0 +1,71 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_FILE_H +#define AVUTIL_FILE_H + +#include + +#include "avutil.h" + +/** + * @file + * Misc file utilities. + */ + +/** + * Read the file with name filename, and put its content in a newly + * allocated buffer or map it with mmap() when available. + * In case of success set *bufptr to the read or mmapped buffer, and + * *size to the size in bytes of the buffer in *bufptr. + * Unlike mmap this function succeeds with zero sized files, in this + * case *bufptr will be set to NULL and *size will be set to 0. + * The returned buffer must be released with av_file_unmap(). + * + * @param log_offset loglevel offset used for logging + * @param log_ctx context used for logging + * @return a non negative number in case of success, a negative value + * corresponding to an AVERROR error code in case of failure + */ +av_warn_unused_result +int av_file_map(const char *filename, uint8_t **bufptr, size_t *size, + int log_offset, void *log_ctx); + +/** + * Unmap or free the buffer bufptr created by av_file_map(). + * + * @param size size in bytes of bufptr, must be the same as returned + * by av_file_map() + */ +void av_file_unmap(uint8_t *bufptr, size_t size); + +/** + * Wrapper to work around the lack of mkstemp() on mingw. + * Also, tries to create file in /tmp first, if possible. + * *prefix can be a character constant; *filename will be allocated internally. + * @return file descriptor of opened file (or negative value corresponding to an + * AVERROR code on error) + * and opened file name in **filename. + * @note On very old libcs it is necessary to set a secure umask before + * calling this, av_tempfile() can't call umask itself as it is used in + * libraries and could interfere with the calling application. + * @deprecated as fd numbers cannot be passed saftely between libs on some platforms + */ +int av_tempfile(const char *prefix, char **filename, int log_offset, void *log_ctx); + +#endif /* AVUTIL_FILE_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/frame.h b/ThirdParty/ffmpeg/arm/include/libavutil/frame.h new file mode 100644 index 000000000..fc67db0f6 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/frame.h @@ -0,0 +1,975 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_frame + * reference-counted frame API + */ + +#ifndef AVUTIL_FRAME_H +#define AVUTIL_FRAME_H + +#include +#include + +#include "avutil.h" +#include "buffer.h" +#include "dict.h" +#include "rational.h" +#include "samplefmt.h" +#include "pixfmt.h" +#include "version.h" + + +/** + * @defgroup lavu_frame AVFrame + * @ingroup lavu_data + * + * @{ + * AVFrame is an abstraction for reference-counted raw multimedia data. + */ + +enum AVFrameSideDataType { + /** + * The data is the AVPanScan struct defined in libavcodec. + */ + AV_FRAME_DATA_PANSCAN, + /** + * ATSC A53 Part 4 Closed Captions. + * A53 CC bitstream is stored as uint8_t in AVFrameSideData.data. + * The number of bytes of CC data is AVFrameSideData.size. + */ + AV_FRAME_DATA_A53_CC, + /** + * Stereoscopic 3d metadata. + * The data is the AVStereo3D struct defined in libavutil/stereo3d.h. + */ + AV_FRAME_DATA_STEREO3D, + /** + * The data is the AVMatrixEncoding enum defined in libavutil/channel_layout.h. + */ + AV_FRAME_DATA_MATRIXENCODING, + /** + * Metadata relevant to a downmix procedure. + * The data is the AVDownmixInfo struct defined in libavutil/downmix_info.h. + */ + AV_FRAME_DATA_DOWNMIX_INFO, + /** + * ReplayGain information in the form of the AVReplayGain struct. + */ + AV_FRAME_DATA_REPLAYGAIN, + /** + * This side data contains a 3x3 transformation matrix describing an affine + * transformation that needs to be applied to the frame for correct + * presentation. + * + * See libavutil/display.h for a detailed description of the data. + */ + AV_FRAME_DATA_DISPLAYMATRIX, + /** + * Active Format Description data consisting of a single byte as specified + * in ETSI TS 101 154 using AVActiveFormatDescription enum. + */ + AV_FRAME_DATA_AFD, + /** + * Motion vectors exported by some codecs (on demand through the export_mvs + * flag set in the libavcodec AVCodecContext flags2 option). + * The data is the AVMotionVector struct defined in + * libavutil/motion_vector.h. + */ + AV_FRAME_DATA_MOTION_VECTORS, + /** + * Recommmends skipping the specified number of samples. This is exported + * only if the "skip_manual" AVOption is set in libavcodec. + * This has the same format as AV_PKT_DATA_SKIP_SAMPLES. + * @code + * u32le number of samples to skip from start of this packet + * u32le number of samples to skip from end of this packet + * u8 reason for start skip + * u8 reason for end skip (0=padding silence, 1=convergence) + * @endcode + */ + AV_FRAME_DATA_SKIP_SAMPLES, + /** + * This side data must be associated with an audio frame and corresponds to + * enum AVAudioServiceType defined in avcodec.h. + */ + AV_FRAME_DATA_AUDIO_SERVICE_TYPE, + /** + * Mastering display metadata associated with a video frame. The payload is + * an AVMasteringDisplayMetadata type and contains information about the + * mastering display color volume. + */ + AV_FRAME_DATA_MASTERING_DISPLAY_METADATA, + /** + * The GOP timecode in 25 bit timecode format. Data format is 64-bit integer. + * This is set on the first frame of a GOP that has a temporal reference of 0. + */ + AV_FRAME_DATA_GOP_TIMECODE, + + /** + * The data represents the AVSphericalMapping structure defined in + * libavutil/spherical.h. + */ + AV_FRAME_DATA_SPHERICAL, + + /** + * Content light level (based on CTA-861.3). This payload contains data in + * the form of the AVContentLightMetadata struct. + */ + AV_FRAME_DATA_CONTENT_LIGHT_LEVEL, + + /** + * The data contains an ICC profile as an opaque octet buffer following the + * format described by ISO 15076-1 with an optional name defined in the + * metadata key entry "name". + */ + AV_FRAME_DATA_ICC_PROFILE, + +#if FF_API_FRAME_QP + /** + * Implementation-specific description of the format of AV_FRAME_QP_TABLE_DATA. + * The contents of this side data are undocumented and internal; use + * av_frame_set_qp_table() and av_frame_get_qp_table() to access this in a + * meaningful way instead. + */ + AV_FRAME_DATA_QP_TABLE_PROPERTIES, + + /** + * Raw QP table data. Its format is described by + * AV_FRAME_DATA_QP_TABLE_PROPERTIES. Use av_frame_set_qp_table() and + * av_frame_get_qp_table() to access this instead. + */ + AV_FRAME_DATA_QP_TABLE_DATA, +#endif + + /** + * Timecode which conforms to SMPTE ST 12-1. The data is an array of 4 uint32_t + * where the first uint32_t describes how many (1-3) of the other timecodes are used. + * The timecode format is described in the av_timecode_get_smpte_from_framenum() + * function in libavutil/timecode.c. + */ + AV_FRAME_DATA_S12M_TIMECODE, + + /** + * HDR dynamic metadata associated with a video frame. The payload is + * an AVDynamicHDRPlus type and contains information for color + * volume transform - application 4 of SMPTE 2094-40:2016 standard. + */ + AV_FRAME_DATA_DYNAMIC_HDR_PLUS, + + /** + * Regions Of Interest, the data is an array of AVRegionOfInterest type, the number of + * array element is implied by AVFrameSideData.size / AVRegionOfInterest.self_size. + */ + AV_FRAME_DATA_REGIONS_OF_INTEREST, + + /** + * Encoding parameters for a video frame, as described by AVVideoEncParams. + */ + AV_FRAME_DATA_VIDEO_ENC_PARAMS, +}; + +enum AVActiveFormatDescription { + AV_AFD_SAME = 8, + AV_AFD_4_3 = 9, + AV_AFD_16_9 = 10, + AV_AFD_14_9 = 11, + AV_AFD_4_3_SP_14_9 = 13, + AV_AFD_16_9_SP_14_9 = 14, + AV_AFD_SP_4_3 = 15, +}; + + +/** + * Structure to hold side data for an AVFrame. + * + * sizeof(AVFrameSideData) is not a part of the public ABI, so new fields may be added + * to the end with a minor bump. + */ +typedef struct AVFrameSideData { + enum AVFrameSideDataType type; + uint8_t *data; + int size; + AVDictionary *metadata; + AVBufferRef *buf; +} AVFrameSideData; + +/** + * Structure describing a single Region Of Interest. + * + * When multiple regions are defined in a single side-data block, they + * should be ordered from most to least important - some encoders are only + * capable of supporting a limited number of distinct regions, so will have + * to truncate the list. + * + * When overlapping regions are defined, the first region containing a given + * area of the frame applies. + */ +typedef struct AVRegionOfInterest { + /** + * Must be set to the size of this data structure (that is, + * sizeof(AVRegionOfInterest)). + */ + uint32_t self_size; + /** + * Distance in pixels from the top edge of the frame to the top and + * bottom edges and from the left edge of the frame to the left and + * right edges of the rectangle defining this region of interest. + * + * The constraints on a region are encoder dependent, so the region + * actually affected may be slightly larger for alignment or other + * reasons. + */ + int top; + int bottom; + int left; + int right; + /** + * Quantisation offset. + * + * Must be in the range -1 to +1. A value of zero indicates no quality + * change. A negative value asks for better quality (less quantisation), + * while a positive value asks for worse quality (greater quantisation). + * + * The range is calibrated so that the extreme values indicate the + * largest possible offset - if the rest of the frame is encoded with the + * worst possible quality, an offset of -1 indicates that this region + * should be encoded with the best possible quality anyway. Intermediate + * values are then interpolated in some codec-dependent way. + * + * For example, in 10-bit H.264 the quantisation parameter varies between + * -12 and 51. A typical qoffset value of -1/10 therefore indicates that + * this region should be encoded with a QP around one-tenth of the full + * range better than the rest of the frame. So, if most of the frame + * were to be encoded with a QP of around 30, this region would get a QP + * of around 24 (an offset of approximately -1/10 * (51 - -12) = -6.3). + * An extreme value of -1 would indicate that this region should be + * encoded with the best possible quality regardless of the treatment of + * the rest of the frame - that is, should be encoded at a QP of -12. + */ + AVRational qoffset; +} AVRegionOfInterest; + +/** + * This structure describes decoded (raw) audio or video data. + * + * AVFrame must be allocated using av_frame_alloc(). Note that this only + * allocates the AVFrame itself, the buffers for the data must be managed + * through other means (see below). + * AVFrame must be freed with av_frame_free(). + * + * AVFrame is typically allocated once and then reused multiple times to hold + * different data (e.g. a single AVFrame to hold frames received from a + * decoder). In such a case, av_frame_unref() will free any references held by + * the frame and reset it to its original clean state before it + * is reused again. + * + * The data described by an AVFrame is usually reference counted through the + * AVBuffer API. The underlying buffer references are stored in AVFrame.buf / + * AVFrame.extended_buf. An AVFrame is considered to be reference counted if at + * least one reference is set, i.e. if AVFrame.buf[0] != NULL. In such a case, + * every single data plane must be contained in one of the buffers in + * AVFrame.buf or AVFrame.extended_buf. + * There may be a single buffer for all the data, or one separate buffer for + * each plane, or anything in between. + * + * sizeof(AVFrame) is not a part of the public ABI, so new fields may be added + * to the end with a minor bump. + * + * Fields can be accessed through AVOptions, the name string used, matches the + * C structure field name for fields accessible through AVOptions. The AVClass + * for AVFrame can be obtained from avcodec_get_frame_class() + */ +typedef struct AVFrame { +#define AV_NUM_DATA_POINTERS 8 + /** + * pointer to the picture/channel planes. + * This might be different from the first allocated byte + * + * Some decoders access areas outside 0,0 - width,height, please + * see avcodec_align_dimensions2(). Some filters and swscale can read + * up to 16 bytes beyond the planes, if these filters are to be used, + * then 16 extra bytes must be allocated. + * + * NOTE: Except for hwaccel formats, pointers not needed by the format + * MUST be set to NULL. + */ + uint8_t *data[AV_NUM_DATA_POINTERS]; + + /** + * For video, size in bytes of each picture line. + * For audio, size in bytes of each plane. + * + * For audio, only linesize[0] may be set. For planar audio, each channel + * plane must be the same size. + * + * For video the linesizes should be multiples of the CPUs alignment + * preference, this is 16 or 32 for modern desktop CPUs. + * Some code requires such alignment other code can be slower without + * correct alignment, for yet other it makes no difference. + * + * @note The linesize may be larger than the size of usable data -- there + * may be extra padding present for performance reasons. + */ + int linesize[AV_NUM_DATA_POINTERS]; + + /** + * pointers to the data planes/channels. + * + * For video, this should simply point to data[]. + * + * For planar audio, each channel has a separate data pointer, and + * linesize[0] contains the size of each channel buffer. + * For packed audio, there is just one data pointer, and linesize[0] + * contains the total size of the buffer for all channels. + * + * Note: Both data and extended_data should always be set in a valid frame, + * but for planar audio with more channels that can fit in data, + * extended_data must be used in order to access all channels. + */ + uint8_t **extended_data; + + /** + * @name Video dimensions + * Video frames only. The coded dimensions (in pixels) of the video frame, + * i.e. the size of the rectangle that contains some well-defined values. + * + * @note The part of the frame intended for display/presentation is further + * restricted by the @ref cropping "Cropping rectangle". + * @{ + */ + int width, height; + /** + * @} + */ + + /** + * number of audio samples (per channel) described by this frame + */ + int nb_samples; + + /** + * format of the frame, -1 if unknown or unset + * Values correspond to enum AVPixelFormat for video frames, + * enum AVSampleFormat for audio) + */ + int format; + + /** + * 1 -> keyframe, 0-> not + */ + int key_frame; + + /** + * Picture type of the frame. + */ + enum AVPictureType pict_type; + + /** + * Sample aspect ratio for the video frame, 0/1 if unknown/unspecified. + */ + AVRational sample_aspect_ratio; + + /** + * Presentation timestamp in time_base units (time when frame should be shown to user). + */ + int64_t pts; + +#if FF_API_PKT_PTS + /** + * PTS copied from the AVPacket that was decoded to produce this frame. + * @deprecated use the pts field instead + */ + attribute_deprecated + int64_t pkt_pts; +#endif + + /** + * DTS copied from the AVPacket that triggered returning this frame. (if frame threading isn't used) + * This is also the Presentation time of this AVFrame calculated from + * only AVPacket.dts values without pts values. + */ + int64_t pkt_dts; + + /** + * picture number in bitstream order + */ + int coded_picture_number; + /** + * picture number in display order + */ + int display_picture_number; + + /** + * quality (between 1 (good) and FF_LAMBDA_MAX (bad)) + */ + int quality; + + /** + * for some private data of the user + */ + void *opaque; + +#if FF_API_ERROR_FRAME + /** + * @deprecated unused + */ + attribute_deprecated + uint64_t error[AV_NUM_DATA_POINTERS]; +#endif + + /** + * When decoding, this signals how much the picture must be delayed. + * extra_delay = repeat_pict / (2*fps) + */ + int repeat_pict; + + /** + * The content of the picture is interlaced. + */ + int interlaced_frame; + + /** + * If the content is interlaced, is top field displayed first. + */ + int top_field_first; + + /** + * Tell user application that palette has changed from previous frame. + */ + int palette_has_changed; + + /** + * reordered opaque 64 bits (generally an integer or a double precision float + * PTS but can be anything). + * The user sets AVCodecContext.reordered_opaque to represent the input at + * that time, + * the decoder reorders values as needed and sets AVFrame.reordered_opaque + * to exactly one of the values provided by the user through AVCodecContext.reordered_opaque + */ + int64_t reordered_opaque; + + /** + * Sample rate of the audio data. + */ + int sample_rate; + + /** + * Channel layout of the audio data. + */ + uint64_t channel_layout; + + /** + * AVBuffer references backing the data for this frame. If all elements of + * this array are NULL, then this frame is not reference counted. This array + * must be filled contiguously -- if buf[i] is non-NULL then buf[j] must + * also be non-NULL for all j < i. + * + * There may be at most one AVBuffer per data plane, so for video this array + * always contains all the references. For planar audio with more than + * AV_NUM_DATA_POINTERS channels, there may be more buffers than can fit in + * this array. Then the extra AVBufferRef pointers are stored in the + * extended_buf array. + */ + AVBufferRef *buf[AV_NUM_DATA_POINTERS]; + + /** + * For planar audio which requires more than AV_NUM_DATA_POINTERS + * AVBufferRef pointers, this array will hold all the references which + * cannot fit into AVFrame.buf. + * + * Note that this is different from AVFrame.extended_data, which always + * contains all the pointers. This array only contains the extra pointers, + * which cannot fit into AVFrame.buf. + * + * This array is always allocated using av_malloc() by whoever constructs + * the frame. It is freed in av_frame_unref(). + */ + AVBufferRef **extended_buf; + /** + * Number of elements in extended_buf. + */ + int nb_extended_buf; + + AVFrameSideData **side_data; + int nb_side_data; + +/** + * @defgroup lavu_frame_flags AV_FRAME_FLAGS + * @ingroup lavu_frame + * Flags describing additional frame properties. + * + * @{ + */ + +/** + * The frame data may be corrupted, e.g. due to decoding errors. + */ +#define AV_FRAME_FLAG_CORRUPT (1 << 0) +/** + * A flag to mark the frames which need to be decoded, but shouldn't be output. + */ +#define AV_FRAME_FLAG_DISCARD (1 << 2) +/** + * @} + */ + + /** + * Frame flags, a combination of @ref lavu_frame_flags + */ + int flags; + + /** + * MPEG vs JPEG YUV range. + * - encoding: Set by user + * - decoding: Set by libavcodec + */ + enum AVColorRange color_range; + + enum AVColorPrimaries color_primaries; + + enum AVColorTransferCharacteristic color_trc; + + /** + * YUV colorspace type. + * - encoding: Set by user + * - decoding: Set by libavcodec + */ + enum AVColorSpace colorspace; + + enum AVChromaLocation chroma_location; + + /** + * frame timestamp estimated using various heuristics, in stream time base + * - encoding: unused + * - decoding: set by libavcodec, read by user. + */ + int64_t best_effort_timestamp; + + /** + * reordered pos from the last AVPacket that has been input into the decoder + * - encoding: unused + * - decoding: Read by user. + */ + int64_t pkt_pos; + + /** + * duration of the corresponding packet, expressed in + * AVStream->time_base units, 0 if unknown. + * - encoding: unused + * - decoding: Read by user. + */ + int64_t pkt_duration; + + /** + * metadata. + * - encoding: Set by user. + * - decoding: Set by libavcodec. + */ + AVDictionary *metadata; + + /** + * decode error flags of the frame, set to a combination of + * FF_DECODE_ERROR_xxx flags if the decoder produced a frame, but there + * were errors during the decoding. + * - encoding: unused + * - decoding: set by libavcodec, read by user. + */ + int decode_error_flags; +#define FF_DECODE_ERROR_INVALID_BITSTREAM 1 +#define FF_DECODE_ERROR_MISSING_REFERENCE 2 +#define FF_DECODE_ERROR_CONCEALMENT_ACTIVE 4 +#define FF_DECODE_ERROR_DECODE_SLICES 8 + + /** + * number of audio channels, only used for audio. + * - encoding: unused + * - decoding: Read by user. + */ + int channels; + + /** + * size of the corresponding packet containing the compressed + * frame. + * It is set to a negative value if unknown. + * - encoding: unused + * - decoding: set by libavcodec, read by user. + */ + int pkt_size; + +#if FF_API_FRAME_QP + /** + * QP table + */ + attribute_deprecated + int8_t *qscale_table; + /** + * QP store stride + */ + attribute_deprecated + int qstride; + + attribute_deprecated + int qscale_type; + + attribute_deprecated + AVBufferRef *qp_table_buf; +#endif + /** + * For hwaccel-format frames, this should be a reference to the + * AVHWFramesContext describing the frame. + */ + AVBufferRef *hw_frames_ctx; + + /** + * AVBufferRef for free use by the API user. FFmpeg will never check the + * contents of the buffer ref. FFmpeg calls av_buffer_unref() on it when + * the frame is unreferenced. av_frame_copy_props() calls create a new + * reference with av_buffer_ref() for the target frame's opaque_ref field. + * + * This is unrelated to the opaque field, although it serves a similar + * purpose. + */ + AVBufferRef *opaque_ref; + + /** + * @anchor cropping + * @name Cropping + * Video frames only. The number of pixels to discard from the the + * top/bottom/left/right border of the frame to obtain the sub-rectangle of + * the frame intended for presentation. + * @{ + */ + size_t crop_top; + size_t crop_bottom; + size_t crop_left; + size_t crop_right; + /** + * @} + */ + + /** + * AVBufferRef for internal use by a single libav* library. + * Must not be used to transfer data between libraries. + * Has to be NULL when ownership of the frame leaves the respective library. + * + * Code outside the FFmpeg libs should never check or change the contents of the buffer ref. + * + * FFmpeg calls av_buffer_unref() on it when the frame is unreferenced. + * av_frame_copy_props() calls create a new reference with av_buffer_ref() + * for the target frame's private_ref field. + */ + AVBufferRef *private_ref; +} AVFrame; + +#if FF_API_FRAME_GET_SET +/** + * Accessors for some AVFrame fields. These used to be provided for ABI + * compatibility, and do not need to be used anymore. + */ +attribute_deprecated +int64_t av_frame_get_best_effort_timestamp(const AVFrame *frame); +attribute_deprecated +void av_frame_set_best_effort_timestamp(AVFrame *frame, int64_t val); +attribute_deprecated +int64_t av_frame_get_pkt_duration (const AVFrame *frame); +attribute_deprecated +void av_frame_set_pkt_duration (AVFrame *frame, int64_t val); +attribute_deprecated +int64_t av_frame_get_pkt_pos (const AVFrame *frame); +attribute_deprecated +void av_frame_set_pkt_pos (AVFrame *frame, int64_t val); +attribute_deprecated +int64_t av_frame_get_channel_layout (const AVFrame *frame); +attribute_deprecated +void av_frame_set_channel_layout (AVFrame *frame, int64_t val); +attribute_deprecated +int av_frame_get_channels (const AVFrame *frame); +attribute_deprecated +void av_frame_set_channels (AVFrame *frame, int val); +attribute_deprecated +int av_frame_get_sample_rate (const AVFrame *frame); +attribute_deprecated +void av_frame_set_sample_rate (AVFrame *frame, int val); +attribute_deprecated +AVDictionary *av_frame_get_metadata (const AVFrame *frame); +attribute_deprecated +void av_frame_set_metadata (AVFrame *frame, AVDictionary *val); +attribute_deprecated +int av_frame_get_decode_error_flags (const AVFrame *frame); +attribute_deprecated +void av_frame_set_decode_error_flags (AVFrame *frame, int val); +attribute_deprecated +int av_frame_get_pkt_size(const AVFrame *frame); +attribute_deprecated +void av_frame_set_pkt_size(AVFrame *frame, int val); +#if FF_API_FRAME_QP +attribute_deprecated +int8_t *av_frame_get_qp_table(AVFrame *f, int *stride, int *type); +attribute_deprecated +int av_frame_set_qp_table(AVFrame *f, AVBufferRef *buf, int stride, int type); +#endif +attribute_deprecated +enum AVColorSpace av_frame_get_colorspace(const AVFrame *frame); +attribute_deprecated +void av_frame_set_colorspace(AVFrame *frame, enum AVColorSpace val); +attribute_deprecated +enum AVColorRange av_frame_get_color_range(const AVFrame *frame); +attribute_deprecated +void av_frame_set_color_range(AVFrame *frame, enum AVColorRange val); +#endif + +/** + * Get the name of a colorspace. + * @return a static string identifying the colorspace; can be NULL. + */ +const char *av_get_colorspace_name(enum AVColorSpace val); + +/** + * Allocate an AVFrame and set its fields to default values. The resulting + * struct must be freed using av_frame_free(). + * + * @return An AVFrame filled with default values or NULL on failure. + * + * @note this only allocates the AVFrame itself, not the data buffers. Those + * must be allocated through other means, e.g. with av_frame_get_buffer() or + * manually. + */ +AVFrame *av_frame_alloc(void); + +/** + * Free the frame and any dynamically allocated objects in it, + * e.g. extended_data. If the frame is reference counted, it will be + * unreferenced first. + * + * @param frame frame to be freed. The pointer will be set to NULL. + */ +void av_frame_free(AVFrame **frame); + +/** + * Set up a new reference to the data described by the source frame. + * + * Copy frame properties from src to dst and create a new reference for each + * AVBufferRef from src. + * + * If src is not reference counted, new buffers are allocated and the data is + * copied. + * + * @warning: dst MUST have been either unreferenced with av_frame_unref(dst), + * or newly allocated with av_frame_alloc() before calling this + * function, or undefined behavior will occur. + * + * @return 0 on success, a negative AVERROR on error + */ +int av_frame_ref(AVFrame *dst, const AVFrame *src); + +/** + * Create a new frame that references the same data as src. + * + * This is a shortcut for av_frame_alloc()+av_frame_ref(). + * + * @return newly created AVFrame on success, NULL on error. + */ +AVFrame *av_frame_clone(const AVFrame *src); + +/** + * Unreference all the buffers referenced by frame and reset the frame fields. + */ +void av_frame_unref(AVFrame *frame); + +/** + * Move everything contained in src to dst and reset src. + * + * @warning: dst is not unreferenced, but directly overwritten without reading + * or deallocating its contents. Call av_frame_unref(dst) manually + * before calling this function to ensure that no memory is leaked. + */ +void av_frame_move_ref(AVFrame *dst, AVFrame *src); + +/** + * Allocate new buffer(s) for audio or video data. + * + * The following fields must be set on frame before calling this function: + * - format (pixel format for video, sample format for audio) + * - width and height for video + * - nb_samples and channel_layout for audio + * + * This function will fill AVFrame.data and AVFrame.buf arrays and, if + * necessary, allocate and fill AVFrame.extended_data and AVFrame.extended_buf. + * For planar formats, one buffer will be allocated for each plane. + * + * @warning: if frame already has been allocated, calling this function will + * leak memory. In addition, undefined behavior can occur in certain + * cases. + * + * @param frame frame in which to store the new buffers. + * @param align Required buffer size alignment. If equal to 0, alignment will be + * chosen automatically for the current CPU. It is highly + * recommended to pass 0 here unless you know what you are doing. + * + * @return 0 on success, a negative AVERROR on error. + */ +int av_frame_get_buffer(AVFrame *frame, int align); + +/** + * Check if the frame data is writable. + * + * @return A positive value if the frame data is writable (which is true if and + * only if each of the underlying buffers has only one reference, namely the one + * stored in this frame). Return 0 otherwise. + * + * If 1 is returned the answer is valid until av_buffer_ref() is called on any + * of the underlying AVBufferRefs (e.g. through av_frame_ref() or directly). + * + * @see av_frame_make_writable(), av_buffer_is_writable() + */ +int av_frame_is_writable(AVFrame *frame); + +/** + * Ensure that the frame data is writable, avoiding data copy if possible. + * + * Do nothing if the frame is writable, allocate new buffers and copy the data + * if it is not. + * + * @return 0 on success, a negative AVERROR on error. + * + * @see av_frame_is_writable(), av_buffer_is_writable(), + * av_buffer_make_writable() + */ +int av_frame_make_writable(AVFrame *frame); + +/** + * Copy the frame data from src to dst. + * + * This function does not allocate anything, dst must be already initialized and + * allocated with the same parameters as src. + * + * This function only copies the frame data (i.e. the contents of the data / + * extended data arrays), not any other properties. + * + * @return >= 0 on success, a negative AVERROR on error. + */ +int av_frame_copy(AVFrame *dst, const AVFrame *src); + +/** + * Copy only "metadata" fields from src to dst. + * + * Metadata for the purpose of this function are those fields that do not affect + * the data layout in the buffers. E.g. pts, sample rate (for audio) or sample + * aspect ratio (for video), but not width/height or channel layout. + * Side data is also copied. + */ +int av_frame_copy_props(AVFrame *dst, const AVFrame *src); + +/** + * Get the buffer reference a given data plane is stored in. + * + * @param plane index of the data plane of interest in frame->extended_data. + * + * @return the buffer reference that contains the plane or NULL if the input + * frame is not valid. + */ +AVBufferRef *av_frame_get_plane_buffer(AVFrame *frame, int plane); + +/** + * Add a new side data to a frame. + * + * @param frame a frame to which the side data should be added + * @param type type of the added side data + * @param size size of the side data + * + * @return newly added side data on success, NULL on error + */ +AVFrameSideData *av_frame_new_side_data(AVFrame *frame, + enum AVFrameSideDataType type, + int size); + +/** + * Add a new side data to a frame from an existing AVBufferRef + * + * @param frame a frame to which the side data should be added + * @param type the type of the added side data + * @param buf an AVBufferRef to add as side data. The ownership of + * the reference is transferred to the frame. + * + * @return newly added side data on success, NULL on error. On failure + * the frame is unchanged and the AVBufferRef remains owned by + * the caller. + */ +AVFrameSideData *av_frame_new_side_data_from_buf(AVFrame *frame, + enum AVFrameSideDataType type, + AVBufferRef *buf); + +/** + * @return a pointer to the side data of a given type on success, NULL if there + * is no side data with such type in this frame. + */ +AVFrameSideData *av_frame_get_side_data(const AVFrame *frame, + enum AVFrameSideDataType type); + +/** + * Remove and free all side data instances of the given type. + */ +void av_frame_remove_side_data(AVFrame *frame, enum AVFrameSideDataType type); + + +/** + * Flags for frame cropping. + */ +enum { + /** + * Apply the maximum possible cropping, even if it requires setting the + * AVFrame.data[] entries to unaligned pointers. Passing unaligned data + * to FFmpeg API is generally not allowed, and causes undefined behavior + * (such as crashes). You can pass unaligned data only to FFmpeg APIs that + * are explicitly documented to accept it. Use this flag only if you + * absolutely know what you are doing. + */ + AV_FRAME_CROP_UNALIGNED = 1 << 0, +}; + +/** + * Crop the given video AVFrame according to its crop_left/crop_top/crop_right/ + * crop_bottom fields. If cropping is successful, the function will adjust the + * data pointers and the width/height fields, and set the crop fields to 0. + * + * In all cases, the cropping boundaries will be rounded to the inherent + * alignment of the pixel format. In some cases, such as for opaque hwaccel + * formats, the left/top cropping is ignored. The crop fields are set to 0 even + * if the cropping was rounded or ignored. + * + * @param frame the frame which should be cropped + * @param flags Some combination of AV_FRAME_CROP_* flags, or 0. + * + * @return >= 0 on success, a negative AVERROR on error. If the cropping fields + * were invalid, AVERROR(ERANGE) is returned, and nothing is changed. + */ +int av_frame_apply_cropping(AVFrame *frame, int flags); + +/** + * @return a string identifying the side data type + */ +const char *av_frame_side_data_name(enum AVFrameSideDataType type); + +/** + * @} + */ + +#endif /* AVUTIL_FRAME_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/hash.h b/ThirdParty/ffmpeg/arm/include/libavutil/hash.h new file mode 100644 index 000000000..7693e6bf0 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/hash.h @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2013 Reimar Döffinger + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_hash_generic + * Generic hashing API + */ + +#ifndef AVUTIL_HASH_H +#define AVUTIL_HASH_H + +#include + +#include "version.h" + +/** + * @defgroup lavu_hash Hash Functions + * @ingroup lavu_crypto + * Hash functions useful in multimedia. + * + * Hash functions are widely used in multimedia, from error checking and + * concealment to internal regression testing. libavutil has efficient + * implementations of a variety of hash functions that may be useful for + * FFmpeg and other multimedia applications. + * + * @{ + * + * @defgroup lavu_hash_generic Generic Hashing API + * An abstraction layer for all hash functions supported by libavutil. + * + * If your application needs to support a wide range of different hash + * functions, then the Generic Hashing API is for you. It provides a generic, + * reusable API for @ref lavu_hash "all hash functions" implemented in libavutil. + * If you just need to use one particular hash function, use the @ref lavu_hash + * "individual hash" directly. + * + * @section Sample Code + * + * A basic template for using the Generic Hashing API follows: + * + * @code + * struct AVHashContext *ctx = NULL; + * const char *hash_name = NULL; + * uint8_t *output_buf = NULL; + * + * // Select from a string returned by av_hash_names() + * hash_name = ...; + * + * // Allocate a hash context + * ret = av_hash_alloc(&ctx, hash_name); + * if (ret < 0) + * return ret; + * + * // Initialize the hash context + * av_hash_init(ctx); + * + * // Update the hash context with data + * while (data_left) { + * av_hash_update(ctx, data, size); + * } + * + * // Now we have no more data, so it is time to finalize the hash and get the + * // output. But we need to first allocate an output buffer. Note that you can + * // use any memory allocation function, including malloc(), not just + * // av_malloc(). + * output_buf = av_malloc(av_hash_get_size(ctx)); + * if (!output_buf) + * return AVERROR(ENOMEM); + * + * // Finalize the hash context. + * // You can use any of the av_hash_final*() functions provided, for other + * // output formats. If you do so, be sure to adjust the memory allocation + * // above. See the function documentation below for the exact amount of extra + * // memory needed. + * av_hash_final(ctx, output_buffer); + * + * // Free the context + * av_hash_freep(&ctx); + * @endcode + * + * @section Hash Function-Specific Information + * If the CRC32 hash is selected, the #AV_CRC_32_IEEE polynomial will be + * used. + * + * If the Murmur3 hash is selected, the default seed will be used. See @ref + * lavu_murmur3_seedinfo "Murmur3" for more information. + * + * @{ + */ + +/** + * @example ffhash.c + * This example is a simple command line application that takes one or more + * arguments. It demonstrates a typical use of the hashing API with allocation, + * initialization, updating, and finalizing. + */ + +struct AVHashContext; + +/** + * Allocate a hash context for the algorithm specified by name. + * + * @return >= 0 for success, a negative error code for failure + * + * @note The context is not initialized after a call to this function; you must + * call av_hash_init() to do so. + */ +int av_hash_alloc(struct AVHashContext **ctx, const char *name); + +/** + * Get the names of available hash algorithms. + * + * This function can be used to enumerate the algorithms. + * + * @param[in] i Index of the hash algorithm, starting from 0 + * @return Pointer to a static string or `NULL` if `i` is out of range + */ +const char *av_hash_names(int i); + +/** + * Get the name of the algorithm corresponding to the given hash context. + */ +const char *av_hash_get_name(const struct AVHashContext *ctx); + +/** + * Maximum value that av_hash_get_size() will currently return. + * + * You can use this if you absolutely want or need to use static allocation for + * the output buffer and are fine with not supporting hashes newly added to + * libavutil without recompilation. + * + * @warning + * Adding new hashes with larger sizes, and increasing the macro while doing + * so, will not be considered an ABI change. To prevent your code from + * overflowing a buffer, either dynamically allocate the output buffer with + * av_hash_get_size(), or limit your use of the Hashing API to hashes that are + * already in FFmpeg during the time of compilation. + */ +#define AV_HASH_MAX_SIZE 64 + +/** + * Get the size of the resulting hash value in bytes. + * + * The maximum value this function will currently return is available as macro + * #AV_HASH_MAX_SIZE. + * + * @param[in] ctx Hash context + * @return Size of the hash value in bytes + */ +int av_hash_get_size(const struct AVHashContext *ctx); + +/** + * Initialize or reset a hash context. + * + * @param[in,out] ctx Hash context + */ +void av_hash_init(struct AVHashContext *ctx); + +/** + * Update a hash context with additional data. + * + * @param[in,out] ctx Hash context + * @param[in] src Data to be added to the hash context + * @param[in] len Size of the additional data + */ +#if FF_API_CRYPTO_SIZE_T +void av_hash_update(struct AVHashContext *ctx, const uint8_t *src, int len); +#else +void av_hash_update(struct AVHashContext *ctx, const uint8_t *src, size_t len); +#endif + +/** + * Finalize a hash context and compute the actual hash value. + * + * The minimum size of `dst` buffer is given by av_hash_get_size() or + * #AV_HASH_MAX_SIZE. The use of the latter macro is discouraged. + * + * It is not safe to update or finalize a hash context again, if it has already + * been finalized. + * + * @param[in,out] ctx Hash context + * @param[out] dst Where the final hash value will be stored + * + * @see av_hash_final_bin() provides an alternative API + */ +void av_hash_final(struct AVHashContext *ctx, uint8_t *dst); + +/** + * Finalize a hash context and store the actual hash value in a buffer. + * + * It is not safe to update or finalize a hash context again, if it has already + * been finalized. + * + * If `size` is smaller than the hash size (given by av_hash_get_size()), the + * hash is truncated; if size is larger, the buffer is padded with 0. + * + * @param[in,out] ctx Hash context + * @param[out] dst Where the final hash value will be stored + * @param[in] size Number of bytes to write to `dst` + */ +void av_hash_final_bin(struct AVHashContext *ctx, uint8_t *dst, int size); + +/** + * Finalize a hash context and store the hexadecimal representation of the + * actual hash value as a string. + * + * It is not safe to update or finalize a hash context again, if it has already + * been finalized. + * + * The string is always 0-terminated. + * + * If `size` is smaller than `2 * hash_size + 1`, where `hash_size` is the + * value returned by av_hash_get_size(), the string will be truncated. + * + * @param[in,out] ctx Hash context + * @param[out] dst Where the string will be stored + * @param[in] size Maximum number of bytes to write to `dst` + */ +void av_hash_final_hex(struct AVHashContext *ctx, uint8_t *dst, int size); + +/** + * Finalize a hash context and store the Base64 representation of the + * actual hash value as a string. + * + * It is not safe to update or finalize a hash context again, if it has already + * been finalized. + * + * The string is always 0-terminated. + * + * If `size` is smaller than AV_BASE64_SIZE(hash_size), where `hash_size` is + * the value returned by av_hash_get_size(), the string will be truncated. + * + * @param[in,out] ctx Hash context + * @param[out] dst Where the final hash value will be stored + * @param[in] size Maximum number of bytes to write to `dst` + */ +void av_hash_final_b64(struct AVHashContext *ctx, uint8_t *dst, int size); + +/** + * Free hash context and set hash context pointer to `NULL`. + * + * @param[in,out] ctx Pointer to hash context + */ +void av_hash_freep(struct AVHashContext **ctx); + +/** + * @} + * @} + */ + +#endif /* AVUTIL_HASH_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/hdr_dynamic_metadata.h b/ThirdParty/ffmpeg/arm/include/libavutil/hdr_dynamic_metadata.h new file mode 100644 index 000000000..2d72de56a --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/hdr_dynamic_metadata.h @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2018 Mohammad Izadi + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HDR_DYNAMIC_METADATA_H +#define AVUTIL_HDR_DYNAMIC_METADATA_H + +#include "frame.h" +#include "rational.h" + +/** + * Option for overlapping elliptical pixel selectors in an image. + */ +enum AVHDRPlusOverlapProcessOption { + AV_HDR_PLUS_OVERLAP_PROCESS_WEIGHTED_AVERAGING = 0, + AV_HDR_PLUS_OVERLAP_PROCESS_LAYERING = 1, +}; + +/** + * Represents the percentile at a specific percentage in + * a distribution. + */ +typedef struct AVHDRPlusPercentile { + /** + * The percentage value corresponding to a specific percentile linearized + * RGB value in the processing window in the scene. The value shall be in + * the range of 0 to100, inclusive. + */ + uint8_t percentage; + + /** + * The linearized maxRGB value at a specific percentile in the processing + * window in the scene. The value shall be in the range of 0 to 1, inclusive + * and in multiples of 0.00001. + */ + AVRational percentile; +} AVHDRPlusPercentile; + +/** + * Color transform parameters at a processing window in a dynamic metadata for + * SMPTE 2094-40. + */ +typedef struct AVHDRPlusColorTransformParams { + /** + * The relative x coordinate of the top left pixel of the processing + * window. The value shall be in the range of 0 and 1, inclusive and + * in multiples of 1/(width of Picture - 1). The value 1 corresponds + * to the absolute coordinate of width of Picture - 1. The value for + * first processing window shall be 0. + */ + AVRational window_upper_left_corner_x; + + /** + * The relative y coordinate of the top left pixel of the processing + * window. The value shall be in the range of 0 and 1, inclusive and + * in multiples of 1/(height of Picture - 1). The value 1 corresponds + * to the absolute coordinate of height of Picture - 1. The value for + * first processing window shall be 0. + */ + AVRational window_upper_left_corner_y; + + /** + * The relative x coordinate of the bottom right pixel of the processing + * window. The value shall be in the range of 0 and 1, inclusive and + * in multiples of 1/(width of Picture - 1). The value 1 corresponds + * to the absolute coordinate of width of Picture - 1. The value for + * first processing window shall be 1. + */ + AVRational window_lower_right_corner_x; + + /** + * The relative y coordinate of the bottom right pixel of the processing + * window. The value shall be in the range of 0 and 1, inclusive and + * in multiples of 1/(height of Picture - 1). The value 1 corresponds + * to the absolute coordinate of height of Picture - 1. The value for + * first processing window shall be 1. + */ + AVRational window_lower_right_corner_y; + + /** + * The x coordinate of the center position of the concentric internal and + * external ellipses of the elliptical pixel selector in the processing + * window. The value shall be in the range of 0 to (width of Picture - 1), + * inclusive and in multiples of 1 pixel. + */ + uint16_t center_of_ellipse_x; + + /** + * The y coordinate of the center position of the concentric internal and + * external ellipses of the elliptical pixel selector in the processing + * window. The value shall be in the range of 0 to (height of Picture - 1), + * inclusive and in multiples of 1 pixel. + */ + uint16_t center_of_ellipse_y; + + /** + * The clockwise rotation angle in degree of arc with respect to the + * positive direction of the x-axis of the concentric internal and external + * ellipses of the elliptical pixel selector in the processing window. The + * value shall be in the range of 0 to 180, inclusive and in multiples of 1. + */ + uint8_t rotation_angle; + + /** + * The semi-major axis value of the internal ellipse of the elliptical pixel + * selector in amount of pixels in the processing window. The value shall be + * in the range of 1 to 65535, inclusive and in multiples of 1 pixel. + */ + uint16_t semimajor_axis_internal_ellipse; + + /** + * The semi-major axis value of the external ellipse of the elliptical pixel + * selector in amount of pixels in the processing window. The value + * shall not be less than semimajor_axis_internal_ellipse of the current + * processing window. The value shall be in the range of 1 to 65535, + * inclusive and in multiples of 1 pixel. + */ + uint16_t semimajor_axis_external_ellipse; + + /** + * The semi-minor axis value of the external ellipse of the elliptical pixel + * selector in amount of pixels in the processing window. The value shall be + * in the range of 1 to 65535, inclusive and in multiples of 1 pixel. + */ + uint16_t semiminor_axis_external_ellipse; + + /** + * Overlap process option indicates one of the two methods of combining + * rendered pixels in the processing window in an image with at least one + * elliptical pixel selector. For overlapping elliptical pixel selectors + * in an image, overlap_process_option shall have the same value. + */ + enum AVHDRPlusOverlapProcessOption overlap_process_option; + + /** + * The maximum of the color components of linearized RGB values in the + * processing window in the scene. The values should be in the range of 0 to + * 1, inclusive and in multiples of 0.00001. maxscl[ 0 ], maxscl[ 1 ], and + * maxscl[ 2 ] are corresponding to R, G, B color components respectively. + */ + AVRational maxscl[3]; + + /** + * The average of linearized maxRGB values in the processing window in the + * scene. The value should be in the range of 0 to 1, inclusive and in + * multiples of 0.00001. + */ + AVRational average_maxrgb; + + /** + * The number of linearized maxRGB values at given percentiles in the + * processing window in the scene. The maximum value shall be 15. + */ + uint8_t num_distribution_maxrgb_percentiles; + + /** + * The linearized maxRGB values at given percentiles in the + * processing window in the scene. + */ + AVHDRPlusPercentile distribution_maxrgb[15]; + + /** + * The fraction of selected pixels in the image that contains the brightest + * pixel in the scene. The value shall be in the range of 0 to 1, inclusive + * and in multiples of 0.001. + */ + AVRational fraction_bright_pixels; + + /** + * This flag indicates that the metadata for the tone mapping function in + * the processing window is present (for value of 1). + */ + uint8_t tone_mapping_flag; + + /** + * The x coordinate of the separation point between the linear part and the + * curved part of the tone mapping function. The value shall be in the range + * of 0 to 1, excluding 0 and in multiples of 1/4095. + */ + AVRational knee_point_x; + + /** + * The y coordinate of the separation point between the linear part and the + * curved part of the tone mapping function. The value shall be in the range + * of 0 to 1, excluding 0 and in multiples of 1/4095. + */ + AVRational knee_point_y; + + /** + * The number of the intermediate anchor parameters of the tone mapping + * function in the processing window. The maximum value shall be 15. + */ + uint8_t num_bezier_curve_anchors; + + /** + * The intermediate anchor parameters of the tone mapping function in the + * processing window in the scene. The values should be in the range of 0 + * to 1, inclusive and in multiples of 1/1023. + */ + AVRational bezier_curve_anchors[15]; + + /** + * This flag shall be equal to 0 in bitstreams conforming to this version of + * this Specification. Other values are reserved for future use. + */ + uint8_t color_saturation_mapping_flag; + + /** + * The color saturation gain in the processing window in the scene. The + * value shall be in the range of 0 to 63/8, inclusive and in multiples of + * 1/8. The default value shall be 1. + */ + AVRational color_saturation_weight; +} AVHDRPlusColorTransformParams; + +/** + * This struct represents dynamic metadata for color volume transform - + * application 4 of SMPTE 2094-40:2016 standard. + * + * To be used as payload of a AVFrameSideData or AVPacketSideData with the + * appropriate type. + * + * @note The struct should be allocated with + * av_dynamic_hdr_plus_alloc() and its size is not a part of + * the public ABI. + */ +typedef struct AVDynamicHDRPlus { + /** + * Country code by Rec. ITU-T T.35 Annex A. The value shall be 0xB5. + */ + uint8_t itu_t_t35_country_code; + + /** + * Application version in the application defining document in ST-2094 + * suite. The value shall be set to 0. + */ + uint8_t application_version; + + /** + * The number of processing windows. The value shall be in the range + * of 1 to 3, inclusive. + */ + uint8_t num_windows; + + /** + * The color transform parameters for every processing window. + */ + AVHDRPlusColorTransformParams params[3]; + + /** + * The nominal maximum display luminance of the targeted system display, + * in units of 0.0001 candelas per square metre. The value shall be in + * the range of 0 to 10000, inclusive. + */ + AVRational targeted_system_display_maximum_luminance; + + /** + * This flag shall be equal to 0 in bit streams conforming to this version + * of this Specification. The value 1 is reserved for future use. + */ + uint8_t targeted_system_display_actual_peak_luminance_flag; + + /** + * The number of rows in the targeted system_display_actual_peak_luminance + * array. The value shall be in the range of 2 to 25, inclusive. + */ + uint8_t num_rows_targeted_system_display_actual_peak_luminance; + + /** + * The number of columns in the + * targeted_system_display_actual_peak_luminance array. The value shall be + * in the range of 2 to 25, inclusive. + */ + uint8_t num_cols_targeted_system_display_actual_peak_luminance; + + /** + * The normalized actual peak luminance of the targeted system display. The + * values should be in the range of 0 to 1, inclusive and in multiples of + * 1/15. + */ + AVRational targeted_system_display_actual_peak_luminance[25][25]; + + /** + * This flag shall be equal to 0 in bitstreams conforming to this version of + * this Specification. The value 1 is reserved for future use. + */ + uint8_t mastering_display_actual_peak_luminance_flag; + + /** + * The number of rows in the mastering_display_actual_peak_luminance array. + * The value shall be in the range of 2 to 25, inclusive. + */ + uint8_t num_rows_mastering_display_actual_peak_luminance; + + /** + * The number of columns in the mastering_display_actual_peak_luminance + * array. The value shall be in the range of 2 to 25, inclusive. + */ + uint8_t num_cols_mastering_display_actual_peak_luminance; + + /** + * The normalized actual peak luminance of the mastering display used for + * mastering the image essence. The values should be in the range of 0 to 1, + * inclusive and in multiples of 1/15. + */ + AVRational mastering_display_actual_peak_luminance[25][25]; +} AVDynamicHDRPlus; + +/** + * Allocate an AVDynamicHDRPlus structure and set its fields to + * default values. The resulting struct can be freed using av_freep(). + * + * @return An AVDynamicHDRPlus filled with default values or NULL + * on failure. + */ +AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size); + +/** + * Allocate a complete AVDynamicHDRPlus and add it to the frame. + * @param frame The frame which side data is added to. + * + * @return The AVDynamicHDRPlus structure to be filled by caller or NULL + * on failure. + */ +AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame); + +#endif /* AVUTIL_HDR_DYNAMIC_METADATA_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/hmac.h b/ThirdParty/ffmpeg/arm/include/libavutil/hmac.h new file mode 100644 index 000000000..412e95071 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/hmac.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2012 Martin Storsjo + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HMAC_H +#define AVUTIL_HMAC_H + +#include + +#include "version.h" +/** + * @defgroup lavu_hmac HMAC + * @ingroup lavu_crypto + * @{ + */ + +enum AVHMACType { + AV_HMAC_MD5, + AV_HMAC_SHA1, + AV_HMAC_SHA224, + AV_HMAC_SHA256, + AV_HMAC_SHA384, + AV_HMAC_SHA512, +}; + +typedef struct AVHMAC AVHMAC; + +/** + * Allocate an AVHMAC context. + * @param type The hash function used for the HMAC. + */ +AVHMAC *av_hmac_alloc(enum AVHMACType type); + +/** + * Free an AVHMAC context. + * @param ctx The context to free, may be NULL + */ +void av_hmac_free(AVHMAC *ctx); + +/** + * Initialize an AVHMAC context with an authentication key. + * @param ctx The HMAC context + * @param key The authentication key + * @param keylen The length of the key, in bytes + */ +void av_hmac_init(AVHMAC *ctx, const uint8_t *key, unsigned int keylen); + +/** + * Hash data with the HMAC. + * @param ctx The HMAC context + * @param data The data to hash + * @param len The length of the data, in bytes + */ +void av_hmac_update(AVHMAC *ctx, const uint8_t *data, unsigned int len); + +/** + * Finish hashing and output the HMAC digest. + * @param ctx The HMAC context + * @param out The output buffer to write the digest into + * @param outlen The length of the out buffer, in bytes + * @return The number of bytes written to out, or a negative error code. + */ +int av_hmac_final(AVHMAC *ctx, uint8_t *out, unsigned int outlen); + +/** + * Hash an array of data with a key. + * @param ctx The HMAC context + * @param data The data to hash + * @param len The length of the data, in bytes + * @param key The authentication key + * @param keylen The length of the key, in bytes + * @param out The output buffer to write the digest into + * @param outlen The length of the out buffer, in bytes + * @return The number of bytes written to out, or a negative error code. + */ +int av_hmac_calc(AVHMAC *ctx, const uint8_t *data, unsigned int len, + const uint8_t *key, unsigned int keylen, + uint8_t *out, unsigned int outlen); + +/** + * @} + */ + +#endif /* AVUTIL_HMAC_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext.h b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext.h new file mode 100644 index 000000000..04d19d89c --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext.h @@ -0,0 +1,605 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_H +#define AVUTIL_HWCONTEXT_H + +#include "buffer.h" +#include "frame.h" +#include "log.h" +#include "pixfmt.h" + +enum AVHWDeviceType { + AV_HWDEVICE_TYPE_NONE, + AV_HWDEVICE_TYPE_VDPAU, + AV_HWDEVICE_TYPE_CUDA, + AV_HWDEVICE_TYPE_VAAPI, + AV_HWDEVICE_TYPE_DXVA2, + AV_HWDEVICE_TYPE_QSV, + AV_HWDEVICE_TYPE_VIDEOTOOLBOX, + AV_HWDEVICE_TYPE_D3D11VA, + AV_HWDEVICE_TYPE_DRM, + AV_HWDEVICE_TYPE_OPENCL, + AV_HWDEVICE_TYPE_MEDIACODEC, + AV_HWDEVICE_TYPE_VULKAN, +}; + +typedef struct AVHWDeviceInternal AVHWDeviceInternal; + +/** + * This struct aggregates all the (hardware/vendor-specific) "high-level" state, + * i.e. state that is not tied to a concrete processing configuration. + * E.g., in an API that supports hardware-accelerated encoding and decoding, + * this struct will (if possible) wrap the state that is common to both encoding + * and decoding and from which specific instances of encoders or decoders can be + * derived. + * + * This struct is reference-counted with the AVBuffer mechanism. The + * av_hwdevice_ctx_alloc() constructor yields a reference, whose data field + * points to the actual AVHWDeviceContext. Further objects derived from + * AVHWDeviceContext (such as AVHWFramesContext, describing a frame pool with + * specific properties) will hold an internal reference to it. After all the + * references are released, the AVHWDeviceContext itself will be freed, + * optionally invoking a user-specified callback for uninitializing the hardware + * state. + */ +typedef struct AVHWDeviceContext { + /** + * A class for logging. Set by av_hwdevice_ctx_alloc(). + */ + const AVClass *av_class; + + /** + * Private data used internally by libavutil. Must not be accessed in any + * way by the caller. + */ + AVHWDeviceInternal *internal; + + /** + * This field identifies the underlying API used for hardware access. + * + * This field is set when this struct is allocated and never changed + * afterwards. + */ + enum AVHWDeviceType type; + + /** + * The format-specific data, allocated and freed by libavutil along with + * this context. + * + * Should be cast by the user to the format-specific context defined in the + * corresponding header (hwcontext_*.h) and filled as described in the + * documentation before calling av_hwdevice_ctx_init(). + * + * After calling av_hwdevice_ctx_init() this struct should not be modified + * by the caller. + */ + void *hwctx; + + /** + * This field may be set by the caller before calling av_hwdevice_ctx_init(). + * + * If non-NULL, this callback will be called when the last reference to + * this context is unreferenced, immediately before it is freed. + * + * @note when other objects (e.g an AVHWFramesContext) are derived from this + * struct, this callback will be invoked after all such child objects + * are fully uninitialized and their respective destructors invoked. + */ + void (*free)(struct AVHWDeviceContext *ctx); + + /** + * Arbitrary user data, to be used e.g. by the free() callback. + */ + void *user_opaque; +} AVHWDeviceContext; + +typedef struct AVHWFramesInternal AVHWFramesInternal; + +/** + * This struct describes a set or pool of "hardware" frames (i.e. those with + * data not located in normal system memory). All the frames in the pool are + * assumed to be allocated in the same way and interchangeable. + * + * This struct is reference-counted with the AVBuffer mechanism and tied to a + * given AVHWDeviceContext instance. The av_hwframe_ctx_alloc() constructor + * yields a reference, whose data field points to the actual AVHWFramesContext + * struct. + */ +typedef struct AVHWFramesContext { + /** + * A class for logging. + */ + const AVClass *av_class; + + /** + * Private data used internally by libavutil. Must not be accessed in any + * way by the caller. + */ + AVHWFramesInternal *internal; + + /** + * A reference to the parent AVHWDeviceContext. This reference is owned and + * managed by the enclosing AVHWFramesContext, but the caller may derive + * additional references from it. + */ + AVBufferRef *device_ref; + + /** + * The parent AVHWDeviceContext. This is simply a pointer to + * device_ref->data provided for convenience. + * + * Set by libavutil in av_hwframe_ctx_init(). + */ + AVHWDeviceContext *device_ctx; + + /** + * The format-specific data, allocated and freed automatically along with + * this context. + * + * Should be cast by the user to the format-specific context defined in the + * corresponding header (hwframe_*.h) and filled as described in the + * documentation before calling av_hwframe_ctx_init(). + * + * After any frames using this context are created, the contents of this + * struct should not be modified by the caller. + */ + void *hwctx; + + /** + * This field may be set by the caller before calling av_hwframe_ctx_init(). + * + * If non-NULL, this callback will be called when the last reference to + * this context is unreferenced, immediately before it is freed. + */ + void (*free)(struct AVHWFramesContext *ctx); + + /** + * Arbitrary user data, to be used e.g. by the free() callback. + */ + void *user_opaque; + + /** + * A pool from which the frames are allocated by av_hwframe_get_buffer(). + * This field may be set by the caller before calling av_hwframe_ctx_init(). + * The buffers returned by calling av_buffer_pool_get() on this pool must + * have the properties described in the documentation in the corresponding hw + * type's header (hwcontext_*.h). The pool will be freed strictly before + * this struct's free() callback is invoked. + * + * This field may be NULL, then libavutil will attempt to allocate a pool + * internally. Note that certain device types enforce pools allocated at + * fixed size (frame count), which cannot be extended dynamically. In such a + * case, initial_pool_size must be set appropriately. + */ + AVBufferPool *pool; + + /** + * Initial size of the frame pool. If a device type does not support + * dynamically resizing the pool, then this is also the maximum pool size. + * + * May be set by the caller before calling av_hwframe_ctx_init(). Must be + * set if pool is NULL and the device type does not support dynamic pools. + */ + int initial_pool_size; + + /** + * The pixel format identifying the underlying HW surface type. + * + * Must be a hwaccel format, i.e. the corresponding descriptor must have the + * AV_PIX_FMT_FLAG_HWACCEL flag set. + * + * Must be set by the user before calling av_hwframe_ctx_init(). + */ + enum AVPixelFormat format; + + /** + * The pixel format identifying the actual data layout of the hardware + * frames. + * + * Must be set by the caller before calling av_hwframe_ctx_init(). + * + * @note when the underlying API does not provide the exact data layout, but + * only the colorspace/bit depth, this field should be set to the fully + * planar version of that format (e.g. for 8-bit 420 YUV it should be + * AV_PIX_FMT_YUV420P, not AV_PIX_FMT_NV12 or anything else). + */ + enum AVPixelFormat sw_format; + + /** + * The allocated dimensions of the frames in this pool. + * + * Must be set by the user before calling av_hwframe_ctx_init(). + */ + int width, height; +} AVHWFramesContext; + +/** + * Look up an AVHWDeviceType by name. + * + * @param name String name of the device type (case-insensitive). + * @return The type from enum AVHWDeviceType, or AV_HWDEVICE_TYPE_NONE if + * not found. + */ +enum AVHWDeviceType av_hwdevice_find_type_by_name(const char *name); + +/** Get the string name of an AVHWDeviceType. + * + * @param type Type from enum AVHWDeviceType. + * @return Pointer to a static string containing the name, or NULL if the type + * is not valid. + */ +const char *av_hwdevice_get_type_name(enum AVHWDeviceType type); + +/** + * Iterate over supported device types. + * + * @param type AV_HWDEVICE_TYPE_NONE initially, then the previous type + * returned by this function in subsequent iterations. + * @return The next usable device type from enum AVHWDeviceType, or + * AV_HWDEVICE_TYPE_NONE if there are no more. + */ +enum AVHWDeviceType av_hwdevice_iterate_types(enum AVHWDeviceType prev); + +/** + * Allocate an AVHWDeviceContext for a given hardware type. + * + * @param type the type of the hardware device to allocate. + * @return a reference to the newly created AVHWDeviceContext on success or NULL + * on failure. + */ +AVBufferRef *av_hwdevice_ctx_alloc(enum AVHWDeviceType type); + +/** + * Finalize the device context before use. This function must be called after + * the context is filled with all the required information and before it is + * used in any way. + * + * @param ref a reference to the AVHWDeviceContext + * @return 0 on success, a negative AVERROR code on failure + */ +int av_hwdevice_ctx_init(AVBufferRef *ref); + +/** + * Open a device of the specified type and create an AVHWDeviceContext for it. + * + * This is a convenience function intended to cover the simple cases. Callers + * who need to fine-tune device creation/management should open the device + * manually and then wrap it in an AVHWDeviceContext using + * av_hwdevice_ctx_alloc()/av_hwdevice_ctx_init(). + * + * The returned context is already initialized and ready for use, the caller + * should not call av_hwdevice_ctx_init() on it. The user_opaque/free fields of + * the created AVHWDeviceContext are set by this function and should not be + * touched by the caller. + * + * @param device_ctx On success, a reference to the newly-created device context + * will be written here. The reference is owned by the caller + * and must be released with av_buffer_unref() when no longer + * needed. On failure, NULL will be written to this pointer. + * @param type The type of the device to create. + * @param device A type-specific string identifying the device to open. + * @param opts A dictionary of additional (type-specific) options to use in + * opening the device. The dictionary remains owned by the caller. + * @param flags currently unused + * + * @return 0 on success, a negative AVERROR code on failure. + */ +int av_hwdevice_ctx_create(AVBufferRef **device_ctx, enum AVHWDeviceType type, + const char *device, AVDictionary *opts, int flags); + +/** + * Create a new device of the specified type from an existing device. + * + * If the source device is a device of the target type or was originally + * derived from such a device (possibly through one or more intermediate + * devices of other types), then this will return a reference to the + * existing device of the same type as is requested. + * + * Otherwise, it will attempt to derive a new device from the given source + * device. If direct derivation to the new type is not implemented, it will + * attempt the same derivation from each ancestor of the source device in + * turn looking for an implemented derivation method. + * + * @param dst_ctx On success, a reference to the newly-created + * AVHWDeviceContext. + * @param type The type of the new device to create. + * @param src_ctx A reference to an existing AVHWDeviceContext which will be + * used to create the new device. + * @param flags Currently unused; should be set to zero. + * @return Zero on success, a negative AVERROR code on failure. + */ +int av_hwdevice_ctx_create_derived(AVBufferRef **dst_ctx, + enum AVHWDeviceType type, + AVBufferRef *src_ctx, int flags); + +/** + * Create a new device of the specified type from an existing device. + * + * This function performs the same action as av_hwdevice_ctx_create_derived, + * however, it is able to set options for the new device to be derived. + * + * @param dst_ctx On success, a reference to the newly-created + * AVHWDeviceContext. + * @param type The type of the new device to create. + * @param src_ctx A reference to an existing AVHWDeviceContext which will be + * used to create the new device. + * @param options Options for the new device to create, same format as in + * av_hwdevice_ctx_create. + * @param flags Currently unused; should be set to zero. + * @return Zero on success, a negative AVERROR code on failure. + */ +int av_hwdevice_ctx_create_derived_opts(AVBufferRef **dst_ctx, + enum AVHWDeviceType type, + AVBufferRef *src_ctx, + AVDictionary *options, int flags); + +/** + * Allocate an AVHWFramesContext tied to a given device context. + * + * @param device_ctx a reference to a AVHWDeviceContext. This function will make + * a new reference for internal use, the one passed to the + * function remains owned by the caller. + * @return a reference to the newly created AVHWFramesContext on success or NULL + * on failure. + */ +AVBufferRef *av_hwframe_ctx_alloc(AVBufferRef *device_ctx); + +/** + * Finalize the context before use. This function must be called after the + * context is filled with all the required information and before it is attached + * to any frames. + * + * @param ref a reference to the AVHWFramesContext + * @return 0 on success, a negative AVERROR code on failure + */ +int av_hwframe_ctx_init(AVBufferRef *ref); + +/** + * Allocate a new frame attached to the given AVHWFramesContext. + * + * @param hwframe_ctx a reference to an AVHWFramesContext + * @param frame an empty (freshly allocated or unreffed) frame to be filled with + * newly allocated buffers. + * @param flags currently unused, should be set to zero + * @return 0 on success, a negative AVERROR code on failure + */ +int av_hwframe_get_buffer(AVBufferRef *hwframe_ctx, AVFrame *frame, int flags); + +/** + * Copy data to or from a hw surface. At least one of dst/src must have an + * AVHWFramesContext attached. + * + * If src has an AVHWFramesContext attached, then the format of dst (if set) + * must use one of the formats returned by av_hwframe_transfer_get_formats(src, + * AV_HWFRAME_TRANSFER_DIRECTION_FROM). + * If dst has an AVHWFramesContext attached, then the format of src must use one + * of the formats returned by av_hwframe_transfer_get_formats(dst, + * AV_HWFRAME_TRANSFER_DIRECTION_TO) + * + * dst may be "clean" (i.e. with data/buf pointers unset), in which case the + * data buffers will be allocated by this function using av_frame_get_buffer(). + * If dst->format is set, then this format will be used, otherwise (when + * dst->format is AV_PIX_FMT_NONE) the first acceptable format will be chosen. + * + * The two frames must have matching allocated dimensions (i.e. equal to + * AVHWFramesContext.width/height), since not all device types support + * transferring a sub-rectangle of the whole surface. The display dimensions + * (i.e. AVFrame.width/height) may be smaller than the allocated dimensions, but + * also have to be equal for both frames. When the display dimensions are + * smaller than the allocated dimensions, the content of the padding in the + * destination frame is unspecified. + * + * @param dst the destination frame. dst is not touched on failure. + * @param src the source frame. + * @param flags currently unused, should be set to zero + * @return 0 on success, a negative AVERROR error code on failure. + */ +int av_hwframe_transfer_data(AVFrame *dst, const AVFrame *src, int flags); + +enum AVHWFrameTransferDirection { + /** + * Transfer the data from the queried hw frame. + */ + AV_HWFRAME_TRANSFER_DIRECTION_FROM, + + /** + * Transfer the data to the queried hw frame. + */ + AV_HWFRAME_TRANSFER_DIRECTION_TO, +}; + +/** + * Get a list of possible source or target formats usable in + * av_hwframe_transfer_data(). + * + * @param hwframe_ctx the frame context to obtain the information for + * @param dir the direction of the transfer + * @param formats the pointer to the output format list will be written here. + * The list is terminated with AV_PIX_FMT_NONE and must be freed + * by the caller when no longer needed using av_free(). + * If this function returns successfully, the format list will + * have at least one item (not counting the terminator). + * On failure, the contents of this pointer are unspecified. + * @param flags currently unused, should be set to zero + * @return 0 on success, a negative AVERROR code on failure. + */ +int av_hwframe_transfer_get_formats(AVBufferRef *hwframe_ctx, + enum AVHWFrameTransferDirection dir, + enum AVPixelFormat **formats, int flags); + + +/** + * This struct describes the constraints on hardware frames attached to + * a given device with a hardware-specific configuration. This is returned + * by av_hwdevice_get_hwframe_constraints() and must be freed by + * av_hwframe_constraints_free() after use. + */ +typedef struct AVHWFramesConstraints { + /** + * A list of possible values for format in the hw_frames_ctx, + * terminated by AV_PIX_FMT_NONE. This member will always be filled. + */ + enum AVPixelFormat *valid_hw_formats; + + /** + * A list of possible values for sw_format in the hw_frames_ctx, + * terminated by AV_PIX_FMT_NONE. Can be NULL if this information is + * not known. + */ + enum AVPixelFormat *valid_sw_formats; + + /** + * The minimum size of frames in this hw_frames_ctx. + * (Zero if not known.) + */ + int min_width; + int min_height; + + /** + * The maximum size of frames in this hw_frames_ctx. + * (INT_MAX if not known / no limit.) + */ + int max_width; + int max_height; +} AVHWFramesConstraints; + +/** + * Allocate a HW-specific configuration structure for a given HW device. + * After use, the user must free all members as required by the specific + * hardware structure being used, then free the structure itself with + * av_free(). + * + * @param device_ctx a reference to the associated AVHWDeviceContext. + * @return The newly created HW-specific configuration structure on + * success or NULL on failure. + */ +void *av_hwdevice_hwconfig_alloc(AVBufferRef *device_ctx); + +/** + * Get the constraints on HW frames given a device and the HW-specific + * configuration to be used with that device. If no HW-specific + * configuration is provided, returns the maximum possible capabilities + * of the device. + * + * @param ref a reference to the associated AVHWDeviceContext. + * @param hwconfig a filled HW-specific configuration structure, or NULL + * to return the maximum possible capabilities of the device. + * @return AVHWFramesConstraints structure describing the constraints + * on the device, or NULL if not available. + */ +AVHWFramesConstraints *av_hwdevice_get_hwframe_constraints(AVBufferRef *ref, + const void *hwconfig); + +/** + * Free an AVHWFrameConstraints structure. + * + * @param constraints The (filled or unfilled) AVHWFrameConstraints structure. + */ +void av_hwframe_constraints_free(AVHWFramesConstraints **constraints); + + +/** + * Flags to apply to frame mappings. + */ +enum { + /** + * The mapping must be readable. + */ + AV_HWFRAME_MAP_READ = 1 << 0, + /** + * The mapping must be writeable. + */ + AV_HWFRAME_MAP_WRITE = 1 << 1, + /** + * The mapped frame will be overwritten completely in subsequent + * operations, so the current frame data need not be loaded. Any values + * which are not overwritten are unspecified. + */ + AV_HWFRAME_MAP_OVERWRITE = 1 << 2, + /** + * The mapping must be direct. That is, there must not be any copying in + * the map or unmap steps. Note that performance of direct mappings may + * be much lower than normal memory. + */ + AV_HWFRAME_MAP_DIRECT = 1 << 3, +}; + +/** + * Map a hardware frame. + * + * This has a number of different possible effects, depending on the format + * and origin of the src and dst frames. On input, src should be a usable + * frame with valid buffers and dst should be blank (typically as just created + * by av_frame_alloc()). src should have an associated hwframe context, and + * dst may optionally have a format and associated hwframe context. + * + * If src was created by mapping a frame from the hwframe context of dst, + * then this function undoes the mapping - dst is replaced by a reference to + * the frame that src was originally mapped from. + * + * If both src and dst have an associated hwframe context, then this function + * attempts to map the src frame from its hardware context to that of dst and + * then fill dst with appropriate data to be usable there. This will only be + * possible if the hwframe contexts and associated devices are compatible - + * given compatible devices, av_hwframe_ctx_create_derived() can be used to + * create a hwframe context for dst in which mapping should be possible. + * + * If src has a hwframe context but dst does not, then the src frame is + * mapped to normal memory and should thereafter be usable as a normal frame. + * If the format is set on dst, then the mapping will attempt to create dst + * with that format and fail if it is not possible. If format is unset (is + * AV_PIX_FMT_NONE) then dst will be mapped with whatever the most appropriate + * format to use is (probably the sw_format of the src hwframe context). + * + * A return value of AVERROR(ENOSYS) indicates that the mapping is not + * possible with the given arguments and hwframe setup, while other return + * values indicate that it failed somehow. + * + * @param dst Destination frame, to contain the mapping. + * @param src Source frame, to be mapped. + * @param flags Some combination of AV_HWFRAME_MAP_* flags. + * @return Zero on success, negative AVERROR code on failure. + */ +int av_hwframe_map(AVFrame *dst, const AVFrame *src, int flags); + + +/** + * Create and initialise an AVHWFramesContext as a mapping of another existing + * AVHWFramesContext on a different device. + * + * av_hwframe_ctx_init() should not be called after this. + * + * @param derived_frame_ctx On success, a reference to the newly created + * AVHWFramesContext. + * @param derived_device_ctx A reference to the device to create the new + * AVHWFramesContext on. + * @param source_frame_ctx A reference to an existing AVHWFramesContext + * which will be mapped to the derived context. + * @param flags Some combination of AV_HWFRAME_MAP_* flags, defining the + * mapping parameters to apply to frames which are allocated + * in the derived device. + * @return Zero on success, negative AVERROR code on failure. + */ +int av_hwframe_ctx_create_derived(AVBufferRef **derived_frame_ctx, + enum AVPixelFormat format, + AVBufferRef *derived_device_ctx, + AVBufferRef *source_frame_ctx, + int flags); + +#endif /* AVUTIL_HWCONTEXT_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_cuda.h b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_cuda.h new file mode 100644 index 000000000..cefbe0cea --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_cuda.h @@ -0,0 +1,69 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef AVUTIL_HWCONTEXT_CUDA_H +#define AVUTIL_HWCONTEXT_CUDA_H + +#ifndef CUDA_VERSION +#include +#endif + +#include "pixfmt.h" + +/** + * @file + * An API-specific header for AV_HWDEVICE_TYPE_CUDA. + * + * This API supports dynamic frame pools. AVHWFramesContext.pool must return + * AVBufferRefs whose data pointer is a CUdeviceptr. + */ + +typedef struct AVCUDADeviceContextInternal AVCUDADeviceContextInternal; + +/** + * This struct is allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVCUDADeviceContext { + CUcontext cuda_ctx; + CUstream stream; + AVCUDADeviceContextInternal *internal; +} AVCUDADeviceContext; + +/** + * AVHWFramesContext.hwctx is currently not used + */ + +/** + * @defgroup hwcontext_cuda Device context creation flags + * + * Flags for av_hwdevice_ctx_create. + * + * @{ + */ + +/** + * Use primary device context instead of creating a new one. + */ +#define AV_CUDA_USE_PRIMARY_CONTEXT (1 << 0) + +/** + * @} + */ + +#endif /* AVUTIL_HWCONTEXT_CUDA_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_d3d11va.h b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_d3d11va.h new file mode 100644 index 000000000..9f91e9b1b --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_d3d11va.h @@ -0,0 +1,169 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_D3D11VA_H +#define AVUTIL_HWCONTEXT_D3D11VA_H + +/** + * @file + * An API-specific header for AV_HWDEVICE_TYPE_D3D11VA. + * + * The default pool implementation will be fixed-size if initial_pool_size is + * set (and allocate elements from an array texture). Otherwise it will allocate + * individual textures. Be aware that decoding requires a single array texture. + * + * Using sw_format==AV_PIX_FMT_YUV420P has special semantics, and maps to + * DXGI_FORMAT_420_OPAQUE. av_hwframe_transfer_data() is not supported for + * this format. Refer to MSDN for details. + * + * av_hwdevice_ctx_create() for this device type supports a key named "debug" + * for the AVDictionary entry. If this is set to any value, the device creation + * code will try to load various supported D3D debugging layers. + */ + +#include +#include + +/** + * This struct is allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVD3D11VADeviceContext { + /** + * Device used for texture creation and access. This can also be used to + * set the libavcodec decoding device. + * + * Must be set by the user. This is the only mandatory field - the other + * device context fields are set from this and are available for convenience. + * + * Deallocating the AVHWDeviceContext will always release this interface, + * and it does not matter whether it was user-allocated. + */ + ID3D11Device *device; + + /** + * If unset, this will be set from the device field on init. + * + * Deallocating the AVHWDeviceContext will always release this interface, + * and it does not matter whether it was user-allocated. + */ + ID3D11DeviceContext *device_context; + + /** + * If unset, this will be set from the device field on init. + * + * Deallocating the AVHWDeviceContext will always release this interface, + * and it does not matter whether it was user-allocated. + */ + ID3D11VideoDevice *video_device; + + /** + * If unset, this will be set from the device_context field on init. + * + * Deallocating the AVHWDeviceContext will always release this interface, + * and it does not matter whether it was user-allocated. + */ + ID3D11VideoContext *video_context; + + /** + * Callbacks for locking. They protect accesses to device_context and + * video_context calls. They also protect access to the internal staging + * texture (for av_hwframe_transfer_data() calls). They do NOT protect + * access to hwcontext or decoder state in general. + * + * If unset on init, the hwcontext implementation will set them to use an + * internal mutex. + * + * The underlying lock must be recursive. lock_ctx is for free use by the + * locking implementation. + */ + void (*lock)(void *lock_ctx); + void (*unlock)(void *lock_ctx); + void *lock_ctx; +} AVD3D11VADeviceContext; + +/** + * D3D11 frame descriptor for pool allocation. + * + * In user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs + * with the data pointer pointing at an object of this type describing the + * planes of the frame. + * + * This has no use outside of custom allocation, and AVFrame AVBufferRef do not + * necessarily point to an instance of this struct. + */ +typedef struct AVD3D11FrameDescriptor { + /** + * The texture in which the frame is located. The reference count is + * managed by the AVBufferRef, and destroying the reference will release + * the interface. + * + * Normally stored in AVFrame.data[0]. + */ + ID3D11Texture2D *texture; + + /** + * The index into the array texture element representing the frame, or 0 + * if the texture is not an array texture. + * + * Normally stored in AVFrame.data[1] (cast from intptr_t). + */ + intptr_t index; +} AVD3D11FrameDescriptor; + +/** + * This struct is allocated as AVHWFramesContext.hwctx + */ +typedef struct AVD3D11VAFramesContext { + /** + * The canonical texture used for pool allocation. If this is set to NULL + * on init, the hwframes implementation will allocate and set an array + * texture if initial_pool_size > 0. + * + * The only situation when the API user should set this is: + * - the user wants to do manual pool allocation (setting + * AVHWFramesContext.pool), instead of letting AVHWFramesContext + * allocate the pool + * - of an array texture + * - and wants it to use it for decoding + * - this has to be done before calling av_hwframe_ctx_init() + * + * Deallocating the AVHWFramesContext will always release this interface, + * and it does not matter whether it was user-allocated. + * + * This is in particular used by the libavcodec D3D11VA hwaccel, which + * requires a single array texture. It will create ID3D11VideoDecoderOutputView + * objects for each array texture element on decoder initialization. + */ + ID3D11Texture2D *texture; + + /** + * D3D11_TEXTURE2D_DESC.BindFlags used for texture creation. The user must + * at least set D3D11_BIND_DECODER if the frames context is to be used for + * video decoding. + * This field is ignored/invalid if a user-allocated texture is provided. + */ + UINT BindFlags; + + /** + * D3D11_TEXTURE2D_DESC.MiscFlags used for texture creation. + * This field is ignored/invalid if a user-allocated texture is provided. + */ + UINT MiscFlags; +} AVD3D11VAFramesContext; + +#endif /* AVUTIL_HWCONTEXT_D3D11VA_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_drm.h b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_drm.h new file mode 100644 index 000000000..42709f215 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_drm.h @@ -0,0 +1,169 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_DRM_H +#define AVUTIL_HWCONTEXT_DRM_H + +#include +#include + +/** + * @file + * API-specific header for AV_HWDEVICE_TYPE_DRM. + * + * Internal frame allocation is not currently supported - all frames + * must be allocated by the user. Thus AVHWFramesContext is always + * NULL, though this may change if support for frame allocation is + * added in future. + */ + +enum { + /** + * The maximum number of layers/planes in a DRM frame. + */ + AV_DRM_MAX_PLANES = 4 +}; + +/** + * DRM object descriptor. + * + * Describes a single DRM object, addressing it as a PRIME file + * descriptor. + */ +typedef struct AVDRMObjectDescriptor { + /** + * DRM PRIME fd for the object. + */ + int fd; + /** + * Total size of the object. + * + * (This includes any parts not which do not contain image data.) + */ + size_t size; + /** + * Format modifier applied to the object (DRM_FORMAT_MOD_*). + * + * If the format modifier is unknown then this should be set to + * DRM_FORMAT_MOD_INVALID. + */ + uint64_t format_modifier; +} AVDRMObjectDescriptor; + +/** + * DRM plane descriptor. + * + * Describes a single plane of a layer, which is contained within + * a single object. + */ +typedef struct AVDRMPlaneDescriptor { + /** + * Index of the object containing this plane in the objects + * array of the enclosing frame descriptor. + */ + int object_index; + /** + * Offset within that object of this plane. + */ + ptrdiff_t offset; + /** + * Pitch (linesize) of this plane. + */ + ptrdiff_t pitch; +} AVDRMPlaneDescriptor; + +/** + * DRM layer descriptor. + * + * Describes a single layer within a frame. This has the structure + * defined by its format, and will contain one or more planes. + */ +typedef struct AVDRMLayerDescriptor { + /** + * Format of the layer (DRM_FORMAT_*). + */ + uint32_t format; + /** + * Number of planes in the layer. + * + * This must match the number of planes required by format. + */ + int nb_planes; + /** + * Array of planes in this layer. + */ + AVDRMPlaneDescriptor planes[AV_DRM_MAX_PLANES]; +} AVDRMLayerDescriptor; + +/** + * DRM frame descriptor. + * + * This is used as the data pointer for AV_PIX_FMT_DRM_PRIME frames. + * It is also used by user-allocated frame pools - allocating in + * AVHWFramesContext.pool must return AVBufferRefs which contain + * an object of this type. + * + * The fields of this structure should be set such it can be + * imported directly by EGL using the EGL_EXT_image_dma_buf_import + * and EGL_EXT_image_dma_buf_import_modifiers extensions. + * (Note that the exact layout of a particular format may vary between + * platforms - we only specify that the same platform should be able + * to import it.) + * + * The total number of planes must not exceed AV_DRM_MAX_PLANES, and + * the order of the planes by increasing layer index followed by + * increasing plane index must be the same as the order which would + * be used for the data pointers in the equivalent software format. + */ +typedef struct AVDRMFrameDescriptor { + /** + * Number of DRM objects making up this frame. + */ + int nb_objects; + /** + * Array of objects making up the frame. + */ + AVDRMObjectDescriptor objects[AV_DRM_MAX_PLANES]; + /** + * Number of layers in the frame. + */ + int nb_layers; + /** + * Array of layers in the frame. + */ + AVDRMLayerDescriptor layers[AV_DRM_MAX_PLANES]; +} AVDRMFrameDescriptor; + +/** + * DRM device. + * + * Allocated as AVHWDeviceContext.hwctx. + */ +typedef struct AVDRMDeviceContext { + /** + * File descriptor of DRM device. + * + * This is used as the device to create frames on, and may also be + * used in some derivation and mapping operations. + * + * If no device is required, set to -1. + */ + int fd; +} AVDRMDeviceContext; + +#endif /* AVUTIL_HWCONTEXT_DRM_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_dxva2.h b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_dxva2.h new file mode 100644 index 000000000..e1b79bc0d --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_dxva2.h @@ -0,0 +1,75 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef AVUTIL_HWCONTEXT_DXVA2_H +#define AVUTIL_HWCONTEXT_DXVA2_H + +/** + * @file + * An API-specific header for AV_HWDEVICE_TYPE_DXVA2. + * + * Only fixed-size pools are supported. + * + * For user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs + * with the data pointer set to a pointer to IDirect3DSurface9. + */ + +#include +#include + +/** + * This struct is allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVDXVA2DeviceContext { + IDirect3DDeviceManager9 *devmgr; +} AVDXVA2DeviceContext; + +/** + * This struct is allocated as AVHWFramesContext.hwctx + */ +typedef struct AVDXVA2FramesContext { + /** + * The surface type (e.g. DXVA2_VideoProcessorRenderTarget or + * DXVA2_VideoDecoderRenderTarget). Must be set by the caller. + */ + DWORD surface_type; + + /** + * The surface pool. When an external pool is not provided by the caller, + * this will be managed (allocated and filled on init, freed on uninit) by + * libavutil. + */ + IDirect3DSurface9 **surfaces; + int nb_surfaces; + + /** + * Certain drivers require the decoder to be destroyed before the surfaces. + * To allow internally managed pools to work properly in such cases, this + * field is provided. + * + * If it is non-NULL, libavutil will call IDirectXVideoDecoder_Release() on + * it just before the internal surface pool is freed. + * + * This is for convenience only. Some code uses other methods to manage the + * decoder reference. + */ + IDirectXVideoDecoder *decoder_to_release; +} AVDXVA2FramesContext; + +#endif /* AVUTIL_HWCONTEXT_DXVA2_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_mediacodec.h b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_mediacodec.h new file mode 100644 index 000000000..101a9806d --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_mediacodec.h @@ -0,0 +1,36 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_MEDIACODEC_H +#define AVUTIL_HWCONTEXT_MEDIACODEC_H + +/** + * MediaCodec details. + * + * Allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVMediaCodecDeviceContext { + /** + * android/view/Surface handle, to be filled by the user. + * + * This is the default surface used by decoders on this device. + */ + void *surface; +} AVMediaCodecDeviceContext; + +#endif /* AVUTIL_HWCONTEXT_MEDIACODEC_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_opencl.h b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_opencl.h new file mode 100644 index 000000000..ef54486c9 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_opencl.h @@ -0,0 +1,100 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_OPENCL_H +#define AVUTIL_HWCONTEXT_OPENCL_H + +#ifdef __APPLE__ +#include +#else +#include +#endif + +#include "frame.h" + +/** + * @file + * API-specific header for AV_HWDEVICE_TYPE_OPENCL. + * + * Pools allocated internally are always dynamic, and are primarily intended + * to be used in OpenCL-only cases. If interoperation is required, it is + * typically required to allocate frames in the other API and then map the + * frames context to OpenCL with av_hwframe_ctx_create_derived(). + */ + +/** + * OpenCL frame descriptor for pool allocation. + * + * In user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs + * with the data pointer pointing at an object of this type describing the + * planes of the frame. + */ +typedef struct AVOpenCLFrameDescriptor { + /** + * Number of planes in the frame. + */ + int nb_planes; + /** + * OpenCL image2d objects for each plane of the frame. + */ + cl_mem planes[AV_NUM_DATA_POINTERS]; +} AVOpenCLFrameDescriptor; + +/** + * OpenCL device details. + * + * Allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVOpenCLDeviceContext { + /** + * The primary device ID of the device. If multiple OpenCL devices + * are associated with the context then this is the one which will + * be used for all operations internal to FFmpeg. + */ + cl_device_id device_id; + /** + * The OpenCL context which will contain all operations and frames on + * this device. + */ + cl_context context; + /** + * The default command queue for this device, which will be used by all + * frames contexts which do not have their own command queue. If not + * intialised by the user, a default queue will be created on the + * primary device. + */ + cl_command_queue command_queue; +} AVOpenCLDeviceContext; + +/** + * OpenCL-specific data associated with a frame pool. + * + * Allocated as AVHWFramesContext.hwctx. + */ +typedef struct AVOpenCLFramesContext { + /** + * The command queue used for internal asynchronous operations on this + * device (av_hwframe_transfer_data(), av_hwframe_map()). + * + * If this is not set, the command queue from the associated device is + * used instead. + */ + cl_command_queue command_queue; +} AVOpenCLFramesContext; + +#endif /* AVUTIL_HWCONTEXT_OPENCL_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_qsv.h b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_qsv.h new file mode 100644 index 000000000..b98d611cf --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_qsv.h @@ -0,0 +1,53 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_QSV_H +#define AVUTIL_HWCONTEXT_QSV_H + +#include + +/** + * @file + * An API-specific header for AV_HWDEVICE_TYPE_QSV. + * + * This API does not support dynamic frame pools. AVHWFramesContext.pool must + * contain AVBufferRefs whose data pointer points to an mfxFrameSurface1 struct. + */ + +/** + * This struct is allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVQSVDeviceContext { + mfxSession session; +} AVQSVDeviceContext; + +/** + * This struct is allocated as AVHWFramesContext.hwctx + */ +typedef struct AVQSVFramesContext { + mfxFrameSurface1 *surfaces; + int nb_surfaces; + + /** + * A combination of MFX_MEMTYPE_* describing the frame pool. + */ + int frame_type; +} AVQSVFramesContext; + +#endif /* AVUTIL_HWCONTEXT_QSV_H */ + diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_vaapi.h b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_vaapi.h new file mode 100644 index 000000000..0b2e071cb --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_vaapi.h @@ -0,0 +1,117 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_VAAPI_H +#define AVUTIL_HWCONTEXT_VAAPI_H + +#include + +/** + * @file + * API-specific header for AV_HWDEVICE_TYPE_VAAPI. + * + * Dynamic frame pools are supported, but note that any pool used as a render + * target is required to be of fixed size in order to be be usable as an + * argument to vaCreateContext(). + * + * For user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs + * with the data pointer set to a VASurfaceID. + */ + +enum { + /** + * The quirks field has been set by the user and should not be detected + * automatically by av_hwdevice_ctx_init(). + */ + AV_VAAPI_DRIVER_QUIRK_USER_SET = (1 << 0), + /** + * The driver does not destroy parameter buffers when they are used by + * vaRenderPicture(). Additional code will be required to destroy them + * separately afterwards. + */ + AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS = (1 << 1), + + /** + * The driver does not support the VASurfaceAttribMemoryType attribute, + * so the surface allocation code will not try to use it. + */ + AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE = (1 << 2), + + /** + * The driver does not support surface attributes at all. + * The surface allocation code will never pass them to surface allocation, + * and the results of the vaQuerySurfaceAttributes() call will be faked. + */ + AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES = (1 << 3), +}; + +/** + * VAAPI connection details. + * + * Allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVVAAPIDeviceContext { + /** + * The VADisplay handle, to be filled by the user. + */ + VADisplay display; + /** + * Driver quirks to apply - this is filled by av_hwdevice_ctx_init(), + * with reference to a table of known drivers, unless the + * AV_VAAPI_DRIVER_QUIRK_USER_SET bit is already present. The user + * may need to refer to this field when performing any later + * operations using VAAPI with the same VADisplay. + */ + unsigned int driver_quirks; +} AVVAAPIDeviceContext; + +/** + * VAAPI-specific data associated with a frame pool. + * + * Allocated as AVHWFramesContext.hwctx. + */ +typedef struct AVVAAPIFramesContext { + /** + * Set by the user to apply surface attributes to all surfaces in + * the frame pool. If null, default settings are used. + */ + VASurfaceAttrib *attributes; + int nb_attributes; + /** + * The surfaces IDs of all surfaces in the pool after creation. + * Only valid if AVHWFramesContext.initial_pool_size was positive. + * These are intended to be used as the render_targets arguments to + * vaCreateContext(). + */ + VASurfaceID *surface_ids; + int nb_surfaces; +} AVVAAPIFramesContext; + +/** + * VAAPI hardware pipeline configuration details. + * + * Allocated with av_hwdevice_hwconfig_alloc(). + */ +typedef struct AVVAAPIHWConfig { + /** + * ID of a VAAPI pipeline configuration. + */ + VAConfigID config_id; +} AVVAAPIHWConfig; + +#endif /* AVUTIL_HWCONTEXT_VAAPI_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_vdpau.h b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_vdpau.h new file mode 100644 index 000000000..1b7ea1e44 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_vdpau.h @@ -0,0 +1,44 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_VDPAU_H +#define AVUTIL_HWCONTEXT_VDPAU_H + +#include + +/** + * @file + * An API-specific header for AV_HWDEVICE_TYPE_VDPAU. + * + * This API supports dynamic frame pools. AVHWFramesContext.pool must return + * AVBufferRefs whose data pointer is a VdpVideoSurface. + */ + +/** + * This struct is allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVVDPAUDeviceContext { + VdpDevice device; + VdpGetProcAddress *get_proc_address; +} AVVDPAUDeviceContext; + +/** + * AVHWFramesContext.hwctx is currently not used + */ + +#endif /* AVUTIL_HWCONTEXT_VDPAU_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_videotoolbox.h b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_videotoolbox.h new file mode 100644 index 000000000..5074d79e6 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_videotoolbox.h @@ -0,0 +1,60 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_VIDEOTOOLBOX_H +#define AVUTIL_HWCONTEXT_VIDEOTOOLBOX_H + +#include + +#include + +#include "pixfmt.h" + +/** + * @file + * An API-specific header for AV_HWDEVICE_TYPE_VIDEOTOOLBOX. + * + * This API currently does not support frame allocation, as the raw VideoToolbox + * API does allocation, and FFmpeg itself never has the need to allocate frames. + * + * If the API user sets a custom pool, AVHWFramesContext.pool must return + * AVBufferRefs whose data pointer is a CVImageBufferRef or CVPixelBufferRef. + * + * Currently AVHWDeviceContext.hwctx and AVHWFramesContext.hwctx are always + * NULL. + */ + +/** + * Convert a VideoToolbox (actually CoreVideo) format to AVPixelFormat. + * Returns AV_PIX_FMT_NONE if no known equivalent was found. + */ +enum AVPixelFormat av_map_videotoolbox_format_to_pixfmt(uint32_t cv_fmt); + +/** + * Convert an AVPixelFormat to a VideoToolbox (actually CoreVideo) format. + * Returns 0 if no known equivalent was found. + */ +uint32_t av_map_videotoolbox_format_from_pixfmt(enum AVPixelFormat pix_fmt); + +/** + * Same as av_map_videotoolbox_format_from_pixfmt function, but can map and + * return full range pixel formats via a flag. + */ +uint32_t av_map_videotoolbox_format_from_pixfmt2(enum AVPixelFormat pix_fmt, bool full_range); + +#endif /* AVUTIL_HWCONTEXT_VIDEOTOOLBOX_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_vulkan.h b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_vulkan.h new file mode 100644 index 000000000..5cbeb8e7e --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/hwcontext_vulkan.h @@ -0,0 +1,204 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_VULKAN_H +#define AVUTIL_HWCONTEXT_VULKAN_H + +#include + +#include "pixfmt.h" +#include "frame.h" + +/** + * @file + * API-specific header for AV_HWDEVICE_TYPE_VULKAN. + * + * For user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs + * with the data pointer set to an AVVkFrame. + */ + +/** + * Main Vulkan context, allocated as AVHWDeviceContext.hwctx. + * All of these can be set before init to change what the context uses + */ +typedef struct AVVulkanDeviceContext { + /** + * Custom memory allocator, else NULL + */ + const VkAllocationCallbacks *alloc; + /** + * Vulkan instance. Must be at least version 1.1. + */ + VkInstance inst; + /** + * Physical device + */ + VkPhysicalDevice phys_dev; + /** + * Active device + */ + VkDevice act_dev; + /** + * Queue family index for graphics + * @note av_hwdevice_create() will set all 3 queue indices if unset + * If there is no dedicated queue for compute or transfer operations, + * they will be set to the graphics queue index which can handle both. + * nb_graphics_queues indicates how many queues were enabled for the + * graphics queue (must be at least 1) + */ + int queue_family_index; + int nb_graphics_queues; + /** + * Queue family index to use for transfer operations, and the amount of queues + * enabled. In case there is no dedicated transfer queue, nb_tx_queues + * must be 0 and queue_family_tx_index must be the same as either the graphics + * queue or the compute queue, if available. + */ + int queue_family_tx_index; + int nb_tx_queues; + /** + * Queue family index for compute ops, and the amount of queues enabled. + * In case there are no dedicated compute queues, nb_comp_queues must be + * 0 and its queue family index must be set to the graphics queue. + */ + int queue_family_comp_index; + int nb_comp_queues; + /** + * Enabled instance extensions. + * If supplying your own device context, set this to an array of strings, with + * each entry containing the specified Vulkan extension string to enable. + * Duplicates are possible and accepted. + * If no extensions are enabled, set these fields to NULL, and 0 respectively. + */ + const char * const *enabled_inst_extensions; + int nb_enabled_inst_extensions; + /** + * Enabled device extensions. By default, VK_KHR_external_memory_fd, + * VK_EXT_external_memory_dma_buf, VK_EXT_image_drm_format_modifier, + * VK_KHR_external_semaphore_fd and VK_EXT_external_memory_host are enabled if found. + * If supplying your own device context, these fields takes the same format as + * the above fields, with the same conditions that duplicates are possible + * and accepted, and that NULL and 0 respectively means no extensions are enabled. + */ + const char * const *enabled_dev_extensions; + int nb_enabled_dev_extensions; + /** + * This structure should be set to the set of features that present and enabled + * during device creation. When a device is created by FFmpeg, it will default to + * enabling all that are present of the shaderImageGatherExtended, + * fragmentStoresAndAtomics, shaderInt64 and vertexPipelineStoresAndAtomics features. + */ + VkPhysicalDeviceFeatures2 device_features; +} AVVulkanDeviceContext; + +/** + * Allocated as AVHWFramesContext.hwctx, used to set pool-specific options + */ +typedef struct AVVulkanFramesContext { + /** + * Controls the tiling of allocated frames. + */ + VkImageTiling tiling; + /** + * Defines extra usage of output frames. If left as 0, the following bits + * are set: TRANSFER_SRC, TRANSFER_DST. SAMPLED and STORAGE. + */ + VkImageUsageFlagBits usage; + /** + * Extension data for image creation. + */ + void *create_pnext; + /** + * Extension data for memory allocation. Must have as many entries as + * the number of planes of the sw_format. + * This will be chained to VkExportMemoryAllocateInfo, which is used + * to make all pool images exportable to other APIs if the necessary + * extensions are present in enabled_dev_extensions. + */ + void *alloc_pnext[AV_NUM_DATA_POINTERS]; +} AVVulkanFramesContext; + +/* + * Frame structure, the VkFormat of the image will always match + * the pool's sw_format. + * All frames, imported or allocated, will be created with the + * VK_IMAGE_CREATE_ALIAS_BIT flag set, so the memory may be aliased if needed. + * + * If all three queue family indices in the device context are the same, + * images will be created with the EXCLUSIVE sharing mode. Otherwise, all images + * will be created using the CONCURRENT sharing mode. + * + * @note the size of this structure is not part of the ABI, to allocate + * you must use @av_vk_frame_alloc(). + */ +typedef struct AVVkFrame { + /** + * Vulkan images to which the memory is bound to. + */ + VkImage img[AV_NUM_DATA_POINTERS]; + + /** + * The same tiling must be used for all images in the frame. + */ + VkImageTiling tiling; + + /** + * Memory backing the images. Could be less than the amount of images + * if importing from a DRM or VAAPI frame. + */ + VkDeviceMemory mem[AV_NUM_DATA_POINTERS]; + size_t size[AV_NUM_DATA_POINTERS]; + + /** + * OR'd flags for all memory allocated + */ + VkMemoryPropertyFlagBits flags; + + /** + * Updated after every barrier + */ + VkAccessFlagBits access[AV_NUM_DATA_POINTERS]; + VkImageLayout layout[AV_NUM_DATA_POINTERS]; + + /** + * Synchronization semaphores. Must not be freed manually. Must be waited on + * and signalled at every queue submission. + * Could be less than the amount of images: either one per VkDeviceMemory + * or one for the entire frame. All others will be set to VK_NULL_HANDLE. + */ + VkSemaphore sem[AV_NUM_DATA_POINTERS]; + + /** + * Internal data. + */ + struct AVVkFrameInternal *internal; +} AVVkFrame; + +/** + * Allocates a single AVVkFrame and initializes everything as 0. + * @note Must be freed via av_free() + */ +AVVkFrame *av_vk_frame_alloc(void); + +/** + * Returns the format of each image up to the number of planes for a given sw_format. + * Returns NULL on unsupported formats. + */ +const VkFormat *av_vkfmt_from_pixfmt(enum AVPixelFormat p); + +#endif /* AVUTIL_HWCONTEXT_VULKAN_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/imgutils.h b/ThirdParty/ffmpeg/arm/include/libavutil/imgutils.h new file mode 100644 index 000000000..5b790ecf0 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/imgutils.h @@ -0,0 +1,277 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_IMGUTILS_H +#define AVUTIL_IMGUTILS_H + +/** + * @file + * misc image utilities + * + * @addtogroup lavu_picture + * @{ + */ + +#include "avutil.h" +#include "pixdesc.h" +#include "rational.h" + +/** + * Compute the max pixel step for each plane of an image with a + * format described by pixdesc. + * + * The pixel step is the distance in bytes between the first byte of + * the group of bytes which describe a pixel component and the first + * byte of the successive group in the same plane for the same + * component. + * + * @param max_pixsteps an array which is filled with the max pixel step + * for each plane. Since a plane may contain different pixel + * components, the computed max_pixsteps[plane] is relative to the + * component in the plane with the max pixel step. + * @param max_pixstep_comps an array which is filled with the component + * for each plane which has the max pixel step. May be NULL. + */ +void av_image_fill_max_pixsteps(int max_pixsteps[4], int max_pixstep_comps[4], + const AVPixFmtDescriptor *pixdesc); + +/** + * Compute the size of an image line with format pix_fmt and width + * width for the plane plane. + * + * @return the computed size in bytes + */ +int av_image_get_linesize(enum AVPixelFormat pix_fmt, int width, int plane); + +/** + * Fill plane linesizes for an image with pixel format pix_fmt and + * width width. + * + * @param linesizes array to be filled with the linesize for each plane + * @return >= 0 in case of success, a negative error code otherwise + */ +int av_image_fill_linesizes(int linesizes[4], enum AVPixelFormat pix_fmt, int width); + +/** + * Fill plane data pointers for an image with pixel format pix_fmt and + * height height. + * + * @param data pointers array to be filled with the pointer for each image plane + * @param ptr the pointer to a buffer which will contain the image + * @param linesizes the array containing the linesize for each + * plane, should be filled by av_image_fill_linesizes() + * @return the size in bytes required for the image buffer, a negative + * error code in case of failure + */ +int av_image_fill_pointers(uint8_t *data[4], enum AVPixelFormat pix_fmt, int height, + uint8_t *ptr, const int linesizes[4]); + +/** + * Allocate an image with size w and h and pixel format pix_fmt, and + * fill pointers and linesizes accordingly. + * The allocated image buffer has to be freed by using + * av_freep(&pointers[0]). + * + * @param align the value to use for buffer size alignment + * @return the size in bytes required for the image buffer, a negative + * error code in case of failure + */ +int av_image_alloc(uint8_t *pointers[4], int linesizes[4], + int w, int h, enum AVPixelFormat pix_fmt, int align); + +/** + * Copy image plane from src to dst. + * That is, copy "height" number of lines of "bytewidth" bytes each. + * The first byte of each successive line is separated by *_linesize + * bytes. + * + * bytewidth must be contained by both absolute values of dst_linesize + * and src_linesize, otherwise the function behavior is undefined. + * + * @param dst_linesize linesize for the image plane in dst + * @param src_linesize linesize for the image plane in src + */ +void av_image_copy_plane(uint8_t *dst, int dst_linesize, + const uint8_t *src, int src_linesize, + int bytewidth, int height); + +/** + * Copy image in src_data to dst_data. + * + * @param dst_linesizes linesizes for the image in dst_data + * @param src_linesizes linesizes for the image in src_data + */ +void av_image_copy(uint8_t *dst_data[4], int dst_linesizes[4], + const uint8_t *src_data[4], const int src_linesizes[4], + enum AVPixelFormat pix_fmt, int width, int height); + +/** + * Copy image data located in uncacheable (e.g. GPU mapped) memory. Where + * available, this function will use special functionality for reading from such + * memory, which may result in greatly improved performance compared to plain + * av_image_copy(). + * + * The data pointers and the linesizes must be aligned to the maximum required + * by the CPU architecture. + * + * @note The linesize parameters have the type ptrdiff_t here, while they are + * int for av_image_copy(). + * @note On x86, the linesizes currently need to be aligned to the cacheline + * size (i.e. 64) to get improved performance. + */ +void av_image_copy_uc_from(uint8_t *dst_data[4], const ptrdiff_t dst_linesizes[4], + const uint8_t *src_data[4], const ptrdiff_t src_linesizes[4], + enum AVPixelFormat pix_fmt, int width, int height); + +/** + * Setup the data pointers and linesizes based on the specified image + * parameters and the provided array. + * + * The fields of the given image are filled in by using the src + * address which points to the image data buffer. Depending on the + * specified pixel format, one or multiple image data pointers and + * line sizes will be set. If a planar format is specified, several + * pointers will be set pointing to the different picture planes and + * the line sizes of the different planes will be stored in the + * lines_sizes array. Call with src == NULL to get the required + * size for the src buffer. + * + * To allocate the buffer and fill in the dst_data and dst_linesize in + * one call, use av_image_alloc(). + * + * @param dst_data data pointers to be filled in + * @param dst_linesize linesizes for the image in dst_data to be filled in + * @param src buffer which will contain or contains the actual image data, can be NULL + * @param pix_fmt the pixel format of the image + * @param width the width of the image in pixels + * @param height the height of the image in pixels + * @param align the value used in src for linesize alignment + * @return the size in bytes required for src, a negative error code + * in case of failure + */ +int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4], + const uint8_t *src, + enum AVPixelFormat pix_fmt, int width, int height, int align); + +/** + * Return the size in bytes of the amount of data required to store an + * image with the given parameters. + * + * @param pix_fmt the pixel format of the image + * @param width the width of the image in pixels + * @param height the height of the image in pixels + * @param align the assumed linesize alignment + * @return the buffer size in bytes, a negative error code in case of failure + */ +int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align); + +/** + * Copy image data from an image into a buffer. + * + * av_image_get_buffer_size() can be used to compute the required size + * for the buffer to fill. + * + * @param dst a buffer into which picture data will be copied + * @param dst_size the size in bytes of dst + * @param src_data pointers containing the source image data + * @param src_linesize linesizes for the image in src_data + * @param pix_fmt the pixel format of the source image + * @param width the width of the source image in pixels + * @param height the height of the source image in pixels + * @param align the assumed linesize alignment for dst + * @return the number of bytes written to dst, or a negative value + * (error code) on error + */ +int av_image_copy_to_buffer(uint8_t *dst, int dst_size, + const uint8_t * const src_data[4], const int src_linesize[4], + enum AVPixelFormat pix_fmt, int width, int height, int align); + +/** + * Check if the given dimension of an image is valid, meaning that all + * bytes of the image can be addressed with a signed int. + * + * @param w the width of the picture + * @param h the height of the picture + * @param log_offset the offset to sum to the log level for logging with log_ctx + * @param log_ctx the parent logging context, it may be NULL + * @return >= 0 if valid, a negative error code otherwise + */ +int av_image_check_size(unsigned int w, unsigned int h, int log_offset, void *log_ctx); + +/** + * Check if the given dimension of an image is valid, meaning that all + * bytes of a plane of an image with the specified pix_fmt can be addressed + * with a signed int. + * + * @param w the width of the picture + * @param h the height of the picture + * @param max_pixels the maximum number of pixels the user wants to accept + * @param pix_fmt the pixel format, can be AV_PIX_FMT_NONE if unknown. + * @param log_offset the offset to sum to the log level for logging with log_ctx + * @param log_ctx the parent logging context, it may be NULL + * @return >= 0 if valid, a negative error code otherwise + */ +int av_image_check_size2(unsigned int w, unsigned int h, int64_t max_pixels, enum AVPixelFormat pix_fmt, int log_offset, void *log_ctx); + +/** + * Check if the given sample aspect ratio of an image is valid. + * + * It is considered invalid if the denominator is 0 or if applying the ratio + * to the image size would make the smaller dimension less than 1. If the + * sar numerator is 0, it is considered unknown and will return as valid. + * + * @param w width of the image + * @param h height of the image + * @param sar sample aspect ratio of the image + * @return 0 if valid, a negative AVERROR code otherwise + */ +int av_image_check_sar(unsigned int w, unsigned int h, AVRational sar); + +/** + * Overwrite the image data with black. This is suitable for filling a + * sub-rectangle of an image, meaning the padding between the right most pixel + * and the left most pixel on the next line will not be overwritten. For some + * formats, the image size might be rounded up due to inherent alignment. + * + * If the pixel format has alpha, the alpha is cleared to opaque. + * + * This can return an error if the pixel format is not supported. Normally, all + * non-hwaccel pixel formats should be supported. + * + * Passing NULL for dst_data is allowed. Then the function returns whether the + * operation would have succeeded. (It can return an error if the pix_fmt is + * not supported.) + * + * @param dst_data data pointers to destination image + * @param dst_linesize linesizes for the destination image + * @param pix_fmt the pixel format of the image + * @param range the color range of the image (important for colorspaces such as YUV) + * @param width the width of the image in pixels + * @param height the height of the image in pixels + * @return 0 if the image data was cleared, a negative AVERROR code otherwise + */ +int av_image_fill_black(uint8_t *dst_data[4], const ptrdiff_t dst_linesize[4], + enum AVPixelFormat pix_fmt, enum AVColorRange range, + int width, int height); + +/** + * @} + */ + + +#endif /* AVUTIL_IMGUTILS_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/intfloat.h b/ThirdParty/ffmpeg/arm/include/libavutil/intfloat.h new file mode 100644 index 000000000..fe3d7ec4a --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/intfloat.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2011 Mans Rullgard + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_INTFLOAT_H +#define AVUTIL_INTFLOAT_H + +#include +#include "attributes.h" + +union av_intfloat32 { + uint32_t i; + float f; +}; + +union av_intfloat64 { + uint64_t i; + double f; +}; + +/** + * Reinterpret a 32-bit integer as a float. + */ +static av_always_inline float av_int2float(uint32_t i) +{ + union av_intfloat32 v; + v.i = i; + return v.f; +} + +/** + * Reinterpret a float as a 32-bit integer. + */ +static av_always_inline uint32_t av_float2int(float f) +{ + union av_intfloat32 v; + v.f = f; + return v.i; +} + +/** + * Reinterpret a 64-bit integer as a double. + */ +static av_always_inline double av_int2double(uint64_t i) +{ + union av_intfloat64 v; + v.i = i; + return v.f; +} + +/** + * Reinterpret a double as a 64-bit integer. + */ +static av_always_inline uint64_t av_double2int(double f) +{ + union av_intfloat64 v; + v.f = f; + return v.i; +} + +#endif /* AVUTIL_INTFLOAT_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/intreadwrite.h b/ThirdParty/ffmpeg/arm/include/libavutil/intreadwrite.h new file mode 100644 index 000000000..4c8413a53 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/intreadwrite.h @@ -0,0 +1,644 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_INTREADWRITE_H +#define AVUTIL_INTREADWRITE_H + +#include +#include "libavutil/avconfig.h" +#include "attributes.h" +#include "bswap.h" + +typedef union { + uint64_t u64; + uint32_t u32[2]; + uint16_t u16[4]; + uint8_t u8 [8]; + double f64; + float f32[2]; +} av_alias av_alias64; + +typedef union { + uint32_t u32; + uint16_t u16[2]; + uint8_t u8 [4]; + float f32; +} av_alias av_alias32; + +typedef union { + uint16_t u16; + uint8_t u8 [2]; +} av_alias av_alias16; + +/* + * Arch-specific headers can provide any combination of + * AV_[RW][BLN](16|24|32|48|64) and AV_(COPY|SWAP|ZERO)(64|128) macros. + * Preprocessor symbols must be defined, even if these are implemented + * as inline functions. + * + * R/W means read/write, B/L/N means big/little/native endianness. + * The following macros require aligned access, compared to their + * unaligned variants: AV_(COPY|SWAP|ZERO)(64|128), AV_[RW]N[8-64]A. + * Incorrect usage may range from abysmal performance to crash + * depending on the platform. + * + * The unaligned variants are AV_[RW][BLN][8-64] and AV_COPY*U. + */ + +#ifdef HAVE_AV_CONFIG_H + +#include "config.h" + +#if ARCH_ARM +# include "arm/intreadwrite.h" +#elif ARCH_AVR32 +# include "avr32/intreadwrite.h" +#elif ARCH_MIPS +# include "mips/intreadwrite.h" +#elif ARCH_PPC +# include "ppc/intreadwrite.h" +#elif ARCH_TOMI +# include "tomi/intreadwrite.h" +#elif ARCH_X86 +# include "x86/intreadwrite.h" +#endif + +#endif /* HAVE_AV_CONFIG_H */ + +/* + * Map AV_RNXX <-> AV_R[BL]XX for all variants provided by per-arch headers. + */ + +#if AV_HAVE_BIGENDIAN + +# if defined(AV_RN16) && !defined(AV_RB16) +# define AV_RB16(p) AV_RN16(p) +# elif !defined(AV_RN16) && defined(AV_RB16) +# define AV_RN16(p) AV_RB16(p) +# endif + +# if defined(AV_WN16) && !defined(AV_WB16) +# define AV_WB16(p, v) AV_WN16(p, v) +# elif !defined(AV_WN16) && defined(AV_WB16) +# define AV_WN16(p, v) AV_WB16(p, v) +# endif + +# if defined(AV_RN24) && !defined(AV_RB24) +# define AV_RB24(p) AV_RN24(p) +# elif !defined(AV_RN24) && defined(AV_RB24) +# define AV_RN24(p) AV_RB24(p) +# endif + +# if defined(AV_WN24) && !defined(AV_WB24) +# define AV_WB24(p, v) AV_WN24(p, v) +# elif !defined(AV_WN24) && defined(AV_WB24) +# define AV_WN24(p, v) AV_WB24(p, v) +# endif + +# if defined(AV_RN32) && !defined(AV_RB32) +# define AV_RB32(p) AV_RN32(p) +# elif !defined(AV_RN32) && defined(AV_RB32) +# define AV_RN32(p) AV_RB32(p) +# endif + +# if defined(AV_WN32) && !defined(AV_WB32) +# define AV_WB32(p, v) AV_WN32(p, v) +# elif !defined(AV_WN32) && defined(AV_WB32) +# define AV_WN32(p, v) AV_WB32(p, v) +# endif + +# if defined(AV_RN48) && !defined(AV_RB48) +# define AV_RB48(p) AV_RN48(p) +# elif !defined(AV_RN48) && defined(AV_RB48) +# define AV_RN48(p) AV_RB48(p) +# endif + +# if defined(AV_WN48) && !defined(AV_WB48) +# define AV_WB48(p, v) AV_WN48(p, v) +# elif !defined(AV_WN48) && defined(AV_WB48) +# define AV_WN48(p, v) AV_WB48(p, v) +# endif + +# if defined(AV_RN64) && !defined(AV_RB64) +# define AV_RB64(p) AV_RN64(p) +# elif !defined(AV_RN64) && defined(AV_RB64) +# define AV_RN64(p) AV_RB64(p) +# endif + +# if defined(AV_WN64) && !defined(AV_WB64) +# define AV_WB64(p, v) AV_WN64(p, v) +# elif !defined(AV_WN64) && defined(AV_WB64) +# define AV_WN64(p, v) AV_WB64(p, v) +# endif + +#else /* AV_HAVE_BIGENDIAN */ + +# if defined(AV_RN16) && !defined(AV_RL16) +# define AV_RL16(p) AV_RN16(p) +# elif !defined(AV_RN16) && defined(AV_RL16) +# define AV_RN16(p) AV_RL16(p) +# endif + +# if defined(AV_WN16) && !defined(AV_WL16) +# define AV_WL16(p, v) AV_WN16(p, v) +# elif !defined(AV_WN16) && defined(AV_WL16) +# define AV_WN16(p, v) AV_WL16(p, v) +# endif + +# if defined(AV_RN24) && !defined(AV_RL24) +# define AV_RL24(p) AV_RN24(p) +# elif !defined(AV_RN24) && defined(AV_RL24) +# define AV_RN24(p) AV_RL24(p) +# endif + +# if defined(AV_WN24) && !defined(AV_WL24) +# define AV_WL24(p, v) AV_WN24(p, v) +# elif !defined(AV_WN24) && defined(AV_WL24) +# define AV_WN24(p, v) AV_WL24(p, v) +# endif + +# if defined(AV_RN32) && !defined(AV_RL32) +# define AV_RL32(p) AV_RN32(p) +# elif !defined(AV_RN32) && defined(AV_RL32) +# define AV_RN32(p) AV_RL32(p) +# endif + +# if defined(AV_WN32) && !defined(AV_WL32) +# define AV_WL32(p, v) AV_WN32(p, v) +# elif !defined(AV_WN32) && defined(AV_WL32) +# define AV_WN32(p, v) AV_WL32(p, v) +# endif + +# if defined(AV_RN48) && !defined(AV_RL48) +# define AV_RL48(p) AV_RN48(p) +# elif !defined(AV_RN48) && defined(AV_RL48) +# define AV_RN48(p) AV_RL48(p) +# endif + +# if defined(AV_WN48) && !defined(AV_WL48) +# define AV_WL48(p, v) AV_WN48(p, v) +# elif !defined(AV_WN48) && defined(AV_WL48) +# define AV_WN48(p, v) AV_WL48(p, v) +# endif + +# if defined(AV_RN64) && !defined(AV_RL64) +# define AV_RL64(p) AV_RN64(p) +# elif !defined(AV_RN64) && defined(AV_RL64) +# define AV_RN64(p) AV_RL64(p) +# endif + +# if defined(AV_WN64) && !defined(AV_WL64) +# define AV_WL64(p, v) AV_WN64(p, v) +# elif !defined(AV_WN64) && defined(AV_WL64) +# define AV_WN64(p, v) AV_WL64(p, v) +# endif + +#endif /* !AV_HAVE_BIGENDIAN */ + +/* + * Define AV_[RW]N helper macros to simplify definitions not provided + * by per-arch headers. + */ + +#if defined(__GNUC__) + +union unaligned_64 { uint64_t l; } __attribute__((packed)) av_alias; +union unaligned_32 { uint32_t l; } __attribute__((packed)) av_alias; +union unaligned_16 { uint16_t l; } __attribute__((packed)) av_alias; + +# define AV_RN(s, p) (((const union unaligned_##s *) (p))->l) +# define AV_WN(s, p, v) ((((union unaligned_##s *) (p))->l) = (v)) + +#elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_X64) || defined(_M_ARM64)) && AV_HAVE_FAST_UNALIGNED + +# define AV_RN(s, p) (*((const __unaligned uint##s##_t*)(p))) +# define AV_WN(s, p, v) (*((__unaligned uint##s##_t*)(p)) = (v)) + +#elif AV_HAVE_FAST_UNALIGNED + +# define AV_RN(s, p) (((const av_alias##s*)(p))->u##s) +# define AV_WN(s, p, v) (((av_alias##s*)(p))->u##s = (v)) + +#else + +#ifndef AV_RB16 +# define AV_RB16(x) \ + ((((const uint8_t*)(x))[0] << 8) | \ + ((const uint8_t*)(x))[1]) +#endif +#ifndef AV_WB16 +# define AV_WB16(p, val) do { \ + uint16_t d = (val); \ + ((uint8_t*)(p))[1] = (d); \ + ((uint8_t*)(p))[0] = (d)>>8; \ + } while(0) +#endif + +#ifndef AV_RL16 +# define AV_RL16(x) \ + ((((const uint8_t*)(x))[1] << 8) | \ + ((const uint8_t*)(x))[0]) +#endif +#ifndef AV_WL16 +# define AV_WL16(p, val) do { \ + uint16_t d = (val); \ + ((uint8_t*)(p))[0] = (d); \ + ((uint8_t*)(p))[1] = (d)>>8; \ + } while(0) +#endif + +#ifndef AV_RB32 +# define AV_RB32(x) \ + (((uint32_t)((const uint8_t*)(x))[0] << 24) | \ + (((const uint8_t*)(x))[1] << 16) | \ + (((const uint8_t*)(x))[2] << 8) | \ + ((const uint8_t*)(x))[3]) +#endif +#ifndef AV_WB32 +# define AV_WB32(p, val) do { \ + uint32_t d = (val); \ + ((uint8_t*)(p))[3] = (d); \ + ((uint8_t*)(p))[2] = (d)>>8; \ + ((uint8_t*)(p))[1] = (d)>>16; \ + ((uint8_t*)(p))[0] = (d)>>24; \ + } while(0) +#endif + +#ifndef AV_RL32 +# define AV_RL32(x) \ + (((uint32_t)((const uint8_t*)(x))[3] << 24) | \ + (((const uint8_t*)(x))[2] << 16) | \ + (((const uint8_t*)(x))[1] << 8) | \ + ((const uint8_t*)(x))[0]) +#endif +#ifndef AV_WL32 +# define AV_WL32(p, val) do { \ + uint32_t d = (val); \ + ((uint8_t*)(p))[0] = (d); \ + ((uint8_t*)(p))[1] = (d)>>8; \ + ((uint8_t*)(p))[2] = (d)>>16; \ + ((uint8_t*)(p))[3] = (d)>>24; \ + } while(0) +#endif + +#ifndef AV_RB64 +# define AV_RB64(x) \ + (((uint64_t)((const uint8_t*)(x))[0] << 56) | \ + ((uint64_t)((const uint8_t*)(x))[1] << 48) | \ + ((uint64_t)((const uint8_t*)(x))[2] << 40) | \ + ((uint64_t)((const uint8_t*)(x))[3] << 32) | \ + ((uint64_t)((const uint8_t*)(x))[4] << 24) | \ + ((uint64_t)((const uint8_t*)(x))[5] << 16) | \ + ((uint64_t)((const uint8_t*)(x))[6] << 8) | \ + (uint64_t)((const uint8_t*)(x))[7]) +#endif +#ifndef AV_WB64 +# define AV_WB64(p, val) do { \ + uint64_t d = (val); \ + ((uint8_t*)(p))[7] = (d); \ + ((uint8_t*)(p))[6] = (d)>>8; \ + ((uint8_t*)(p))[5] = (d)>>16; \ + ((uint8_t*)(p))[4] = (d)>>24; \ + ((uint8_t*)(p))[3] = (d)>>32; \ + ((uint8_t*)(p))[2] = (d)>>40; \ + ((uint8_t*)(p))[1] = (d)>>48; \ + ((uint8_t*)(p))[0] = (d)>>56; \ + } while(0) +#endif + +#ifndef AV_RL64 +# define AV_RL64(x) \ + (((uint64_t)((const uint8_t*)(x))[7] << 56) | \ + ((uint64_t)((const uint8_t*)(x))[6] << 48) | \ + ((uint64_t)((const uint8_t*)(x))[5] << 40) | \ + ((uint64_t)((const uint8_t*)(x))[4] << 32) | \ + ((uint64_t)((const uint8_t*)(x))[3] << 24) | \ + ((uint64_t)((const uint8_t*)(x))[2] << 16) | \ + ((uint64_t)((const uint8_t*)(x))[1] << 8) | \ + (uint64_t)((const uint8_t*)(x))[0]) +#endif +#ifndef AV_WL64 +# define AV_WL64(p, val) do { \ + uint64_t d = (val); \ + ((uint8_t*)(p))[0] = (d); \ + ((uint8_t*)(p))[1] = (d)>>8; \ + ((uint8_t*)(p))[2] = (d)>>16; \ + ((uint8_t*)(p))[3] = (d)>>24; \ + ((uint8_t*)(p))[4] = (d)>>32; \ + ((uint8_t*)(p))[5] = (d)>>40; \ + ((uint8_t*)(p))[6] = (d)>>48; \ + ((uint8_t*)(p))[7] = (d)>>56; \ + } while(0) +#endif + +#if AV_HAVE_BIGENDIAN +# define AV_RN(s, p) AV_RB##s(p) +# define AV_WN(s, p, v) AV_WB##s(p, v) +#else +# define AV_RN(s, p) AV_RL##s(p) +# define AV_WN(s, p, v) AV_WL##s(p, v) +#endif + +#endif /* HAVE_FAST_UNALIGNED */ + +#ifndef AV_RN16 +# define AV_RN16(p) AV_RN(16, p) +#endif + +#ifndef AV_RN32 +# define AV_RN32(p) AV_RN(32, p) +#endif + +#ifndef AV_RN64 +# define AV_RN64(p) AV_RN(64, p) +#endif + +#ifndef AV_WN16 +# define AV_WN16(p, v) AV_WN(16, p, v) +#endif + +#ifndef AV_WN32 +# define AV_WN32(p, v) AV_WN(32, p, v) +#endif + +#ifndef AV_WN64 +# define AV_WN64(p, v) AV_WN(64, p, v) +#endif + +#if AV_HAVE_BIGENDIAN +# define AV_RB(s, p) AV_RN##s(p) +# define AV_WB(s, p, v) AV_WN##s(p, v) +# define AV_RL(s, p) av_bswap##s(AV_RN##s(p)) +# define AV_WL(s, p, v) AV_WN##s(p, av_bswap##s(v)) +#else +# define AV_RB(s, p) av_bswap##s(AV_RN##s(p)) +# define AV_WB(s, p, v) AV_WN##s(p, av_bswap##s(v)) +# define AV_RL(s, p) AV_RN##s(p) +# define AV_WL(s, p, v) AV_WN##s(p, v) +#endif + +#define AV_RB8(x) (((const uint8_t*)(x))[0]) +#define AV_WB8(p, d) do { ((uint8_t*)(p))[0] = (d); } while(0) + +#define AV_RL8(x) AV_RB8(x) +#define AV_WL8(p, d) AV_WB8(p, d) + +#ifndef AV_RB16 +# define AV_RB16(p) AV_RB(16, p) +#endif +#ifndef AV_WB16 +# define AV_WB16(p, v) AV_WB(16, p, v) +#endif + +#ifndef AV_RL16 +# define AV_RL16(p) AV_RL(16, p) +#endif +#ifndef AV_WL16 +# define AV_WL16(p, v) AV_WL(16, p, v) +#endif + +#ifndef AV_RB32 +# define AV_RB32(p) AV_RB(32, p) +#endif +#ifndef AV_WB32 +# define AV_WB32(p, v) AV_WB(32, p, v) +#endif + +#ifndef AV_RL32 +# define AV_RL32(p) AV_RL(32, p) +#endif +#ifndef AV_WL32 +# define AV_WL32(p, v) AV_WL(32, p, v) +#endif + +#ifndef AV_RB64 +# define AV_RB64(p) AV_RB(64, p) +#endif +#ifndef AV_WB64 +# define AV_WB64(p, v) AV_WB(64, p, v) +#endif + +#ifndef AV_RL64 +# define AV_RL64(p) AV_RL(64, p) +#endif +#ifndef AV_WL64 +# define AV_WL64(p, v) AV_WL(64, p, v) +#endif + +#ifndef AV_RB24 +# define AV_RB24(x) \ + ((((const uint8_t*)(x))[0] << 16) | \ + (((const uint8_t*)(x))[1] << 8) | \ + ((const uint8_t*)(x))[2]) +#endif +#ifndef AV_WB24 +# define AV_WB24(p, d) do { \ + ((uint8_t*)(p))[2] = (d); \ + ((uint8_t*)(p))[1] = (d)>>8; \ + ((uint8_t*)(p))[0] = (d)>>16; \ + } while(0) +#endif + +#ifndef AV_RL24 +# define AV_RL24(x) \ + ((((const uint8_t*)(x))[2] << 16) | \ + (((const uint8_t*)(x))[1] << 8) | \ + ((const uint8_t*)(x))[0]) +#endif +#ifndef AV_WL24 +# define AV_WL24(p, d) do { \ + ((uint8_t*)(p))[0] = (d); \ + ((uint8_t*)(p))[1] = (d)>>8; \ + ((uint8_t*)(p))[2] = (d)>>16; \ + } while(0) +#endif + +#ifndef AV_RB48 +# define AV_RB48(x) \ + (((uint64_t)((const uint8_t*)(x))[0] << 40) | \ + ((uint64_t)((const uint8_t*)(x))[1] << 32) | \ + ((uint64_t)((const uint8_t*)(x))[2] << 24) | \ + ((uint64_t)((const uint8_t*)(x))[3] << 16) | \ + ((uint64_t)((const uint8_t*)(x))[4] << 8) | \ + (uint64_t)((const uint8_t*)(x))[5]) +#endif +#ifndef AV_WB48 +# define AV_WB48(p, darg) do { \ + uint64_t d = (darg); \ + ((uint8_t*)(p))[5] = (d); \ + ((uint8_t*)(p))[4] = (d)>>8; \ + ((uint8_t*)(p))[3] = (d)>>16; \ + ((uint8_t*)(p))[2] = (d)>>24; \ + ((uint8_t*)(p))[1] = (d)>>32; \ + ((uint8_t*)(p))[0] = (d)>>40; \ + } while(0) +#endif + +#ifndef AV_RL48 +# define AV_RL48(x) \ + (((uint64_t)((const uint8_t*)(x))[5] << 40) | \ + ((uint64_t)((const uint8_t*)(x))[4] << 32) | \ + ((uint64_t)((const uint8_t*)(x))[3] << 24) | \ + ((uint64_t)((const uint8_t*)(x))[2] << 16) | \ + ((uint64_t)((const uint8_t*)(x))[1] << 8) | \ + (uint64_t)((const uint8_t*)(x))[0]) +#endif +#ifndef AV_WL48 +# define AV_WL48(p, darg) do { \ + uint64_t d = (darg); \ + ((uint8_t*)(p))[0] = (d); \ + ((uint8_t*)(p))[1] = (d)>>8; \ + ((uint8_t*)(p))[2] = (d)>>16; \ + ((uint8_t*)(p))[3] = (d)>>24; \ + ((uint8_t*)(p))[4] = (d)>>32; \ + ((uint8_t*)(p))[5] = (d)>>40; \ + } while(0) +#endif + +/* + * The AV_[RW]NA macros access naturally aligned data + * in a type-safe way. + */ + +#define AV_RNA(s, p) (((const av_alias##s*)(p))->u##s) +#define AV_WNA(s, p, v) (((av_alias##s*)(p))->u##s = (v)) + +#ifndef AV_RN16A +# define AV_RN16A(p) AV_RNA(16, p) +#endif + +#ifndef AV_RN32A +# define AV_RN32A(p) AV_RNA(32, p) +#endif + +#ifndef AV_RN64A +# define AV_RN64A(p) AV_RNA(64, p) +#endif + +#ifndef AV_WN16A +# define AV_WN16A(p, v) AV_WNA(16, p, v) +#endif + +#ifndef AV_WN32A +# define AV_WN32A(p, v) AV_WNA(32, p, v) +#endif + +#ifndef AV_WN64A +# define AV_WN64A(p, v) AV_WNA(64, p, v) +#endif + +#if AV_HAVE_BIGENDIAN +# define AV_RLA(s, p) av_bswap##s(AV_RN##s##A(p)) +# define AV_WLA(s, p, v) AV_WN##s##A(p, av_bswap##s(v)) +#else +# define AV_RLA(s, p) AV_RN##s##A(p) +# define AV_WLA(s, p, v) AV_WN##s##A(p, v) +#endif + +#ifndef AV_RL64A +# define AV_RL64A(p) AV_RLA(64, p) +#endif +#ifndef AV_WL64A +# define AV_WL64A(p, v) AV_WLA(64, p, v) +#endif + +/* + * The AV_COPYxxU macros are suitable for copying data to/from unaligned + * memory locations. + */ + +#define AV_COPYU(n, d, s) AV_WN##n(d, AV_RN##n(s)); + +#ifndef AV_COPY16U +# define AV_COPY16U(d, s) AV_COPYU(16, d, s) +#endif + +#ifndef AV_COPY32U +# define AV_COPY32U(d, s) AV_COPYU(32, d, s) +#endif + +#ifndef AV_COPY64U +# define AV_COPY64U(d, s) AV_COPYU(64, d, s) +#endif + +#ifndef AV_COPY128U +# define AV_COPY128U(d, s) \ + do { \ + AV_COPY64U(d, s); \ + AV_COPY64U((char *)(d) + 8, (const char *)(s) + 8); \ + } while(0) +#endif + +/* Parameters for AV_COPY*, AV_SWAP*, AV_ZERO* must be + * naturally aligned. They may be implemented using MMX, + * so emms_c() must be called before using any float code + * afterwards. + */ + +#define AV_COPY(n, d, s) \ + (((av_alias##n*)(d))->u##n = ((const av_alias##n*)(s))->u##n) + +#ifndef AV_COPY16 +# define AV_COPY16(d, s) AV_COPY(16, d, s) +#endif + +#ifndef AV_COPY32 +# define AV_COPY32(d, s) AV_COPY(32, d, s) +#endif + +#ifndef AV_COPY64 +# define AV_COPY64(d, s) AV_COPY(64, d, s) +#endif + +#ifndef AV_COPY128 +# define AV_COPY128(d, s) \ + do { \ + AV_COPY64(d, s); \ + AV_COPY64((char*)(d)+8, (char*)(s)+8); \ + } while(0) +#endif + +#define AV_SWAP(n, a, b) FFSWAP(av_alias##n, *(av_alias##n*)(a), *(av_alias##n*)(b)) + +#ifndef AV_SWAP64 +# define AV_SWAP64(a, b) AV_SWAP(64, a, b) +#endif + +#define AV_ZERO(n, d) (((av_alias##n*)(d))->u##n = 0) + +#ifndef AV_ZERO16 +# define AV_ZERO16(d) AV_ZERO(16, d) +#endif + +#ifndef AV_ZERO32 +# define AV_ZERO32(d) AV_ZERO(32, d) +#endif + +#ifndef AV_ZERO64 +# define AV_ZERO64(d) AV_ZERO(64, d) +#endif + +#ifndef AV_ZERO128 +# define AV_ZERO128(d) \ + do { \ + AV_ZERO64(d); \ + AV_ZERO64((char*)(d)+8); \ + } while(0) +#endif + +#endif /* AVUTIL_INTREADWRITE_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/lfg.h b/ThirdParty/ffmpeg/arm/include/libavutil/lfg.h new file mode 100644 index 000000000..2b669205d --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/lfg.h @@ -0,0 +1,80 @@ +/* + * Lagged Fibonacci PRNG + * Copyright (c) 2008 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_LFG_H +#define AVUTIL_LFG_H + +#include + +/** + * Context structure for the Lagged Fibonacci PRNG. + * The exact layout, types and content of this struct may change and should + * not be accessed directly. Only its sizeof() is guranteed to stay the same + * to allow easy instanciation. + */ +typedef struct AVLFG { + unsigned int state[64]; + int index; +} AVLFG; + +void av_lfg_init(AVLFG *c, unsigned int seed); + +/** + * Seed the state of the ALFG using binary data. + * + * Return value: 0 on success, negative value (AVERROR) on failure. + */ +int av_lfg_init_from_data(AVLFG *c, const uint8_t *data, unsigned int length); + +/** + * Get the next random unsigned 32-bit number using an ALFG. + * + * Please also consider a simple LCG like state= state*1664525+1013904223, + * it may be good enough and faster for your specific use case. + */ +static inline unsigned int av_lfg_get(AVLFG *c){ + unsigned a = c->state[c->index & 63] = c->state[(c->index-24) & 63] + c->state[(c->index-55) & 63]; + c->index += 1U; + return a; +} + +/** + * Get the next random unsigned 32-bit number using a MLFG. + * + * Please also consider av_lfg_get() above, it is faster. + */ +static inline unsigned int av_mlfg_get(AVLFG *c){ + unsigned int a= c->state[(c->index-55) & 63]; + unsigned int b= c->state[(c->index-24) & 63]; + a = c->state[c->index & 63] = 2*a*b+a+b; + c->index += 1U; + return a; +} + +/** + * Get the next two numbers generated by a Box-Muller Gaussian + * generator using the random numbers issued by lfg. + * + * @param out array where the two generated numbers are placed + */ +void av_bmg_get(AVLFG *lfg, double out[2]); + +#endif /* AVUTIL_LFG_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/log.h b/ThirdParty/ffmpeg/arm/include/libavutil/log.h new file mode 100644 index 000000000..9c14188a9 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/log.h @@ -0,0 +1,383 @@ +/* + * copyright (c) 2006 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_LOG_H +#define AVUTIL_LOG_H + +#include +#include "avutil.h" +#include "attributes.h" +#include "version.h" + +typedef enum { + AV_CLASS_CATEGORY_NA = 0, + AV_CLASS_CATEGORY_INPUT, + AV_CLASS_CATEGORY_OUTPUT, + AV_CLASS_CATEGORY_MUXER, + AV_CLASS_CATEGORY_DEMUXER, + AV_CLASS_CATEGORY_ENCODER, + AV_CLASS_CATEGORY_DECODER, + AV_CLASS_CATEGORY_FILTER, + AV_CLASS_CATEGORY_BITSTREAM_FILTER, + AV_CLASS_CATEGORY_SWSCALER, + AV_CLASS_CATEGORY_SWRESAMPLER, + AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT = 40, + AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, + AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT, + AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT, + AV_CLASS_CATEGORY_DEVICE_OUTPUT, + AV_CLASS_CATEGORY_DEVICE_INPUT, + AV_CLASS_CATEGORY_NB ///< not part of ABI/API +}AVClassCategory; + +#define AV_IS_INPUT_DEVICE(category) \ + (((category) == AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT) || \ + ((category) == AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT) || \ + ((category) == AV_CLASS_CATEGORY_DEVICE_INPUT)) + +#define AV_IS_OUTPUT_DEVICE(category) \ + (((category) == AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT) || \ + ((category) == AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT) || \ + ((category) == AV_CLASS_CATEGORY_DEVICE_OUTPUT)) + +struct AVOptionRanges; + +/** + * Describe the class of an AVClass context structure. That is an + * arbitrary struct of which the first field is a pointer to an + * AVClass struct (e.g. AVCodecContext, AVFormatContext etc.). + */ +typedef struct AVClass { + /** + * The name of the class; usually it is the same name as the + * context structure type to which the AVClass is associated. + */ + const char* class_name; + + /** + * A pointer to a function which returns the name of a context + * instance ctx associated with the class. + */ + const char* (*item_name)(void* ctx); + + /** + * a pointer to the first option specified in the class if any or NULL + * + * @see av_set_default_options() + */ + const struct AVOption *option; + + /** + * LIBAVUTIL_VERSION with which this structure was created. + * This is used to allow fields to be added without requiring major + * version bumps everywhere. + */ + + int version; + + /** + * Offset in the structure where log_level_offset is stored. + * 0 means there is no such variable + */ + int log_level_offset_offset; + + /** + * Offset in the structure where a pointer to the parent context for + * logging is stored. For example a decoder could pass its AVCodecContext + * to eval as such a parent context, which an av_log() implementation + * could then leverage to display the parent context. + * The offset can be NULL. + */ + int parent_log_context_offset; + + /** + * Return next AVOptions-enabled child or NULL + */ + void* (*child_next)(void *obj, void *prev); + + /** + * Return an AVClass corresponding to the next potential + * AVOptions-enabled child. + * + * The difference between child_next and this is that + * child_next iterates over _already existing_ objects, while + * child_class_next iterates over _all possible_ children. + */ + const struct AVClass* (*child_class_next)(const struct AVClass *prev); + + /** + * Category used for visualization (like color) + * This is only set if the category is equal for all objects using this class. + * available since version (51 << 16 | 56 << 8 | 100) + */ + AVClassCategory category; + + /** + * Callback to return the category. + * available since version (51 << 16 | 59 << 8 | 100) + */ + AVClassCategory (*get_category)(void* ctx); + + /** + * Callback to return the supported/allowed ranges. + * available since version (52.12) + */ + int (*query_ranges)(struct AVOptionRanges **, void *obj, const char *key, int flags); +} AVClass; + +/** + * @addtogroup lavu_log + * + * @{ + * + * @defgroup lavu_log_constants Logging Constants + * + * @{ + */ + +/** + * Print no output. + */ +#define AV_LOG_QUIET -8 + +/** + * Something went really wrong and we will crash now. + */ +#define AV_LOG_PANIC 0 + +/** + * Something went wrong and recovery is not possible. + * For example, no header was found for a format which depends + * on headers or an illegal combination of parameters is used. + */ +#define AV_LOG_FATAL 8 + +/** + * Something went wrong and cannot losslessly be recovered. + * However, not all future data is affected. + */ +#define AV_LOG_ERROR 16 + +/** + * Something somehow does not look correct. This may or may not + * lead to problems. An example would be the use of '-vstrict -2'. + */ +#define AV_LOG_WARNING 24 + +/** + * Standard information. + */ +#define AV_LOG_INFO 32 + +/** + * Detailed information. + */ +#define AV_LOG_VERBOSE 40 + +/** + * Stuff which is only useful for libav* developers. + */ +#define AV_LOG_DEBUG 48 + +/** + * Extremely verbose debugging, useful for libav* development. + */ +#define AV_LOG_TRACE 56 + +#define AV_LOG_MAX_OFFSET (AV_LOG_TRACE - AV_LOG_QUIET) + +/** + * @} + */ + +/** + * Sets additional colors for extended debugging sessions. + * @code + av_log(ctx, AV_LOG_DEBUG|AV_LOG_C(134), "Message in purple\n"); + @endcode + * Requires 256color terminal support. Uses outside debugging is not + * recommended. + */ +#define AV_LOG_C(x) ((x) << 8) + +/** + * Send the specified message to the log if the level is less than or equal + * to the current av_log_level. By default, all logging messages are sent to + * stderr. This behavior can be altered by setting a different logging callback + * function. + * @see av_log_set_callback + * + * @param avcl A pointer to an arbitrary struct of which the first field is a + * pointer to an AVClass struct or NULL if general log. + * @param level The importance level of the message expressed using a @ref + * lavu_log_constants "Logging Constant". + * @param fmt The format string (printf-compatible) that specifies how + * subsequent arguments are converted to output. + */ +void av_log(void *avcl, int level, const char *fmt, ...) av_printf_format(3, 4); + +/** + * Send the specified message to the log once with the initial_level and then with + * the subsequent_level. By default, all logging messages are sent to + * stderr. This behavior can be altered by setting a different logging callback + * function. + * @see av_log + * + * @param avcl A pointer to an arbitrary struct of which the first field is a + * pointer to an AVClass struct or NULL if general log. + * @param initial_level importance level of the message expressed using a @ref + * lavu_log_constants "Logging Constant" for the first occurance. + * @param subsequent_level importance level of the message expressed using a @ref + * lavu_log_constants "Logging Constant" after the first occurance. + * @param fmt The format string (printf-compatible) that specifies how + * subsequent arguments are converted to output. + * @param state a variable to keep trak of if a message has already been printed + * this must be initialized to 0 before the first use. The same state + * must not be accessed by 2 Threads simultaneously. + */ +void av_log_once(void* avcl, int initial_level, int subsequent_level, int *state, const char *fmt, ...) av_printf_format(5, 6); + + +/** + * Send the specified message to the log if the level is less than or equal + * to the current av_log_level. By default, all logging messages are sent to + * stderr. This behavior can be altered by setting a different logging callback + * function. + * @see av_log_set_callback + * + * @param avcl A pointer to an arbitrary struct of which the first field is a + * pointer to an AVClass struct. + * @param level The importance level of the message expressed using a @ref + * lavu_log_constants "Logging Constant". + * @param fmt The format string (printf-compatible) that specifies how + * subsequent arguments are converted to output. + * @param vl The arguments referenced by the format string. + */ +void av_vlog(void *avcl, int level, const char *fmt, va_list vl); + +/** + * Get the current log level + * + * @see lavu_log_constants + * + * @return Current log level + */ +int av_log_get_level(void); + +/** + * Set the log level + * + * @see lavu_log_constants + * + * @param level Logging level + */ +void av_log_set_level(int level); + +/** + * Set the logging callback + * + * @note The callback must be thread safe, even if the application does not use + * threads itself as some codecs are multithreaded. + * + * @see av_log_default_callback + * + * @param callback A logging function with a compatible signature. + */ +void av_log_set_callback(void (*callback)(void*, int, const char*, va_list)); + +/** + * Default logging callback + * + * It prints the message to stderr, optionally colorizing it. + * + * @param avcl A pointer to an arbitrary struct of which the first field is a + * pointer to an AVClass struct. + * @param level The importance level of the message expressed using a @ref + * lavu_log_constants "Logging Constant". + * @param fmt The format string (printf-compatible) that specifies how + * subsequent arguments are converted to output. + * @param vl The arguments referenced by the format string. + */ +void av_log_default_callback(void *avcl, int level, const char *fmt, + va_list vl); + +/** + * Return the context name + * + * @param ctx The AVClass context + * + * @return The AVClass class_name + */ +const char* av_default_item_name(void* ctx); +AVClassCategory av_default_get_category(void *ptr); + +/** + * Format a line of log the same way as the default callback. + * @param line buffer to receive the formatted line + * @param line_size size of the buffer + * @param print_prefix used to store whether the prefix must be printed; + * must point to a persistent integer initially set to 1 + */ +void av_log_format_line(void *ptr, int level, const char *fmt, va_list vl, + char *line, int line_size, int *print_prefix); + +/** + * Format a line of log the same way as the default callback. + * @param line buffer to receive the formatted line; + * may be NULL if line_size is 0 + * @param line_size size of the buffer; at most line_size-1 characters will + * be written to the buffer, plus one null terminator + * @param print_prefix used to store whether the prefix must be printed; + * must point to a persistent integer initially set to 1 + * @return Returns a negative value if an error occurred, otherwise returns + * the number of characters that would have been written for a + * sufficiently large buffer, not including the terminating null + * character. If the return value is not less than line_size, it means + * that the log message was truncated to fit the buffer. + */ +int av_log_format_line2(void *ptr, int level, const char *fmt, va_list vl, + char *line, int line_size, int *print_prefix); + +/** + * Skip repeated messages, this requires the user app to use av_log() instead of + * (f)printf as the 2 would otherwise interfere and lead to + * "Last message repeated x times" messages below (f)printf messages with some + * bad luck. + * Also to receive the last, "last repeated" line if any, the user app must + * call av_log(NULL, AV_LOG_QUIET, "%s", ""); at the end + */ +#define AV_LOG_SKIP_REPEATED 1 + +/** + * Include the log severity in messages originating from codecs. + * + * Results in messages such as: + * [rawvideo @ 0xDEADBEEF] [error] encode did not produce valid pts + */ +#define AV_LOG_PRINT_LEVEL 2 + +void av_log_set_flags(int arg); +int av_log_get_flags(void); + +/** + * @} + */ + +#endif /* AVUTIL_LOG_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/macros.h b/ThirdParty/ffmpeg/arm/include/libavutil/macros.h new file mode 100644 index 000000000..2007ee561 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/macros.h @@ -0,0 +1,50 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu + * Utility Preprocessor macros + */ + +#ifndef AVUTIL_MACROS_H +#define AVUTIL_MACROS_H + +/** + * @addtogroup preproc_misc Preprocessor String Macros + * + * String manipulation macros + * + * @{ + */ + +#define AV_STRINGIFY(s) AV_TOSTRING(s) +#define AV_TOSTRING(s) #s + +#define AV_GLUE(a, b) a ## b +#define AV_JOIN(a, b) AV_GLUE(a, b) + +/** + * @} + */ + +#define AV_PRAGMA(s) _Pragma(#s) + +#define FFALIGN(x, a) (((x)+(a)-1)&~((a)-1)) + +#endif /* AVUTIL_MACROS_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/mastering_display_metadata.h b/ThirdParty/ffmpeg/arm/include/libavutil/mastering_display_metadata.h new file mode 100644 index 000000000..c23b07c3c --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/mastering_display_metadata.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2016 Neil Birkbeck + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_MASTERING_DISPLAY_METADATA_H +#define AVUTIL_MASTERING_DISPLAY_METADATA_H + +#include "frame.h" +#include "rational.h" + + +/** + * Mastering display metadata capable of representing the color volume of + * the display used to master the content (SMPTE 2086:2014). + * + * To be used as payload of a AVFrameSideData or AVPacketSideData with the + * appropriate type. + * + * @note The struct should be allocated with av_mastering_display_metadata_alloc() + * and its size is not a part of the public ABI. + */ +typedef struct AVMasteringDisplayMetadata { + /** + * CIE 1931 xy chromaticity coords of color primaries (r, g, b order). + */ + AVRational display_primaries[3][2]; + + /** + * CIE 1931 xy chromaticity coords of white point. + */ + AVRational white_point[2]; + + /** + * Min luminance of mastering display (cd/m^2). + */ + AVRational min_luminance; + + /** + * Max luminance of mastering display (cd/m^2). + */ + AVRational max_luminance; + + /** + * Flag indicating whether the display primaries (and white point) are set. + */ + int has_primaries; + + /** + * Flag indicating whether the luminance (min_ and max_) have been set. + */ + int has_luminance; + +} AVMasteringDisplayMetadata; + +/** + * Allocate an AVMasteringDisplayMetadata structure and set its fields to + * default values. The resulting struct can be freed using av_freep(). + * + * @return An AVMasteringDisplayMetadata filled with default values or NULL + * on failure. + */ +AVMasteringDisplayMetadata *av_mastering_display_metadata_alloc(void); + +/** + * Allocate a complete AVMasteringDisplayMetadata and add it to the frame. + * + * @param frame The frame which side data is added to. + * + * @return The AVMasteringDisplayMetadata structure to be filled by caller. + */ +AVMasteringDisplayMetadata *av_mastering_display_metadata_create_side_data(AVFrame *frame); + +/** + * Content light level needed by to transmit HDR over HDMI (CTA-861.3). + * + * To be used as payload of a AVFrameSideData or AVPacketSideData with the + * appropriate type. + * + * @note The struct should be allocated with av_content_light_metadata_alloc() + * and its size is not a part of the public ABI. + */ +typedef struct AVContentLightMetadata { + /** + * Max content light level (cd/m^2). + */ + unsigned MaxCLL; + + /** + * Max average light level per frame (cd/m^2). + */ + unsigned MaxFALL; +} AVContentLightMetadata; + +/** + * Allocate an AVContentLightMetadata structure and set its fields to + * default values. The resulting struct can be freed using av_freep(). + * + * @return An AVContentLightMetadata filled with default values or NULL + * on failure. + */ +AVContentLightMetadata *av_content_light_metadata_alloc(size_t *size); + +/** + * Allocate a complete AVContentLightMetadata and add it to the frame. + * + * @param frame The frame which side data is added to. + * + * @return The AVContentLightMetadata structure to be filled by caller. + */ +AVContentLightMetadata *av_content_light_metadata_create_side_data(AVFrame *frame); + +#endif /* AVUTIL_MASTERING_DISPLAY_METADATA_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/mathematics.h b/ThirdParty/ffmpeg/arm/include/libavutil/mathematics.h new file mode 100644 index 000000000..54901800b --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/mathematics.h @@ -0,0 +1,242 @@ +/* + * copyright (c) 2005-2012 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @addtogroup lavu_math + * Mathematical utilities for working with timestamp and time base. + */ + +#ifndef AVUTIL_MATHEMATICS_H +#define AVUTIL_MATHEMATICS_H + +#include +#include +#include "attributes.h" +#include "rational.h" +#include "intfloat.h" + +#ifndef M_E +#define M_E 2.7182818284590452354 /* e */ +#endif +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 /* log_e 2 */ +#endif +#ifndef M_LN10 +#define M_LN10 2.30258509299404568402 /* log_e 10 */ +#endif +#ifndef M_LOG2_10 +#define M_LOG2_10 3.32192809488736234787 /* log_2 10 */ +#endif +#ifndef M_PHI +#define M_PHI 1.61803398874989484820 /* phi / golden ratio */ +#endif +#ifndef M_PI +#define M_PI 3.14159265358979323846 /* pi */ +#endif +#ifndef M_PI_2 +#define M_PI_2 1.57079632679489661923 /* pi/2 */ +#endif +#ifndef M_SQRT1_2 +#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ +#endif +#ifndef M_SQRT2 +#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ +#endif +#ifndef NAN +#define NAN av_int2float(0x7fc00000) +#endif +#ifndef INFINITY +#define INFINITY av_int2float(0x7f800000) +#endif + +/** + * @addtogroup lavu_math + * + * @{ + */ + +/** + * Rounding methods. + */ +enum AVRounding { + AV_ROUND_ZERO = 0, ///< Round toward zero. + AV_ROUND_INF = 1, ///< Round away from zero. + AV_ROUND_DOWN = 2, ///< Round toward -infinity. + AV_ROUND_UP = 3, ///< Round toward +infinity. + AV_ROUND_NEAR_INF = 5, ///< Round to nearest and halfway cases away from zero. + /** + * Flag telling rescaling functions to pass `INT64_MIN`/`MAX` through + * unchanged, avoiding special cases for #AV_NOPTS_VALUE. + * + * Unlike other values of the enumeration AVRounding, this value is a + * bitmask that must be used in conjunction with another value of the + * enumeration through a bitwise OR, in order to set behavior for normal + * cases. + * + * @code{.c} + * av_rescale_rnd(3, 1, 2, AV_ROUND_UP | AV_ROUND_PASS_MINMAX); + * // Rescaling 3: + * // Calculating 3 * 1 / 2 + * // 3 / 2 is rounded up to 2 + * // => 2 + * + * av_rescale_rnd(AV_NOPTS_VALUE, 1, 2, AV_ROUND_UP | AV_ROUND_PASS_MINMAX); + * // Rescaling AV_NOPTS_VALUE: + * // AV_NOPTS_VALUE == INT64_MIN + * // AV_NOPTS_VALUE is passed through + * // => AV_NOPTS_VALUE + * @endcode + */ + AV_ROUND_PASS_MINMAX = 8192, +}; + +/** + * Compute the greatest common divisor of two integer operands. + * + * @param a,b Operands + * @return GCD of a and b up to sign; if a >= 0 and b >= 0, return value is >= 0; + * if a == 0 and b == 0, returns 0. + */ +int64_t av_const av_gcd(int64_t a, int64_t b); + +/** + * Rescale a 64-bit integer with rounding to nearest. + * + * The operation is mathematically equivalent to `a * b / c`, but writing that + * directly can overflow. + * + * This function is equivalent to av_rescale_rnd() with #AV_ROUND_NEAR_INF. + * + * @see av_rescale_rnd(), av_rescale_q(), av_rescale_q_rnd() + */ +int64_t av_rescale(int64_t a, int64_t b, int64_t c) av_const; + +/** + * Rescale a 64-bit integer with specified rounding. + * + * The operation is mathematically equivalent to `a * b / c`, but writing that + * directly can overflow, and does not support different rounding methods. + * + * @see av_rescale(), av_rescale_q(), av_rescale_q_rnd() + */ +int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd) av_const; + +/** + * Rescale a 64-bit integer by 2 rational numbers. + * + * The operation is mathematically equivalent to `a * bq / cq`. + * + * This function is equivalent to av_rescale_q_rnd() with #AV_ROUND_NEAR_INF. + * + * @see av_rescale(), av_rescale_rnd(), av_rescale_q_rnd() + */ +int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq) av_const; + +/** + * Rescale a 64-bit integer by 2 rational numbers with specified rounding. + * + * The operation is mathematically equivalent to `a * bq / cq`. + * + * @see av_rescale(), av_rescale_rnd(), av_rescale_q() + */ +int64_t av_rescale_q_rnd(int64_t a, AVRational bq, AVRational cq, + enum AVRounding rnd) av_const; + +/** + * Compare two timestamps each in its own time base. + * + * @return One of the following values: + * - -1 if `ts_a` is before `ts_b` + * - 1 if `ts_a` is after `ts_b` + * - 0 if they represent the same position + * + * @warning + * The result of the function is undefined if one of the timestamps is outside + * the `int64_t` range when represented in the other's timebase. + */ +int av_compare_ts(int64_t ts_a, AVRational tb_a, int64_t ts_b, AVRational tb_b); + +/** + * Compare the remainders of two integer operands divided by a common divisor. + * + * In other words, compare the least significant `log2(mod)` bits of integers + * `a` and `b`. + * + * @code{.c} + * av_compare_mod(0x11, 0x02, 0x10) < 0 // since 0x11 % 0x10 (0x1) < 0x02 % 0x10 (0x2) + * av_compare_mod(0x11, 0x02, 0x20) > 0 // since 0x11 % 0x20 (0x11) > 0x02 % 0x20 (0x02) + * @endcode + * + * @param a,b Operands + * @param mod Divisor; must be a power of 2 + * @return + * - a negative value if `a % mod < b % mod` + * - a positive value if `a % mod > b % mod` + * - zero if `a % mod == b % mod` + */ +int64_t av_compare_mod(uint64_t a, uint64_t b, uint64_t mod); + +/** + * Rescale a timestamp while preserving known durations. + * + * This function is designed to be called per audio packet to scale the input + * timestamp to a different time base. Compared to a simple av_rescale_q() + * call, this function is robust against possible inconsistent frame durations. + * + * The `last` parameter is a state variable that must be preserved for all + * subsequent calls for the same stream. For the first call, `*last` should be + * initialized to #AV_NOPTS_VALUE. + * + * @param[in] in_tb Input time base + * @param[in] in_ts Input timestamp + * @param[in] fs_tb Duration time base; typically this is finer-grained + * (greater) than `in_tb` and `out_tb` + * @param[in] duration Duration till the next call to this function (i.e. + * duration of the current packet/frame) + * @param[in,out] last Pointer to a timestamp expressed in terms of + * `fs_tb`, acting as a state variable + * @param[in] out_tb Output timebase + * @return Timestamp expressed in terms of `out_tb` + * + * @note In the context of this function, "duration" is in term of samples, not + * seconds. + */ +int64_t av_rescale_delta(AVRational in_tb, int64_t in_ts, AVRational fs_tb, int duration, int64_t *last, AVRational out_tb); + +/** + * Add a value to a timestamp. + * + * This function guarantees that when the same value is repeatly added that + * no accumulation of rounding errors occurs. + * + * @param[in] ts Input timestamp + * @param[in] ts_tb Input timestamp time base + * @param[in] inc Value to be added + * @param[in] inc_tb Time base of `inc` + */ +int64_t av_add_stable(AVRational ts_tb, int64_t ts, AVRational inc_tb, int64_t inc); + + +/** + * @} + */ + +#endif /* AVUTIL_MATHEMATICS_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/md5.h b/ThirdParty/ffmpeg/arm/include/libavutil/md5.h new file mode 100644 index 000000000..ca72ccbf8 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/md5.h @@ -0,0 +1,98 @@ +/* + * copyright (c) 2006 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_md5 + * Public header for MD5 hash function implementation. + */ + +#ifndef AVUTIL_MD5_H +#define AVUTIL_MD5_H + +#include +#include + +#include "attributes.h" +#include "version.h" + +/** + * @defgroup lavu_md5 MD5 + * @ingroup lavu_hash + * MD5 hash function implementation. + * + * @{ + */ + +extern const int av_md5_size; + +struct AVMD5; + +/** + * Allocate an AVMD5 context. + */ +struct AVMD5 *av_md5_alloc(void); + +/** + * Initialize MD5 hashing. + * + * @param ctx pointer to the function context (of size av_md5_size) + */ +void av_md5_init(struct AVMD5 *ctx); + +/** + * Update hash value. + * + * @param ctx hash function context + * @param src input data to update hash with + * @param len input data length + */ +#if FF_API_CRYPTO_SIZE_T +void av_md5_update(struct AVMD5 *ctx, const uint8_t *src, int len); +#else +void av_md5_update(struct AVMD5 *ctx, const uint8_t *src, size_t len); +#endif + +/** + * Finish hashing and output digest value. + * + * @param ctx hash function context + * @param dst buffer where output digest value is stored + */ +void av_md5_final(struct AVMD5 *ctx, uint8_t *dst); + +/** + * Hash an array of data. + * + * @param dst The output buffer to write the digest into + * @param src The data to hash + * @param len The length of the data, in bytes + */ +#if FF_API_CRYPTO_SIZE_T +void av_md5_sum(uint8_t *dst, const uint8_t *src, const int len); +#else +void av_md5_sum(uint8_t *dst, const uint8_t *src, size_t len); +#endif + +/** + * @} + */ + +#endif /* AVUTIL_MD5_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/mem.h b/ThirdParty/ffmpeg/arm/include/libavutil/mem.h new file mode 100644 index 000000000..5fb1a02dd --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/mem.h @@ -0,0 +1,700 @@ +/* + * copyright (c) 2006 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_mem + * Memory handling functions + */ + +#ifndef AVUTIL_MEM_H +#define AVUTIL_MEM_H + +#include +#include + +#include "attributes.h" +#include "error.h" +#include "avutil.h" + +/** + * @addtogroup lavu_mem + * Utilities for manipulating memory. + * + * FFmpeg has several applications of memory that are not required of a typical + * program. For example, the computing-heavy components like video decoding and + * encoding can be sped up significantly through the use of aligned memory. + * + * However, for each of FFmpeg's applications of memory, there might not be a + * recognized or standardized API for that specific use. Memory alignment, for + * instance, varies wildly depending on operating systems, architectures, and + * compilers. Hence, this component of @ref libavutil is created to make + * dealing with memory consistently possible on all platforms. + * + * @{ + * + * @defgroup lavu_mem_macros Alignment Macros + * Helper macros for declaring aligned variables. + * @{ + */ + +/** + * @def DECLARE_ALIGNED(n,t,v) + * Declare a variable that is aligned in memory. + * + * @code{.c} + * DECLARE_ALIGNED(16, uint16_t, aligned_int) = 42; + * DECLARE_ALIGNED(32, uint8_t, aligned_array)[128]; + * + * // The default-alignment equivalent would be + * uint16_t aligned_int = 42; + * uint8_t aligned_array[128]; + * @endcode + * + * @param n Minimum alignment in bytes + * @param t Type of the variable (or array element) + * @param v Name of the variable + */ + +/** + * @def DECLARE_ASM_ALIGNED(n,t,v) + * Declare an aligned variable appropriate for use in inline assembly code. + * + * @code{.c} + * DECLARE_ASM_ALIGNED(16, uint64_t, pw_08) = UINT64_C(0x0008000800080008); + * @endcode + * + * @param n Minimum alignment in bytes + * @param t Type of the variable (or array element) + * @param v Name of the variable + */ + +/** + * @def DECLARE_ASM_CONST(n,t,v) + * Declare a static constant aligned variable appropriate for use in inline + * assembly code. + * + * @code{.c} + * DECLARE_ASM_CONST(16, uint64_t, pw_08) = UINT64_C(0x0008000800080008); + * @endcode + * + * @param n Minimum alignment in bytes + * @param t Type of the variable (or array element) + * @param v Name of the variable + */ + +#if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 1110 || defined(__SUNPRO_C) + #define DECLARE_ALIGNED(n,t,v) t __attribute__ ((aligned (n))) v + #define DECLARE_ASM_ALIGNED(n,t,v) t __attribute__ ((aligned (n))) v + #define DECLARE_ASM_CONST(n,t,v) const t __attribute__ ((aligned (n))) v +#elif defined(__DJGPP__) + #define DECLARE_ALIGNED(n,t,v) t __attribute__ ((aligned (FFMIN(n, 16)))) v + #define DECLARE_ASM_ALIGNED(n,t,v) t av_used __attribute__ ((aligned (FFMIN(n, 16)))) v + #define DECLARE_ASM_CONST(n,t,v) static const t av_used __attribute__ ((aligned (FFMIN(n, 16)))) v +#elif defined(__GNUC__) || defined(__clang__) + #define DECLARE_ALIGNED(n,t,v) t __attribute__ ((aligned (n))) v + #define DECLARE_ASM_ALIGNED(n,t,v) t av_used __attribute__ ((aligned (n))) v + #define DECLARE_ASM_CONST(n,t,v) static const t av_used __attribute__ ((aligned (n))) v +#elif defined(_MSC_VER) + #define DECLARE_ALIGNED(n,t,v) __declspec(align(n)) t v + #define DECLARE_ASM_ALIGNED(n,t,v) __declspec(align(n)) t v + #define DECLARE_ASM_CONST(n,t,v) __declspec(align(n)) static const t v +#else + #define DECLARE_ALIGNED(n,t,v) t v + #define DECLARE_ASM_ALIGNED(n,t,v) t v + #define DECLARE_ASM_CONST(n,t,v) static const t v +#endif + +/** + * @} + */ + +/** + * @defgroup lavu_mem_attrs Function Attributes + * Function attributes applicable to memory handling functions. + * + * These function attributes can help compilers emit more useful warnings, or + * generate better code. + * @{ + */ + +/** + * @def av_malloc_attrib + * Function attribute denoting a malloc-like function. + * + * @see Function attribute `malloc` in GCC's documentation + */ + +#if AV_GCC_VERSION_AT_LEAST(3,1) + #define av_malloc_attrib __attribute__((__malloc__)) +#else + #define av_malloc_attrib +#endif + +/** + * @def av_alloc_size(...) + * Function attribute used on a function that allocates memory, whose size is + * given by the specified parameter(s). + * + * @code{.c} + * void *av_malloc(size_t size) av_alloc_size(1); + * void *av_calloc(size_t nmemb, size_t size) av_alloc_size(1, 2); + * @endcode + * + * @param ... One or two parameter indexes, separated by a comma + * + * @see Function attribute `alloc_size` in GCC's documentation + */ + +#if AV_GCC_VERSION_AT_LEAST(4,3) + #define av_alloc_size(...) __attribute__((alloc_size(__VA_ARGS__))) +#else + #define av_alloc_size(...) +#endif + +/** + * @} + */ + +/** + * @defgroup lavu_mem_funcs Heap Management + * Functions responsible for allocating, freeing, and copying memory. + * + * All memory allocation functions have a built-in upper limit of `INT_MAX` + * bytes. This may be changed with av_max_alloc(), although exercise extreme + * caution when doing so. + * + * @{ + */ + +/** + * Allocate a memory block with alignment suitable for all memory accesses + * (including vectors if available on the CPU). + * + * @param size Size in bytes for the memory block to be allocated + * @return Pointer to the allocated block, or `NULL` if the block cannot + * be allocated + * @see av_mallocz() + */ +void *av_malloc(size_t size) av_malloc_attrib av_alloc_size(1); + +/** + * Allocate a memory block with alignment suitable for all memory accesses + * (including vectors if available on the CPU) and zero all the bytes of the + * block. + * + * @param size Size in bytes for the memory block to be allocated + * @return Pointer to the allocated block, or `NULL` if it cannot be allocated + * @see av_malloc() + */ +void *av_mallocz(size_t size) av_malloc_attrib av_alloc_size(1); + +/** + * Allocate a memory block for an array with av_malloc(). + * + * The allocated memory will have size `size * nmemb` bytes. + * + * @param nmemb Number of element + * @param size Size of a single element + * @return Pointer to the allocated block, or `NULL` if the block cannot + * be allocated + * @see av_malloc() + */ +av_alloc_size(1, 2) void *av_malloc_array(size_t nmemb, size_t size); + +/** + * Allocate a memory block for an array with av_mallocz(). + * + * The allocated memory will have size `size * nmemb` bytes. + * + * @param nmemb Number of elements + * @param size Size of the single element + * @return Pointer to the allocated block, or `NULL` if the block cannot + * be allocated + * + * @see av_mallocz() + * @see av_malloc_array() + */ +av_alloc_size(1, 2) void *av_mallocz_array(size_t nmemb, size_t size); + +/** + * Non-inlined equivalent of av_mallocz_array(). + * + * Created for symmetry with the calloc() C function. + */ +void *av_calloc(size_t nmemb, size_t size) av_malloc_attrib; + +/** + * Allocate, reallocate, or free a block of memory. + * + * If `ptr` is `NULL` and `size` > 0, allocate a new block. If `size` is + * zero, free the memory block pointed to by `ptr`. Otherwise, expand or + * shrink that block of memory according to `size`. + * + * @param ptr Pointer to a memory block already allocated with + * av_realloc() or `NULL` + * @param size Size in bytes of the memory block to be allocated or + * reallocated + * + * @return Pointer to a newly-reallocated block or `NULL` if the block + * cannot be reallocated or the function is used to free the memory block + * + * @warning Unlike av_malloc(), the returned pointer is not guaranteed to be + * correctly aligned. + * @see av_fast_realloc() + * @see av_reallocp() + */ +void *av_realloc(void *ptr, size_t size) av_alloc_size(2); + +/** + * Allocate, reallocate, or free a block of memory through a pointer to a + * pointer. + * + * If `*ptr` is `NULL` and `size` > 0, allocate a new block. If `size` is + * zero, free the memory block pointed to by `*ptr`. Otherwise, expand or + * shrink that block of memory according to `size`. + * + * @param[in,out] ptr Pointer to a pointer to a memory block already allocated + * with av_realloc(), or a pointer to `NULL`. The pointer + * is updated on success, or freed on failure. + * @param[in] size Size in bytes for the memory block to be allocated or + * reallocated + * + * @return Zero on success, an AVERROR error code on failure + * + * @warning Unlike av_malloc(), the allocated memory is not guaranteed to be + * correctly aligned. + */ +av_warn_unused_result +int av_reallocp(void *ptr, size_t size); + +/** + * Allocate, reallocate, or free a block of memory. + * + * This function does the same thing as av_realloc(), except: + * - It takes two size arguments and allocates `nelem * elsize` bytes, + * after checking the result of the multiplication for integer overflow. + * - It frees the input block in case of failure, thus avoiding the memory + * leak with the classic + * @code{.c} + * buf = realloc(buf); + * if (!buf) + * return -1; + * @endcode + * pattern. + */ +void *av_realloc_f(void *ptr, size_t nelem, size_t elsize); + +/** + * Allocate, reallocate, or free an array. + * + * If `ptr` is `NULL` and `nmemb` > 0, allocate a new block. If + * `nmemb` is zero, free the memory block pointed to by `ptr`. + * + * @param ptr Pointer to a memory block already allocated with + * av_realloc() or `NULL` + * @param nmemb Number of elements in the array + * @param size Size of the single element of the array + * + * @return Pointer to a newly-reallocated block or NULL if the block + * cannot be reallocated or the function is used to free the memory block + * + * @warning Unlike av_malloc(), the allocated memory is not guaranteed to be + * correctly aligned. + * @see av_reallocp_array() + */ +av_alloc_size(2, 3) void *av_realloc_array(void *ptr, size_t nmemb, size_t size); + +/** + * Allocate, reallocate, or free an array through a pointer to a pointer. + * + * If `*ptr` is `NULL` and `nmemb` > 0, allocate a new block. If `nmemb` is + * zero, free the memory block pointed to by `*ptr`. + * + * @param[in,out] ptr Pointer to a pointer to a memory block already + * allocated with av_realloc(), or a pointer to `NULL`. + * The pointer is updated on success, or freed on failure. + * @param[in] nmemb Number of elements + * @param[in] size Size of the single element + * + * @return Zero on success, an AVERROR error code on failure + * + * @warning Unlike av_malloc(), the allocated memory is not guaranteed to be + * correctly aligned. + */ +int av_reallocp_array(void *ptr, size_t nmemb, size_t size); + +/** + * Reallocate the given buffer if it is not large enough, otherwise do nothing. + * + * If the given buffer is `NULL`, then a new uninitialized buffer is allocated. + * + * If the given buffer is not large enough, and reallocation fails, `NULL` is + * returned and `*size` is set to 0, but the original buffer is not changed or + * freed. + * + * A typical use pattern follows: + * + * @code{.c} + * uint8_t *buf = ...; + * uint8_t *new_buf = av_fast_realloc(buf, ¤t_size, size_needed); + * if (!new_buf) { + * // Allocation failed; clean up original buffer + * av_freep(&buf); + * return AVERROR(ENOMEM); + * } + * @endcode + * + * @param[in,out] ptr Already allocated buffer, or `NULL` + * @param[in,out] size Pointer to the size of buffer `ptr`. `*size` is + * updated to the new allocated size, in particular 0 + * in case of failure. + * @param[in] min_size Desired minimal size of buffer `ptr` + * @return `ptr` if the buffer is large enough, a pointer to newly reallocated + * buffer if the buffer was not large enough, or `NULL` in case of + * error + * @see av_realloc() + * @see av_fast_malloc() + */ +void *av_fast_realloc(void *ptr, unsigned int *size, size_t min_size); + +/** + * Allocate a buffer, reusing the given one if large enough. + * + * Contrary to av_fast_realloc(), the current buffer contents might not be + * preserved and on error the old buffer is freed, thus no special handling to + * avoid memleaks is necessary. + * + * `*ptr` is allowed to be `NULL`, in which case allocation always happens if + * `size_needed` is greater than 0. + * + * @code{.c} + * uint8_t *buf = ...; + * av_fast_malloc(&buf, ¤t_size, size_needed); + * if (!buf) { + * // Allocation failed; buf already freed + * return AVERROR(ENOMEM); + * } + * @endcode + * + * @param[in,out] ptr Pointer to pointer to an already allocated buffer. + * `*ptr` will be overwritten with pointer to new + * buffer on success or `NULL` on failure + * @param[in,out] size Pointer to the size of buffer `*ptr`. `*size` is + * updated to the new allocated size, in particular 0 + * in case of failure. + * @param[in] min_size Desired minimal size of buffer `*ptr` + * @see av_realloc() + * @see av_fast_mallocz() + */ +void av_fast_malloc(void *ptr, unsigned int *size, size_t min_size); + +/** + * Allocate and clear a buffer, reusing the given one if large enough. + * + * Like av_fast_malloc(), but all newly allocated space is initially cleared. + * Reused buffer is not cleared. + * + * `*ptr` is allowed to be `NULL`, in which case allocation always happens if + * `size_needed` is greater than 0. + * + * @param[in,out] ptr Pointer to pointer to an already allocated buffer. + * `*ptr` will be overwritten with pointer to new + * buffer on success or `NULL` on failure + * @param[in,out] size Pointer to the size of buffer `*ptr`. `*size` is + * updated to the new allocated size, in particular 0 + * in case of failure. + * @param[in] min_size Desired minimal size of buffer `*ptr` + * @see av_fast_malloc() + */ +void av_fast_mallocz(void *ptr, unsigned int *size, size_t min_size); + +/** + * Free a memory block which has been allocated with a function of av_malloc() + * or av_realloc() family. + * + * @param ptr Pointer to the memory block which should be freed. + * + * @note `ptr = NULL` is explicitly allowed. + * @note It is recommended that you use av_freep() instead, to prevent leaving + * behind dangling pointers. + * @see av_freep() + */ +void av_free(void *ptr); + +/** + * Free a memory block which has been allocated with a function of av_malloc() + * or av_realloc() family, and set the pointer pointing to it to `NULL`. + * + * @code{.c} + * uint8_t *buf = av_malloc(16); + * av_free(buf); + * // buf now contains a dangling pointer to freed memory, and accidental + * // dereference of buf will result in a use-after-free, which may be a + * // security risk. + * + * uint8_t *buf = av_malloc(16); + * av_freep(&buf); + * // buf is now NULL, and accidental dereference will only result in a + * // NULL-pointer dereference. + * @endcode + * + * @param ptr Pointer to the pointer to the memory block which should be freed + * @note `*ptr = NULL` is safe and leads to no action. + * @see av_free() + */ +void av_freep(void *ptr); + +/** + * Duplicate a string. + * + * @param s String to be duplicated + * @return Pointer to a newly-allocated string containing a + * copy of `s` or `NULL` if the string cannot be allocated + * @see av_strndup() + */ +char *av_strdup(const char *s) av_malloc_attrib; + +/** + * Duplicate a substring of a string. + * + * @param s String to be duplicated + * @param len Maximum length of the resulting string (not counting the + * terminating byte) + * @return Pointer to a newly-allocated string containing a + * substring of `s` or `NULL` if the string cannot be allocated + */ +char *av_strndup(const char *s, size_t len) av_malloc_attrib; + +/** + * Duplicate a buffer with av_malloc(). + * + * @param p Buffer to be duplicated + * @param size Size in bytes of the buffer copied + * @return Pointer to a newly allocated buffer containing a + * copy of `p` or `NULL` if the buffer cannot be allocated + */ +void *av_memdup(const void *p, size_t size); + +/** + * Overlapping memcpy() implementation. + * + * @param dst Destination buffer + * @param back Number of bytes back to start copying (i.e. the initial size of + * the overlapping window); must be > 0 + * @param cnt Number of bytes to copy; must be >= 0 + * + * @note `cnt > back` is valid, this will copy the bytes we just copied, + * thus creating a repeating pattern with a period length of `back`. + */ +void av_memcpy_backptr(uint8_t *dst, int back, int cnt); + +/** + * @} + */ + +/** + * @defgroup lavu_mem_dynarray Dynamic Array + * + * Utilities to make an array grow when needed. + * + * Sometimes, the programmer would want to have an array that can grow when + * needed. The libavutil dynamic array utilities fill that need. + * + * libavutil supports two systems of appending elements onto a dynamically + * allocated array, the first one storing the pointer to the value in the + * array, and the second storing the value directly. In both systems, the + * caller is responsible for maintaining a variable containing the length of + * the array, as well as freeing of the array after use. + * + * The first system stores pointers to values in a block of dynamically + * allocated memory. Since only pointers are stored, the function does not need + * to know the size of the type. Both av_dynarray_add() and + * av_dynarray_add_nofree() implement this system. + * + * @code + * type **array = NULL; //< an array of pointers to values + * int nb = 0; //< a variable to keep track of the length of the array + * + * type to_be_added = ...; + * type to_be_added2 = ...; + * + * av_dynarray_add(&array, &nb, &to_be_added); + * if (nb == 0) + * return AVERROR(ENOMEM); + * + * av_dynarray_add(&array, &nb, &to_be_added2); + * if (nb == 0) + * return AVERROR(ENOMEM); + * + * // Now: + * // nb == 2 + * // &to_be_added == array[0] + * // &to_be_added2 == array[1] + * + * av_freep(&array); + * @endcode + * + * The second system stores the value directly in a block of memory. As a + * result, the function has to know the size of the type. av_dynarray2_add() + * implements this mechanism. + * + * @code + * type *array = NULL; //< an array of values + * int nb = 0; //< a variable to keep track of the length of the array + * + * type to_be_added = ...; + * type to_be_added2 = ...; + * + * type *addr = av_dynarray2_add((void **)&array, &nb, sizeof(*array), NULL); + * if (!addr) + * return AVERROR(ENOMEM); + * memcpy(addr, &to_be_added, sizeof(to_be_added)); + * + * // Shortcut of the above. + * type *addr = av_dynarray2_add((void **)&array, &nb, sizeof(*array), + * (const void *)&to_be_added2); + * if (!addr) + * return AVERROR(ENOMEM); + * + * // Now: + * // nb == 2 + * // to_be_added == array[0] + * // to_be_added2 == array[1] + * + * av_freep(&array); + * @endcode + * + * @{ + */ + +/** + * Add the pointer to an element to a dynamic array. + * + * The array to grow is supposed to be an array of pointers to + * structures, and the element to add must be a pointer to an already + * allocated structure. + * + * The array is reallocated when its size reaches powers of 2. + * Therefore, the amortized cost of adding an element is constant. + * + * In case of success, the pointer to the array is updated in order to + * point to the new grown array, and the number pointed to by `nb_ptr` + * is incremented. + * In case of failure, the array is freed, `*tab_ptr` is set to `NULL` and + * `*nb_ptr` is set to 0. + * + * @param[in,out] tab_ptr Pointer to the array to grow + * @param[in,out] nb_ptr Pointer to the number of elements in the array + * @param[in] elem Element to add + * @see av_dynarray_add_nofree(), av_dynarray2_add() + */ +void av_dynarray_add(void *tab_ptr, int *nb_ptr, void *elem); + +/** + * Add an element to a dynamic array. + * + * Function has the same functionality as av_dynarray_add(), + * but it doesn't free memory on fails. It returns error code + * instead and leave current buffer untouched. + * + * @return >=0 on success, negative otherwise + * @see av_dynarray_add(), av_dynarray2_add() + */ +av_warn_unused_result +int av_dynarray_add_nofree(void *tab_ptr, int *nb_ptr, void *elem); + +/** + * Add an element of size `elem_size` to a dynamic array. + * + * The array is reallocated when its number of elements reaches powers of 2. + * Therefore, the amortized cost of adding an element is constant. + * + * In case of success, the pointer to the array is updated in order to + * point to the new grown array, and the number pointed to by `nb_ptr` + * is incremented. + * In case of failure, the array is freed, `*tab_ptr` is set to `NULL` and + * `*nb_ptr` is set to 0. + * + * @param[in,out] tab_ptr Pointer to the array to grow + * @param[in,out] nb_ptr Pointer to the number of elements in the array + * @param[in] elem_size Size in bytes of an element in the array + * @param[in] elem_data Pointer to the data of the element to add. If + * `NULL`, the space of the newly added element is + * allocated but left uninitialized. + * + * @return Pointer to the data of the element to copy in the newly allocated + * space + * @see av_dynarray_add(), av_dynarray_add_nofree() + */ +void *av_dynarray2_add(void **tab_ptr, int *nb_ptr, size_t elem_size, + const uint8_t *elem_data); + +/** + * @} + */ + +/** + * @defgroup lavu_mem_misc Miscellaneous Functions + * + * Other functions related to memory allocation. + * + * @{ + */ + +/** + * Multiply two `size_t` values checking for overflow. + * + * @param[in] a,b Operands of multiplication + * @param[out] r Pointer to the result of the operation + * @return 0 on success, AVERROR(EINVAL) on overflow + */ +static inline int av_size_mult(size_t a, size_t b, size_t *r) +{ + size_t t = a * b; + /* Hack inspired from glibc: don't try the division if nelem and elsize + * are both less than sqrt(SIZE_MAX). */ + if ((a | b) >= ((size_t)1 << (sizeof(size_t) * 4)) && a && t / a != b) + return AVERROR(EINVAL); + *r = t; + return 0; +} + +/** + * Set the maximum size that may be allocated in one block. + * + * The value specified with this function is effective for all libavutil's @ref + * lavu_mem_funcs "heap management functions." + * + * By default, the max value is defined as `INT_MAX`. + * + * @param max Value to be set as the new maximum size + * + * @warning Exercise extreme caution when using this function. Don't touch + * this if you do not understand the full consequence of doing so. + */ +void av_max_alloc(size_t max); + +/** + * @} + * @} + */ + +#endif /* AVUTIL_MEM_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/motion_vector.h b/ThirdParty/ffmpeg/arm/include/libavutil/motion_vector.h new file mode 100644 index 000000000..ec2955638 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/motion_vector.h @@ -0,0 +1,57 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_MOTION_VECTOR_H +#define AVUTIL_MOTION_VECTOR_H + +#include + +typedef struct AVMotionVector { + /** + * Where the current macroblock comes from; negative value when it comes + * from the past, positive value when it comes from the future. + * XXX: set exact relative ref frame reference instead of a +/- 1 "direction". + */ + int32_t source; + /** + * Width and height of the block. + */ + uint8_t w, h; + /** + * Absolute source position. Can be outside the frame area. + */ + int16_t src_x, src_y; + /** + * Absolute destination position. Can be outside the frame area. + */ + int16_t dst_x, dst_y; + /** + * Extra flag information. + * Currently unused. + */ + uint64_t flags; + /** + * Motion vector + * src_x = dst_x + motion_x / motion_scale + * src_y = dst_y + motion_y / motion_scale + */ + int32_t motion_x, motion_y; + uint16_t motion_scale; +} AVMotionVector; + +#endif /* AVUTIL_MOTION_VECTOR_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/murmur3.h b/ThirdParty/ffmpeg/arm/include/libavutil/murmur3.h new file mode 100644 index 000000000..1b09175c1 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/murmur3.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2013 Reimar Döffinger + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_murmur3 + * Public header for MurmurHash3 hash function implementation. + */ + +#ifndef AVUTIL_MURMUR3_H +#define AVUTIL_MURMUR3_H + +#include + +#include "version.h" + +/** + * @defgroup lavu_murmur3 Murmur3 + * @ingroup lavu_hash + * MurmurHash3 hash function implementation. + * + * MurmurHash3 is a non-cryptographic hash function, of which three + * incompatible versions were created by its inventor Austin Appleby: + * + * - 32-bit output + * - 128-bit output for 32-bit platforms + * - 128-bit output for 64-bit platforms + * + * FFmpeg only implements the last variant: 128-bit output designed for 64-bit + * platforms. Even though the hash function was designed for 64-bit platforms, + * the function in reality works on 32-bit systems too, only with reduced + * performance. + * + * @anchor lavu_murmur3_seedinfo + * By design, MurmurHash3 requires a seed to operate. In response to this, + * libavutil provides two functions for hash initiation, one that requires a + * seed (av_murmur3_init_seeded()) and one that uses a fixed arbitrary integer + * as the seed, and therefore does not (av_murmur3_init()). + * + * To make hashes comparable, you should provide the same seed for all calls to + * this hash function -- if you are supplying one yourself, that is. + * + * @{ + */ + +/** + * Allocate an AVMurMur3 hash context. + * + * @return Uninitialized hash context or `NULL` in case of error + */ +struct AVMurMur3 *av_murmur3_alloc(void); + +/** + * Initialize or reinitialize an AVMurMur3 hash context with a seed. + * + * @param[out] c Hash context + * @param[in] seed Random seed + * + * @see av_murmur3_init() + * @see @ref lavu_murmur3_seedinfo "Detailed description" on a discussion of + * seeds for MurmurHash3. + */ +void av_murmur3_init_seeded(struct AVMurMur3 *c, uint64_t seed); + +/** + * Initialize or reinitialize an AVMurMur3 hash context. + * + * Equivalent to av_murmur3_init_seeded() with a built-in seed. + * + * @param[out] c Hash context + * + * @see av_murmur3_init_seeded() + * @see @ref lavu_murmur3_seedinfo "Detailed description" on a discussion of + * seeds for MurmurHash3. + */ +void av_murmur3_init(struct AVMurMur3 *c); + +/** + * Update hash context with new data. + * + * @param[out] c Hash context + * @param[in] src Input data to update hash with + * @param[in] len Number of bytes to read from `src` + */ +#if FF_API_CRYPTO_SIZE_T +void av_murmur3_update(struct AVMurMur3 *c, const uint8_t *src, int len); +#else +void av_murmur3_update(struct AVMurMur3 *c, const uint8_t *src, size_t len); +#endif + +/** + * Finish hashing and output digest value. + * + * @param[in,out] c Hash context + * @param[out] dst Buffer where output digest value is stored + */ +void av_murmur3_final(struct AVMurMur3 *c, uint8_t dst[16]); + +/** + * @} + */ + +#endif /* AVUTIL_MURMUR3_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/opt.h b/ThirdParty/ffmpeg/arm/include/libavutil/opt.h new file mode 100644 index 000000000..e46119572 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/opt.h @@ -0,0 +1,871 @@ +/* + * AVOptions + * copyright (c) 2005 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_OPT_H +#define AVUTIL_OPT_H + +/** + * @file + * AVOptions + */ + +#include "rational.h" +#include "avutil.h" +#include "dict.h" +#include "log.h" +#include "pixfmt.h" +#include "samplefmt.h" +#include "version.h" + +/** + * @defgroup avoptions AVOptions + * @ingroup lavu_data + * @{ + * AVOptions provide a generic system to declare options on arbitrary structs + * ("objects"). An option can have a help text, a type and a range of possible + * values. Options may then be enumerated, read and written to. + * + * @section avoptions_implement Implementing AVOptions + * This section describes how to add AVOptions capabilities to a struct. + * + * All AVOptions-related information is stored in an AVClass. Therefore + * the first member of the struct should be a pointer to an AVClass describing it. + * The option field of the AVClass must be set to a NULL-terminated static array + * of AVOptions. Each AVOption must have a non-empty name, a type, a default + * value and for number-type AVOptions also a range of allowed values. It must + * also declare an offset in bytes from the start of the struct, where the field + * associated with this AVOption is located. Other fields in the AVOption struct + * should also be set when applicable, but are not required. + * + * The following example illustrates an AVOptions-enabled struct: + * @code + * typedef struct test_struct { + * const AVClass *class; + * int int_opt; + * char *str_opt; + * uint8_t *bin_opt; + * int bin_len; + * } test_struct; + * + * static const AVOption test_options[] = { + * { "test_int", "This is a test option of int type.", offsetof(test_struct, int_opt), + * AV_OPT_TYPE_INT, { .i64 = -1 }, INT_MIN, INT_MAX }, + * { "test_str", "This is a test option of string type.", offsetof(test_struct, str_opt), + * AV_OPT_TYPE_STRING }, + * { "test_bin", "This is a test option of binary type.", offsetof(test_struct, bin_opt), + * AV_OPT_TYPE_BINARY }, + * { NULL }, + * }; + * + * static const AVClass test_class = { + * .class_name = "test class", + * .item_name = av_default_item_name, + * .option = test_options, + * .version = LIBAVUTIL_VERSION_INT, + * }; + * @endcode + * + * Next, when allocating your struct, you must ensure that the AVClass pointer + * is set to the correct value. Then, av_opt_set_defaults() can be called to + * initialize defaults. After that the struct is ready to be used with the + * AVOptions API. + * + * When cleaning up, you may use the av_opt_free() function to automatically + * free all the allocated string and binary options. + * + * Continuing with the above example: + * + * @code + * test_struct *alloc_test_struct(void) + * { + * test_struct *ret = av_mallocz(sizeof(*ret)); + * ret->class = &test_class; + * av_opt_set_defaults(ret); + * return ret; + * } + * void free_test_struct(test_struct **foo) + * { + * av_opt_free(*foo); + * av_freep(foo); + * } + * @endcode + * + * @subsection avoptions_implement_nesting Nesting + * It may happen that an AVOptions-enabled struct contains another + * AVOptions-enabled struct as a member (e.g. AVCodecContext in + * libavcodec exports generic options, while its priv_data field exports + * codec-specific options). In such a case, it is possible to set up the + * parent struct to export a child's options. To do that, simply + * implement AVClass.child_next() and AVClass.child_class_next() in the + * parent struct's AVClass. + * Assuming that the test_struct from above now also contains a + * child_struct field: + * + * @code + * typedef struct child_struct { + * AVClass *class; + * int flags_opt; + * } child_struct; + * static const AVOption child_opts[] = { + * { "test_flags", "This is a test option of flags type.", + * offsetof(child_struct, flags_opt), AV_OPT_TYPE_FLAGS, { .i64 = 0 }, INT_MIN, INT_MAX }, + * { NULL }, + * }; + * static const AVClass child_class = { + * .class_name = "child class", + * .item_name = av_default_item_name, + * .option = child_opts, + * .version = LIBAVUTIL_VERSION_INT, + * }; + * + * void *child_next(void *obj, void *prev) + * { + * test_struct *t = obj; + * if (!prev && t->child_struct) + * return t->child_struct; + * return NULL + * } + * const AVClass child_class_next(const AVClass *prev) + * { + * return prev ? NULL : &child_class; + * } + * @endcode + * Putting child_next() and child_class_next() as defined above into + * test_class will now make child_struct's options accessible through + * test_struct (again, proper setup as described above needs to be done on + * child_struct right after it is created). + * + * From the above example it might not be clear why both child_next() + * and child_class_next() are needed. The distinction is that child_next() + * iterates over actually existing objects, while child_class_next() + * iterates over all possible child classes. E.g. if an AVCodecContext + * was initialized to use a codec which has private options, then its + * child_next() will return AVCodecContext.priv_data and finish + * iterating. OTOH child_class_next() on AVCodecContext.av_class will + * iterate over all available codecs with private options. + * + * @subsection avoptions_implement_named_constants Named constants + * It is possible to create named constants for options. Simply set the unit + * field of the option the constants should apply to a string and + * create the constants themselves as options of type AV_OPT_TYPE_CONST + * with their unit field set to the same string. + * Their default_val field should contain the value of the named + * constant. + * For example, to add some named constants for the test_flags option + * above, put the following into the child_opts array: + * @code + * { "test_flags", "This is a test option of flags type.", + * offsetof(child_struct, flags_opt), AV_OPT_TYPE_FLAGS, { .i64 = 0 }, INT_MIN, INT_MAX, "test_unit" }, + * { "flag1", "This is a flag with value 16", 0, AV_OPT_TYPE_CONST, { .i64 = 16 }, 0, 0, "test_unit" }, + * @endcode + * + * @section avoptions_use Using AVOptions + * This section deals with accessing options in an AVOptions-enabled struct. + * Such structs in FFmpeg are e.g. AVCodecContext in libavcodec or + * AVFormatContext in libavformat. + * + * @subsection avoptions_use_examine Examining AVOptions + * The basic functions for examining options are av_opt_next(), which iterates + * over all options defined for one object, and av_opt_find(), which searches + * for an option with the given name. + * + * The situation is more complicated with nesting. An AVOptions-enabled struct + * may have AVOptions-enabled children. Passing the AV_OPT_SEARCH_CHILDREN flag + * to av_opt_find() will make the function search children recursively. + * + * For enumerating there are basically two cases. The first is when you want to + * get all options that may potentially exist on the struct and its children + * (e.g. when constructing documentation). In that case you should call + * av_opt_child_class_next() recursively on the parent struct's AVClass. The + * second case is when you have an already initialized struct with all its + * children and you want to get all options that can be actually written or read + * from it. In that case you should call av_opt_child_next() recursively (and + * av_opt_next() on each result). + * + * @subsection avoptions_use_get_set Reading and writing AVOptions + * When setting options, you often have a string read directly from the + * user. In such a case, simply passing it to av_opt_set() is enough. For + * non-string type options, av_opt_set() will parse the string according to the + * option type. + * + * Similarly av_opt_get() will read any option type and convert it to a string + * which will be returned. Do not forget that the string is allocated, so you + * have to free it with av_free(). + * + * In some cases it may be more convenient to put all options into an + * AVDictionary and call av_opt_set_dict() on it. A specific case of this + * are the format/codec open functions in lavf/lavc which take a dictionary + * filled with option as a parameter. This makes it possible to set some options + * that cannot be set otherwise, since e.g. the input file format is not known + * before the file is actually opened. + */ + +enum AVOptionType{ + AV_OPT_TYPE_FLAGS, + AV_OPT_TYPE_INT, + AV_OPT_TYPE_INT64, + AV_OPT_TYPE_DOUBLE, + AV_OPT_TYPE_FLOAT, + AV_OPT_TYPE_STRING, + AV_OPT_TYPE_RATIONAL, + AV_OPT_TYPE_BINARY, ///< offset must point to a pointer immediately followed by an int for the length + AV_OPT_TYPE_DICT, + AV_OPT_TYPE_UINT64, + AV_OPT_TYPE_CONST, + AV_OPT_TYPE_IMAGE_SIZE, ///< offset must point to two consecutive integers + AV_OPT_TYPE_PIXEL_FMT, + AV_OPT_TYPE_SAMPLE_FMT, + AV_OPT_TYPE_VIDEO_RATE, ///< offset must point to AVRational + AV_OPT_TYPE_DURATION, + AV_OPT_TYPE_COLOR, + AV_OPT_TYPE_CHANNEL_LAYOUT, + AV_OPT_TYPE_BOOL, +}; + +/** + * AVOption + */ +typedef struct AVOption { + const char *name; + + /** + * short English help text + * @todo What about other languages? + */ + const char *help; + + /** + * The offset relative to the context structure where the option + * value is stored. It should be 0 for named constants. + */ + int offset; + enum AVOptionType type; + + /** + * the default value for scalar options + */ + union { + int64_t i64; + double dbl; + const char *str; + /* TODO those are unused now */ + AVRational q; + } default_val; + double min; ///< minimum valid value for the option + double max; ///< maximum valid value for the option + + int flags; +#define AV_OPT_FLAG_ENCODING_PARAM 1 ///< a generic parameter which can be set by the user for muxing or encoding +#define AV_OPT_FLAG_DECODING_PARAM 2 ///< a generic parameter which can be set by the user for demuxing or decoding +#define AV_OPT_FLAG_AUDIO_PARAM 8 +#define AV_OPT_FLAG_VIDEO_PARAM 16 +#define AV_OPT_FLAG_SUBTITLE_PARAM 32 +/** + * The option is intended for exporting values to the caller. + */ +#define AV_OPT_FLAG_EXPORT 64 +/** + * The option may not be set through the AVOptions API, only read. + * This flag only makes sense when AV_OPT_FLAG_EXPORT is also set. + */ +#define AV_OPT_FLAG_READONLY 128 +#define AV_OPT_FLAG_BSF_PARAM (1<<8) ///< a generic parameter which can be set by the user for bit stream filtering +#define AV_OPT_FLAG_RUNTIME_PARAM (1<<15) ///< a generic parameter which can be set by the user at runtime +#define AV_OPT_FLAG_FILTERING_PARAM (1<<16) ///< a generic parameter which can be set by the user for filtering +#define AV_OPT_FLAG_DEPRECATED (1<<17) ///< set if option is deprecated, users should refer to AVOption.help text for more information +#define AV_OPT_FLAG_CHILD_CONSTS (1<<18) ///< set if option constants can also reside in child objects +//FIXME think about enc-audio, ... style flags + + /** + * The logical unit to which the option belongs. Non-constant + * options and corresponding named constants share the same + * unit. May be NULL. + */ + const char *unit; +} AVOption; + +/** + * A single allowed range of values, or a single allowed value. + */ +typedef struct AVOptionRange { + const char *str; + /** + * Value range. + * For string ranges this represents the min/max length. + * For dimensions this represents the min/max pixel count or width/height in multi-component case. + */ + double value_min, value_max; + /** + * Value's component range. + * For string this represents the unicode range for chars, 0-127 limits to ASCII. + */ + double component_min, component_max; + /** + * Range flag. + * If set to 1 the struct encodes a range, if set to 0 a single value. + */ + int is_range; +} AVOptionRange; + +/** + * List of AVOptionRange structs. + */ +typedef struct AVOptionRanges { + /** + * Array of option ranges. + * + * Most of option types use just one component. + * Following describes multi-component option types: + * + * AV_OPT_TYPE_IMAGE_SIZE: + * component index 0: range of pixel count (width * height). + * component index 1: range of width. + * component index 2: range of height. + * + * @note To obtain multi-component version of this structure, user must + * provide AV_OPT_MULTI_COMPONENT_RANGE to av_opt_query_ranges or + * av_opt_query_ranges_default function. + * + * Multi-component range can be read as in following example: + * + * @code + * int range_index, component_index; + * AVOptionRanges *ranges; + * AVOptionRange *range[3]; //may require more than 3 in the future. + * av_opt_query_ranges(&ranges, obj, key, AV_OPT_MULTI_COMPONENT_RANGE); + * for (range_index = 0; range_index < ranges->nb_ranges; range_index++) { + * for (component_index = 0; component_index < ranges->nb_components; component_index++) + * range[component_index] = ranges->range[ranges->nb_ranges * component_index + range_index]; + * //do something with range here. + * } + * av_opt_freep_ranges(&ranges); + * @endcode + */ + AVOptionRange **range; + /** + * Number of ranges per component. + */ + int nb_ranges; + /** + * Number of componentes. + */ + int nb_components; +} AVOptionRanges; + +/** + * Show the obj options. + * + * @param req_flags requested flags for the options to show. Show only the + * options for which it is opt->flags & req_flags. + * @param rej_flags rejected flags for the options to show. Show only the + * options for which it is !(opt->flags & req_flags). + * @param av_log_obj log context to use for showing the options + */ +int av_opt_show2(void *obj, void *av_log_obj, int req_flags, int rej_flags); + +/** + * Set the values of all AVOption fields to their default values. + * + * @param s an AVOption-enabled struct (its first member must be a pointer to AVClass) + */ +void av_opt_set_defaults(void *s); + +/** + * Set the values of all AVOption fields to their default values. Only these + * AVOption fields for which (opt->flags & mask) == flags will have their + * default applied to s. + * + * @param s an AVOption-enabled struct (its first member must be a pointer to AVClass) + * @param mask combination of AV_OPT_FLAG_* + * @param flags combination of AV_OPT_FLAG_* + */ +void av_opt_set_defaults2(void *s, int mask, int flags); + +/** + * Parse the key/value pairs list in opts. For each key/value pair + * found, stores the value in the field in ctx that is named like the + * key. ctx must be an AVClass context, storing is done using + * AVOptions. + * + * @param opts options string to parse, may be NULL + * @param key_val_sep a 0-terminated list of characters used to + * separate key from value + * @param pairs_sep a 0-terminated list of characters used to separate + * two pairs from each other + * @return the number of successfully set key/value pairs, or a negative + * value corresponding to an AVERROR code in case of error: + * AVERROR(EINVAL) if opts cannot be parsed, + * the error code issued by av_opt_set() if a key/value pair + * cannot be set + */ +int av_set_options_string(void *ctx, const char *opts, + const char *key_val_sep, const char *pairs_sep); + +/** + * Parse the key-value pairs list in opts. For each key=value pair found, + * set the value of the corresponding option in ctx. + * + * @param ctx the AVClass object to set options on + * @param opts the options string, key-value pairs separated by a + * delimiter + * @param shorthand a NULL-terminated array of options names for shorthand + * notation: if the first field in opts has no key part, + * the key is taken from the first element of shorthand; + * then again for the second, etc., until either opts is + * finished, shorthand is finished or a named option is + * found; after that, all options must be named + * @param key_val_sep a 0-terminated list of characters used to separate + * key from value, for example '=' + * @param pairs_sep a 0-terminated list of characters used to separate + * two pairs from each other, for example ':' or ',' + * @return the number of successfully set key=value pairs, or a negative + * value corresponding to an AVERROR code in case of error: + * AVERROR(EINVAL) if opts cannot be parsed, + * the error code issued by av_set_string3() if a key/value pair + * cannot be set + * + * Options names must use only the following characters: a-z A-Z 0-9 - . / _ + * Separators must use characters distinct from option names and from each + * other. + */ +int av_opt_set_from_string(void *ctx, const char *opts, + const char *const *shorthand, + const char *key_val_sep, const char *pairs_sep); +/** + * Free all allocated objects in obj. + */ +void av_opt_free(void *obj); + +/** + * Check whether a particular flag is set in a flags field. + * + * @param field_name the name of the flag field option + * @param flag_name the name of the flag to check + * @return non-zero if the flag is set, zero if the flag isn't set, + * isn't of the right type, or the flags field doesn't exist. + */ +int av_opt_flag_is_set(void *obj, const char *field_name, const char *flag_name); + +/** + * Set all the options from a given dictionary on an object. + * + * @param obj a struct whose first element is a pointer to AVClass + * @param options options to process. This dictionary will be freed and replaced + * by a new one containing all options not found in obj. + * Of course this new dictionary needs to be freed by caller + * with av_dict_free(). + * + * @return 0 on success, a negative AVERROR if some option was found in obj, + * but could not be set. + * + * @see av_dict_copy() + */ +int av_opt_set_dict(void *obj, struct AVDictionary **options); + + +/** + * Set all the options from a given dictionary on an object. + * + * @param obj a struct whose first element is a pointer to AVClass + * @param options options to process. This dictionary will be freed and replaced + * by a new one containing all options not found in obj. + * Of course this new dictionary needs to be freed by caller + * with av_dict_free(). + * @param search_flags A combination of AV_OPT_SEARCH_*. + * + * @return 0 on success, a negative AVERROR if some option was found in obj, + * but could not be set. + * + * @see av_dict_copy() + */ +int av_opt_set_dict2(void *obj, struct AVDictionary **options, int search_flags); + +/** + * Extract a key-value pair from the beginning of a string. + * + * @param ropts pointer to the options string, will be updated to + * point to the rest of the string (one of the pairs_sep + * or the final NUL) + * @param key_val_sep a 0-terminated list of characters used to separate + * key from value, for example '=' + * @param pairs_sep a 0-terminated list of characters used to separate + * two pairs from each other, for example ':' or ',' + * @param flags flags; see the AV_OPT_FLAG_* values below + * @param rkey parsed key; must be freed using av_free() + * @param rval parsed value; must be freed using av_free() + * + * @return >=0 for success, or a negative value corresponding to an + * AVERROR code in case of error; in particular: + * AVERROR(EINVAL) if no key is present + * + */ +int av_opt_get_key_value(const char **ropts, + const char *key_val_sep, const char *pairs_sep, + unsigned flags, + char **rkey, char **rval); + +enum { + + /** + * Accept to parse a value without a key; the key will then be returned + * as NULL. + */ + AV_OPT_FLAG_IMPLICIT_KEY = 1, +}; + +/** + * @defgroup opt_eval_funcs Evaluating option strings + * @{ + * This group of functions can be used to evaluate option strings + * and get numbers out of them. They do the same thing as av_opt_set(), + * except the result is written into the caller-supplied pointer. + * + * @param obj a struct whose first element is a pointer to AVClass. + * @param o an option for which the string is to be evaluated. + * @param val string to be evaluated. + * @param *_out value of the string will be written here. + * + * @return 0 on success, a negative number on failure. + */ +int av_opt_eval_flags (void *obj, const AVOption *o, const char *val, int *flags_out); +int av_opt_eval_int (void *obj, const AVOption *o, const char *val, int *int_out); +int av_opt_eval_int64 (void *obj, const AVOption *o, const char *val, int64_t *int64_out); +int av_opt_eval_float (void *obj, const AVOption *o, const char *val, float *float_out); +int av_opt_eval_double(void *obj, const AVOption *o, const char *val, double *double_out); +int av_opt_eval_q (void *obj, const AVOption *o, const char *val, AVRational *q_out); +/** + * @} + */ + +#define AV_OPT_SEARCH_CHILDREN (1 << 0) /**< Search in possible children of the + given object first. */ +/** + * The obj passed to av_opt_find() is fake -- only a double pointer to AVClass + * instead of a required pointer to a struct containing AVClass. This is + * useful for searching for options without needing to allocate the corresponding + * object. + */ +#define AV_OPT_SEARCH_FAKE_OBJ (1 << 1) + +/** + * In av_opt_get, return NULL if the option has a pointer type and is set to NULL, + * rather than returning an empty string. + */ +#define AV_OPT_ALLOW_NULL (1 << 2) + +/** + * Allows av_opt_query_ranges and av_opt_query_ranges_default to return more than + * one component for certain option types. + * @see AVOptionRanges for details. + */ +#define AV_OPT_MULTI_COMPONENT_RANGE (1 << 12) + +/** + * Look for an option in an object. Consider only options which + * have all the specified flags set. + * + * @param[in] obj A pointer to a struct whose first element is a + * pointer to an AVClass. + * Alternatively a double pointer to an AVClass, if + * AV_OPT_SEARCH_FAKE_OBJ search flag is set. + * @param[in] name The name of the option to look for. + * @param[in] unit When searching for named constants, name of the unit + * it belongs to. + * @param opt_flags Find only options with all the specified flags set (AV_OPT_FLAG). + * @param search_flags A combination of AV_OPT_SEARCH_*. + * + * @return A pointer to the option found, or NULL if no option + * was found. + * + * @note Options found with AV_OPT_SEARCH_CHILDREN flag may not be settable + * directly with av_opt_set(). Use special calls which take an options + * AVDictionary (e.g. avformat_open_input()) to set options found with this + * flag. + */ +const AVOption *av_opt_find(void *obj, const char *name, const char *unit, + int opt_flags, int search_flags); + +/** + * Look for an option in an object. Consider only options which + * have all the specified flags set. + * + * @param[in] obj A pointer to a struct whose first element is a + * pointer to an AVClass. + * Alternatively a double pointer to an AVClass, if + * AV_OPT_SEARCH_FAKE_OBJ search flag is set. + * @param[in] name The name of the option to look for. + * @param[in] unit When searching for named constants, name of the unit + * it belongs to. + * @param opt_flags Find only options with all the specified flags set (AV_OPT_FLAG). + * @param search_flags A combination of AV_OPT_SEARCH_*. + * @param[out] target_obj if non-NULL, an object to which the option belongs will be + * written here. It may be different from obj if AV_OPT_SEARCH_CHILDREN is present + * in search_flags. This parameter is ignored if search_flags contain + * AV_OPT_SEARCH_FAKE_OBJ. + * + * @return A pointer to the option found, or NULL if no option + * was found. + */ +const AVOption *av_opt_find2(void *obj, const char *name, const char *unit, + int opt_flags, int search_flags, void **target_obj); + +/** + * Iterate over all AVOptions belonging to obj. + * + * @param obj an AVOptions-enabled struct or a double pointer to an + * AVClass describing it. + * @param prev result of the previous call to av_opt_next() on this object + * or NULL + * @return next AVOption or NULL + */ +const AVOption *av_opt_next(const void *obj, const AVOption *prev); + +/** + * Iterate over AVOptions-enabled children of obj. + * + * @param prev result of a previous call to this function or NULL + * @return next AVOptions-enabled child or NULL + */ +void *av_opt_child_next(void *obj, void *prev); + +/** + * Iterate over potential AVOptions-enabled children of parent. + * + * @param prev result of a previous call to this function or NULL + * @return AVClass corresponding to next potential child or NULL + */ +const AVClass *av_opt_child_class_next(const AVClass *parent, const AVClass *prev); + +/** + * @defgroup opt_set_funcs Option setting functions + * @{ + * Those functions set the field of obj with the given name to value. + * + * @param[in] obj A struct whose first element is a pointer to an AVClass. + * @param[in] name the name of the field to set + * @param[in] val The value to set. In case of av_opt_set() if the field is not + * of a string type, then the given string is parsed. + * SI postfixes and some named scalars are supported. + * If the field is of a numeric type, it has to be a numeric or named + * scalar. Behavior with more than one scalar and +- infix operators + * is undefined. + * If the field is of a flags type, it has to be a sequence of numeric + * scalars or named flags separated by '+' or '-'. Prefixing a flag + * with '+' causes it to be set without affecting the other flags; + * similarly, '-' unsets a flag. + * If the field is of a dictionary type, it has to be a ':' separated list of + * key=value parameters. Values containing ':' special characters must be + * escaped. + * @param search_flags flags passed to av_opt_find2. I.e. if AV_OPT_SEARCH_CHILDREN + * is passed here, then the option may be set on a child of obj. + * + * @return 0 if the value has been set, or an AVERROR code in case of + * error: + * AVERROR_OPTION_NOT_FOUND if no matching option exists + * AVERROR(ERANGE) if the value is out of range + * AVERROR(EINVAL) if the value is not valid + */ +int av_opt_set (void *obj, const char *name, const char *val, int search_flags); +int av_opt_set_int (void *obj, const char *name, int64_t val, int search_flags); +int av_opt_set_double (void *obj, const char *name, double val, int search_flags); +int av_opt_set_q (void *obj, const char *name, AVRational val, int search_flags); +int av_opt_set_bin (void *obj, const char *name, const uint8_t *val, int size, int search_flags); +int av_opt_set_image_size(void *obj, const char *name, int w, int h, int search_flags); +int av_opt_set_pixel_fmt (void *obj, const char *name, enum AVPixelFormat fmt, int search_flags); +int av_opt_set_sample_fmt(void *obj, const char *name, enum AVSampleFormat fmt, int search_flags); +int av_opt_set_video_rate(void *obj, const char *name, AVRational val, int search_flags); +int av_opt_set_channel_layout(void *obj, const char *name, int64_t ch_layout, int search_flags); +/** + * @note Any old dictionary present is discarded and replaced with a copy of the new one. The + * caller still owns val is and responsible for freeing it. + */ +int av_opt_set_dict_val(void *obj, const char *name, const AVDictionary *val, int search_flags); + +/** + * Set a binary option to an integer list. + * + * @param obj AVClass object to set options on + * @param name name of the binary option + * @param val pointer to an integer list (must have the correct type with + * regard to the contents of the list) + * @param term list terminator (usually 0 or -1) + * @param flags search flags + */ +#define av_opt_set_int_list(obj, name, val, term, flags) \ + (av_int_list_length(val, term) > INT_MAX / sizeof(*(val)) ? \ + AVERROR(EINVAL) : \ + av_opt_set_bin(obj, name, (const uint8_t *)(val), \ + av_int_list_length(val, term) * sizeof(*(val)), flags)) + +/** + * @} + */ + +/** + * @defgroup opt_get_funcs Option getting functions + * @{ + * Those functions get a value of the option with the given name from an object. + * + * @param[in] obj a struct whose first element is a pointer to an AVClass. + * @param[in] name name of the option to get. + * @param[in] search_flags flags passed to av_opt_find2. I.e. if AV_OPT_SEARCH_CHILDREN + * is passed here, then the option may be found in a child of obj. + * @param[out] out_val value of the option will be written here + * @return >=0 on success, a negative error code otherwise + */ +/** + * @note the returned string will be av_malloc()ed and must be av_free()ed by the caller + * + * @note if AV_OPT_ALLOW_NULL is set in search_flags in av_opt_get, and the + * option is of type AV_OPT_TYPE_STRING, AV_OPT_TYPE_BINARY or AV_OPT_TYPE_DICT + * and is set to NULL, *out_val will be set to NULL instead of an allocated + * empty string. + */ +int av_opt_get (void *obj, const char *name, int search_flags, uint8_t **out_val); +int av_opt_get_int (void *obj, const char *name, int search_flags, int64_t *out_val); +int av_opt_get_double (void *obj, const char *name, int search_flags, double *out_val); +int av_opt_get_q (void *obj, const char *name, int search_flags, AVRational *out_val); +int av_opt_get_image_size(void *obj, const char *name, int search_flags, int *w_out, int *h_out); +int av_opt_get_pixel_fmt (void *obj, const char *name, int search_flags, enum AVPixelFormat *out_fmt); +int av_opt_get_sample_fmt(void *obj, const char *name, int search_flags, enum AVSampleFormat *out_fmt); +int av_opt_get_video_rate(void *obj, const char *name, int search_flags, AVRational *out_val); +int av_opt_get_channel_layout(void *obj, const char *name, int search_flags, int64_t *ch_layout); +/** + * @param[out] out_val The returned dictionary is a copy of the actual value and must + * be freed with av_dict_free() by the caller + */ +int av_opt_get_dict_val(void *obj, const char *name, int search_flags, AVDictionary **out_val); +/** + * @} + */ +/** + * Gets a pointer to the requested field in a struct. + * This function allows accessing a struct even when its fields are moved or + * renamed since the application making the access has been compiled, + * + * @returns a pointer to the field, it can be cast to the correct type and read + * or written to. + */ +void *av_opt_ptr(const AVClass *avclass, void *obj, const char *name); + +/** + * Free an AVOptionRanges struct and set it to NULL. + */ +void av_opt_freep_ranges(AVOptionRanges **ranges); + +/** + * Get a list of allowed ranges for the given option. + * + * The returned list may depend on other fields in obj like for example profile. + * + * @param flags is a bitmask of flags, undefined flags should not be set and should be ignored + * AV_OPT_SEARCH_FAKE_OBJ indicates that the obj is a double pointer to a AVClass instead of a full instance + * AV_OPT_MULTI_COMPONENT_RANGE indicates that function may return more than one component, @see AVOptionRanges + * + * The result must be freed with av_opt_freep_ranges. + * + * @return number of compontents returned on success, a negative errro code otherwise + */ +int av_opt_query_ranges(AVOptionRanges **, void *obj, const char *key, int flags); + +/** + * Copy options from src object into dest object. + * + * Options that require memory allocation (e.g. string or binary) are malloc'ed in dest object. + * Original memory allocated for such options is freed unless both src and dest options points to the same memory. + * + * @param dest Object to copy from + * @param src Object to copy into + * @return 0 on success, negative on error + */ +int av_opt_copy(void *dest, const void *src); + +/** + * Get a default list of allowed ranges for the given option. + * + * This list is constructed without using the AVClass.query_ranges() callback + * and can be used as fallback from within the callback. + * + * @param flags is a bitmask of flags, undefined flags should not be set and should be ignored + * AV_OPT_SEARCH_FAKE_OBJ indicates that the obj is a double pointer to a AVClass instead of a full instance + * AV_OPT_MULTI_COMPONENT_RANGE indicates that function may return more than one component, @see AVOptionRanges + * + * The result must be freed with av_opt_free_ranges. + * + * @return number of compontents returned on success, a negative errro code otherwise + */ +int av_opt_query_ranges_default(AVOptionRanges **, void *obj, const char *key, int flags); + +/** + * Check if given option is set to its default value. + * + * Options o must belong to the obj. This function must not be called to check child's options state. + * @see av_opt_is_set_to_default_by_name(). + * + * @param obj AVClass object to check option on + * @param o option to be checked + * @return >0 when option is set to its default, + * 0 when option is not set its default, + * <0 on error + */ +int av_opt_is_set_to_default(void *obj, const AVOption *o); + +/** + * Check if given option is set to its default value. + * + * @param obj AVClass object to check option on + * @param name option name + * @param search_flags combination of AV_OPT_SEARCH_* + * @return >0 when option is set to its default, + * 0 when option is not set its default, + * <0 on error + */ +int av_opt_is_set_to_default_by_name(void *obj, const char *name, int search_flags); + + +#define AV_OPT_SERIALIZE_SKIP_DEFAULTS 0x00000001 ///< Serialize options that are not set to default values only. +#define AV_OPT_SERIALIZE_OPT_FLAGS_EXACT 0x00000002 ///< Serialize options that exactly match opt_flags only. + +/** + * Serialize object's options. + * + * Create a string containing object's serialized options. + * Such string may be passed back to av_opt_set_from_string() in order to restore option values. + * A key/value or pairs separator occurring in the serialized value or + * name string are escaped through the av_escape() function. + * + * @param[in] obj AVClass object to serialize + * @param[in] opt_flags serialize options with all the specified flags set (AV_OPT_FLAG) + * @param[in] flags combination of AV_OPT_SERIALIZE_* flags + * @param[out] buffer Pointer to buffer that will be allocated with string containg serialized options. + * Buffer must be freed by the caller when is no longer needed. + * @param[in] key_val_sep character used to separate key from value + * @param[in] pairs_sep character used to separate two pairs from each other + * @return >= 0 on success, negative on error + * @warning Separators cannot be neither '\\' nor '\0'. They also cannot be the same. + */ +int av_opt_serialize(void *obj, int opt_flags, int flags, char **buffer, + const char key_val_sep, const char pairs_sep); +/** + * @} + */ + +#endif /* AVUTIL_OPT_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/parseutils.h b/ThirdParty/ffmpeg/arm/include/libavutil/parseutils.h new file mode 100644 index 000000000..e66d24b76 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/parseutils.h @@ -0,0 +1,193 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_PARSEUTILS_H +#define AVUTIL_PARSEUTILS_H + +#include + +#include "rational.h" + +/** + * @file + * misc parsing utilities + */ + +/** + * Parse str and store the parsed ratio in q. + * + * Note that a ratio with infinite (1/0) or negative value is + * considered valid, so you should check on the returned value if you + * want to exclude those values. + * + * The undefined value can be expressed using the "0:0" string. + * + * @param[in,out] q pointer to the AVRational which will contain the ratio + * @param[in] str the string to parse: it has to be a string in the format + * num:den, a float number or an expression + * @param[in] max the maximum allowed numerator and denominator + * @param[in] log_offset log level offset which is applied to the log + * level of log_ctx + * @param[in] log_ctx parent logging context + * @return >= 0 on success, a negative error code otherwise + */ +int av_parse_ratio(AVRational *q, const char *str, int max, + int log_offset, void *log_ctx); + +#define av_parse_ratio_quiet(rate, str, max) \ + av_parse_ratio(rate, str, max, AV_LOG_MAX_OFFSET, NULL) + +/** + * Parse str and put in width_ptr and height_ptr the detected values. + * + * @param[in,out] width_ptr pointer to the variable which will contain the detected + * width value + * @param[in,out] height_ptr pointer to the variable which will contain the detected + * height value + * @param[in] str the string to parse: it has to be a string in the format + * width x height or a valid video size abbreviation. + * @return >= 0 on success, a negative error code otherwise + */ +int av_parse_video_size(int *width_ptr, int *height_ptr, const char *str); + +/** + * Parse str and store the detected values in *rate. + * + * @param[in,out] rate pointer to the AVRational which will contain the detected + * frame rate + * @param[in] str the string to parse: it has to be a string in the format + * rate_num / rate_den, a float number or a valid video rate abbreviation + * @return >= 0 on success, a negative error code otherwise + */ +int av_parse_video_rate(AVRational *rate, const char *str); + +/** + * Put the RGBA values that correspond to color_string in rgba_color. + * + * @param color_string a string specifying a color. It can be the name of + * a color (case insensitive match) or a [0x|#]RRGGBB[AA] sequence, + * possibly followed by "@" and a string representing the alpha + * component. + * The alpha component may be a string composed by "0x" followed by an + * hexadecimal number or a decimal number between 0.0 and 1.0, which + * represents the opacity value (0x00/0.0 means completely transparent, + * 0xff/1.0 completely opaque). + * If the alpha component is not specified then 0xff is assumed. + * The string "random" will result in a random color. + * @param slen length of the initial part of color_string containing the + * color. It can be set to -1 if color_string is a null terminated string + * containing nothing else than the color. + * @return >= 0 in case of success, a negative value in case of + * failure (for example if color_string cannot be parsed). + */ +int av_parse_color(uint8_t *rgba_color, const char *color_string, int slen, + void *log_ctx); + +/** + * Get the name of a color from the internal table of hard-coded named + * colors. + * + * This function is meant to enumerate the color names recognized by + * av_parse_color(). + * + * @param color_idx index of the requested color, starting from 0 + * @param rgbp if not NULL, will point to a 3-elements array with the color value in RGB + * @return the color name string or NULL if color_idx is not in the array + */ +const char *av_get_known_color_name(int color_idx, const uint8_t **rgb); + +/** + * Parse timestr and return in *time a corresponding number of + * microseconds. + * + * @param timeval puts here the number of microseconds corresponding + * to the string in timestr. If the string represents a duration, it + * is the number of microseconds contained in the time interval. If + * the string is a date, is the number of microseconds since 1st of + * January, 1970 up to the time of the parsed date. If timestr cannot + * be successfully parsed, set *time to INT64_MIN. + + * @param timestr a string representing a date or a duration. + * - If a date the syntax is: + * @code + * [{YYYY-MM-DD|YYYYMMDD}[T|t| ]]{{HH:MM:SS[.m...]]]}|{HHMMSS[.m...]]]}}[Z] + * now + * @endcode + * If the value is "now" it takes the current time. + * Time is local time unless Z is appended, in which case it is + * interpreted as UTC. + * If the year-month-day part is not specified it takes the current + * year-month-day. + * - If a duration the syntax is: + * @code + * [-][HH:]MM:SS[.m...] + * [-]S+[.m...] + * @endcode + * @param duration flag which tells how to interpret timestr, if not + * zero timestr is interpreted as a duration, otherwise as a date + * @return >= 0 in case of success, a negative value corresponding to an + * AVERROR code otherwise + */ +int av_parse_time(int64_t *timeval, const char *timestr, int duration); + +/** + * Attempt to find a specific tag in a URL. + * + * syntax: '?tag1=val1&tag2=val2...'. Little URL decoding is done. + * Return 1 if found. + */ +int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info); + +/** + * Simplified version of strptime + * + * Parse the input string p according to the format string fmt and + * store its results in the structure dt. + * This implementation supports only a subset of the formats supported + * by the standard strptime(). + * + * The supported input field descriptors are listed below. + * - %H: the hour as a decimal number, using a 24-hour clock, in the + * range '00' through '23' + * - %J: hours as a decimal number, in the range '0' through INT_MAX + * - %M: the minute as a decimal number, using a 24-hour clock, in the + * range '00' through '59' + * - %S: the second as a decimal number, using a 24-hour clock, in the + * range '00' through '59' + * - %Y: the year as a decimal number, using the Gregorian calendar + * - %m: the month as a decimal number, in the range '1' through '12' + * - %d: the day of the month as a decimal number, in the range '1' + * through '31' + * - %T: alias for '%H:%M:%S' + * - %%: a literal '%' + * + * @return a pointer to the first character not processed in this function + * call. In case the input string contains more characters than + * required by the format string the return value points right after + * the last consumed input character. In case the whole input string + * is consumed the return value points to the null byte at the end of + * the string. On failure NULL is returned. + */ +char *av_small_strptime(const char *p, const char *fmt, struct tm *dt); + +/** + * Convert the decomposed UTC time in tm to a time_t value. + */ +time_t av_timegm(struct tm *tm); + +#endif /* AVUTIL_PARSEUTILS_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/pixdesc.h b/ThirdParty/ffmpeg/arm/include/libavutil/pixdesc.h new file mode 100644 index 000000000..c055810ae --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/pixdesc.h @@ -0,0 +1,440 @@ +/* + * pixel format descriptor + * Copyright (c) 2009 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_PIXDESC_H +#define AVUTIL_PIXDESC_H + +#include + +#include "attributes.h" +#include "pixfmt.h" +#include "version.h" + +typedef struct AVComponentDescriptor { + /** + * Which of the 4 planes contains the component. + */ + int plane; + + /** + * Number of elements between 2 horizontally consecutive pixels. + * Elements are bits for bitstream formats, bytes otherwise. + */ + int step; + + /** + * Number of elements before the component of the first pixel. + * Elements are bits for bitstream formats, bytes otherwise. + */ + int offset; + + /** + * Number of least significant bits that must be shifted away + * to get the value. + */ + int shift; + + /** + * Number of bits in the component. + */ + int depth; + +#if FF_API_PLUS1_MINUS1 + /** deprecated, use step instead */ + attribute_deprecated int step_minus1; + + /** deprecated, use depth instead */ + attribute_deprecated int depth_minus1; + + /** deprecated, use offset instead */ + attribute_deprecated int offset_plus1; +#endif +} AVComponentDescriptor; + +/** + * Descriptor that unambiguously describes how the bits of a pixel are + * stored in the up to 4 data planes of an image. It also stores the + * subsampling factors and number of components. + * + * @note This is separate of the colorspace (RGB, YCbCr, YPbPr, JPEG-style YUV + * and all the YUV variants) AVPixFmtDescriptor just stores how values + * are stored not what these values represent. + */ +typedef struct AVPixFmtDescriptor { + const char *name; + uint8_t nb_components; ///< The number of components each pixel has, (1-4) + + /** + * Amount to shift the luma width right to find the chroma width. + * For YV12 this is 1 for example. + * chroma_width = AV_CEIL_RSHIFT(luma_width, log2_chroma_w) + * The note above is needed to ensure rounding up. + * This value only refers to the chroma components. + */ + uint8_t log2_chroma_w; + + /** + * Amount to shift the luma height right to find the chroma height. + * For YV12 this is 1 for example. + * chroma_height= AV_CEIL_RSHIFT(luma_height, log2_chroma_h) + * The note above is needed to ensure rounding up. + * This value only refers to the chroma components. + */ + uint8_t log2_chroma_h; + + /** + * Combination of AV_PIX_FMT_FLAG_... flags. + */ + uint64_t flags; + + /** + * Parameters that describe how pixels are packed. + * If the format has 1 or 2 components, then luma is 0. + * If the format has 3 or 4 components: + * if the RGB flag is set then 0 is red, 1 is green and 2 is blue; + * otherwise 0 is luma, 1 is chroma-U and 2 is chroma-V. + * + * If present, the Alpha channel is always the last component. + */ + AVComponentDescriptor comp[4]; + + /** + * Alternative comma-separated names. + */ + const char *alias; +} AVPixFmtDescriptor; + +/** + * Pixel format is big-endian. + */ +#define AV_PIX_FMT_FLAG_BE (1 << 0) +/** + * Pixel format has a palette in data[1], values are indexes in this palette. + */ +#define AV_PIX_FMT_FLAG_PAL (1 << 1) +/** + * All values of a component are bit-wise packed end to end. + */ +#define AV_PIX_FMT_FLAG_BITSTREAM (1 << 2) +/** + * Pixel format is an HW accelerated format. + */ +#define AV_PIX_FMT_FLAG_HWACCEL (1 << 3) +/** + * At least one pixel component is not in the first data plane. + */ +#define AV_PIX_FMT_FLAG_PLANAR (1 << 4) +/** + * The pixel format contains RGB-like data (as opposed to YUV/grayscale). + */ +#define AV_PIX_FMT_FLAG_RGB (1 << 5) + +/** + * The pixel format is "pseudo-paletted". This means that it contains a + * fixed palette in the 2nd plane but the palette is fixed/constant for each + * PIX_FMT. This allows interpreting the data as if it was PAL8, which can + * in some cases be simpler. Or the data can be interpreted purely based on + * the pixel format without using the palette. + * An example of a pseudo-paletted format is AV_PIX_FMT_GRAY8 + * + * @deprecated This flag is deprecated, and will be removed. When it is removed, + * the extra palette allocation in AVFrame.data[1] is removed as well. Only + * actual paletted formats (as indicated by AV_PIX_FMT_FLAG_PAL) will have a + * palette. Starting with FFmpeg versions which have this flag deprecated, the + * extra "pseudo" palette is already ignored, and API users are not required to + * allocate a palette for AV_PIX_FMT_FLAG_PSEUDOPAL formats (it was required + * before the deprecation, though). + */ +#define AV_PIX_FMT_FLAG_PSEUDOPAL (1 << 6) + +/** + * The pixel format has an alpha channel. This is set on all formats that + * support alpha in some way, including AV_PIX_FMT_PAL8. The alpha is always + * straight, never pre-multiplied. + * + * If a codec or a filter does not support alpha, it should set all alpha to + * opaque, or use the equivalent pixel formats without alpha component, e.g. + * AV_PIX_FMT_RGB0 (or AV_PIX_FMT_RGB24 etc.) instead of AV_PIX_FMT_RGBA. + */ +#define AV_PIX_FMT_FLAG_ALPHA (1 << 7) + +/** + * The pixel format is following a Bayer pattern + */ +#define AV_PIX_FMT_FLAG_BAYER (1 << 8) + +/** + * The pixel format contains IEEE-754 floating point values. Precision (double, + * single, or half) should be determined by the pixel size (64, 32, or 16 bits). + */ +#define AV_PIX_FMT_FLAG_FLOAT (1 << 9) + +/** + * Return the number of bits per pixel used by the pixel format + * described by pixdesc. Note that this is not the same as the number + * of bits per sample. + * + * The returned number of bits refers to the number of bits actually + * used for storing the pixel information, that is padding bits are + * not counted. + */ +int av_get_bits_per_pixel(const AVPixFmtDescriptor *pixdesc); + +/** + * Return the number of bits per pixel for the pixel format + * described by pixdesc, including any padding or unused bits. + */ +int av_get_padded_bits_per_pixel(const AVPixFmtDescriptor *pixdesc); + +/** + * @return a pixel format descriptor for provided pixel format or NULL if + * this pixel format is unknown. + */ +const AVPixFmtDescriptor *av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt); + +/** + * Iterate over all pixel format descriptors known to libavutil. + * + * @param prev previous descriptor. NULL to get the first descriptor. + * + * @return next descriptor or NULL after the last descriptor + */ +const AVPixFmtDescriptor *av_pix_fmt_desc_next(const AVPixFmtDescriptor *prev); + +/** + * @return an AVPixelFormat id described by desc, or AV_PIX_FMT_NONE if desc + * is not a valid pointer to a pixel format descriptor. + */ +enum AVPixelFormat av_pix_fmt_desc_get_id(const AVPixFmtDescriptor *desc); + +/** + * Utility function to access log2_chroma_w log2_chroma_h from + * the pixel format AVPixFmtDescriptor. + * + * @param[in] pix_fmt the pixel format + * @param[out] h_shift store log2_chroma_w (horizontal/width shift) + * @param[out] v_shift store log2_chroma_h (vertical/height shift) + * + * @return 0 on success, AVERROR(ENOSYS) on invalid or unknown pixel format + */ +int av_pix_fmt_get_chroma_sub_sample(enum AVPixelFormat pix_fmt, + int *h_shift, int *v_shift); + +/** + * @return number of planes in pix_fmt, a negative AVERROR if pix_fmt is not a + * valid pixel format. + */ +int av_pix_fmt_count_planes(enum AVPixelFormat pix_fmt); + +/** + * @return the name for provided color range or NULL if unknown. + */ +const char *av_color_range_name(enum AVColorRange range); + +/** + * @return the AVColorRange value for name or an AVError if not found. + */ +int av_color_range_from_name(const char *name); + +/** + * @return the name for provided color primaries or NULL if unknown. + */ +const char *av_color_primaries_name(enum AVColorPrimaries primaries); + +/** + * @return the AVColorPrimaries value for name or an AVError if not found. + */ +int av_color_primaries_from_name(const char *name); + +/** + * @return the name for provided color transfer or NULL if unknown. + */ +const char *av_color_transfer_name(enum AVColorTransferCharacteristic transfer); + +/** + * @return the AVColorTransferCharacteristic value for name or an AVError if not found. + */ +int av_color_transfer_from_name(const char *name); + +/** + * @return the name for provided color space or NULL if unknown. + */ +const char *av_color_space_name(enum AVColorSpace space); + +/** + * @return the AVColorSpace value for name or an AVError if not found. + */ +int av_color_space_from_name(const char *name); + +/** + * @return the name for provided chroma location or NULL if unknown. + */ +const char *av_chroma_location_name(enum AVChromaLocation location); + +/** + * @return the AVChromaLocation value for name or an AVError if not found. + */ +int av_chroma_location_from_name(const char *name); + +/** + * Return the pixel format corresponding to name. + * + * If there is no pixel format with name name, then looks for a + * pixel format with the name corresponding to the native endian + * format of name. + * For example in a little-endian system, first looks for "gray16", + * then for "gray16le". + * + * Finally if no pixel format has been found, returns AV_PIX_FMT_NONE. + */ +enum AVPixelFormat av_get_pix_fmt(const char *name); + +/** + * Return the short name for a pixel format, NULL in case pix_fmt is + * unknown. + * + * @see av_get_pix_fmt(), av_get_pix_fmt_string() + */ +const char *av_get_pix_fmt_name(enum AVPixelFormat pix_fmt); + +/** + * Print in buf the string corresponding to the pixel format with + * number pix_fmt, or a header if pix_fmt is negative. + * + * @param buf the buffer where to write the string + * @param buf_size the size of buf + * @param pix_fmt the number of the pixel format to print the + * corresponding info string, or a negative value to print the + * corresponding header. + */ +char *av_get_pix_fmt_string(char *buf, int buf_size, + enum AVPixelFormat pix_fmt); + +/** + * Read a line from an image, and write the values of the + * pixel format component c to dst. + * + * @param data the array containing the pointers to the planes of the image + * @param linesize the array containing the linesizes of the image + * @param desc the pixel format descriptor for the image + * @param x the horizontal coordinate of the first pixel to read + * @param y the vertical coordinate of the first pixel to read + * @param w the width of the line to read, that is the number of + * values to write to dst + * @param read_pal_component if not zero and the format is a paletted + * format writes the values corresponding to the palette + * component c in data[1] to dst, rather than the palette indexes in + * data[0]. The behavior is undefined if the format is not paletted. + * @param dst_element_size size of elements in dst array (2 or 4 byte) + */ +void av_read_image_line2(void *dst, const uint8_t *data[4], + const int linesize[4], const AVPixFmtDescriptor *desc, + int x, int y, int c, int w, int read_pal_component, + int dst_element_size); + +void av_read_image_line(uint16_t *dst, const uint8_t *data[4], + const int linesize[4], const AVPixFmtDescriptor *desc, + int x, int y, int c, int w, int read_pal_component); + +/** + * Write the values from src to the pixel format component c of an + * image line. + * + * @param src array containing the values to write + * @param data the array containing the pointers to the planes of the + * image to write into. It is supposed to be zeroed. + * @param linesize the array containing the linesizes of the image + * @param desc the pixel format descriptor for the image + * @param x the horizontal coordinate of the first pixel to write + * @param y the vertical coordinate of the first pixel to write + * @param w the width of the line to write, that is the number of + * values to write to the image line + * @param src_element_size size of elements in src array (2 or 4 byte) + */ +void av_write_image_line2(const void *src, uint8_t *data[4], + const int linesize[4], const AVPixFmtDescriptor *desc, + int x, int y, int c, int w, int src_element_size); + +void av_write_image_line(const uint16_t *src, uint8_t *data[4], + const int linesize[4], const AVPixFmtDescriptor *desc, + int x, int y, int c, int w); + +/** + * Utility function to swap the endianness of a pixel format. + * + * @param[in] pix_fmt the pixel format + * + * @return pixel format with swapped endianness if it exists, + * otherwise AV_PIX_FMT_NONE + */ +enum AVPixelFormat av_pix_fmt_swap_endianness(enum AVPixelFormat pix_fmt); + +#define FF_LOSS_RESOLUTION 0x0001 /**< loss due to resolution change */ +#define FF_LOSS_DEPTH 0x0002 /**< loss due to color depth change */ +#define FF_LOSS_COLORSPACE 0x0004 /**< loss due to color space conversion */ +#define FF_LOSS_ALPHA 0x0008 /**< loss of alpha bits */ +#define FF_LOSS_COLORQUANT 0x0010 /**< loss due to color quantization */ +#define FF_LOSS_CHROMA 0x0020 /**< loss of chroma (e.g. RGB to gray conversion) */ + +/** + * Compute what kind of losses will occur when converting from one specific + * pixel format to another. + * When converting from one pixel format to another, information loss may occur. + * For example, when converting from RGB24 to GRAY, the color information will + * be lost. Similarly, other losses occur when converting from some formats to + * other formats. These losses can involve loss of chroma, but also loss of + * resolution, loss of color depth, loss due to the color space conversion, loss + * of the alpha bits or loss due to color quantization. + * av_get_fix_fmt_loss() informs you about the various types of losses + * which will occur when converting from one pixel format to another. + * + * @param[in] dst_pix_fmt destination pixel format + * @param[in] src_pix_fmt source pixel format + * @param[in] has_alpha Whether the source pixel format alpha channel is used. + * @return Combination of flags informing you what kind of losses will occur + * (maximum loss for an invalid dst_pix_fmt). + */ +int av_get_pix_fmt_loss(enum AVPixelFormat dst_pix_fmt, + enum AVPixelFormat src_pix_fmt, + int has_alpha); + +/** + * Compute what kind of losses will occur when converting from one specific + * pixel format to another. + * When converting from one pixel format to another, information loss may occur. + * For example, when converting from RGB24 to GRAY, the color information will + * be lost. Similarly, other losses occur when converting from some formats to + * other formats. These losses can involve loss of chroma, but also loss of + * resolution, loss of color depth, loss due to the color space conversion, loss + * of the alpha bits or loss due to color quantization. + * av_get_fix_fmt_loss() informs you about the various types of losses + * which will occur when converting from one pixel format to another. + * + * @param[in] dst_pix_fmt destination pixel format + * @param[in] src_pix_fmt source pixel format + * @param[in] has_alpha Whether the source pixel format alpha channel is used. + * @return Combination of flags informing you what kind of losses will occur + * (maximum loss for an invalid dst_pix_fmt). + */ +enum AVPixelFormat av_find_best_pix_fmt_of_2(enum AVPixelFormat dst_pix_fmt1, enum AVPixelFormat dst_pix_fmt2, + enum AVPixelFormat src_pix_fmt, int has_alpha, int *loss_ptr); + +#endif /* AVUTIL_PIXDESC_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/pixelutils.h b/ThirdParty/ffmpeg/arm/include/libavutil/pixelutils.h new file mode 100644 index 000000000..a8dbc157e --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/pixelutils.h @@ -0,0 +1,52 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_PIXELUTILS_H +#define AVUTIL_PIXELUTILS_H + +#include +#include +#include "common.h" + +/** + * Sum of abs(src1[x] - src2[x]) + */ +typedef int (*av_pixelutils_sad_fn)(const uint8_t *src1, ptrdiff_t stride1, + const uint8_t *src2, ptrdiff_t stride2); + +/** + * Get a potentially optimized pointer to a Sum-of-absolute-differences + * function (see the av_pixelutils_sad_fn prototype). + * + * @param w_bits 1< + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_PIXFMT_H +#define AVUTIL_PIXFMT_H + +/** + * @file + * pixel format definitions + */ + +#include "libavutil/avconfig.h" +#include "version.h" + +#define AVPALETTE_SIZE 1024 +#define AVPALETTE_COUNT 256 + +/** + * Pixel format. + * + * @note + * AV_PIX_FMT_RGB32 is handled in an endian-specific manner. An RGBA + * color is put together as: + * (A << 24) | (R << 16) | (G << 8) | B + * This is stored as BGRA on little-endian CPU architectures and ARGB on + * big-endian CPUs. + * + * @note + * If the resolution is not a multiple of the chroma subsampling factor + * then the chroma plane resolution must be rounded up. + * + * @par + * When the pixel format is palettized RGB32 (AV_PIX_FMT_PAL8), the palettized + * image data is stored in AVFrame.data[0]. The palette is transported in + * AVFrame.data[1], is 1024 bytes long (256 4-byte entries) and is + * formatted the same as in AV_PIX_FMT_RGB32 described above (i.e., it is + * also endian-specific). Note also that the individual RGB32 palette + * components stored in AVFrame.data[1] should be in the range 0..255. + * This is important as many custom PAL8 video codecs that were designed + * to run on the IBM VGA graphics adapter use 6-bit palette components. + * + * @par + * For all the 8 bits per pixel formats, an RGB32 palette is in data[1] like + * for pal8. This palette is filled in automatically by the function + * allocating the picture. + */ +enum AVPixelFormat { + AV_PIX_FMT_NONE = -1, + AV_PIX_FMT_YUV420P, ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) + AV_PIX_FMT_YUYV422, ///< packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr + AV_PIX_FMT_RGB24, ///< packed RGB 8:8:8, 24bpp, RGBRGB... + AV_PIX_FMT_BGR24, ///< packed RGB 8:8:8, 24bpp, BGRBGR... + AV_PIX_FMT_YUV422P, ///< planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples) + AV_PIX_FMT_YUV444P, ///< planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples) + AV_PIX_FMT_YUV410P, ///< planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples) + AV_PIX_FMT_YUV411P, ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) + AV_PIX_FMT_GRAY8, ///< Y , 8bpp + AV_PIX_FMT_MONOWHITE, ///< Y , 1bpp, 0 is white, 1 is black, in each byte pixels are ordered from the msb to the lsb + AV_PIX_FMT_MONOBLACK, ///< Y , 1bpp, 0 is black, 1 is white, in each byte pixels are ordered from the msb to the lsb + AV_PIX_FMT_PAL8, ///< 8 bits with AV_PIX_FMT_RGB32 palette + AV_PIX_FMT_YUVJ420P, ///< planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting color_range + AV_PIX_FMT_YUVJ422P, ///< planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV422P and setting color_range + AV_PIX_FMT_YUVJ444P, ///< planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV444P and setting color_range + AV_PIX_FMT_UYVY422, ///< packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1 + AV_PIX_FMT_UYYVYY411, ///< packed YUV 4:1:1, 12bpp, Cb Y0 Y1 Cr Y2 Y3 + AV_PIX_FMT_BGR8, ///< packed RGB 3:3:2, 8bpp, (msb)2B 3G 3R(lsb) + AV_PIX_FMT_BGR4, ///< packed RGB 1:2:1 bitstream, 4bpp, (msb)1B 2G 1R(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits + AV_PIX_FMT_BGR4_BYTE, ///< packed RGB 1:2:1, 8bpp, (msb)1B 2G 1R(lsb) + AV_PIX_FMT_RGB8, ///< packed RGB 3:3:2, 8bpp, (msb)2R 3G 3B(lsb) + AV_PIX_FMT_RGB4, ///< packed RGB 1:2:1 bitstream, 4bpp, (msb)1R 2G 1B(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits + AV_PIX_FMT_RGB4_BYTE, ///< packed RGB 1:2:1, 8bpp, (msb)1R 2G 1B(lsb) + AV_PIX_FMT_NV12, ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V) + AV_PIX_FMT_NV21, ///< as above, but U and V bytes are swapped + + AV_PIX_FMT_ARGB, ///< packed ARGB 8:8:8:8, 32bpp, ARGBARGB... + AV_PIX_FMT_RGBA, ///< packed RGBA 8:8:8:8, 32bpp, RGBARGBA... + AV_PIX_FMT_ABGR, ///< packed ABGR 8:8:8:8, 32bpp, ABGRABGR... + AV_PIX_FMT_BGRA, ///< packed BGRA 8:8:8:8, 32bpp, BGRABGRA... + + AV_PIX_FMT_GRAY16BE, ///< Y , 16bpp, big-endian + AV_PIX_FMT_GRAY16LE, ///< Y , 16bpp, little-endian + AV_PIX_FMT_YUV440P, ///< planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples) + AV_PIX_FMT_YUVJ440P, ///< planar YUV 4:4:0 full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV440P and setting color_range + AV_PIX_FMT_YUVA420P, ///< planar YUV 4:2:0, 20bpp, (1 Cr & Cb sample per 2x2 Y & A samples) + AV_PIX_FMT_RGB48BE, ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as big-endian + AV_PIX_FMT_RGB48LE, ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as little-endian + + AV_PIX_FMT_RGB565BE, ///< packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), big-endian + AV_PIX_FMT_RGB565LE, ///< packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), little-endian + AV_PIX_FMT_RGB555BE, ///< packed RGB 5:5:5, 16bpp, (msb)1X 5R 5G 5B(lsb), big-endian , X=unused/undefined + AV_PIX_FMT_RGB555LE, ///< packed RGB 5:5:5, 16bpp, (msb)1X 5R 5G 5B(lsb), little-endian, X=unused/undefined + + AV_PIX_FMT_BGR565BE, ///< packed BGR 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), big-endian + AV_PIX_FMT_BGR565LE, ///< packed BGR 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), little-endian + AV_PIX_FMT_BGR555BE, ///< packed BGR 5:5:5, 16bpp, (msb)1X 5B 5G 5R(lsb), big-endian , X=unused/undefined + AV_PIX_FMT_BGR555LE, ///< packed BGR 5:5:5, 16bpp, (msb)1X 5B 5G 5R(lsb), little-endian, X=unused/undefined + +#if FF_API_VAAPI + /** @name Deprecated pixel formats */ + /**@{*/ + AV_PIX_FMT_VAAPI_MOCO, ///< HW acceleration through VA API at motion compensation entry-point, Picture.data[3] contains a vaapi_render_state struct which contains macroblocks as well as various fields extracted from headers + AV_PIX_FMT_VAAPI_IDCT, ///< HW acceleration through VA API at IDCT entry-point, Picture.data[3] contains a vaapi_render_state struct which contains fields extracted from headers + AV_PIX_FMT_VAAPI_VLD, ///< HW decoding through VA API, Picture.data[3] contains a VASurfaceID + /**@}*/ + AV_PIX_FMT_VAAPI = AV_PIX_FMT_VAAPI_VLD, +#else + /** + * Hardware acceleration through VA-API, data[3] contains a + * VASurfaceID. + */ + AV_PIX_FMT_VAAPI, +#endif + + AV_PIX_FMT_YUV420P16LE, ///< planar YUV 4:2:0, 24bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian + AV_PIX_FMT_YUV420P16BE, ///< planar YUV 4:2:0, 24bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian + AV_PIX_FMT_YUV422P16LE, ///< planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian + AV_PIX_FMT_YUV422P16BE, ///< planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian + AV_PIX_FMT_YUV444P16LE, ///< planar YUV 4:4:4, 48bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian + AV_PIX_FMT_YUV444P16BE, ///< planar YUV 4:4:4, 48bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian + AV_PIX_FMT_DXVA2_VLD, ///< HW decoding through DXVA2, Picture.data[3] contains a LPDIRECT3DSURFACE9 pointer + + AV_PIX_FMT_RGB444LE, ///< packed RGB 4:4:4, 16bpp, (msb)4X 4R 4G 4B(lsb), little-endian, X=unused/undefined + AV_PIX_FMT_RGB444BE, ///< packed RGB 4:4:4, 16bpp, (msb)4X 4R 4G 4B(lsb), big-endian, X=unused/undefined + AV_PIX_FMT_BGR444LE, ///< packed BGR 4:4:4, 16bpp, (msb)4X 4B 4G 4R(lsb), little-endian, X=unused/undefined + AV_PIX_FMT_BGR444BE, ///< packed BGR 4:4:4, 16bpp, (msb)4X 4B 4G 4R(lsb), big-endian, X=unused/undefined + AV_PIX_FMT_YA8, ///< 8 bits gray, 8 bits alpha + + AV_PIX_FMT_Y400A = AV_PIX_FMT_YA8, ///< alias for AV_PIX_FMT_YA8 + AV_PIX_FMT_GRAY8A= AV_PIX_FMT_YA8, ///< alias for AV_PIX_FMT_YA8 + + AV_PIX_FMT_BGR48BE, ///< packed RGB 16:16:16, 48bpp, 16B, 16G, 16R, the 2-byte value for each R/G/B component is stored as big-endian + AV_PIX_FMT_BGR48LE, ///< packed RGB 16:16:16, 48bpp, 16B, 16G, 16R, the 2-byte value for each R/G/B component is stored as little-endian + + /** + * The following 12 formats have the disadvantage of needing 1 format for each bit depth. + * Notice that each 9/10 bits sample is stored in 16 bits with extra padding. + * If you want to support multiple bit depths, then using AV_PIX_FMT_YUV420P16* with the bpp stored separately is better. + */ + AV_PIX_FMT_YUV420P9BE, ///< planar YUV 4:2:0, 13.5bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian + AV_PIX_FMT_YUV420P9LE, ///< planar YUV 4:2:0, 13.5bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian + AV_PIX_FMT_YUV420P10BE,///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian + AV_PIX_FMT_YUV420P10LE,///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian + AV_PIX_FMT_YUV422P10BE,///< planar YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian + AV_PIX_FMT_YUV422P10LE,///< planar YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian + AV_PIX_FMT_YUV444P9BE, ///< planar YUV 4:4:4, 27bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian + AV_PIX_FMT_YUV444P9LE, ///< planar YUV 4:4:4, 27bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian + AV_PIX_FMT_YUV444P10BE,///< planar YUV 4:4:4, 30bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian + AV_PIX_FMT_YUV444P10LE,///< planar YUV 4:4:4, 30bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian + AV_PIX_FMT_YUV422P9BE, ///< planar YUV 4:2:2, 18bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian + AV_PIX_FMT_YUV422P9LE, ///< planar YUV 4:2:2, 18bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian + AV_PIX_FMT_GBRP, ///< planar GBR 4:4:4 24bpp + AV_PIX_FMT_GBR24P = AV_PIX_FMT_GBRP, // alias for #AV_PIX_FMT_GBRP + AV_PIX_FMT_GBRP9BE, ///< planar GBR 4:4:4 27bpp, big-endian + AV_PIX_FMT_GBRP9LE, ///< planar GBR 4:4:4 27bpp, little-endian + AV_PIX_FMT_GBRP10BE, ///< planar GBR 4:4:4 30bpp, big-endian + AV_PIX_FMT_GBRP10LE, ///< planar GBR 4:4:4 30bpp, little-endian + AV_PIX_FMT_GBRP16BE, ///< planar GBR 4:4:4 48bpp, big-endian + AV_PIX_FMT_GBRP16LE, ///< planar GBR 4:4:4 48bpp, little-endian + AV_PIX_FMT_YUVA422P, ///< planar YUV 4:2:2 24bpp, (1 Cr & Cb sample per 2x1 Y & A samples) + AV_PIX_FMT_YUVA444P, ///< planar YUV 4:4:4 32bpp, (1 Cr & Cb sample per 1x1 Y & A samples) + AV_PIX_FMT_YUVA420P9BE, ///< planar YUV 4:2:0 22.5bpp, (1 Cr & Cb sample per 2x2 Y & A samples), big-endian + AV_PIX_FMT_YUVA420P9LE, ///< planar YUV 4:2:0 22.5bpp, (1 Cr & Cb sample per 2x2 Y & A samples), little-endian + AV_PIX_FMT_YUVA422P9BE, ///< planar YUV 4:2:2 27bpp, (1 Cr & Cb sample per 2x1 Y & A samples), big-endian + AV_PIX_FMT_YUVA422P9LE, ///< planar YUV 4:2:2 27bpp, (1 Cr & Cb sample per 2x1 Y & A samples), little-endian + AV_PIX_FMT_YUVA444P9BE, ///< planar YUV 4:4:4 36bpp, (1 Cr & Cb sample per 1x1 Y & A samples), big-endian + AV_PIX_FMT_YUVA444P9LE, ///< planar YUV 4:4:4 36bpp, (1 Cr & Cb sample per 1x1 Y & A samples), little-endian + AV_PIX_FMT_YUVA420P10BE, ///< planar YUV 4:2:0 25bpp, (1 Cr & Cb sample per 2x2 Y & A samples, big-endian) + AV_PIX_FMT_YUVA420P10LE, ///< planar YUV 4:2:0 25bpp, (1 Cr & Cb sample per 2x2 Y & A samples, little-endian) + AV_PIX_FMT_YUVA422P10BE, ///< planar YUV 4:2:2 30bpp, (1 Cr & Cb sample per 2x1 Y & A samples, big-endian) + AV_PIX_FMT_YUVA422P10LE, ///< planar YUV 4:2:2 30bpp, (1 Cr & Cb sample per 2x1 Y & A samples, little-endian) + AV_PIX_FMT_YUVA444P10BE, ///< planar YUV 4:4:4 40bpp, (1 Cr & Cb sample per 1x1 Y & A samples, big-endian) + AV_PIX_FMT_YUVA444P10LE, ///< planar YUV 4:4:4 40bpp, (1 Cr & Cb sample per 1x1 Y & A samples, little-endian) + AV_PIX_FMT_YUVA420P16BE, ///< planar YUV 4:2:0 40bpp, (1 Cr & Cb sample per 2x2 Y & A samples, big-endian) + AV_PIX_FMT_YUVA420P16LE, ///< planar YUV 4:2:0 40bpp, (1 Cr & Cb sample per 2x2 Y & A samples, little-endian) + AV_PIX_FMT_YUVA422P16BE, ///< planar YUV 4:2:2 48bpp, (1 Cr & Cb sample per 2x1 Y & A samples, big-endian) + AV_PIX_FMT_YUVA422P16LE, ///< planar YUV 4:2:2 48bpp, (1 Cr & Cb sample per 2x1 Y & A samples, little-endian) + AV_PIX_FMT_YUVA444P16BE, ///< planar YUV 4:4:4 64bpp, (1 Cr & Cb sample per 1x1 Y & A samples, big-endian) + AV_PIX_FMT_YUVA444P16LE, ///< planar YUV 4:4:4 64bpp, (1 Cr & Cb sample per 1x1 Y & A samples, little-endian) + + AV_PIX_FMT_VDPAU, ///< HW acceleration through VDPAU, Picture.data[3] contains a VdpVideoSurface + + AV_PIX_FMT_XYZ12LE, ///< packed XYZ 4:4:4, 36 bpp, (msb) 12X, 12Y, 12Z (lsb), the 2-byte value for each X/Y/Z is stored as little-endian, the 4 lower bits are set to 0 + AV_PIX_FMT_XYZ12BE, ///< packed XYZ 4:4:4, 36 bpp, (msb) 12X, 12Y, 12Z (lsb), the 2-byte value for each X/Y/Z is stored as big-endian, the 4 lower bits are set to 0 + AV_PIX_FMT_NV16, ///< interleaved chroma YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples) + AV_PIX_FMT_NV20LE, ///< interleaved chroma YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian + AV_PIX_FMT_NV20BE, ///< interleaved chroma YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian + + AV_PIX_FMT_RGBA64BE, ///< packed RGBA 16:16:16:16, 64bpp, 16R, 16G, 16B, 16A, the 2-byte value for each R/G/B/A component is stored as big-endian + AV_PIX_FMT_RGBA64LE, ///< packed RGBA 16:16:16:16, 64bpp, 16R, 16G, 16B, 16A, the 2-byte value for each R/G/B/A component is stored as little-endian + AV_PIX_FMT_BGRA64BE, ///< packed RGBA 16:16:16:16, 64bpp, 16B, 16G, 16R, 16A, the 2-byte value for each R/G/B/A component is stored as big-endian + AV_PIX_FMT_BGRA64LE, ///< packed RGBA 16:16:16:16, 64bpp, 16B, 16G, 16R, 16A, the 2-byte value for each R/G/B/A component is stored as little-endian + + AV_PIX_FMT_YVYU422, ///< packed YUV 4:2:2, 16bpp, Y0 Cr Y1 Cb + + AV_PIX_FMT_YA16BE, ///< 16 bits gray, 16 bits alpha (big-endian) + AV_PIX_FMT_YA16LE, ///< 16 bits gray, 16 bits alpha (little-endian) + + AV_PIX_FMT_GBRAP, ///< planar GBRA 4:4:4:4 32bpp + AV_PIX_FMT_GBRAP16BE, ///< planar GBRA 4:4:4:4 64bpp, big-endian + AV_PIX_FMT_GBRAP16LE, ///< planar GBRA 4:4:4:4 64bpp, little-endian + /** + * HW acceleration through QSV, data[3] contains a pointer to the + * mfxFrameSurface1 structure. + */ + AV_PIX_FMT_QSV, + /** + * HW acceleration though MMAL, data[3] contains a pointer to the + * MMAL_BUFFER_HEADER_T structure. + */ + AV_PIX_FMT_MMAL, + + AV_PIX_FMT_D3D11VA_VLD, ///< HW decoding through Direct3D11 via old API, Picture.data[3] contains a ID3D11VideoDecoderOutputView pointer + + /** + * HW acceleration through CUDA. data[i] contain CUdeviceptr pointers + * exactly as for system memory frames. + */ + AV_PIX_FMT_CUDA, + + AV_PIX_FMT_0RGB, ///< packed RGB 8:8:8, 32bpp, XRGBXRGB... X=unused/undefined + AV_PIX_FMT_RGB0, ///< packed RGB 8:8:8, 32bpp, RGBXRGBX... X=unused/undefined + AV_PIX_FMT_0BGR, ///< packed BGR 8:8:8, 32bpp, XBGRXBGR... X=unused/undefined + AV_PIX_FMT_BGR0, ///< packed BGR 8:8:8, 32bpp, BGRXBGRX... X=unused/undefined + + AV_PIX_FMT_YUV420P12BE, ///< planar YUV 4:2:0,18bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian + AV_PIX_FMT_YUV420P12LE, ///< planar YUV 4:2:0,18bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian + AV_PIX_FMT_YUV420P14BE, ///< planar YUV 4:2:0,21bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian + AV_PIX_FMT_YUV420P14LE, ///< planar YUV 4:2:0,21bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian + AV_PIX_FMT_YUV422P12BE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian + AV_PIX_FMT_YUV422P12LE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian + AV_PIX_FMT_YUV422P14BE, ///< planar YUV 4:2:2,28bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian + AV_PIX_FMT_YUV422P14LE, ///< planar YUV 4:2:2,28bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian + AV_PIX_FMT_YUV444P12BE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian + AV_PIX_FMT_YUV444P12LE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian + AV_PIX_FMT_YUV444P14BE, ///< planar YUV 4:4:4,42bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian + AV_PIX_FMT_YUV444P14LE, ///< planar YUV 4:4:4,42bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian + AV_PIX_FMT_GBRP12BE, ///< planar GBR 4:4:4 36bpp, big-endian + AV_PIX_FMT_GBRP12LE, ///< planar GBR 4:4:4 36bpp, little-endian + AV_PIX_FMT_GBRP14BE, ///< planar GBR 4:4:4 42bpp, big-endian + AV_PIX_FMT_GBRP14LE, ///< planar GBR 4:4:4 42bpp, little-endian + AV_PIX_FMT_YUVJ411P, ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV411P and setting color_range + + AV_PIX_FMT_BAYER_BGGR8, ///< bayer, BGBG..(odd line), GRGR..(even line), 8-bit samples + AV_PIX_FMT_BAYER_RGGB8, ///< bayer, RGRG..(odd line), GBGB..(even line), 8-bit samples + AV_PIX_FMT_BAYER_GBRG8, ///< bayer, GBGB..(odd line), RGRG..(even line), 8-bit samples + AV_PIX_FMT_BAYER_GRBG8, ///< bayer, GRGR..(odd line), BGBG..(even line), 8-bit samples + AV_PIX_FMT_BAYER_BGGR16LE, ///< bayer, BGBG..(odd line), GRGR..(even line), 16-bit samples, little-endian + AV_PIX_FMT_BAYER_BGGR16BE, ///< bayer, BGBG..(odd line), GRGR..(even line), 16-bit samples, big-endian + AV_PIX_FMT_BAYER_RGGB16LE, ///< bayer, RGRG..(odd line), GBGB..(even line), 16-bit samples, little-endian + AV_PIX_FMT_BAYER_RGGB16BE, ///< bayer, RGRG..(odd line), GBGB..(even line), 16-bit samples, big-endian + AV_PIX_FMT_BAYER_GBRG16LE, ///< bayer, GBGB..(odd line), RGRG..(even line), 16-bit samples, little-endian + AV_PIX_FMT_BAYER_GBRG16BE, ///< bayer, GBGB..(odd line), RGRG..(even line), 16-bit samples, big-endian + AV_PIX_FMT_BAYER_GRBG16LE, ///< bayer, GRGR..(odd line), BGBG..(even line), 16-bit samples, little-endian + AV_PIX_FMT_BAYER_GRBG16BE, ///< bayer, GRGR..(odd line), BGBG..(even line), 16-bit samples, big-endian + + AV_PIX_FMT_XVMC,///< XVideo Motion Acceleration via common packet passing + + AV_PIX_FMT_YUV440P10LE, ///< planar YUV 4:4:0,20bpp, (1 Cr & Cb sample per 1x2 Y samples), little-endian + AV_PIX_FMT_YUV440P10BE, ///< planar YUV 4:4:0,20bpp, (1 Cr & Cb sample per 1x2 Y samples), big-endian + AV_PIX_FMT_YUV440P12LE, ///< planar YUV 4:4:0,24bpp, (1 Cr & Cb sample per 1x2 Y samples), little-endian + AV_PIX_FMT_YUV440P12BE, ///< planar YUV 4:4:0,24bpp, (1 Cr & Cb sample per 1x2 Y samples), big-endian + AV_PIX_FMT_AYUV64LE, ///< packed AYUV 4:4:4,64bpp (1 Cr & Cb sample per 1x1 Y & A samples), little-endian + AV_PIX_FMT_AYUV64BE, ///< packed AYUV 4:4:4,64bpp (1 Cr & Cb sample per 1x1 Y & A samples), big-endian + + AV_PIX_FMT_VIDEOTOOLBOX, ///< hardware decoding through Videotoolbox + + AV_PIX_FMT_P010LE, ///< like NV12, with 10bpp per component, data in the high bits, zeros in the low bits, little-endian + AV_PIX_FMT_P010BE, ///< like NV12, with 10bpp per component, data in the high bits, zeros in the low bits, big-endian + + AV_PIX_FMT_GBRAP12BE, ///< planar GBR 4:4:4:4 48bpp, big-endian + AV_PIX_FMT_GBRAP12LE, ///< planar GBR 4:4:4:4 48bpp, little-endian + + AV_PIX_FMT_GBRAP10BE, ///< planar GBR 4:4:4:4 40bpp, big-endian + AV_PIX_FMT_GBRAP10LE, ///< planar GBR 4:4:4:4 40bpp, little-endian + + AV_PIX_FMT_MEDIACODEC, ///< hardware decoding through MediaCodec + + AV_PIX_FMT_GRAY12BE, ///< Y , 12bpp, big-endian + AV_PIX_FMT_GRAY12LE, ///< Y , 12bpp, little-endian + AV_PIX_FMT_GRAY10BE, ///< Y , 10bpp, big-endian + AV_PIX_FMT_GRAY10LE, ///< Y , 10bpp, little-endian + + AV_PIX_FMT_P016LE, ///< like NV12, with 16bpp per component, little-endian + AV_PIX_FMT_P016BE, ///< like NV12, with 16bpp per component, big-endian + + /** + * Hardware surfaces for Direct3D11. + * + * This is preferred over the legacy AV_PIX_FMT_D3D11VA_VLD. The new D3D11 + * hwaccel API and filtering support AV_PIX_FMT_D3D11 only. + * + * data[0] contains a ID3D11Texture2D pointer, and data[1] contains the + * texture array index of the frame as intptr_t if the ID3D11Texture2D is + * an array texture (or always 0 if it's a normal texture). + */ + AV_PIX_FMT_D3D11, + + AV_PIX_FMT_GRAY9BE, ///< Y , 9bpp, big-endian + AV_PIX_FMT_GRAY9LE, ///< Y , 9bpp, little-endian + + AV_PIX_FMT_GBRPF32BE, ///< IEEE-754 single precision planar GBR 4:4:4, 96bpp, big-endian + AV_PIX_FMT_GBRPF32LE, ///< IEEE-754 single precision planar GBR 4:4:4, 96bpp, little-endian + AV_PIX_FMT_GBRAPF32BE, ///< IEEE-754 single precision planar GBRA 4:4:4:4, 128bpp, big-endian + AV_PIX_FMT_GBRAPF32LE, ///< IEEE-754 single precision planar GBRA 4:4:4:4, 128bpp, little-endian + + /** + * DRM-managed buffers exposed through PRIME buffer sharing. + * + * data[0] points to an AVDRMFrameDescriptor. + */ + AV_PIX_FMT_DRM_PRIME, + /** + * Hardware surfaces for OpenCL. + * + * data[i] contain 2D image objects (typed in C as cl_mem, used + * in OpenCL as image2d_t) for each plane of the surface. + */ + AV_PIX_FMT_OPENCL, + + AV_PIX_FMT_GRAY14BE, ///< Y , 14bpp, big-endian + AV_PIX_FMT_GRAY14LE, ///< Y , 14bpp, little-endian + + AV_PIX_FMT_GRAYF32BE, ///< IEEE-754 single precision Y, 32bpp, big-endian + AV_PIX_FMT_GRAYF32LE, ///< IEEE-754 single precision Y, 32bpp, little-endian + + AV_PIX_FMT_YUVA422P12BE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), 12b alpha, big-endian + AV_PIX_FMT_YUVA422P12LE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), 12b alpha, little-endian + AV_PIX_FMT_YUVA444P12BE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), 12b alpha, big-endian + AV_PIX_FMT_YUVA444P12LE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), 12b alpha, little-endian + + AV_PIX_FMT_NV24, ///< planar YUV 4:4:4, 24bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V) + AV_PIX_FMT_NV42, ///< as above, but U and V bytes are swapped + + /** + * Vulkan hardware images. + * + * data[0] points to an AVVkFrame + */ + AV_PIX_FMT_VULKAN, + + AV_PIX_FMT_Y210BE, ///< packed YUV 4:2:2 like YUYV422, 20bpp, data in the high bits, big-endian + AV_PIX_FMT_Y210LE, ///< packed YUV 4:2:2 like YUYV422, 20bpp, data in the high bits, little-endian + + AV_PIX_FMT_NB ///< number of pixel formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions +}; + +#if AV_HAVE_BIGENDIAN +# define AV_PIX_FMT_NE(be, le) AV_PIX_FMT_##be +#else +# define AV_PIX_FMT_NE(be, le) AV_PIX_FMT_##le +#endif + +#define AV_PIX_FMT_RGB32 AV_PIX_FMT_NE(ARGB, BGRA) +#define AV_PIX_FMT_RGB32_1 AV_PIX_FMT_NE(RGBA, ABGR) +#define AV_PIX_FMT_BGR32 AV_PIX_FMT_NE(ABGR, RGBA) +#define AV_PIX_FMT_BGR32_1 AV_PIX_FMT_NE(BGRA, ARGB) +#define AV_PIX_FMT_0RGB32 AV_PIX_FMT_NE(0RGB, BGR0) +#define AV_PIX_FMT_0BGR32 AV_PIX_FMT_NE(0BGR, RGB0) + +#define AV_PIX_FMT_GRAY9 AV_PIX_FMT_NE(GRAY9BE, GRAY9LE) +#define AV_PIX_FMT_GRAY10 AV_PIX_FMT_NE(GRAY10BE, GRAY10LE) +#define AV_PIX_FMT_GRAY12 AV_PIX_FMT_NE(GRAY12BE, GRAY12LE) +#define AV_PIX_FMT_GRAY14 AV_PIX_FMT_NE(GRAY14BE, GRAY14LE) +#define AV_PIX_FMT_GRAY16 AV_PIX_FMT_NE(GRAY16BE, GRAY16LE) +#define AV_PIX_FMT_YA16 AV_PIX_FMT_NE(YA16BE, YA16LE) +#define AV_PIX_FMT_RGB48 AV_PIX_FMT_NE(RGB48BE, RGB48LE) +#define AV_PIX_FMT_RGB565 AV_PIX_FMT_NE(RGB565BE, RGB565LE) +#define AV_PIX_FMT_RGB555 AV_PIX_FMT_NE(RGB555BE, RGB555LE) +#define AV_PIX_FMT_RGB444 AV_PIX_FMT_NE(RGB444BE, RGB444LE) +#define AV_PIX_FMT_RGBA64 AV_PIX_FMT_NE(RGBA64BE, RGBA64LE) +#define AV_PIX_FMT_BGR48 AV_PIX_FMT_NE(BGR48BE, BGR48LE) +#define AV_PIX_FMT_BGR565 AV_PIX_FMT_NE(BGR565BE, BGR565LE) +#define AV_PIX_FMT_BGR555 AV_PIX_FMT_NE(BGR555BE, BGR555LE) +#define AV_PIX_FMT_BGR444 AV_PIX_FMT_NE(BGR444BE, BGR444LE) +#define AV_PIX_FMT_BGRA64 AV_PIX_FMT_NE(BGRA64BE, BGRA64LE) + +#define AV_PIX_FMT_YUV420P9 AV_PIX_FMT_NE(YUV420P9BE , YUV420P9LE) +#define AV_PIX_FMT_YUV422P9 AV_PIX_FMT_NE(YUV422P9BE , YUV422P9LE) +#define AV_PIX_FMT_YUV444P9 AV_PIX_FMT_NE(YUV444P9BE , YUV444P9LE) +#define AV_PIX_FMT_YUV420P10 AV_PIX_FMT_NE(YUV420P10BE, YUV420P10LE) +#define AV_PIX_FMT_YUV422P10 AV_PIX_FMT_NE(YUV422P10BE, YUV422P10LE) +#define AV_PIX_FMT_YUV440P10 AV_PIX_FMT_NE(YUV440P10BE, YUV440P10LE) +#define AV_PIX_FMT_YUV444P10 AV_PIX_FMT_NE(YUV444P10BE, YUV444P10LE) +#define AV_PIX_FMT_YUV420P12 AV_PIX_FMT_NE(YUV420P12BE, YUV420P12LE) +#define AV_PIX_FMT_YUV422P12 AV_PIX_FMT_NE(YUV422P12BE, YUV422P12LE) +#define AV_PIX_FMT_YUV440P12 AV_PIX_FMT_NE(YUV440P12BE, YUV440P12LE) +#define AV_PIX_FMT_YUV444P12 AV_PIX_FMT_NE(YUV444P12BE, YUV444P12LE) +#define AV_PIX_FMT_YUV420P14 AV_PIX_FMT_NE(YUV420P14BE, YUV420P14LE) +#define AV_PIX_FMT_YUV422P14 AV_PIX_FMT_NE(YUV422P14BE, YUV422P14LE) +#define AV_PIX_FMT_YUV444P14 AV_PIX_FMT_NE(YUV444P14BE, YUV444P14LE) +#define AV_PIX_FMT_YUV420P16 AV_PIX_FMT_NE(YUV420P16BE, YUV420P16LE) +#define AV_PIX_FMT_YUV422P16 AV_PIX_FMT_NE(YUV422P16BE, YUV422P16LE) +#define AV_PIX_FMT_YUV444P16 AV_PIX_FMT_NE(YUV444P16BE, YUV444P16LE) + +#define AV_PIX_FMT_GBRP9 AV_PIX_FMT_NE(GBRP9BE , GBRP9LE) +#define AV_PIX_FMT_GBRP10 AV_PIX_FMT_NE(GBRP10BE, GBRP10LE) +#define AV_PIX_FMT_GBRP12 AV_PIX_FMT_NE(GBRP12BE, GBRP12LE) +#define AV_PIX_FMT_GBRP14 AV_PIX_FMT_NE(GBRP14BE, GBRP14LE) +#define AV_PIX_FMT_GBRP16 AV_PIX_FMT_NE(GBRP16BE, GBRP16LE) +#define AV_PIX_FMT_GBRAP10 AV_PIX_FMT_NE(GBRAP10BE, GBRAP10LE) +#define AV_PIX_FMT_GBRAP12 AV_PIX_FMT_NE(GBRAP12BE, GBRAP12LE) +#define AV_PIX_FMT_GBRAP16 AV_PIX_FMT_NE(GBRAP16BE, GBRAP16LE) + +#define AV_PIX_FMT_BAYER_BGGR16 AV_PIX_FMT_NE(BAYER_BGGR16BE, BAYER_BGGR16LE) +#define AV_PIX_FMT_BAYER_RGGB16 AV_PIX_FMT_NE(BAYER_RGGB16BE, BAYER_RGGB16LE) +#define AV_PIX_FMT_BAYER_GBRG16 AV_PIX_FMT_NE(BAYER_GBRG16BE, BAYER_GBRG16LE) +#define AV_PIX_FMT_BAYER_GRBG16 AV_PIX_FMT_NE(BAYER_GRBG16BE, BAYER_GRBG16LE) + +#define AV_PIX_FMT_GBRPF32 AV_PIX_FMT_NE(GBRPF32BE, GBRPF32LE) +#define AV_PIX_FMT_GBRAPF32 AV_PIX_FMT_NE(GBRAPF32BE, GBRAPF32LE) + +#define AV_PIX_FMT_GRAYF32 AV_PIX_FMT_NE(GRAYF32BE, GRAYF32LE) + +#define AV_PIX_FMT_YUVA420P9 AV_PIX_FMT_NE(YUVA420P9BE , YUVA420P9LE) +#define AV_PIX_FMT_YUVA422P9 AV_PIX_FMT_NE(YUVA422P9BE , YUVA422P9LE) +#define AV_PIX_FMT_YUVA444P9 AV_PIX_FMT_NE(YUVA444P9BE , YUVA444P9LE) +#define AV_PIX_FMT_YUVA420P10 AV_PIX_FMT_NE(YUVA420P10BE, YUVA420P10LE) +#define AV_PIX_FMT_YUVA422P10 AV_PIX_FMT_NE(YUVA422P10BE, YUVA422P10LE) +#define AV_PIX_FMT_YUVA444P10 AV_PIX_FMT_NE(YUVA444P10BE, YUVA444P10LE) +#define AV_PIX_FMT_YUVA422P12 AV_PIX_FMT_NE(YUVA422P12BE, YUVA422P12LE) +#define AV_PIX_FMT_YUVA444P12 AV_PIX_FMT_NE(YUVA444P12BE, YUVA444P12LE) +#define AV_PIX_FMT_YUVA420P16 AV_PIX_FMT_NE(YUVA420P16BE, YUVA420P16LE) +#define AV_PIX_FMT_YUVA422P16 AV_PIX_FMT_NE(YUVA422P16BE, YUVA422P16LE) +#define AV_PIX_FMT_YUVA444P16 AV_PIX_FMT_NE(YUVA444P16BE, YUVA444P16LE) + +#define AV_PIX_FMT_XYZ12 AV_PIX_FMT_NE(XYZ12BE, XYZ12LE) +#define AV_PIX_FMT_NV20 AV_PIX_FMT_NE(NV20BE, NV20LE) +#define AV_PIX_FMT_AYUV64 AV_PIX_FMT_NE(AYUV64BE, AYUV64LE) +#define AV_PIX_FMT_P010 AV_PIX_FMT_NE(P010BE, P010LE) +#define AV_PIX_FMT_P016 AV_PIX_FMT_NE(P016BE, P016LE) + +#define AV_PIX_FMT_Y210 AV_PIX_FMT_NE(Y210BE, Y210LE) + +/** + * Chromaticity coordinates of the source primaries. + * These values match the ones defined by ISO/IEC 23001-8_2013 § 7.1. + */ +enum AVColorPrimaries { + AVCOL_PRI_RESERVED0 = 0, + AVCOL_PRI_BT709 = 1, ///< also ITU-R BT1361 / IEC 61966-2-4 / SMPTE RP177 Annex B + AVCOL_PRI_UNSPECIFIED = 2, + AVCOL_PRI_RESERVED = 3, + AVCOL_PRI_BT470M = 4, ///< also FCC Title 47 Code of Federal Regulations 73.682 (a)(20) + + AVCOL_PRI_BT470BG = 5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM + AVCOL_PRI_SMPTE170M = 6, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC + AVCOL_PRI_SMPTE240M = 7, ///< functionally identical to above + AVCOL_PRI_FILM = 8, ///< colour filters using Illuminant C + AVCOL_PRI_BT2020 = 9, ///< ITU-R BT2020 + AVCOL_PRI_SMPTE428 = 10, ///< SMPTE ST 428-1 (CIE 1931 XYZ) + AVCOL_PRI_SMPTEST428_1 = AVCOL_PRI_SMPTE428, + AVCOL_PRI_SMPTE431 = 11, ///< SMPTE ST 431-2 (2011) / DCI P3 + AVCOL_PRI_SMPTE432 = 12, ///< SMPTE ST 432-1 (2010) / P3 D65 / Display P3 + AVCOL_PRI_EBU3213 = 22, ///< EBU Tech. 3213-E / JEDEC P22 phosphors + AVCOL_PRI_JEDEC_P22 = AVCOL_PRI_EBU3213, + AVCOL_PRI_NB ///< Not part of ABI +}; + +/** + * Color Transfer Characteristic. + * These values match the ones defined by ISO/IEC 23001-8_2013 § 7.2. + */ +enum AVColorTransferCharacteristic { + AVCOL_TRC_RESERVED0 = 0, + AVCOL_TRC_BT709 = 1, ///< also ITU-R BT1361 + AVCOL_TRC_UNSPECIFIED = 2, + AVCOL_TRC_RESERVED = 3, + AVCOL_TRC_GAMMA22 = 4, ///< also ITU-R BT470M / ITU-R BT1700 625 PAL & SECAM + AVCOL_TRC_GAMMA28 = 5, ///< also ITU-R BT470BG + AVCOL_TRC_SMPTE170M = 6, ///< also ITU-R BT601-6 525 or 625 / ITU-R BT1358 525 or 625 / ITU-R BT1700 NTSC + AVCOL_TRC_SMPTE240M = 7, + AVCOL_TRC_LINEAR = 8, ///< "Linear transfer characteristics" + AVCOL_TRC_LOG = 9, ///< "Logarithmic transfer characteristic (100:1 range)" + AVCOL_TRC_LOG_SQRT = 10, ///< "Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range)" + AVCOL_TRC_IEC61966_2_4 = 11, ///< IEC 61966-2-4 + AVCOL_TRC_BT1361_ECG = 12, ///< ITU-R BT1361 Extended Colour Gamut + AVCOL_TRC_IEC61966_2_1 = 13, ///< IEC 61966-2-1 (sRGB or sYCC) + AVCOL_TRC_BT2020_10 = 14, ///< ITU-R BT2020 for 10-bit system + AVCOL_TRC_BT2020_12 = 15, ///< ITU-R BT2020 for 12-bit system + AVCOL_TRC_SMPTE2084 = 16, ///< SMPTE ST 2084 for 10-, 12-, 14- and 16-bit systems + AVCOL_TRC_SMPTEST2084 = AVCOL_TRC_SMPTE2084, + AVCOL_TRC_SMPTE428 = 17, ///< SMPTE ST 428-1 + AVCOL_TRC_SMPTEST428_1 = AVCOL_TRC_SMPTE428, + AVCOL_TRC_ARIB_STD_B67 = 18, ///< ARIB STD-B67, known as "Hybrid log-gamma" + AVCOL_TRC_NB ///< Not part of ABI +}; + +/** + * YUV colorspace type. + * These values match the ones defined by ISO/IEC 23001-8_2013 § 7.3. + */ +enum AVColorSpace { + AVCOL_SPC_RGB = 0, ///< order of coefficients is actually GBR, also IEC 61966-2-1 (sRGB) + AVCOL_SPC_BT709 = 1, ///< also ITU-R BT1361 / IEC 61966-2-4 xvYCC709 / SMPTE RP177 Annex B + AVCOL_SPC_UNSPECIFIED = 2, + AVCOL_SPC_RESERVED = 3, + AVCOL_SPC_FCC = 4, ///< FCC Title 47 Code of Federal Regulations 73.682 (a)(20) + AVCOL_SPC_BT470BG = 5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM / IEC 61966-2-4 xvYCC601 + AVCOL_SPC_SMPTE170M = 6, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC + AVCOL_SPC_SMPTE240M = 7, ///< functionally identical to above + AVCOL_SPC_YCGCO = 8, ///< Used by Dirac / VC-2 and H.264 FRext, see ITU-T SG16 + AVCOL_SPC_YCOCG = AVCOL_SPC_YCGCO, + AVCOL_SPC_BT2020_NCL = 9, ///< ITU-R BT2020 non-constant luminance system + AVCOL_SPC_BT2020_CL = 10, ///< ITU-R BT2020 constant luminance system + AVCOL_SPC_SMPTE2085 = 11, ///< SMPTE 2085, Y'D'zD'x + AVCOL_SPC_CHROMA_DERIVED_NCL = 12, ///< Chromaticity-derived non-constant luminance system + AVCOL_SPC_CHROMA_DERIVED_CL = 13, ///< Chromaticity-derived constant luminance system + AVCOL_SPC_ICTCP = 14, ///< ITU-R BT.2100-0, ICtCp + AVCOL_SPC_NB ///< Not part of ABI +}; + +/** + * MPEG vs JPEG YUV range. + */ +enum AVColorRange { + AVCOL_RANGE_UNSPECIFIED = 0, + AVCOL_RANGE_MPEG = 1, ///< the normal 219*2^(n-8) "MPEG" YUV ranges + AVCOL_RANGE_JPEG = 2, ///< the normal 2^n-1 "JPEG" YUV ranges + AVCOL_RANGE_NB ///< Not part of ABI +}; + +/** + * Location of chroma samples. + * + * Illustration showing the location of the first (top left) chroma sample of the + * image, the left shows only luma, the right + * shows the location of the chroma sample, the 2 could be imagined to overlay + * each other but are drawn separately due to limitations of ASCII + * + * 1st 2nd 1st 2nd horizontal luma sample positions + * v v v v + * ______ ______ + *1st luma line > |X X ... |3 4 X ... X are luma samples, + * | |1 2 1-6 are possible chroma positions + *2nd luma line > |X X ... |5 6 X ... 0 is undefined/unknown position + */ +enum AVChromaLocation { + AVCHROMA_LOC_UNSPECIFIED = 0, + AVCHROMA_LOC_LEFT = 1, ///< MPEG-2/4 4:2:0, H.264 default for 4:2:0 + AVCHROMA_LOC_CENTER = 2, ///< MPEG-1 4:2:0, JPEG 4:2:0, H.263 4:2:0 + AVCHROMA_LOC_TOPLEFT = 3, ///< ITU-R 601, SMPTE 274M 296M S314M(DV 4:1:1), mpeg2 4:2:2 + AVCHROMA_LOC_TOP = 4, + AVCHROMA_LOC_BOTTOMLEFT = 5, + AVCHROMA_LOC_BOTTOM = 6, + AVCHROMA_LOC_NB ///< Not part of ABI +}; + +#endif /* AVUTIL_PIXFMT_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/random_seed.h b/ThirdParty/ffmpeg/arm/include/libavutil/random_seed.h new file mode 100644 index 000000000..0462a048e --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/random_seed.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2009 Baptiste Coudurier + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_RANDOM_SEED_H +#define AVUTIL_RANDOM_SEED_H + +#include +/** + * @addtogroup lavu_crypto + * @{ + */ + +/** + * Get a seed to use in conjunction with random functions. + * This function tries to provide a good seed at a best effort bases. + * Its possible to call this function multiple times if more bits are needed. + * It can be quite slow, which is why it should only be used as seed for a faster + * PRNG. The quality of the seed depends on the platform. + */ +uint32_t av_get_random_seed(void); + +/** + * @} + */ + +#endif /* AVUTIL_RANDOM_SEED_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/rational.h b/ThirdParty/ffmpeg/arm/include/libavutil/rational.h new file mode 100644 index 000000000..cbb08a0ba --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/rational.h @@ -0,0 +1,220 @@ +/* + * rational numbers + * Copyright (c) 2003 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_math_rational + * Utilties for rational number calculation. + * @author Michael Niedermayer + */ + +#ifndef AVUTIL_RATIONAL_H +#define AVUTIL_RATIONAL_H + +#include +#include +#include "attributes.h" + +/** + * @defgroup lavu_math_rational AVRational + * @ingroup lavu_math + * Rational number calculation. + * + * While rational numbers can be expressed as floating-point numbers, the + * conversion process is a lossy one, so are floating-point operations. On the + * other hand, the nature of FFmpeg demands highly accurate calculation of + * timestamps. This set of rational number utilities serves as a generic + * interface for manipulating rational numbers as pairs of numerators and + * denominators. + * + * Many of the functions that operate on AVRational's have the suffix `_q`, in + * reference to the mathematical symbol "ℚ" (Q) which denotes the set of all + * rational numbers. + * + * @{ + */ + +/** + * Rational number (pair of numerator and denominator). + */ +typedef struct AVRational{ + int num; ///< Numerator + int den; ///< Denominator +} AVRational; + +/** + * Create an AVRational. + * + * Useful for compilers that do not support compound literals. + * + * @note The return value is not reduced. + * @see av_reduce() + */ +static inline AVRational av_make_q(int num, int den) +{ + AVRational r = { num, den }; + return r; +} + +/** + * Compare two rationals. + * + * @param a First rational + * @param b Second rational + * + * @return One of the following values: + * - 0 if `a == b` + * - 1 if `a > b` + * - -1 if `a < b` + * - `INT_MIN` if one of the values is of the form `0 / 0` + */ +static inline int av_cmp_q(AVRational a, AVRational b){ + const int64_t tmp= a.num * (int64_t)b.den - b.num * (int64_t)a.den; + + if(tmp) return (int)((tmp ^ a.den ^ b.den)>>63)|1; + else if(b.den && a.den) return 0; + else if(a.num && b.num) return (a.num>>31) - (b.num>>31); + else return INT_MIN; +} + +/** + * Convert an AVRational to a `double`. + * @param a AVRational to convert + * @return `a` in floating-point form + * @see av_d2q() + */ +static inline double av_q2d(AVRational a){ + return a.num / (double) a.den; +} + +/** + * Reduce a fraction. + * + * This is useful for framerate calculations. + * + * @param[out] dst_num Destination numerator + * @param[out] dst_den Destination denominator + * @param[in] num Source numerator + * @param[in] den Source denominator + * @param[in] max Maximum allowed values for `dst_num` & `dst_den` + * @return 1 if the operation is exact, 0 otherwise + */ +int av_reduce(int *dst_num, int *dst_den, int64_t num, int64_t den, int64_t max); + +/** + * Multiply two rationals. + * @param b First rational + * @param c Second rational + * @return b*c + */ +AVRational av_mul_q(AVRational b, AVRational c) av_const; + +/** + * Divide one rational by another. + * @param b First rational + * @param c Second rational + * @return b/c + */ +AVRational av_div_q(AVRational b, AVRational c) av_const; + +/** + * Add two rationals. + * @param b First rational + * @param c Second rational + * @return b+c + */ +AVRational av_add_q(AVRational b, AVRational c) av_const; + +/** + * Subtract one rational from another. + * @param b First rational + * @param c Second rational + * @return b-c + */ +AVRational av_sub_q(AVRational b, AVRational c) av_const; + +/** + * Invert a rational. + * @param q value + * @return 1 / q + */ +static av_always_inline AVRational av_inv_q(AVRational q) +{ + AVRational r = { q.den, q.num }; + return r; +} + +/** + * Convert a double precision floating point number to a rational. + * + * In case of infinity, the returned value is expressed as `{1, 0}` or + * `{-1, 0}` depending on the sign. + * + * @param d `double` to convert + * @param max Maximum allowed numerator and denominator + * @return `d` in AVRational form + * @see av_q2d() + */ +AVRational av_d2q(double d, int max) av_const; + +/** + * Find which of the two rationals is closer to another rational. + * + * @param q Rational to be compared against + * @param q1,q2 Rationals to be tested + * @return One of the following values: + * - 1 if `q1` is nearer to `q` than `q2` + * - -1 if `q2` is nearer to `q` than `q1` + * - 0 if they have the same distance + */ +int av_nearer_q(AVRational q, AVRational q1, AVRational q2); + +/** + * Find the value in a list of rationals nearest a given reference rational. + * + * @param q Reference rational + * @param q_list Array of rationals terminated by `{0, 0}` + * @return Index of the nearest value found in the array + */ +int av_find_nearest_q_idx(AVRational q, const AVRational* q_list); + +/** + * Convert an AVRational to a IEEE 32-bit `float` expressed in fixed-point + * format. + * + * @param q Rational to be converted + * @return Equivalent floating-point value, expressed as an unsigned 32-bit + * integer. + * @note The returned value is platform-indepedant. + */ +uint32_t av_q2intfloat(AVRational q); + +/** + * Return the best rational so that a and b are multiple of it. + * If the resulting denominator is larger than max_den, return def. + */ +AVRational av_gcd_q(AVRational a, AVRational b, int max_den, AVRational def); + +/** + * @} + */ + +#endif /* AVUTIL_RATIONAL_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/rc4.h b/ThirdParty/ffmpeg/arm/include/libavutil/rc4.h new file mode 100644 index 000000000..029cd2ad5 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/rc4.h @@ -0,0 +1,66 @@ +/* + * RC4 encryption/decryption/pseudo-random number generator + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_RC4_H +#define AVUTIL_RC4_H + +#include + +/** + * @defgroup lavu_rc4 RC4 + * @ingroup lavu_crypto + * @{ + */ + +typedef struct AVRC4 { + uint8_t state[256]; + int x, y; +} AVRC4; + +/** + * Allocate an AVRC4 context. + */ +AVRC4 *av_rc4_alloc(void); + +/** + * @brief Initializes an AVRC4 context. + * + * @param key_bits must be a multiple of 8 + * @param decrypt 0 for encryption, 1 for decryption, currently has no effect + * @return zero on success, negative value otherwise + */ +int av_rc4_init(struct AVRC4 *d, const uint8_t *key, int key_bits, int decrypt); + +/** + * @brief Encrypts / decrypts using the RC4 algorithm. + * + * @param count number of bytes + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst, may be NULL + * @param iv not (yet) used for RC4, should be NULL + * @param decrypt 0 for encryption, 1 for decryption, not (yet) used + */ +void av_rc4_crypt(struct AVRC4 *d, uint8_t *dst, const uint8_t *src, int count, uint8_t *iv, int decrypt); + +/** + * @} + */ + +#endif /* AVUTIL_RC4_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/replaygain.h b/ThirdParty/ffmpeg/arm/include/libavutil/replaygain.h new file mode 100644 index 000000000..b49bf1a3d --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/replaygain.h @@ -0,0 +1,50 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_REPLAYGAIN_H +#define AVUTIL_REPLAYGAIN_H + +#include + +/** + * ReplayGain information (see + * http://wiki.hydrogenaudio.org/index.php?title=ReplayGain_1.0_specification). + * The size of this struct is a part of the public ABI. + */ +typedef struct AVReplayGain { + /** + * Track replay gain in microbels (divide by 100000 to get the value in dB). + * Should be set to INT32_MIN when unknown. + */ + int32_t track_gain; + /** + * Peak track amplitude, with 100000 representing full scale (but values + * may overflow). 0 when unknown. + */ + uint32_t track_peak; + /** + * Same as track_gain, but for the whole album. + */ + int32_t album_gain; + /** + * Same as track_peak, but for the whole album, + */ + uint32_t album_peak; +} AVReplayGain; + +#endif /* AVUTIL_REPLAYGAIN_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/ripemd.h b/ThirdParty/ffmpeg/arm/include/libavutil/ripemd.h new file mode 100644 index 000000000..0db6858ff --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/ripemd.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2007 Michael Niedermayer + * Copyright (C) 2013 James Almer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_ripemd + * Public header for RIPEMD hash function implementation. + */ + +#ifndef AVUTIL_RIPEMD_H +#define AVUTIL_RIPEMD_H + +#include + +#include "attributes.h" +#include "version.h" + +/** + * @defgroup lavu_ripemd RIPEMD + * @ingroup lavu_hash + * RIPEMD hash function implementation. + * + * @{ + */ + +extern const int av_ripemd_size; + +struct AVRIPEMD; + +/** + * Allocate an AVRIPEMD context. + */ +struct AVRIPEMD *av_ripemd_alloc(void); + +/** + * Initialize RIPEMD hashing. + * + * @param context pointer to the function context (of size av_ripemd_size) + * @param bits number of bits in digest (128, 160, 256 or 320 bits) + * @return zero if initialization succeeded, -1 otherwise + */ +int av_ripemd_init(struct AVRIPEMD* context, int bits); + +/** + * Update hash value. + * + * @param context hash function context + * @param data input data to update hash with + * @param len input data length + */ +#if FF_API_CRYPTO_SIZE_T +void av_ripemd_update(struct AVRIPEMD* context, const uint8_t* data, unsigned int len); +#else +void av_ripemd_update(struct AVRIPEMD* context, const uint8_t* data, size_t len); +#endif + +/** + * Finish hashing and output digest value. + * + * @param context hash function context + * @param digest buffer where output digest value is stored + */ +void av_ripemd_final(struct AVRIPEMD* context, uint8_t *digest); + +/** + * @} + */ + +#endif /* AVUTIL_RIPEMD_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/samplefmt.h b/ThirdParty/ffmpeg/arm/include/libavutil/samplefmt.h new file mode 100644 index 000000000..8cd43ae85 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/samplefmt.h @@ -0,0 +1,272 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_SAMPLEFMT_H +#define AVUTIL_SAMPLEFMT_H + +#include + +#include "avutil.h" +#include "attributes.h" + +/** + * @addtogroup lavu_audio + * @{ + * + * @defgroup lavu_sampfmts Audio sample formats + * + * Audio sample format enumeration and related convenience functions. + * @{ + */ + +/** + * Audio sample formats + * + * - The data described by the sample format is always in native-endian order. + * Sample values can be expressed by native C types, hence the lack of a signed + * 24-bit sample format even though it is a common raw audio data format. + * + * - The floating-point formats are based on full volume being in the range + * [-1.0, 1.0]. Any values outside this range are beyond full volume level. + * + * - The data layout as used in av_samples_fill_arrays() and elsewhere in FFmpeg + * (such as AVFrame in libavcodec) is as follows: + * + * @par + * For planar sample formats, each audio channel is in a separate data plane, + * and linesize is the buffer size, in bytes, for a single plane. All data + * planes must be the same size. For packed sample formats, only the first data + * plane is used, and samples for each channel are interleaved. In this case, + * linesize is the buffer size, in bytes, for the 1 plane. + * + */ +enum AVSampleFormat { + AV_SAMPLE_FMT_NONE = -1, + AV_SAMPLE_FMT_U8, ///< unsigned 8 bits + AV_SAMPLE_FMT_S16, ///< signed 16 bits + AV_SAMPLE_FMT_S32, ///< signed 32 bits + AV_SAMPLE_FMT_FLT, ///< float + AV_SAMPLE_FMT_DBL, ///< double + + AV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planar + AV_SAMPLE_FMT_S16P, ///< signed 16 bits, planar + AV_SAMPLE_FMT_S32P, ///< signed 32 bits, planar + AV_SAMPLE_FMT_FLTP, ///< float, planar + AV_SAMPLE_FMT_DBLP, ///< double, planar + AV_SAMPLE_FMT_S64, ///< signed 64 bits + AV_SAMPLE_FMT_S64P, ///< signed 64 bits, planar + + AV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically +}; + +/** + * Return the name of sample_fmt, or NULL if sample_fmt is not + * recognized. + */ +const char *av_get_sample_fmt_name(enum AVSampleFormat sample_fmt); + +/** + * Return a sample format corresponding to name, or AV_SAMPLE_FMT_NONE + * on error. + */ +enum AVSampleFormat av_get_sample_fmt(const char *name); + +/** + * Return the planar<->packed alternative form of the given sample format, or + * AV_SAMPLE_FMT_NONE on error. If the passed sample_fmt is already in the + * requested planar/packed format, the format returned is the same as the + * input. + */ +enum AVSampleFormat av_get_alt_sample_fmt(enum AVSampleFormat sample_fmt, int planar); + +/** + * Get the packed alternative form of the given sample format. + * + * If the passed sample_fmt is already in packed format, the format returned is + * the same as the input. + * + * @return the packed alternative form of the given sample format or + AV_SAMPLE_FMT_NONE on error. + */ +enum AVSampleFormat av_get_packed_sample_fmt(enum AVSampleFormat sample_fmt); + +/** + * Get the planar alternative form of the given sample format. + * + * If the passed sample_fmt is already in planar format, the format returned is + * the same as the input. + * + * @return the planar alternative form of the given sample format or + AV_SAMPLE_FMT_NONE on error. + */ +enum AVSampleFormat av_get_planar_sample_fmt(enum AVSampleFormat sample_fmt); + +/** + * Generate a string corresponding to the sample format with + * sample_fmt, or a header if sample_fmt is negative. + * + * @param buf the buffer where to write the string + * @param buf_size the size of buf + * @param sample_fmt the number of the sample format to print the + * corresponding info string, or a negative value to print the + * corresponding header. + * @return the pointer to the filled buffer or NULL if sample_fmt is + * unknown or in case of other errors + */ +char *av_get_sample_fmt_string(char *buf, int buf_size, enum AVSampleFormat sample_fmt); + +/** + * Return number of bytes per sample. + * + * @param sample_fmt the sample format + * @return number of bytes per sample or zero if unknown for the given + * sample format + */ +int av_get_bytes_per_sample(enum AVSampleFormat sample_fmt); + +/** + * Check if the sample format is planar. + * + * @param sample_fmt the sample format to inspect + * @return 1 if the sample format is planar, 0 if it is interleaved + */ +int av_sample_fmt_is_planar(enum AVSampleFormat sample_fmt); + +/** + * Get the required buffer size for the given audio parameters. + * + * @param[out] linesize calculated linesize, may be NULL + * @param nb_channels the number of channels + * @param nb_samples the number of samples in a single channel + * @param sample_fmt the sample format + * @param align buffer size alignment (0 = default, 1 = no alignment) + * @return required buffer size, or negative error code on failure + */ +int av_samples_get_buffer_size(int *linesize, int nb_channels, int nb_samples, + enum AVSampleFormat sample_fmt, int align); + +/** + * @} + * + * @defgroup lavu_sampmanip Samples manipulation + * + * Functions that manipulate audio samples + * @{ + */ + +/** + * Fill plane data pointers and linesize for samples with sample + * format sample_fmt. + * + * The audio_data array is filled with the pointers to the samples data planes: + * for planar, set the start point of each channel's data within the buffer, + * for packed, set the start point of the entire buffer only. + * + * The value pointed to by linesize is set to the aligned size of each + * channel's data buffer for planar layout, or to the aligned size of the + * buffer for all channels for packed layout. + * + * The buffer in buf must be big enough to contain all the samples + * (use av_samples_get_buffer_size() to compute its minimum size), + * otherwise the audio_data pointers will point to invalid data. + * + * @see enum AVSampleFormat + * The documentation for AVSampleFormat describes the data layout. + * + * @param[out] audio_data array to be filled with the pointer for each channel + * @param[out] linesize calculated linesize, may be NULL + * @param buf the pointer to a buffer containing the samples + * @param nb_channels the number of channels + * @param nb_samples the number of samples in a single channel + * @param sample_fmt the sample format + * @param align buffer size alignment (0 = default, 1 = no alignment) + * @return >=0 on success or a negative error code on failure + * @todo return minimum size in bytes required for the buffer in case + * of success at the next bump + */ +int av_samples_fill_arrays(uint8_t **audio_data, int *linesize, + const uint8_t *buf, + int nb_channels, int nb_samples, + enum AVSampleFormat sample_fmt, int align); + +/** + * Allocate a samples buffer for nb_samples samples, and fill data pointers and + * linesize accordingly. + * The allocated samples buffer can be freed by using av_freep(&audio_data[0]) + * Allocated data will be initialized to silence. + * + * @see enum AVSampleFormat + * The documentation for AVSampleFormat describes the data layout. + * + * @param[out] audio_data array to be filled with the pointer for each channel + * @param[out] linesize aligned size for audio buffer(s), may be NULL + * @param nb_channels number of audio channels + * @param nb_samples number of samples per channel + * @param align buffer size alignment (0 = default, 1 = no alignment) + * @return >=0 on success or a negative error code on failure + * @todo return the size of the allocated buffer in case of success at the next bump + * @see av_samples_fill_arrays() + * @see av_samples_alloc_array_and_samples() + */ +int av_samples_alloc(uint8_t **audio_data, int *linesize, int nb_channels, + int nb_samples, enum AVSampleFormat sample_fmt, int align); + +/** + * Allocate a data pointers array, samples buffer for nb_samples + * samples, and fill data pointers and linesize accordingly. + * + * This is the same as av_samples_alloc(), but also allocates the data + * pointers array. + * + * @see av_samples_alloc() + */ +int av_samples_alloc_array_and_samples(uint8_t ***audio_data, int *linesize, int nb_channels, + int nb_samples, enum AVSampleFormat sample_fmt, int align); + +/** + * Copy samples from src to dst. + * + * @param dst destination array of pointers to data planes + * @param src source array of pointers to data planes + * @param dst_offset offset in samples at which the data will be written to dst + * @param src_offset offset in samples at which the data will be read from src + * @param nb_samples number of samples to be copied + * @param nb_channels number of audio channels + * @param sample_fmt audio sample format + */ +int av_samples_copy(uint8_t **dst, uint8_t * const *src, int dst_offset, + int src_offset, int nb_samples, int nb_channels, + enum AVSampleFormat sample_fmt); + +/** + * Fill an audio buffer with silence. + * + * @param audio_data array of pointers to data planes + * @param offset offset in samples at which to start filling + * @param nb_samples number of samples to fill + * @param nb_channels number of audio channels + * @param sample_fmt audio sample format + */ +int av_samples_set_silence(uint8_t **audio_data, int offset, int nb_samples, + int nb_channels, enum AVSampleFormat sample_fmt); + +/** + * @} + * @} + */ +#endif /* AVUTIL_SAMPLEFMT_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/sha.h b/ThirdParty/ffmpeg/arm/include/libavutil/sha.h new file mode 100644 index 000000000..c0180e572 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/sha.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2007 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_sha + * Public header for SHA-1 & SHA-256 hash function implementations. + */ + +#ifndef AVUTIL_SHA_H +#define AVUTIL_SHA_H + +#include +#include + +#include "attributes.h" +#include "version.h" + +/** + * @defgroup lavu_sha SHA + * @ingroup lavu_hash + * SHA-1 and SHA-256 (Secure Hash Algorithm) hash function implementations. + * + * This module supports the following SHA hash functions: + * + * - SHA-1: 160 bits + * - SHA-224: 224 bits, as a variant of SHA-2 + * - SHA-256: 256 bits, as a variant of SHA-2 + * + * @see For SHA-384, SHA-512, and variants thereof, see @ref lavu_sha512. + * + * @{ + */ + +extern const int av_sha_size; + +struct AVSHA; + +/** + * Allocate an AVSHA context. + */ +struct AVSHA *av_sha_alloc(void); + +/** + * Initialize SHA-1 or SHA-2 hashing. + * + * @param context pointer to the function context (of size av_sha_size) + * @param bits number of bits in digest (SHA-1 - 160 bits, SHA-2 224 or 256 bits) + * @return zero if initialization succeeded, -1 otherwise + */ +int av_sha_init(struct AVSHA* context, int bits); + +/** + * Update hash value. + * + * @param ctx hash function context + * @param data input data to update hash with + * @param len input data length + */ +#if FF_API_CRYPTO_SIZE_T +void av_sha_update(struct AVSHA *ctx, const uint8_t *data, unsigned int len); +#else +void av_sha_update(struct AVSHA *ctx, const uint8_t *data, size_t len); +#endif + +/** + * Finish hashing and output digest value. + * + * @param context hash function context + * @param digest buffer where output digest value is stored + */ +void av_sha_final(struct AVSHA* context, uint8_t *digest); + +/** + * @} + */ + +#endif /* AVUTIL_SHA_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/sha512.h b/ThirdParty/ffmpeg/arm/include/libavutil/sha512.h new file mode 100644 index 000000000..bef714b41 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/sha512.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2007 Michael Niedermayer + * Copyright (C) 2013 James Almer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu_sha512 + * Public header for SHA-512 implementation. + */ + +#ifndef AVUTIL_SHA512_H +#define AVUTIL_SHA512_H + +#include +#include + +#include "attributes.h" +#include "version.h" + +/** + * @defgroup lavu_sha512 SHA-512 + * @ingroup lavu_hash + * SHA-512 (Secure Hash Algorithm) hash function implementations. + * + * This module supports the following SHA-2 hash functions: + * + * - SHA-512/224: 224 bits + * - SHA-512/256: 256 bits + * - SHA-384: 384 bits + * - SHA-512: 512 bits + * + * @see For SHA-1, SHA-256, and variants thereof, see @ref lavu_sha. + * + * @{ + */ + +extern const int av_sha512_size; + +struct AVSHA512; + +/** + * Allocate an AVSHA512 context. + */ +struct AVSHA512 *av_sha512_alloc(void); + +/** + * Initialize SHA-2 512 hashing. + * + * @param context pointer to the function context (of size av_sha512_size) + * @param bits number of bits in digest (224, 256, 384 or 512 bits) + * @return zero if initialization succeeded, -1 otherwise + */ +int av_sha512_init(struct AVSHA512* context, int bits); + +/** + * Update hash value. + * + * @param context hash function context + * @param data input data to update hash with + * @param len input data length + */ +#if FF_API_CRYPTO_SIZE_T +void av_sha512_update(struct AVSHA512* context, const uint8_t* data, unsigned int len); +#else +void av_sha512_update(struct AVSHA512* context, const uint8_t* data, size_t len); +#endif + +/** + * Finish hashing and output digest value. + * + * @param context hash function context + * @param digest buffer where output digest value is stored + */ +void av_sha512_final(struct AVSHA512* context, uint8_t *digest); + +/** + * @} + */ + +#endif /* AVUTIL_SHA512_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/spherical.h b/ThirdParty/ffmpeg/arm/include/libavutil/spherical.h new file mode 100644 index 000000000..cef759cf2 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/spherical.h @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2016 Vittorio Giovara + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Spherical video + */ + +#ifndef AVUTIL_SPHERICAL_H +#define AVUTIL_SPHERICAL_H + +#include +#include + +/** + * @addtogroup lavu_video + * @{ + * + * @defgroup lavu_video_spherical Spherical video mapping + * @{ + */ + +/** + * @addtogroup lavu_video_spherical + * A spherical video file contains surfaces that need to be mapped onto a + * sphere. Depending on how the frame was converted, a different distortion + * transformation or surface recomposition function needs to be applied before + * the video should be mapped and displayed. + */ + +/** + * Projection of the video surface(s) on a sphere. + */ +enum AVSphericalProjection { + /** + * Video represents a sphere mapped on a flat surface using + * equirectangular projection. + */ + AV_SPHERICAL_EQUIRECTANGULAR, + + /** + * Video frame is split into 6 faces of a cube, and arranged on a + * 3x2 layout. Faces are oriented upwards for the front, left, right, + * and back faces. The up face is oriented so the top of the face is + * forwards and the down face is oriented so the top of the face is + * to the back. + */ + AV_SPHERICAL_CUBEMAP, + + /** + * Video represents a portion of a sphere mapped on a flat surface + * using equirectangular projection. The @ref bounding fields indicate + * the position of the current video in a larger surface. + */ + AV_SPHERICAL_EQUIRECTANGULAR_TILE, +}; + +/** + * This structure describes how to handle spherical videos, outlining + * information about projection, initial layout, and any other view modifier. + * + * @note The struct must be allocated with av_spherical_alloc() and + * its size is not a part of the public ABI. + */ +typedef struct AVSphericalMapping { + /** + * Projection type. + */ + enum AVSphericalProjection projection; + + /** + * @name Initial orientation + * @{ + * There fields describe additional rotations applied to the sphere after + * the video frame is mapped onto it. The sphere is rotated around the + * viewer, who remains stationary. The order of transformation is always + * yaw, followed by pitch, and finally by roll. + * + * The coordinate system matches the one defined in OpenGL, where the + * forward vector (z) is coming out of screen, and it is equivalent to + * a rotation matrix of R = r_y(yaw) * r_x(pitch) * r_z(roll). + * + * A positive yaw rotates the portion of the sphere in front of the viewer + * toward their right. A positive pitch rotates the portion of the sphere + * in front of the viewer upwards. A positive roll tilts the portion of + * the sphere in front of the viewer to the viewer's right. + * + * These values are exported as 16.16 fixed point. + * + * See this equirectangular projection as example: + * + * @code{.unparsed} + * Yaw + * -180 0 180 + * 90 +-------------+-------------+ 180 + * | | | up + * P | | | y| forward + * i | ^ | | /z + * t 0 +-------------X-------------+ 0 Roll | / + * c | | | | / + * h | | | 0|/_____right + * | | | x + * -90 +-------------+-------------+ -180 + * + * X - the default camera center + * ^ - the default up vector + * @endcode + */ + int32_t yaw; ///< Rotation around the up vector [-180, 180]. + int32_t pitch; ///< Rotation around the right vector [-90, 90]. + int32_t roll; ///< Rotation around the forward vector [-180, 180]. + /** + * @} + */ + + /** + * @name Bounding rectangle + * @anchor bounding + * @{ + * These fields indicate the location of the current tile, and where + * it should be mapped relative to the original surface. They are + * exported as 0.32 fixed point, and can be converted to classic + * pixel values with av_spherical_bounds(). + * + * @code{.unparsed} + * +----------------+----------+ + * | |bound_top | + * | +--------+ | + * | bound_left |tile | | + * +<---------->| |<--->+bound_right + * | +--------+ | + * | | | + * | bound_bottom| | + * +----------------+----------+ + * @endcode + * + * If needed, the original video surface dimensions can be derived + * by adding the current stream or frame size to the related bounds, + * like in the following example: + * + * @code{c} + * original_width = tile->width + bound_left + bound_right; + * original_height = tile->height + bound_top + bound_bottom; + * @endcode + * + * @note These values are valid only for the tiled equirectangular + * projection type (@ref AV_SPHERICAL_EQUIRECTANGULAR_TILE), + * and should be ignored in all other cases. + */ + uint32_t bound_left; ///< Distance from the left edge + uint32_t bound_top; ///< Distance from the top edge + uint32_t bound_right; ///< Distance from the right edge + uint32_t bound_bottom; ///< Distance from the bottom edge + /** + * @} + */ + + /** + * Number of pixels to pad from the edge of each cube face. + * + * @note This value is valid for only for the cubemap projection type + * (@ref AV_SPHERICAL_CUBEMAP), and should be ignored in all other + * cases. + */ + uint32_t padding; +} AVSphericalMapping; + +/** + * Allocate a AVSphericalVideo structure and initialize its fields to default + * values. + * + * @return the newly allocated struct or NULL on failure + */ +AVSphericalMapping *av_spherical_alloc(size_t *size); + +/** + * Convert the @ref bounding fields from an AVSphericalVideo + * from 0.32 fixed point to pixels. + * + * @param map The AVSphericalVideo map to read bound values from. + * @param width Width of the current frame or stream. + * @param height Height of the current frame or stream. + * @param left Pixels from the left edge. + * @param top Pixels from the top edge. + * @param right Pixels from the right edge. + * @param bottom Pixels from the bottom edge. + */ +void av_spherical_tile_bounds(const AVSphericalMapping *map, + size_t width, size_t height, + size_t *left, size_t *top, + size_t *right, size_t *bottom); + +/** + * Provide a human-readable name of a given AVSphericalProjection. + * + * @param projection The input AVSphericalProjection. + * + * @return The name of the AVSphericalProjection, or "unknown". + */ +const char *av_spherical_projection_name(enum AVSphericalProjection projection); + +/** + * Get the AVSphericalProjection form a human-readable name. + * + * @param name The input string. + * + * @return The AVSphericalProjection value, or -1 if not found. + */ +int av_spherical_from_name(const char *name); +/** + * @} + * @} + */ + +#endif /* AVUTIL_SPHERICAL_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/stereo3d.h b/ThirdParty/ffmpeg/arm/include/libavutil/stereo3d.h new file mode 100644 index 000000000..d421aac2a --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/stereo3d.h @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2013 Vittorio Giovara + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Stereoscopic video + */ + +#ifndef AVUTIL_STEREO3D_H +#define AVUTIL_STEREO3D_H + +#include + +#include "frame.h" + +/** + * @addtogroup lavu_video + * @{ + * + * @defgroup lavu_video_stereo3d Stereo3D types and functions + * @{ + */ + +/** + * @addtogroup lavu_video_stereo3d + * A stereoscopic video file consists in multiple views embedded in a single + * frame, usually describing two views of a scene. This file describes all + * possible codec-independent view arrangements. + * */ + +/** + * List of possible 3D Types + */ +enum AVStereo3DType { + /** + * Video is not stereoscopic (and metadata has to be there). + */ + AV_STEREO3D_2D, + + /** + * Views are next to each other. + * + * @code{.unparsed} + * LLLLRRRR + * LLLLRRRR + * LLLLRRRR + * ... + * @endcode + */ + AV_STEREO3D_SIDEBYSIDE, + + /** + * Views are on top of each other. + * + * @code{.unparsed} + * LLLLLLLL + * LLLLLLLL + * RRRRRRRR + * RRRRRRRR + * @endcode + */ + AV_STEREO3D_TOPBOTTOM, + + /** + * Views are alternated temporally. + * + * @code{.unparsed} + * frame0 frame1 frame2 ... + * LLLLLLLL RRRRRRRR LLLLLLLL + * LLLLLLLL RRRRRRRR LLLLLLLL + * LLLLLLLL RRRRRRRR LLLLLLLL + * ... ... ... + * @endcode + */ + AV_STEREO3D_FRAMESEQUENCE, + + /** + * Views are packed in a checkerboard-like structure per pixel. + * + * @code{.unparsed} + * LRLRLRLR + * RLRLRLRL + * LRLRLRLR + * ... + * @endcode + */ + AV_STEREO3D_CHECKERBOARD, + + /** + * Views are next to each other, but when upscaling + * apply a checkerboard pattern. + * + * @code{.unparsed} + * LLLLRRRR L L L L R R R R + * LLLLRRRR => L L L L R R R R + * LLLLRRRR L L L L R R R R + * LLLLRRRR L L L L R R R R + * @endcode + */ + AV_STEREO3D_SIDEBYSIDE_QUINCUNX, + + /** + * Views are packed per line, as if interlaced. + * + * @code{.unparsed} + * LLLLLLLL + * RRRRRRRR + * LLLLLLLL + * ... + * @endcode + */ + AV_STEREO3D_LINES, + + /** + * Views are packed per column. + * + * @code{.unparsed} + * LRLRLRLR + * LRLRLRLR + * LRLRLRLR + * ... + * @endcode + */ + AV_STEREO3D_COLUMNS, +}; + +/** + * List of possible view types. + */ +enum AVStereo3DView { + /** + * Frame contains two packed views. + */ + AV_STEREO3D_VIEW_PACKED, + + /** + * Frame contains only the left view. + */ + AV_STEREO3D_VIEW_LEFT, + + /** + * Frame contains only the right view. + */ + AV_STEREO3D_VIEW_RIGHT, +}; + +/** + * Inverted views, Right/Bottom represents the left view. + */ +#define AV_STEREO3D_FLAG_INVERT (1 << 0) + +/** + * Stereo 3D type: this structure describes how two videos are packed + * within a single video surface, with additional information as needed. + * + * @note The struct must be allocated with av_stereo3d_alloc() and + * its size is not a part of the public ABI. + */ +typedef struct AVStereo3D { + /** + * How views are packed within the video. + */ + enum AVStereo3DType type; + + /** + * Additional information about the frame packing. + */ + int flags; + + /** + * Determines which views are packed. + */ + enum AVStereo3DView view; +} AVStereo3D; + +/** + * Allocate an AVStereo3D structure and set its fields to default values. + * The resulting struct can be freed using av_freep(). + * + * @return An AVStereo3D filled with default values or NULL on failure. + */ +AVStereo3D *av_stereo3d_alloc(void); + +/** + * Allocate a complete AVFrameSideData and add it to the frame. + * + * @param frame The frame which side data is added to. + * + * @return The AVStereo3D structure to be filled by caller. + */ +AVStereo3D *av_stereo3d_create_side_data(AVFrame *frame); + +/** + * Provide a human-readable name of a given stereo3d type. + * + * @param type The input stereo3d type value. + * + * @return The name of the stereo3d value, or "unknown". + */ +const char *av_stereo3d_type_name(unsigned int type); + +/** + * Get the AVStereo3DType form a human-readable name. + * + * @param name The input string. + * + * @return The AVStereo3DType value, or -1 if not found. + */ +int av_stereo3d_from_name(const char *name); + +/** + * @} + * @} + */ + +#endif /* AVUTIL_STEREO3D_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/tea.h b/ThirdParty/ffmpeg/arm/include/libavutil/tea.h new file mode 100644 index 000000000..dd929bdaf --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/tea.h @@ -0,0 +1,71 @@ +/* + * A 32-bit implementation of the TEA algorithm + * Copyright (c) 2015 Vesselin Bontchev + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_TEA_H +#define AVUTIL_TEA_H + +#include + +/** + * @file + * @brief Public header for libavutil TEA algorithm + * @defgroup lavu_tea TEA + * @ingroup lavu_crypto + * @{ + */ + +extern const int av_tea_size; + +struct AVTEA; + +/** + * Allocate an AVTEA context + * To free the struct: av_free(ptr) + */ +struct AVTEA *av_tea_alloc(void); + +/** + * Initialize an AVTEA context. + * + * @param ctx an AVTEA context + * @param key a key of 16 bytes used for encryption/decryption + * @param rounds the number of rounds in TEA (64 is the "standard") + */ +void av_tea_init(struct AVTEA *ctx, const uint8_t key[16], int rounds); + +/** + * Encrypt or decrypt a buffer using a previously initialized context. + * + * @param ctx an AVTEA context + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param count number of 8 byte blocks + * @param iv initialization vector for CBC mode, if NULL then ECB will be used + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_tea_crypt(struct AVTEA *ctx, uint8_t *dst, const uint8_t *src, + int count, uint8_t *iv, int decrypt); + +/** + * @} + */ + +#endif /* AVUTIL_TEA_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/threadmessage.h b/ThirdParty/ffmpeg/arm/include/libavutil/threadmessage.h new file mode 100644 index 000000000..42ce655f3 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/threadmessage.h @@ -0,0 +1,115 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_THREADMESSAGE_H +#define AVUTIL_THREADMESSAGE_H + +typedef struct AVThreadMessageQueue AVThreadMessageQueue; + +typedef enum AVThreadMessageFlags { + + /** + * Perform non-blocking operation. + * If this flag is set, send and recv operations are non-blocking and + * return AVERROR(EAGAIN) immediately if they can not proceed. + */ + AV_THREAD_MESSAGE_NONBLOCK = 1, + +} AVThreadMessageFlags; + +/** + * Allocate a new message queue. + * + * @param mq pointer to the message queue + * @param nelem maximum number of elements in the queue + * @param elsize size of each element in the queue + * @return >=0 for success; <0 for error, in particular AVERROR(ENOSYS) if + * lavu was built without thread support + */ +int av_thread_message_queue_alloc(AVThreadMessageQueue **mq, + unsigned nelem, + unsigned elsize); + +/** + * Free a message queue. + * + * The message queue must no longer be in use by another thread. + */ +void av_thread_message_queue_free(AVThreadMessageQueue **mq); + +/** + * Send a message on the queue. + */ +int av_thread_message_queue_send(AVThreadMessageQueue *mq, + void *msg, + unsigned flags); + +/** + * Receive a message from the queue. + */ +int av_thread_message_queue_recv(AVThreadMessageQueue *mq, + void *msg, + unsigned flags); + +/** + * Set the sending error code. + * + * If the error code is set to non-zero, av_thread_message_queue_send() will + * return it immediately. Conventional values, such as AVERROR_EOF or + * AVERROR(EAGAIN), can be used to cause the sending thread to stop or + * suspend its operation. + */ +void av_thread_message_queue_set_err_send(AVThreadMessageQueue *mq, + int err); + +/** + * Set the receiving error code. + * + * If the error code is set to non-zero, av_thread_message_queue_recv() will + * return it immediately when there are no longer available messages. + * Conventional values, such as AVERROR_EOF or AVERROR(EAGAIN), can be used + * to cause the receiving thread to stop or suspend its operation. + */ +void av_thread_message_queue_set_err_recv(AVThreadMessageQueue *mq, + int err); + +/** + * Set the optional free message callback function which will be called if an + * operation is removing messages from the queue. + */ +void av_thread_message_queue_set_free_func(AVThreadMessageQueue *mq, + void (*free_func)(void *msg)); + +/** + * Return the current number of messages in the queue. + * + * @return the current number of messages or AVERROR(ENOSYS) if lavu was built + * without thread support + */ +int av_thread_message_queue_nb_elems(AVThreadMessageQueue *mq); + +/** + * Flush the message queue + * + * This function is mostly equivalent to reading and free-ing every message + * except that it will be done in a single operation (no lock/unlock between + * reads). + */ +void av_thread_message_flush(AVThreadMessageQueue *mq); + +#endif /* AVUTIL_THREADMESSAGE_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/time.h b/ThirdParty/ffmpeg/arm/include/libavutil/time.h new file mode 100644 index 000000000..dc169b064 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/time.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2000-2003 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_TIME_H +#define AVUTIL_TIME_H + +#include + +/** + * Get the current time in microseconds. + */ +int64_t av_gettime(void); + +/** + * Get the current time in microseconds since some unspecified starting point. + * On platforms that support it, the time comes from a monotonic clock + * This property makes this time source ideal for measuring relative time. + * The returned values may not be monotonic on platforms where a monotonic + * clock is not available. + */ +int64_t av_gettime_relative(void); + +/** + * Indicates with a boolean result if the av_gettime_relative() time source + * is monotonic. + */ +int av_gettime_relative_is_monotonic(void); + +/** + * Sleep for a period of time. Although the duration is expressed in + * microseconds, the actual delay may be rounded to the precision of the + * system timer. + * + * @param usec Number of microseconds to sleep. + * @return zero on success or (negative) error code. + */ +int av_usleep(unsigned usec); + +#endif /* AVUTIL_TIME_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/timecode.h b/ThirdParty/ffmpeg/arm/include/libavutil/timecode.h new file mode 100644 index 000000000..37c1361bc --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/timecode.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2006 Smartjog S.A.S, Baptiste Coudurier + * Copyright (c) 2011-2012 Smartjog S.A.S, Clément Bœsch + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Timecode helpers header + */ + +#ifndef AVUTIL_TIMECODE_H +#define AVUTIL_TIMECODE_H + +#include +#include "rational.h" + +#define AV_TIMECODE_STR_SIZE 23 + +enum AVTimecodeFlag { + AV_TIMECODE_FLAG_DROPFRAME = 1<<0, ///< timecode is drop frame + AV_TIMECODE_FLAG_24HOURSMAX = 1<<1, ///< timecode wraps after 24 hours + AV_TIMECODE_FLAG_ALLOWNEGATIVE = 1<<2, ///< negative time values are allowed +}; + +typedef struct { + int start; ///< timecode frame start (first base frame number) + uint32_t flags; ///< flags such as drop frame, +24 hours support, ... + AVRational rate; ///< frame rate in rational form + unsigned fps; ///< frame per second; must be consistent with the rate field +} AVTimecode; + +/** + * Adjust frame number for NTSC drop frame time code. + * + * @param framenum frame number to adjust + * @param fps frame per second, 30 or 60 + * @return adjusted frame number + * @warning adjustment is only valid in NTSC 29.97 and 59.94 + */ +int av_timecode_adjust_ntsc_framenum2(int framenum, int fps); + +/** + * Convert frame number to SMPTE 12M binary representation. + * + * @param tc timecode data correctly initialized + * @param framenum frame number + * @return the SMPTE binary representation + * + * @note Frame number adjustment is automatically done in case of drop timecode, + * you do NOT have to call av_timecode_adjust_ntsc_framenum2(). + * @note The frame number is relative to tc->start. + * @note Color frame (CF), binary group flags (BGF) and biphase mark polarity + * correction (PC) bits are set to zero. + */ +uint32_t av_timecode_get_smpte_from_framenum(const AVTimecode *tc, int framenum); + +/** + * Load timecode string in buf. + * + * @param buf destination buffer, must be at least AV_TIMECODE_STR_SIZE long + * @param tc timecode data correctly initialized + * @param framenum frame number + * @return the buf parameter + * + * @note Timecode representation can be a negative timecode and have more than + * 24 hours, but will only be honored if the flags are correctly set. + * @note The frame number is relative to tc->start. + */ +char *av_timecode_make_string(const AVTimecode *tc, char *buf, int framenum); + +/** + * Get the timecode string from the SMPTE timecode format. + * + * @param buf destination buffer, must be at least AV_TIMECODE_STR_SIZE long + * @param tcsmpte the 32-bit SMPTE timecode + * @param prevent_df prevent the use of a drop flag when it is known the DF bit + * is arbitrary + * @return the buf parameter + */ +char *av_timecode_make_smpte_tc_string(char *buf, uint32_t tcsmpte, int prevent_df); + +/** + * Get the timecode string from the 25-bit timecode format (MPEG GOP format). + * + * @param buf destination buffer, must be at least AV_TIMECODE_STR_SIZE long + * @param tc25bit the 25-bits timecode + * @return the buf parameter + */ +char *av_timecode_make_mpeg_tc_string(char *buf, uint32_t tc25bit); + +/** + * Init a timecode struct with the passed parameters. + * + * @param log_ctx a pointer to an arbitrary struct of which the first field + * is a pointer to an AVClass struct (used for av_log) + * @param tc pointer to an allocated AVTimecode + * @param rate frame rate in rational form + * @param flags miscellaneous flags such as drop frame, +24 hours, ... + * (see AVTimecodeFlag) + * @param frame_start the first frame number + * @return 0 on success, AVERROR otherwise + */ +int av_timecode_init(AVTimecode *tc, AVRational rate, int flags, int frame_start, void *log_ctx); + +/** + * Parse timecode representation (hh:mm:ss[:;.]ff). + * + * @param log_ctx a pointer to an arbitrary struct of which the first field is a + * pointer to an AVClass struct (used for av_log). + * @param tc pointer to an allocated AVTimecode + * @param rate frame rate in rational form + * @param str timecode string which will determine the frame start + * @return 0 on success, AVERROR otherwise + */ +int av_timecode_init_from_string(AVTimecode *tc, AVRational rate, const char *str, void *log_ctx); + +/** + * Check if the timecode feature is available for the given frame rate + * + * @return 0 if supported, <0 otherwise + */ +int av_timecode_check_frame_rate(AVRational rate); + +#endif /* AVUTIL_TIMECODE_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/timestamp.h b/ThirdParty/ffmpeg/arm/include/libavutil/timestamp.h new file mode 100644 index 000000000..e082f01b4 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/timestamp.h @@ -0,0 +1,78 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * timestamp utils, mostly useful for debugging/logging purposes + */ + +#ifndef AVUTIL_TIMESTAMP_H +#define AVUTIL_TIMESTAMP_H + +#include "common.h" + +#if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) && !defined(PRId64) +#error missing -D__STDC_FORMAT_MACROS / #define __STDC_FORMAT_MACROS +#endif + +#define AV_TS_MAX_STRING_SIZE 32 + +/** + * Fill the provided buffer with a string containing a timestamp + * representation. + * + * @param buf a buffer with size in bytes of at least AV_TS_MAX_STRING_SIZE + * @param ts the timestamp to represent + * @return the buffer in input + */ +static inline char *av_ts_make_string(char *buf, int64_t ts) +{ + if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS"); + else snprintf(buf, AV_TS_MAX_STRING_SIZE, "%" PRId64, ts); + return buf; +} + +/** + * Convenience macro, the return value should be used only directly in + * function arguments but never stand-alone. + */ +#define av_ts2str(ts) av_ts_make_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts) + +/** + * Fill the provided buffer with a string containing a timestamp time + * representation. + * + * @param buf a buffer with size in bytes of at least AV_TS_MAX_STRING_SIZE + * @param ts the timestamp to represent + * @param tb the timebase of the timestamp + * @return the buffer in input + */ +static inline char *av_ts_make_time_string(char *buf, int64_t ts, AVRational *tb) +{ + if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS"); + else snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts); + return buf; +} + +/** + * Convenience macro, the return value should be used only directly in + * function arguments but never stand-alone. + */ +#define av_ts2timestr(ts, tb) av_ts_make_time_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts, tb) + +#endif /* AVUTIL_TIMESTAMP_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/tree.h b/ThirdParty/ffmpeg/arm/include/libavutil/tree.h new file mode 100644 index 000000000..d5e0aebfb --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/tree.h @@ -0,0 +1,138 @@ +/* + * copyright (c) 2006 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * A tree container. + * @author Michael Niedermayer + */ + +#ifndef AVUTIL_TREE_H +#define AVUTIL_TREE_H + +#include "attributes.h" +#include "version.h" + +/** + * @addtogroup lavu_tree AVTree + * @ingroup lavu_data + * + * Low-complexity tree container + * + * Insertion, removal, finding equal, largest which is smaller than and + * smallest which is larger than, all have O(log n) worst-case complexity. + * @{ + */ + + +struct AVTreeNode; +extern const int av_tree_node_size; + +/** + * Allocate an AVTreeNode. + */ +struct AVTreeNode *av_tree_node_alloc(void); + +/** + * Find an element. + * @param root a pointer to the root node of the tree + * @param next If next is not NULL, then next[0] will contain the previous + * element and next[1] the next element. If either does not exist, + * then the corresponding entry in next is unchanged. + * @param cmp compare function used to compare elements in the tree, + * API identical to that of Standard C's qsort + * It is guaranteed that the first and only the first argument to cmp() + * will be the key parameter to av_tree_find(), thus it could if the + * user wants, be a different type (like an opaque context). + * @return An element with cmp(key, elem) == 0 or NULL if no such element + * exists in the tree. + */ +void *av_tree_find(const struct AVTreeNode *root, void *key, + int (*cmp)(const void *key, const void *b), void *next[2]); + +/** + * Insert or remove an element. + * + * If *next is NULL, then the supplied element will be removed if it exists. + * If *next is non-NULL, then the supplied element will be inserted, unless + * it already exists in the tree. + * + * @param rootp A pointer to a pointer to the root node of the tree; note that + * the root node can change during insertions, this is required + * to keep the tree balanced. + * @param key pointer to the element key to insert in the tree + * @param next Used to allocate and free AVTreeNodes. For insertion the user + * must set it to an allocated and zeroed object of at least + * av_tree_node_size bytes size. av_tree_insert() will set it to + * NULL if it has been consumed. + * For deleting elements *next is set to NULL by the user and + * av_tree_insert() will set it to the AVTreeNode which was + * used for the removed element. + * This allows the use of flat arrays, which have + * lower overhead compared to many malloced elements. + * You might want to define a function like: + * @code + * void *tree_insert(struct AVTreeNode **rootp, void *key, + * int (*cmp)(void *key, const void *b), + * AVTreeNode **next) + * { + * if (!*next) + * *next = av_mallocz(av_tree_node_size); + * return av_tree_insert(rootp, key, cmp, next); + * } + * void *tree_remove(struct AVTreeNode **rootp, void *key, + * int (*cmp)(void *key, const void *b, AVTreeNode **next)) + * { + * av_freep(next); + * return av_tree_insert(rootp, key, cmp, next); + * } + * @endcode + * @param cmp compare function used to compare elements in the tree, API identical + * to that of Standard C's qsort + * @return If no insertion happened, the found element; if an insertion or + * removal happened, then either key or NULL will be returned. + * Which one it is depends on the tree state and the implementation. You + * should make no assumptions that it's one or the other in the code. + */ +void *av_tree_insert(struct AVTreeNode **rootp, void *key, + int (*cmp)(const void *key, const void *b), + struct AVTreeNode **next); + +void av_tree_destroy(struct AVTreeNode *t); + +/** + * Apply enu(opaque, &elem) to all the elements in the tree in a given range. + * + * @param cmp a comparison function that returns < 0 for an element below the + * range, > 0 for an element above the range and == 0 for an + * element inside the range + * + * @note The cmp function should use the same ordering used to construct the + * tree. + */ +void av_tree_enumerate(struct AVTreeNode *t, void *opaque, + int (*cmp)(void *opaque, void *elem), + int (*enu)(void *opaque, void *elem)); + +/** + * @} + */ + +#endif /* AVUTIL_TREE_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/twofish.h b/ThirdParty/ffmpeg/arm/include/libavutil/twofish.h new file mode 100644 index 000000000..813cfecdf --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/twofish.h @@ -0,0 +1,70 @@ +/* + * An implementation of the TwoFish algorithm + * Copyright (c) 2015 Supraja Meedinti + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_TWOFISH_H +#define AVUTIL_TWOFISH_H + +#include + + +/** + * @file + * @brief Public header for libavutil TWOFISH algorithm + * @defgroup lavu_twofish TWOFISH + * @ingroup lavu_crypto + * @{ + */ + +extern const int av_twofish_size; + +struct AVTWOFISH; + +/** + * Allocate an AVTWOFISH context + * To free the struct: av_free(ptr) + */ +struct AVTWOFISH *av_twofish_alloc(void); + +/** + * Initialize an AVTWOFISH context. + * + * @param ctx an AVTWOFISH context + * @param key a key of size ranging from 1 to 32 bytes used for encryption/decryption + * @param key_bits number of keybits: 128, 192, 256 If less than the required, padded with zeroes to nearest valid value; return value is 0 if key_bits is 128/192/256, -1 if less than 0, 1 otherwise + */ +int av_twofish_init(struct AVTWOFISH *ctx, const uint8_t *key, int key_bits); + +/** + * Encrypt or decrypt a buffer using a previously initialized context + * + * @param ctx an AVTWOFISH context + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param count number of 16 byte blocks + * @paran iv initialization vector for CBC mode, NULL for ECB mode + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_twofish_crypt(struct AVTWOFISH *ctx, uint8_t *dst, const uint8_t *src, int count, uint8_t* iv, int decrypt); + +/** + * @} + */ +#endif /* AVUTIL_TWOFISH_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/tx.h b/ThirdParty/ffmpeg/arm/include/libavutil/tx.h new file mode 100644 index 000000000..418e8ec1e --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/tx.h @@ -0,0 +1,117 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_TX_H +#define AVUTIL_TX_H + +#include +#include + +typedef struct AVTXContext AVTXContext; + +typedef struct AVComplexFloat { + float re, im; +} AVComplexFloat; + +typedef struct AVComplexDouble { + double re, im; +} AVComplexDouble; + +typedef struct AVComplexInt32 { + int32_t re, im; +} AVComplexInt32; + +enum AVTXType { + /** + * Standard complex to complex FFT with sample data type AVComplexFloat. + * Output is not 1/len normalized. Scaling currently unsupported. + * The stride parameter is ignored. + */ + AV_TX_FLOAT_FFT = 0, + /** + * Standard MDCT with sample data type of float and a scale type of + * float. Length is the frame size, not the window size (which is 2x frame) + * For forward transforms, the stride specifies the spacing between each + * sample in the output array in bytes. The input must be a flat array. + * For inverse transforms, the stride specifies the spacing between each + * sample in the input array in bytes. The output will be a flat array. + * Stride must be a non-zero multiple of sizeof(float). + */ + AV_TX_FLOAT_MDCT = 1, + /** + * Same as AV_TX_FLOAT_FFT with a data type of AVComplexDouble. + */ + AV_TX_DOUBLE_FFT = 2, + /** + * Same as AV_TX_FLOAT_MDCT with data and scale type of double. + * Stride must be a non-zero multiple of sizeof(double). + */ + AV_TX_DOUBLE_MDCT = 3, + /** + * Same as AV_TX_FLOAT_FFT with a data type of AVComplexInt32. + */ + AV_TX_INT32_FFT = 4, + /** + * Same as AV_TX_FLOAT_MDCT with data type of int32_t and scale type of float. + * Only scale values less than or equal to 1.0 are supported. + * Stride must be a non-zero multiple of sizeof(int32_t). + */ + AV_TX_INT32_MDCT = 5, +}; + +/** + * Function pointer to a function to perform the transform. + * + * @note Using a different context than the one allocated during av_tx_init() + * is not allowed. + * + * @param s the transform context + * @param out the output array + * @param in the input array + * @param stride the input or output stride in bytes + * + * The out and in arrays must be aligned to the maximum required by the CPU + * architecture. + * The stride must follow the constraints the transform type has specified. + */ +typedef void (*av_tx_fn)(AVTXContext *s, void *out, void *in, ptrdiff_t stride); + +/** + * Initialize a transform context with the given configuration + * Currently power of two lengths from 2 to 131072 are supported, along with + * any length decomposable to a power of two and either 3, 5 or 15. + * + * @param ctx the context to allocate, will be NULL on error + * @param tx pointer to the transform function pointer to set + * @param type type the type of transform + * @param inv whether to do an inverse or a forward transform + * @param len the size of the transform in samples + * @param scale pointer to the value to scale the output if supported by type + * @param flags currently unused + * + * @return 0 on success, negative error code on failure + */ +int av_tx_init(AVTXContext **ctx, av_tx_fn *tx, enum AVTXType type, + int inv, int len, const void *scale, uint64_t flags); + +/** + * Frees a context and sets ctx to NULL, does nothing when ctx == NULL + */ +void av_tx_uninit(AVTXContext **ctx); + +#endif /* AVUTIL_TX_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/version.h b/ThirdParty/ffmpeg/arm/include/libavutil/version.h new file mode 100644 index 000000000..0ff722fbf --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/version.h @@ -0,0 +1,139 @@ +/* + * copyright (c) 2003 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @ingroup lavu + * Libavutil version macros + */ + +#ifndef AVUTIL_VERSION_H +#define AVUTIL_VERSION_H + +#include "macros.h" + +/** + * @addtogroup version_utils + * + * Useful to check and match library version in order to maintain + * backward compatibility. + * + * The FFmpeg libraries follow a versioning sheme very similar to + * Semantic Versioning (http://semver.org/) + * The difference is that the component called PATCH is called MICRO in FFmpeg + * and its value is reset to 100 instead of 0 to keep it above or equal to 100. + * Also we do not increase MICRO for every bugfix or change in git master. + * + * Prior to FFmpeg 3.2 point releases did not change any lib version number to + * avoid aliassing different git master checkouts. + * Starting with FFmpeg 3.2, the released library versions will occupy + * a separate MAJOR.MINOR that is not used on the master development branch. + * That is if we branch a release of master 55.10.123 we will bump to 55.11.100 + * for the release and master will continue at 55.12.100 after it. Each new + * point release will then bump the MICRO improving the usefulness of the lib + * versions. + * + * @{ + */ + +#define AV_VERSION_INT(a, b, c) ((a)<<16 | (b)<<8 | (c)) +#define AV_VERSION_DOT(a, b, c) a ##.## b ##.## c +#define AV_VERSION(a, b, c) AV_VERSION_DOT(a, b, c) + +/** + * Extract version components from the full ::AV_VERSION_INT int as returned + * by functions like ::avformat_version() and ::avcodec_version() + */ +#define AV_VERSION_MAJOR(a) ((a) >> 16) +#define AV_VERSION_MINOR(a) (((a) & 0x00FF00) >> 8) +#define AV_VERSION_MICRO(a) ((a) & 0xFF) + +/** + * @} + */ + +/** + * @defgroup lavu_ver Version and Build diagnostics + * + * Macros and function useful to check at compiletime and at runtime + * which version of libavutil is in use. + * + * @{ + */ + +#define LIBAVUTIL_VERSION_MAJOR 56 +#define LIBAVUTIL_VERSION_MINOR 51 +#define LIBAVUTIL_VERSION_MICRO 100 + +#define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ + LIBAVUTIL_VERSION_MINOR, \ + LIBAVUTIL_VERSION_MICRO) +#define LIBAVUTIL_VERSION AV_VERSION(LIBAVUTIL_VERSION_MAJOR, \ + LIBAVUTIL_VERSION_MINOR, \ + LIBAVUTIL_VERSION_MICRO) +#define LIBAVUTIL_BUILD LIBAVUTIL_VERSION_INT + +#define LIBAVUTIL_IDENT "Lavu" AV_STRINGIFY(LIBAVUTIL_VERSION) + +/** + * @defgroup lavu_depr_guards Deprecation Guards + * FF_API_* defines may be placed below to indicate public API that will be + * dropped at a future version bump. The defines themselves are not part of + * the public API and may change, break or disappear at any time. + * + * @note, when bumping the major version it is recommended to manually + * disable each FF_API_* in its own commit instead of disabling them all + * at once through the bump. This improves the git bisect-ability of the change. + * + * @{ + */ + +#ifndef FF_API_VAAPI +#define FF_API_VAAPI (LIBAVUTIL_VERSION_MAJOR < 57) +#endif +#ifndef FF_API_FRAME_QP +#define FF_API_FRAME_QP (LIBAVUTIL_VERSION_MAJOR < 57) +#endif +#ifndef FF_API_PLUS1_MINUS1 +#define FF_API_PLUS1_MINUS1 (LIBAVUTIL_VERSION_MAJOR < 57) +#endif +#ifndef FF_API_ERROR_FRAME +#define FF_API_ERROR_FRAME (LIBAVUTIL_VERSION_MAJOR < 57) +#endif +#ifndef FF_API_PKT_PTS +#define FF_API_PKT_PTS (LIBAVUTIL_VERSION_MAJOR < 57) +#endif +#ifndef FF_API_CRYPTO_SIZE_T +#define FF_API_CRYPTO_SIZE_T (LIBAVUTIL_VERSION_MAJOR < 57) +#endif +#ifndef FF_API_FRAME_GET_SET +#define FF_API_FRAME_GET_SET (LIBAVUTIL_VERSION_MAJOR < 57) +#endif +#ifndef FF_API_PSEUDOPAL +#define FF_API_PSEUDOPAL (LIBAVUTIL_VERSION_MAJOR < 57) +#endif + + +/** + * @} + * @} + */ + +#endif /* AVUTIL_VERSION_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/video_enc_params.h b/ThirdParty/ffmpeg/arm/include/libavutil/video_enc_params.h new file mode 100644 index 000000000..43fa44315 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/video_enc_params.h @@ -0,0 +1,163 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_VIDEO_ENC_PARAMS_H +#define AVUTIL_VIDEO_ENC_PARAMS_H + +#include +#include + +#include "libavutil/avassert.h" +#include "libavutil/frame.h" + +enum AVVideoEncParamsType { + AV_VIDEO_ENC_PARAMS_NONE = -1, + /** + * VP9 stores: + * - per-frame base (luma AC) quantizer index, exported as AVVideoEncParams.qp + * - deltas for luma DC, chroma AC and chroma DC, exported in the + * corresponding entries in AVVideoEncParams.delta_qp + * - per-segment delta, exported as for each block as AVVideoBlockParams.delta_qp + * + * To compute the resulting quantizer index for a block: + * - for luma AC, add the base qp and the per-block delta_qp, saturating to + * unsigned 8-bit. + * - for luma DC and chroma AC/DC, add the corresponding + * AVVideoBlockParams.delta_qp to the luma AC index, again saturating to + * unsigned 8-bit. + */ + AV_VIDEO_ENC_PARAMS_VP9, + + /** + * H.264 stores: + * - in PPS (per-picture): + * * initial QP_Y (luma) value, exported as AVVideoEncParams.qp + * * delta(s) for chroma QP values (same for both, or each separately), + * exported as in the corresponding entries in AVVideoEncParams.delta_qp + * - per-slice QP delta, not exported directly, added to the per-MB value + * - per-MB delta; not exported directly; the final per-MB quantizer + * parameter - QP_Y - minus the value in AVVideoEncParams.qp is exported + * as AVVideoBlockParams.qp_delta. + */ + AV_VIDEO_ENC_PARAMS_H264, +}; + +/** + * Video encoding parameters for a given frame. This struct is allocated along + * with an optional array of per-block AVVideoBlockParams descriptors. + * Must be allocated with av_video_enc_params_alloc(). + */ +typedef struct AVVideoEncParams { + /** + * Number of blocks in the array. + * + * May be 0, in which case no per-block information is present. In this case + * the values of blocks_offset / block_size are unspecified and should not + * be accessed. + */ + unsigned int nb_blocks; + /** + * Offset in bytes from the beginning of this structure at which the array + * of blocks starts. + */ + size_t blocks_offset; + /* + * Size of each block in bytes. May not match sizeof(AVVideoBlockParams). + */ + size_t block_size; + + /** + * Type of the parameters (the codec they are used with). + */ + enum AVVideoEncParamsType type; + + /** + * Base quantisation parameter for the frame. The final quantiser for a + * given block in a given plane is obtained from this value, possibly + * combined with {@code delta_qp} and the per-block delta in a manner + * documented for each type. + */ + int32_t qp; + + /** + * Quantisation parameter offset from the base (per-frame) qp for a given + * plane (first index) and AC/DC coefficients (second index). + */ + int32_t delta_qp[4][2]; +} AVVideoEncParams; + +/** + * Data structure for storing block-level encoding information. + * It is allocated as a part of AVVideoEncParams and should be retrieved with + * av_video_enc_params_block(). + * + * sizeof(AVVideoBlockParams) is not a part of the ABI and new fields may be + * added to it. + */ +typedef struct AVVideoBlockParams { + /** + * Distance in luma pixels from the top-left corner of the visible frame + * to the top-left corner of the block. + * Can be negative if top/right padding is present on the coded frame. + */ + int src_x, src_y; + /** + * Width and height of the block in luma pixels. + */ + int w, h; + + /** + * Difference between this block's final quantization parameter and the + * corresponding per-frame value. + */ + int32_t delta_qp; +} AVVideoBlockParams; + +/* + * Get the block at the specified {@code idx}. Must be between 0 and nb_blocks. + */ +static av_always_inline AVVideoBlockParams* +av_video_enc_params_block(AVVideoEncParams *par, unsigned int idx) +{ + av_assert0(idx < par->nb_blocks); + return (AVVideoBlockParams *)((uint8_t *)par + par->blocks_offset + + idx * par->block_size); +} + +/** + * Allocates memory for AVVideoEncParams of the given type, plus an array of + * {@code nb_blocks} AVVideoBlockParams and initializes the variables. Can be + * freed with a normal av_free() call. + * + * @param out_size if non-NULL, the size in bytes of the resulting data array is + * written here. + */ +AVVideoEncParams *av_video_enc_params_alloc(enum AVVideoEncParamsType type, + unsigned int nb_blocks, size_t *out_size); + +/** + * Allocates memory for AVEncodeInfoFrame plus an array of + * {@code nb_blocks} AVEncodeInfoBlock in the given AVFrame {@code frame} + * as AVFrameSideData of type AV_FRAME_DATA_ENCODE_INFO + * and initializes the variables. + */ +AVVideoEncParams* +av_video_enc_params_create_side_data(AVFrame *frame, enum AVVideoEncParamsType type, + unsigned int nb_blocks); + +#endif /* AVUTIL_VIDEO_ENC_PARAMS_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libavutil/xtea.h b/ThirdParty/ffmpeg/arm/include/libavutil/xtea.h new file mode 100644 index 000000000..735427c10 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libavutil/xtea.h @@ -0,0 +1,94 @@ +/* + * A 32-bit implementation of the XTEA algorithm + * Copyright (c) 2012 Samuel Pitoiset + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_XTEA_H +#define AVUTIL_XTEA_H + +#include + +/** + * @file + * @brief Public header for libavutil XTEA algorithm + * @defgroup lavu_xtea XTEA + * @ingroup lavu_crypto + * @{ + */ + +typedef struct AVXTEA { + uint32_t key[16]; +} AVXTEA; + +/** + * Allocate an AVXTEA context. + */ +AVXTEA *av_xtea_alloc(void); + +/** + * Initialize an AVXTEA context. + * + * @param ctx an AVXTEA context + * @param key a key of 16 bytes used for encryption/decryption, + * interpreted as big endian 32 bit numbers + */ +void av_xtea_init(struct AVXTEA *ctx, const uint8_t key[16]); + +/** + * Initialize an AVXTEA context. + * + * @param ctx an AVXTEA context + * @param key a key of 16 bytes used for encryption/decryption, + * interpreted as little endian 32 bit numbers + */ +void av_xtea_le_init(struct AVXTEA *ctx, const uint8_t key[16]); + +/** + * Encrypt or decrypt a buffer using a previously initialized context, + * in big endian format. + * + * @param ctx an AVXTEA context + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param count number of 8 byte blocks + * @param iv initialization vector for CBC mode, if NULL then ECB will be used + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_xtea_crypt(struct AVXTEA *ctx, uint8_t *dst, const uint8_t *src, + int count, uint8_t *iv, int decrypt); + +/** + * Encrypt or decrypt a buffer using a previously initialized context, + * in little endian format. + * + * @param ctx an AVXTEA context + * @param dst destination array, can be equal to src + * @param src source array, can be equal to dst + * @param count number of 8 byte blocks + * @param iv initialization vector for CBC mode, if NULL then ECB will be used + * @param decrypt 0 for encryption, 1 for decryption + */ +void av_xtea_le_crypt(struct AVXTEA *ctx, uint8_t *dst, const uint8_t *src, + int count, uint8_t *iv, int decrypt); + +/** + * @} + */ + +#endif /* AVUTIL_XTEA_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libswresample/swresample.h b/ThirdParty/ffmpeg/arm/include/libswresample/swresample.h new file mode 100644 index 000000000..c7b84fbca --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libswresample/swresample.h @@ -0,0 +1,579 @@ +/* + * Copyright (C) 2011-2013 Michael Niedermayer (michaelni@gmx.at) + * + * This file is part of libswresample + * + * libswresample 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. + * + * libswresample 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 libswresample; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SWRESAMPLE_SWRESAMPLE_H +#define SWRESAMPLE_SWRESAMPLE_H + +/** + * @file + * @ingroup lswr + * libswresample public header + */ + +/** + * @defgroup lswr libswresample + * @{ + * + * Audio resampling, sample format conversion and mixing library. + * + * Interaction with lswr is done through SwrContext, which is + * allocated with swr_alloc() or swr_alloc_set_opts(). It is opaque, so all parameters + * must be set with the @ref avoptions API. + * + * The first thing you will need to do in order to use lswr is to allocate + * SwrContext. This can be done with swr_alloc() or swr_alloc_set_opts(). If you + * are using the former, you must set options through the @ref avoptions API. + * The latter function provides the same feature, but it allows you to set some + * common options in the same statement. + * + * For example the following code will setup conversion from planar float sample + * format to interleaved signed 16-bit integer, downsampling from 48kHz to + * 44.1kHz and downmixing from 5.1 channels to stereo (using the default mixing + * matrix). This is using the swr_alloc() function. + * @code + * SwrContext *swr = swr_alloc(); + * av_opt_set_channel_layout(swr, "in_channel_layout", AV_CH_LAYOUT_5POINT1, 0); + * av_opt_set_channel_layout(swr, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0); + * av_opt_set_int(swr, "in_sample_rate", 48000, 0); + * av_opt_set_int(swr, "out_sample_rate", 44100, 0); + * av_opt_set_sample_fmt(swr, "in_sample_fmt", AV_SAMPLE_FMT_FLTP, 0); + * av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); + * @endcode + * + * The same job can be done using swr_alloc_set_opts() as well: + * @code + * SwrContext *swr = swr_alloc_set_opts(NULL, // we're allocating a new context + * AV_CH_LAYOUT_STEREO, // out_ch_layout + * AV_SAMPLE_FMT_S16, // out_sample_fmt + * 44100, // out_sample_rate + * AV_CH_LAYOUT_5POINT1, // in_ch_layout + * AV_SAMPLE_FMT_FLTP, // in_sample_fmt + * 48000, // in_sample_rate + * 0, // log_offset + * NULL); // log_ctx + * @endcode + * + * Once all values have been set, it must be initialized with swr_init(). If + * you need to change the conversion parameters, you can change the parameters + * using @ref AVOptions, as described above in the first example; or by using + * swr_alloc_set_opts(), but with the first argument the allocated context. + * You must then call swr_init() again. + * + * The conversion itself is done by repeatedly calling swr_convert(). + * Note that the samples may get buffered in swr if you provide insufficient + * output space or if sample rate conversion is done, which requires "future" + * samples. Samples that do not require future input can be retrieved at any + * time by using swr_convert() (in_count can be set to 0). + * At the end of conversion the resampling buffer can be flushed by calling + * swr_convert() with NULL in and 0 in_count. + * + * The samples used in the conversion process can be managed with the libavutil + * @ref lavu_sampmanip "samples manipulation" API, including av_samples_alloc() + * function used in the following example. + * + * The delay between input and output, can at any time be found by using + * swr_get_delay(). + * + * The following code demonstrates the conversion loop assuming the parameters + * from above and caller-defined functions get_input() and handle_output(): + * @code + * uint8_t **input; + * int in_samples; + * + * while (get_input(&input, &in_samples)) { + * uint8_t *output; + * int out_samples = av_rescale_rnd(swr_get_delay(swr, 48000) + + * in_samples, 44100, 48000, AV_ROUND_UP); + * av_samples_alloc(&output, NULL, 2, out_samples, + * AV_SAMPLE_FMT_S16, 0); + * out_samples = swr_convert(swr, &output, out_samples, + * input, in_samples); + * handle_output(output, out_samples); + * av_freep(&output); + * } + * @endcode + * + * When the conversion is finished, the conversion + * context and everything associated with it must be freed with swr_free(). + * A swr_close() function is also available, but it exists mainly for + * compatibility with libavresample, and is not required to be called. + * + * There will be no memory leak if the data is not completely flushed before + * swr_free(). + */ + +#include +#include "libavutil/channel_layout.h" +#include "libavutil/frame.h" +#include "libavutil/samplefmt.h" + +#include "libswresample/version.h" + +/** + * @name Option constants + * These constants are used for the @ref avoptions interface for lswr. + * @{ + * + */ + +#define SWR_FLAG_RESAMPLE 1 ///< Force resampling even if equal sample rate +//TODO use int resample ? +//long term TODO can we enable this dynamically? + +/** Dithering algorithms */ +enum SwrDitherType { + SWR_DITHER_NONE = 0, + SWR_DITHER_RECTANGULAR, + SWR_DITHER_TRIANGULAR, + SWR_DITHER_TRIANGULAR_HIGHPASS, + + SWR_DITHER_NS = 64, ///< not part of API/ABI + SWR_DITHER_NS_LIPSHITZ, + SWR_DITHER_NS_F_WEIGHTED, + SWR_DITHER_NS_MODIFIED_E_WEIGHTED, + SWR_DITHER_NS_IMPROVED_E_WEIGHTED, + SWR_DITHER_NS_SHIBATA, + SWR_DITHER_NS_LOW_SHIBATA, + SWR_DITHER_NS_HIGH_SHIBATA, + SWR_DITHER_NB, ///< not part of API/ABI +}; + +/** Resampling Engines */ +enum SwrEngine { + SWR_ENGINE_SWR, /**< SW Resampler */ + SWR_ENGINE_SOXR, /**< SoX Resampler */ + SWR_ENGINE_NB, ///< not part of API/ABI +}; + +/** Resampling Filter Types */ +enum SwrFilterType { + SWR_FILTER_TYPE_CUBIC, /**< Cubic */ + SWR_FILTER_TYPE_BLACKMAN_NUTTALL, /**< Blackman Nuttall windowed sinc */ + SWR_FILTER_TYPE_KAISER, /**< Kaiser windowed sinc */ +}; + +/** + * @} + */ + +/** + * The libswresample context. Unlike libavcodec and libavformat, this structure + * is opaque. This means that if you would like to set options, you must use + * the @ref avoptions API and cannot directly set values to members of the + * structure. + */ +typedef struct SwrContext SwrContext; + +/** + * Get the AVClass for SwrContext. It can be used in combination with + * AV_OPT_SEARCH_FAKE_OBJ for examining options. + * + * @see av_opt_find(). + * @return the AVClass of SwrContext + */ +const AVClass *swr_get_class(void); + +/** + * @name SwrContext constructor functions + * @{ + */ + +/** + * Allocate SwrContext. + * + * If you use this function you will need to set the parameters (manually or + * with swr_alloc_set_opts()) before calling swr_init(). + * + * @see swr_alloc_set_opts(), swr_init(), swr_free() + * @return NULL on error, allocated context otherwise + */ +struct SwrContext *swr_alloc(void); + +/** + * Initialize context after user parameters have been set. + * @note The context must be configured using the AVOption API. + * + * @see av_opt_set_int() + * @see av_opt_set_dict() + * + * @param[in,out] s Swr context to initialize + * @return AVERROR error code in case of failure. + */ +int swr_init(struct SwrContext *s); + +/** + * Check whether an swr context has been initialized or not. + * + * @param[in] s Swr context to check + * @see swr_init() + * @return positive if it has been initialized, 0 if not initialized + */ +int swr_is_initialized(struct SwrContext *s); + +/** + * Allocate SwrContext if needed and set/reset common parameters. + * + * This function does not require s to be allocated with swr_alloc(). On the + * other hand, swr_alloc() can use swr_alloc_set_opts() to set the parameters + * on the allocated context. + * + * @param s existing Swr context if available, or NULL if not + * @param out_ch_layout output channel layout (AV_CH_LAYOUT_*) + * @param out_sample_fmt output sample format (AV_SAMPLE_FMT_*). + * @param out_sample_rate output sample rate (frequency in Hz) + * @param in_ch_layout input channel layout (AV_CH_LAYOUT_*) + * @param in_sample_fmt input sample format (AV_SAMPLE_FMT_*). + * @param in_sample_rate input sample rate (frequency in Hz) + * @param log_offset logging level offset + * @param log_ctx parent logging context, can be NULL + * + * @see swr_init(), swr_free() + * @return NULL on error, allocated context otherwise + */ +struct SwrContext *swr_alloc_set_opts(struct SwrContext *s, + int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate, + int64_t in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate, + int log_offset, void *log_ctx); + +/** + * @} + * + * @name SwrContext destructor functions + * @{ + */ + +/** + * Free the given SwrContext and set the pointer to NULL. + * + * @param[in] s a pointer to a pointer to Swr context + */ +void swr_free(struct SwrContext **s); + +/** + * Closes the context so that swr_is_initialized() returns 0. + * + * The context can be brought back to life by running swr_init(), + * swr_init() can also be used without swr_close(). + * This function is mainly provided for simplifying the usecase + * where one tries to support libavresample and libswresample. + * + * @param[in,out] s Swr context to be closed + */ +void swr_close(struct SwrContext *s); + +/** + * @} + * + * @name Core conversion functions + * @{ + */ + +/** Convert audio. + * + * in and in_count can be set to 0 to flush the last few samples out at the + * end. + * + * If more input is provided than output space, then the input will be buffered. + * You can avoid this buffering by using swr_get_out_samples() to retrieve an + * upper bound on the required number of output samples for the given number of + * input samples. Conversion will run directly without copying whenever possible. + * + * @param s allocated Swr context, with parameters set + * @param out output buffers, only the first one need be set in case of packed audio + * @param out_count amount of space available for output in samples per channel + * @param in input buffers, only the first one need to be set in case of packed audio + * @param in_count number of input samples available in one channel + * + * @return number of samples output per channel, negative value on error + */ +int swr_convert(struct SwrContext *s, uint8_t **out, int out_count, + const uint8_t **in , int in_count); + +/** + * Convert the next timestamp from input to output + * timestamps are in 1/(in_sample_rate * out_sample_rate) units. + * + * @note There are 2 slightly differently behaving modes. + * @li When automatic timestamp compensation is not used, (min_compensation >= FLT_MAX) + * in this case timestamps will be passed through with delays compensated + * @li When automatic timestamp compensation is used, (min_compensation < FLT_MAX) + * in this case the output timestamps will match output sample numbers. + * See ffmpeg-resampler(1) for the two modes of compensation. + * + * @param s[in] initialized Swr context + * @param pts[in] timestamp for the next input sample, INT64_MIN if unknown + * @see swr_set_compensation(), swr_drop_output(), and swr_inject_silence() are + * function used internally for timestamp compensation. + * @return the output timestamp for the next output sample + */ +int64_t swr_next_pts(struct SwrContext *s, int64_t pts); + +/** + * @} + * + * @name Low-level option setting functions + * These functons provide a means to set low-level options that is not possible + * with the AVOption API. + * @{ + */ + +/** + * Activate resampling compensation ("soft" compensation). This function is + * internally called when needed in swr_next_pts(). + * + * @param[in,out] s allocated Swr context. If it is not initialized, + * or SWR_FLAG_RESAMPLE is not set, swr_init() is + * called with the flag set. + * @param[in] sample_delta delta in PTS per sample + * @param[in] compensation_distance number of samples to compensate for + * @return >= 0 on success, AVERROR error codes if: + * @li @c s is NULL, + * @li @c compensation_distance is less than 0, + * @li @c compensation_distance is 0 but sample_delta is not, + * @li compensation unsupported by resampler, or + * @li swr_init() fails when called. + */ +int swr_set_compensation(struct SwrContext *s, int sample_delta, int compensation_distance); + +/** + * Set a customized input channel mapping. + * + * @param[in,out] s allocated Swr context, not yet initialized + * @param[in] channel_map customized input channel mapping (array of channel + * indexes, -1 for a muted channel) + * @return >= 0 on success, or AVERROR error code in case of failure. + */ +int swr_set_channel_mapping(struct SwrContext *s, const int *channel_map); + +/** + * Generate a channel mixing matrix. + * + * This function is the one used internally by libswresample for building the + * default mixing matrix. It is made public just as a utility function for + * building custom matrices. + * + * @param in_layout input channel layout + * @param out_layout output channel layout + * @param center_mix_level mix level for the center channel + * @param surround_mix_level mix level for the surround channel(s) + * @param lfe_mix_level mix level for the low-frequency effects channel + * @param rematrix_maxval if 1.0, coefficients will be normalized to prevent + * overflow. if INT_MAX, coefficients will not be + * normalized. + * @param[out] matrix mixing coefficients; matrix[i + stride * o] is + * the weight of input channel i in output channel o. + * @param stride distance between adjacent input channels in the + * matrix array + * @param matrix_encoding matrixed stereo downmix mode (e.g. dplii) + * @param log_ctx parent logging context, can be NULL + * @return 0 on success, negative AVERROR code on failure + */ +int swr_build_matrix(uint64_t in_layout, uint64_t out_layout, + double center_mix_level, double surround_mix_level, + double lfe_mix_level, double rematrix_maxval, + double rematrix_volume, double *matrix, + int stride, enum AVMatrixEncoding matrix_encoding, + void *log_ctx); + +/** + * Set a customized remix matrix. + * + * @param s allocated Swr context, not yet initialized + * @param matrix remix coefficients; matrix[i + stride * o] is + * the weight of input channel i in output channel o + * @param stride offset between lines of the matrix + * @return >= 0 on success, or AVERROR error code in case of failure. + */ +int swr_set_matrix(struct SwrContext *s, const double *matrix, int stride); + +/** + * @} + * + * @name Sample handling functions + * @{ + */ + +/** + * Drops the specified number of output samples. + * + * This function, along with swr_inject_silence(), is called by swr_next_pts() + * if needed for "hard" compensation. + * + * @param s allocated Swr context + * @param count number of samples to be dropped + * + * @return >= 0 on success, or a negative AVERROR code on failure + */ +int swr_drop_output(struct SwrContext *s, int count); + +/** + * Injects the specified number of silence samples. + * + * This function, along with swr_drop_output(), is called by swr_next_pts() + * if needed for "hard" compensation. + * + * @param s allocated Swr context + * @param count number of samples to be dropped + * + * @return >= 0 on success, or a negative AVERROR code on failure + */ +int swr_inject_silence(struct SwrContext *s, int count); + +/** + * Gets the delay the next input sample will experience relative to the next output sample. + * + * Swresample can buffer data if more input has been provided than available + * output space, also converting between sample rates needs a delay. + * This function returns the sum of all such delays. + * The exact delay is not necessarily an integer value in either input or + * output sample rate. Especially when downsampling by a large value, the + * output sample rate may be a poor choice to represent the delay, similarly + * for upsampling and the input sample rate. + * + * @param s swr context + * @param base timebase in which the returned delay will be: + * @li if it's set to 1 the returned delay is in seconds + * @li if it's set to 1000 the returned delay is in milliseconds + * @li if it's set to the input sample rate then the returned + * delay is in input samples + * @li if it's set to the output sample rate then the returned + * delay is in output samples + * @li if it's the least common multiple of in_sample_rate and + * out_sample_rate then an exact rounding-free delay will be + * returned + * @returns the delay in 1 / @c base units. + */ +int64_t swr_get_delay(struct SwrContext *s, int64_t base); + +/** + * Find an upper bound on the number of samples that the next swr_convert + * call will output, if called with in_samples of input samples. This + * depends on the internal state, and anything changing the internal state + * (like further swr_convert() calls) will may change the number of samples + * swr_get_out_samples() returns for the same number of input samples. + * + * @param in_samples number of input samples. + * @note any call to swr_inject_silence(), swr_convert(), swr_next_pts() + * or swr_set_compensation() invalidates this limit + * @note it is recommended to pass the correct available buffer size + * to all functions like swr_convert() even if swr_get_out_samples() + * indicates that less would be used. + * @returns an upper bound on the number of samples that the next swr_convert + * will output or a negative value to indicate an error + */ +int swr_get_out_samples(struct SwrContext *s, int in_samples); + +/** + * @} + * + * @name Configuration accessors + * @{ + */ + +/** + * Return the @ref LIBSWRESAMPLE_VERSION_INT constant. + * + * This is useful to check if the build-time libswresample has the same version + * as the run-time one. + * + * @returns the unsigned int-typed version + */ +unsigned swresample_version(void); + +/** + * Return the swr build-time configuration. + * + * @returns the build-time @c ./configure flags + */ +const char *swresample_configuration(void); + +/** + * Return the swr license. + * + * @returns the license of libswresample, determined at build-time + */ +const char *swresample_license(void); + +/** + * @} + * + * @name AVFrame based API + * @{ + */ + +/** + * Convert the samples in the input AVFrame and write them to the output AVFrame. + * + * Input and output AVFrames must have channel_layout, sample_rate and format set. + * + * If the output AVFrame does not have the data pointers allocated the nb_samples + * field will be set using av_frame_get_buffer() + * is called to allocate the frame. + * + * The output AVFrame can be NULL or have fewer allocated samples than required. + * In this case, any remaining samples not written to the output will be added + * to an internal FIFO buffer, to be returned at the next call to this function + * or to swr_convert(). + * + * If converting sample rate, there may be data remaining in the internal + * resampling delay buffer. swr_get_delay() tells the number of + * remaining samples. To get this data as output, call this function or + * swr_convert() with NULL input. + * + * If the SwrContext configuration does not match the output and + * input AVFrame settings the conversion does not take place and depending on + * which AVFrame is not matching AVERROR_OUTPUT_CHANGED, AVERROR_INPUT_CHANGED + * or the result of a bitwise-OR of them is returned. + * + * @see swr_delay() + * @see swr_convert() + * @see swr_get_delay() + * + * @param swr audio resample context + * @param output output AVFrame + * @param input input AVFrame + * @return 0 on success, AVERROR on failure or nonmatching + * configuration. + */ +int swr_convert_frame(SwrContext *swr, + AVFrame *output, const AVFrame *input); + +/** + * Configure or reconfigure the SwrContext using the information + * provided by the AVFrames. + * + * The original resampling context is reset even on failure. + * The function calls swr_close() internally if the context is open. + * + * @see swr_close(); + * + * @param swr audio resample context + * @param output output AVFrame + * @param input input AVFrame + * @return 0 on success, AVERROR on failure. + */ +int swr_config_frame(SwrContext *swr, const AVFrame *out, const AVFrame *in); + +/** + * @} + * @} + */ + +#endif /* SWRESAMPLE_SWRESAMPLE_H */ diff --git a/ThirdParty/ffmpeg/arm/include/libswresample/version.h b/ThirdParty/ffmpeg/arm/include/libswresample/version.h new file mode 100644 index 000000000..257739195 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/include/libswresample/version.h @@ -0,0 +1,45 @@ +/* + * Version macros. + * + * This file is part of libswresample + * + * libswresample 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. + * + * libswresample 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 libswresample; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SWRESAMPLE_VERSION_H +#define SWRESAMPLE_VERSION_H + +/** + * @file + * Libswresample version macros + */ + +#include "libavutil/avutil.h" + +#define LIBSWRESAMPLE_VERSION_MAJOR 3 +#define LIBSWRESAMPLE_VERSION_MINOR 7 +#define LIBSWRESAMPLE_VERSION_MICRO 100 + +#define LIBSWRESAMPLE_VERSION_INT AV_VERSION_INT(LIBSWRESAMPLE_VERSION_MAJOR, \ + LIBSWRESAMPLE_VERSION_MINOR, \ + LIBSWRESAMPLE_VERSION_MICRO) +#define LIBSWRESAMPLE_VERSION AV_VERSION(LIBSWRESAMPLE_VERSION_MAJOR, \ + LIBSWRESAMPLE_VERSION_MINOR, \ + LIBSWRESAMPLE_VERSION_MICRO) +#define LIBSWRESAMPLE_BUILD LIBSWRESAMPLE_VERSION_INT + +#define LIBSWRESAMPLE_IDENT "SwR" AV_STRINGIFY(LIBSWRESAMPLE_VERSION) + +#endif /* SWRESAMPLE_VERSION_H */ diff --git a/ThirdParty/ffmpeg/arm/lib/libavcodec.a b/ThirdParty/ffmpeg/arm/lib/libavcodec.a new file mode 100644 index 000000000..1143936ae Binary files /dev/null and b/ThirdParty/ffmpeg/arm/lib/libavcodec.a differ diff --git a/ThirdParty/ffmpeg/arm/lib/libavformat.a b/ThirdParty/ffmpeg/arm/lib/libavformat.a new file mode 100644 index 000000000..6ae9afe36 Binary files /dev/null and b/ThirdParty/ffmpeg/arm/lib/libavformat.a differ diff --git a/ThirdParty/ffmpeg/arm/lib/libavutil.a b/ThirdParty/ffmpeg/arm/lib/libavutil.a new file mode 100644 index 000000000..a0bbe6d8f Binary files /dev/null and b/ThirdParty/ffmpeg/arm/lib/libavutil.a differ diff --git a/ThirdParty/ffmpeg/arm/lib/libswresample.a b/ThirdParty/ffmpeg/arm/lib/libswresample.a new file mode 100644 index 000000000..7ee08798f Binary files /dev/null and b/ThirdParty/ffmpeg/arm/lib/libswresample.a differ diff --git a/ThirdParty/ffmpeg/arm/lib/pkgconfig/libavcodec.pc b/ThirdParty/ffmpeg/arm/lib/pkgconfig/libavcodec.pc new file mode 100644 index 000000000..acd704146 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/lib/pkgconfig/libavcodec.pc @@ -0,0 +1,14 @@ +prefix=/Users/chris/Source/Repos/cog/ThirdParty/ffmpeg/arm +exec_prefix=${prefix} +libdir=/Users/chris/Source/Repos/cog/ThirdParty/ffmpeg/arm/lib +includedir=/Users/chris/Source/Repos/cog/ThirdParty/ffmpeg/arm/include + +Name: libavcodec +Description: FFmpeg codec library +Version: 58.91.100 +Requires: libswresample >= 3.7.100, libavutil >= 56.51.100 +Requires.private: +Conflicts: +Libs: -L${libdir} -lavcodec -pthread -liconv -lm +Libs.private: +Cflags: -I${includedir} diff --git a/ThirdParty/ffmpeg/arm/lib/pkgconfig/libavformat.pc b/ThirdParty/ffmpeg/arm/lib/pkgconfig/libavformat.pc new file mode 100644 index 000000000..91acc9a36 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/lib/pkgconfig/libavformat.pc @@ -0,0 +1,14 @@ +prefix=/Users/chris/Source/Repos/cog/ThirdParty/ffmpeg/arm +exec_prefix=${prefix} +libdir=/Users/chris/Source/Repos/cog/ThirdParty/ffmpeg/arm/lib +includedir=/Users/chris/Source/Repos/cog/ThirdParty/ffmpeg/arm/include + +Name: libavformat +Description: FFmpeg container format library +Version: 58.45.100 +Requires: libavcodec >= 58.91.100, libswresample >= 3.7.100, libavutil >= 56.51.100 +Requires.private: +Conflicts: +Libs: -L${libdir} -lavformat -lm -lz +Libs.private: +Cflags: -I${includedir} diff --git a/ThirdParty/ffmpeg/lib/pkgconfig/libavutil.pc b/ThirdParty/ffmpeg/arm/lib/pkgconfig/libavutil.pc similarity index 59% rename from ThirdParty/ffmpeg/lib/pkgconfig/libavutil.pc rename to ThirdParty/ffmpeg/arm/lib/pkgconfig/libavutil.pc index 5bcb1bfe9..107b5fe90 100644 --- a/ThirdParty/ffmpeg/lib/pkgconfig/libavutil.pc +++ b/ThirdParty/ffmpeg/arm/lib/pkgconfig/libavutil.pc @@ -1,11 +1,11 @@ -prefix=/Users/chris/Source/Repos/cog/ThirdParty/ffmpeg +prefix=/Users/chris/Source/Repos/cog/ThirdParty/ffmpeg/arm exec_prefix=${prefix} -libdir=/Users/chris/Source/Repos/cog/ThirdParty/ffmpeg/lib -includedir=/Users/chris/Source/Repos/cog/ThirdParty/ffmpeg/include +libdir=/Users/chris/Source/Repos/cog/ThirdParty/ffmpeg/arm/lib +includedir=/Users/chris/Source/Repos/cog/ThirdParty/ffmpeg/arm/include Name: libavutil Description: FFmpeg utility library -Version: 56.35.101 +Version: 56.51.100 Requires: Requires.private: Conflicts: diff --git a/ThirdParty/ffmpeg/arm/lib/pkgconfig/libswresample.pc b/ThirdParty/ffmpeg/arm/lib/pkgconfig/libswresample.pc new file mode 100644 index 000000000..772ae1038 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/lib/pkgconfig/libswresample.pc @@ -0,0 +1,14 @@ +prefix=/Users/chris/Source/Repos/cog/ThirdParty/ffmpeg/arm +exec_prefix=${prefix} +libdir=/Users/chris/Source/Repos/cog/ThirdParty/ffmpeg/arm/lib +includedir=/Users/chris/Source/Repos/cog/ThirdParty/ffmpeg/arm/include + +Name: libswresample +Description: FFmpeg audio resampling library +Version: 3.7.100 +Requires: libavutil >= 56.51.100 +Requires.private: +Conflicts: +Libs: -L${libdir} -lswresample -lm +Libs.private: +Cflags: -I${includedir} diff --git a/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/Makefile b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/Makefile new file mode 100644 index 000000000..a232d97f9 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/Makefile @@ -0,0 +1,50 @@ +# use pkg-config for getting CFLAGS and LDLIBS +FFMPEG_LIBS= libavdevice \ + libavformat \ + libavfilter \ + libavcodec \ + libswresample \ + libswscale \ + libavutil \ + +CFLAGS += -Wall -g +CFLAGS := $(shell pkg-config --cflags $(FFMPEG_LIBS)) $(CFLAGS) +LDLIBS := $(shell pkg-config --libs $(FFMPEG_LIBS)) $(LDLIBS) + +EXAMPLES= avio_list_dir \ + avio_reading \ + decode_audio \ + decode_video \ + demuxing_decoding \ + encode_audio \ + encode_video \ + extract_mvs \ + filtering_video \ + filtering_audio \ + http_multiclient \ + hw_decode \ + metadata \ + muxing \ + remuxing \ + resampling_audio \ + scaling_video \ + transcode_aac \ + transcoding \ + +OBJS=$(addsuffix .o,$(EXAMPLES)) + +# the following examples make explicit use of the math library +avcodec: LDLIBS += -lm +encode_audio: LDLIBS += -lm +muxing: LDLIBS += -lm +resampling_audio: LDLIBS += -lm + +.phony: all clean-test clean + +all: $(OBJS) $(EXAMPLES) + +clean-test: + $(RM) test*.pgm test.h264 test.mp2 test.sw test.mpg + +clean: clean-test + $(RM) $(EXAMPLES) $(OBJS) diff --git a/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/README b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/README new file mode 100644 index 000000000..c1ce619d3 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/README @@ -0,0 +1,23 @@ +FFmpeg examples README +---------------------- + +Both following use cases rely on pkg-config and make, thus make sure +that you have them installed and working on your system. + + +Method 1: build the installed examples in a generic read/write user directory + +Copy to a read/write user directory and just use "make", it will link +to the libraries on your system, assuming the PKG_CONFIG_PATH is +correctly configured. + +Method 2: build the examples in-tree + +Assuming you are in the source FFmpeg checkout directory, you need to build +FFmpeg (no need to make install in any prefix). Then just run "make examples". +This will build the examples using the FFmpeg build system. You can clean those +examples using "make examplesclean" + +If you want to try the dedicated Makefile examples (to emulate the first +method), go into doc/examples and run a command such as +PKG_CONFIG_PATH=pc-uninstalled make. diff --git a/ThirdParty/ffmpeg/share/ffmpeg/examples/avio_dir_cmd.c b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/avio_list_dir.c similarity index 69% rename from ThirdParty/ffmpeg/share/ffmpeg/examples/avio_dir_cmd.c rename to ThirdParty/ffmpeg/arm/share/ffmpeg/examples/avio_list_dir.c index 0722bd9ab..3073baaef 100644 --- a/ThirdParty/ffmpeg/share/ffmpeg/examples/avio_dir_cmd.c +++ b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/avio_list_dir.c @@ -102,38 +102,15 @@ static int list_op(const char *input_dir) return ret; } -static int del_op(const char *url) -{ - int ret = avpriv_io_delete(url); - if (ret < 0) - av_log(NULL, AV_LOG_ERROR, "Cannot delete '%s': %s.\n", url, av_err2str(ret)); - return ret; -} - -static int move_op(const char *src, const char *dst) -{ - int ret = avpriv_io_move(src, dst); - if (ret < 0) - av_log(NULL, AV_LOG_ERROR, "Cannot move '%s' into '%s': %s.\n", src, dst, av_err2str(ret)); - return ret; -} - - static void usage(const char *program_name) { - fprintf(stderr, "usage: %s OPERATION entry1 [entry2]\n" - "API example program to show how to manipulate resources " - "accessed through AVIOContext.\n" - "OPERATIONS:\n" - "list list content of the directory\n" - "move rename content in directory\n" - "del delete content in directory\n", - program_name); + fprintf(stderr, "usage: %s input_dir\n" + "API example program to show how to list files in directory " + "accessed through AVIOContext.\n", program_name); } int main(int argc, char *argv[]) { - const char *op = NULL; int ret; av_log_set_level(AV_LOG_DEBUG); @@ -145,32 +122,7 @@ int main(int argc, char *argv[]) avformat_network_init(); - op = argv[1]; - if (strcmp(op, "list") == 0) { - if (argc < 3) { - av_log(NULL, AV_LOG_INFO, "Missing argument for list operation.\n"); - ret = AVERROR(EINVAL); - } else { - ret = list_op(argv[2]); - } - } else if (strcmp(op, "del") == 0) { - if (argc < 3) { - av_log(NULL, AV_LOG_INFO, "Missing argument for del operation.\n"); - ret = AVERROR(EINVAL); - } else { - ret = del_op(argv[2]); - } - } else if (strcmp(op, "move") == 0) { - if (argc < 4) { - av_log(NULL, AV_LOG_INFO, "Missing argument for move operation.\n"); - ret = AVERROR(EINVAL); - } else { - ret = move_op(argv[2], argv[3]); - } - } else { - av_log(NULL, AV_LOG_INFO, "Invalid operation %s\n", op); - ret = AVERROR(EINVAL); - } + ret = list_op(argv[1]); avformat_network_deinit(); diff --git a/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/avio_reading.c b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/avio_reading.c new file mode 100644 index 000000000..36ee02afa --- /dev/null +++ b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/avio_reading.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2014 Stefano Sabatini + * + * 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. + */ + +/** + * @file + * libavformat AVIOContext API example. + * + * Make libavformat demuxer access media content through a custom + * AVIOContext read callback. + * @example avio_reading.c + */ + +#include +#include +#include +#include + +struct buffer_data { + uint8_t *ptr; + size_t size; ///< size left in the buffer +}; + +static int read_packet(void *opaque, uint8_t *buf, int buf_size) +{ + struct buffer_data *bd = (struct buffer_data *)opaque; + buf_size = FFMIN(buf_size, bd->size); + + if (!buf_size) + return AVERROR_EOF; + printf("ptr:%p size:%zu\n", bd->ptr, bd->size); + + /* copy internal buffer data to buf */ + memcpy(buf, bd->ptr, buf_size); + bd->ptr += buf_size; + bd->size -= buf_size; + + return buf_size; +} + +int main(int argc, char *argv[]) +{ + AVFormatContext *fmt_ctx = NULL; + AVIOContext *avio_ctx = NULL; + uint8_t *buffer = NULL, *avio_ctx_buffer = NULL; + size_t buffer_size, avio_ctx_buffer_size = 4096; + char *input_filename = NULL; + int ret = 0; + struct buffer_data bd = { 0 }; + + if (argc != 2) { + fprintf(stderr, "usage: %s input_file\n" + "API example program to show how to read from a custom buffer " + "accessed through AVIOContext.\n", argv[0]); + return 1; + } + input_filename = argv[1]; + + /* slurp file content into buffer */ + ret = av_file_map(input_filename, &buffer, &buffer_size, 0, NULL); + if (ret < 0) + goto end; + + /* fill opaque structure used by the AVIOContext read callback */ + bd.ptr = buffer; + bd.size = buffer_size; + + if (!(fmt_ctx = avformat_alloc_context())) { + ret = AVERROR(ENOMEM); + goto end; + } + + avio_ctx_buffer = av_malloc(avio_ctx_buffer_size); + if (!avio_ctx_buffer) { + ret = AVERROR(ENOMEM); + goto end; + } + avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size, + 0, &bd, &read_packet, NULL, NULL); + if (!avio_ctx) { + ret = AVERROR(ENOMEM); + goto end; + } + fmt_ctx->pb = avio_ctx; + + ret = avformat_open_input(&fmt_ctx, NULL, NULL, NULL); + if (ret < 0) { + fprintf(stderr, "Could not open input\n"); + goto end; + } + + ret = avformat_find_stream_info(fmt_ctx, NULL); + if (ret < 0) { + fprintf(stderr, "Could not find stream information\n"); + goto end; + } + + av_dump_format(fmt_ctx, 0, input_filename, 0); + +end: + avformat_close_input(&fmt_ctx); + + /* note: the internal buffer could have changed, and be != avio_ctx_buffer */ + if (avio_ctx) + av_freep(&avio_ctx->buffer); + avio_context_free(&avio_ctx); + + av_file_unmap(buffer, buffer_size); + + if (ret < 0) { + fprintf(stderr, "Error occurred: %s\n", av_err2str(ret)); + return 1; + } + + return 0; +} diff --git a/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/decode_audio.c b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/decode_audio.c new file mode 100644 index 000000000..6c2a8ed55 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/decode_audio.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2001 Fabrice Bellard + * + * 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. + */ + +/** + * @file + * audio decoding with libavcodec API example + * + * @example decode_audio.c + */ + +#include +#include +#include + +#include +#include + +#include + +#define AUDIO_INBUF_SIZE 20480 +#define AUDIO_REFILL_THRESH 4096 + +static int get_format_from_sample_fmt(const char **fmt, + enum AVSampleFormat sample_fmt) +{ + int i; + struct sample_fmt_entry { + enum AVSampleFormat sample_fmt; const char *fmt_be, *fmt_le; + } sample_fmt_entries[] = { + { AV_SAMPLE_FMT_U8, "u8", "u8" }, + { AV_SAMPLE_FMT_S16, "s16be", "s16le" }, + { AV_SAMPLE_FMT_S32, "s32be", "s32le" }, + { AV_SAMPLE_FMT_FLT, "f32be", "f32le" }, + { AV_SAMPLE_FMT_DBL, "f64be", "f64le" }, + }; + *fmt = NULL; + + for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) { + struct sample_fmt_entry *entry = &sample_fmt_entries[i]; + if (sample_fmt == entry->sample_fmt) { + *fmt = AV_NE(entry->fmt_be, entry->fmt_le); + return 0; + } + } + + fprintf(stderr, + "sample format %s is not supported as output format\n", + av_get_sample_fmt_name(sample_fmt)); + return -1; +} + +static void decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame, + FILE *outfile) +{ + int i, ch; + int ret, data_size; + + /* send the packet with the compressed data to the decoder */ + ret = avcodec_send_packet(dec_ctx, pkt); + if (ret < 0) { + fprintf(stderr, "Error submitting the packet to the decoder\n"); + exit(1); + } + + /* read all the output frames (in general there may be any number of them */ + while (ret >= 0) { + ret = avcodec_receive_frame(dec_ctx, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) + return; + else if (ret < 0) { + fprintf(stderr, "Error during decoding\n"); + exit(1); + } + data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt); + if (data_size < 0) { + /* This should not occur, checking just for paranoia */ + fprintf(stderr, "Failed to calculate data size\n"); + exit(1); + } + for (i = 0; i < frame->nb_samples; i++) + for (ch = 0; ch < dec_ctx->channels; ch++) + fwrite(frame->data[ch] + data_size*i, 1, data_size, outfile); + } +} + +int main(int argc, char **argv) +{ + const char *outfilename, *filename; + const AVCodec *codec; + AVCodecContext *c= NULL; + AVCodecParserContext *parser = NULL; + int len, ret; + FILE *f, *outfile; + uint8_t inbuf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE]; + uint8_t *data; + size_t data_size; + AVPacket *pkt; + AVFrame *decoded_frame = NULL; + enum AVSampleFormat sfmt; + int n_channels = 0; + const char *fmt; + + if (argc <= 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(0); + } + filename = argv[1]; + outfilename = argv[2]; + + pkt = av_packet_alloc(); + + /* find the MPEG audio decoder */ + codec = avcodec_find_decoder(AV_CODEC_ID_MP2); + if (!codec) { + fprintf(stderr, "Codec not found\n"); + exit(1); + } + + parser = av_parser_init(codec->id); + if (!parser) { + fprintf(stderr, "Parser not found\n"); + exit(1); + } + + c = avcodec_alloc_context3(codec); + if (!c) { + fprintf(stderr, "Could not allocate audio codec context\n"); + exit(1); + } + + /* open it */ + if (avcodec_open2(c, codec, NULL) < 0) { + fprintf(stderr, "Could not open codec\n"); + exit(1); + } + + f = fopen(filename, "rb"); + if (!f) { + fprintf(stderr, "Could not open %s\n", filename); + exit(1); + } + outfile = fopen(outfilename, "wb"); + if (!outfile) { + av_free(c); + exit(1); + } + + /* decode until eof */ + data = inbuf; + data_size = fread(inbuf, 1, AUDIO_INBUF_SIZE, f); + + while (data_size > 0) { + if (!decoded_frame) { + if (!(decoded_frame = av_frame_alloc())) { + fprintf(stderr, "Could not allocate audio frame\n"); + exit(1); + } + } + + ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size, + data, data_size, + AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); + if (ret < 0) { + fprintf(stderr, "Error while parsing\n"); + exit(1); + } + data += ret; + data_size -= ret; + + if (pkt->size) + decode(c, pkt, decoded_frame, outfile); + + if (data_size < AUDIO_REFILL_THRESH) { + memmove(inbuf, data, data_size); + data = inbuf; + len = fread(data + data_size, 1, + AUDIO_INBUF_SIZE - data_size, f); + if (len > 0) + data_size += len; + } + } + + /* flush the decoder */ + pkt->data = NULL; + pkt->size = 0; + decode(c, pkt, decoded_frame, outfile); + + /* print output pcm infomations, because there have no metadata of pcm */ + sfmt = c->sample_fmt; + + if (av_sample_fmt_is_planar(sfmt)) { + const char *packed = av_get_sample_fmt_name(sfmt); + printf("Warning: the sample format the decoder produced is planar " + "(%s). This example will output the first channel only.\n", + packed ? packed : "?"); + sfmt = av_get_packed_sample_fmt(sfmt); + } + + n_channels = c->channels; + if ((ret = get_format_from_sample_fmt(&fmt, sfmt)) < 0) + goto end; + + printf("Play the output audio file with the command:\n" + "ffplay -f %s -ac %d -ar %d %s\n", + fmt, n_channels, c->sample_rate, + outfilename); +end: + fclose(outfile); + fclose(f); + + avcodec_free_context(&c); + av_parser_close(parser); + av_frame_free(&decoded_frame); + av_packet_free(&pkt); + + return 0; +} diff --git a/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/decode_video.c b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/decode_video.c new file mode 100644 index 000000000..169188a4b --- /dev/null +++ b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/decode_video.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2001 Fabrice Bellard + * + * 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. + */ + +/** + * @file + * video decoding with libavcodec API example + * + * @example decode_video.c + */ + +#include +#include +#include + +#include + +#define INBUF_SIZE 4096 + +static void pgm_save(unsigned char *buf, int wrap, int xsize, int ysize, + char *filename) +{ + FILE *f; + int i; + + f = fopen(filename,"w"); + fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255); + for (i = 0; i < ysize; i++) + fwrite(buf + i * wrap, 1, xsize, f); + fclose(f); +} + +static void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt, + const char *filename) +{ + char buf[1024]; + int ret; + + ret = avcodec_send_packet(dec_ctx, pkt); + if (ret < 0) { + fprintf(stderr, "Error sending a packet for decoding\n"); + exit(1); + } + + while (ret >= 0) { + ret = avcodec_receive_frame(dec_ctx, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) + return; + else if (ret < 0) { + fprintf(stderr, "Error during decoding\n"); + exit(1); + } + + printf("saving frame %3d\n", dec_ctx->frame_number); + fflush(stdout); + + /* the picture is allocated by the decoder. no need to + free it */ + snprintf(buf, sizeof(buf), "%s-%d", filename, dec_ctx->frame_number); + pgm_save(frame->data[0], frame->linesize[0], + frame->width, frame->height, buf); + } +} + +int main(int argc, char **argv) +{ + const char *filename, *outfilename; + const AVCodec *codec; + AVCodecParserContext *parser; + AVCodecContext *c= NULL; + FILE *f; + AVFrame *frame; + uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE]; + uint8_t *data; + size_t data_size; + int ret; + AVPacket *pkt; + + if (argc <= 2) { + fprintf(stderr, "Usage: %s \n" + "And check your input file is encoded by mpeg1video please.\n", argv[0]); + exit(0); + } + filename = argv[1]; + outfilename = argv[2]; + + pkt = av_packet_alloc(); + if (!pkt) + exit(1); + + /* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */ + memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE); + + /* find the MPEG-1 video decoder */ + codec = avcodec_find_decoder(AV_CODEC_ID_MPEG1VIDEO); + if (!codec) { + fprintf(stderr, "Codec not found\n"); + exit(1); + } + + parser = av_parser_init(codec->id); + if (!parser) { + fprintf(stderr, "parser not found\n"); + exit(1); + } + + c = avcodec_alloc_context3(codec); + if (!c) { + fprintf(stderr, "Could not allocate video codec context\n"); + exit(1); + } + + /* For some codecs, such as msmpeg4 and mpeg4, width and height + MUST be initialized there because this information is not + available in the bitstream. */ + + /* open it */ + if (avcodec_open2(c, codec, NULL) < 0) { + fprintf(stderr, "Could not open codec\n"); + exit(1); + } + + f = fopen(filename, "rb"); + if (!f) { + fprintf(stderr, "Could not open %s\n", filename); + exit(1); + } + + frame = av_frame_alloc(); + if (!frame) { + fprintf(stderr, "Could not allocate video frame\n"); + exit(1); + } + + while (!feof(f)) { + /* read raw data from the input file */ + data_size = fread(inbuf, 1, INBUF_SIZE, f); + if (!data_size) + break; + + /* use the parser to split the data into frames */ + data = inbuf; + while (data_size > 0) { + ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size, + data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); + if (ret < 0) { + fprintf(stderr, "Error while parsing\n"); + exit(1); + } + data += ret; + data_size -= ret; + + if (pkt->size) + decode(c, frame, pkt, outfilename); + } + } + + /* flush the decoder */ + decode(c, frame, NULL, outfilename); + + fclose(f); + + av_parser_close(parser); + avcodec_free_context(&c); + av_frame_free(&frame); + av_packet_free(&pkt); + + return 0; +} diff --git a/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/demuxing_decoding.c b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/demuxing_decoding.c new file mode 100644 index 000000000..803e35d25 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/demuxing_decoding.c @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2012 Stefano Sabatini + * + * 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. + */ + +/** + * @file + * Demuxing and decoding example. + * + * Show how to use the libavformat and libavcodec API to demux and + * decode audio and video data. + * @example demuxing_decoding.c + */ + +#include +#include +#include +#include + +static AVFormatContext *fmt_ctx = NULL; +static AVCodecContext *video_dec_ctx = NULL, *audio_dec_ctx; +static int width, height; +static enum AVPixelFormat pix_fmt; +static AVStream *video_stream = NULL, *audio_stream = NULL; +static const char *src_filename = NULL; +static const char *video_dst_filename = NULL; +static const char *audio_dst_filename = NULL; +static FILE *video_dst_file = NULL; +static FILE *audio_dst_file = NULL; + +static uint8_t *video_dst_data[4] = {NULL}; +static int video_dst_linesize[4]; +static int video_dst_bufsize; + +static int video_stream_idx = -1, audio_stream_idx = -1; +static AVFrame *frame = NULL; +static AVPacket pkt; +static int video_frame_count = 0; +static int audio_frame_count = 0; + +static int output_video_frame(AVFrame *frame) +{ + if (frame->width != width || frame->height != height || + frame->format != pix_fmt) { + /* To handle this change, one could call av_image_alloc again and + * decode the following frames into another rawvideo file. */ + fprintf(stderr, "Error: Width, height and pixel format have to be " + "constant in a rawvideo file, but the width, height or " + "pixel format of the input video changed:\n" + "old: width = %d, height = %d, format = %s\n" + "new: width = %d, height = %d, format = %s\n", + width, height, av_get_pix_fmt_name(pix_fmt), + frame->width, frame->height, + av_get_pix_fmt_name(frame->format)); + return -1; + } + + printf("video_frame n:%d coded_n:%d\n", + video_frame_count++, frame->coded_picture_number); + + /* copy decoded frame to destination buffer: + * this is required since rawvideo expects non aligned data */ + av_image_copy(video_dst_data, video_dst_linesize, + (const uint8_t **)(frame->data), frame->linesize, + pix_fmt, width, height); + + /* write to rawvideo file */ + fwrite(video_dst_data[0], 1, video_dst_bufsize, video_dst_file); + return 0; +} + +static int output_audio_frame(AVFrame *frame) +{ + size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample(frame->format); + printf("audio_frame n:%d nb_samples:%d pts:%s\n", + audio_frame_count++, frame->nb_samples, + av_ts2timestr(frame->pts, &audio_dec_ctx->time_base)); + + /* Write the raw audio data samples of the first plane. This works + * fine for packed formats (e.g. AV_SAMPLE_FMT_S16). However, + * most audio decoders output planar audio, which uses a separate + * plane of audio samples for each channel (e.g. AV_SAMPLE_FMT_S16P). + * In other words, this code will write only the first audio channel + * in these cases. + * You should use libswresample or libavfilter to convert the frame + * to packed data. */ + fwrite(frame->extended_data[0], 1, unpadded_linesize, audio_dst_file); + + return 0; +} + +static int decode_packet(AVCodecContext *dec, const AVPacket *pkt) +{ + int ret = 0; + + // submit the packet to the decoder + ret = avcodec_send_packet(dec, pkt); + if (ret < 0) { + fprintf(stderr, "Error submitting a packet for decoding (%s)\n", av_err2str(ret)); + return ret; + } + + // get all the available frames from the decoder + while (ret >= 0) { + ret = avcodec_receive_frame(dec, frame); + if (ret < 0) { + // those two return values are special and mean there is no output + // frame available, but there were no errors during decoding + if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) + return 0; + + fprintf(stderr, "Error during decoding (%s)\n", av_err2str(ret)); + return ret; + } + + // write the frame data to output file + if (dec->codec->type == AVMEDIA_TYPE_VIDEO) + ret = output_video_frame(frame); + else + ret = output_audio_frame(frame); + + av_frame_unref(frame); + if (ret < 0) + return ret; + } + + return 0; +} + +static int open_codec_context(int *stream_idx, + AVCodecContext **dec_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type) +{ + int ret, stream_index; + AVStream *st; + AVCodec *dec = NULL; + AVDictionary *opts = NULL; + + ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0); + if (ret < 0) { + fprintf(stderr, "Could not find %s stream in input file '%s'\n", + av_get_media_type_string(type), src_filename); + return ret; + } else { + stream_index = ret; + st = fmt_ctx->streams[stream_index]; + + /* find decoder for the stream */ + dec = avcodec_find_decoder(st->codecpar->codec_id); + if (!dec) { + fprintf(stderr, "Failed to find %s codec\n", + av_get_media_type_string(type)); + return AVERROR(EINVAL); + } + + /* Allocate a codec context for the decoder */ + *dec_ctx = avcodec_alloc_context3(dec); + if (!*dec_ctx) { + fprintf(stderr, "Failed to allocate the %s codec context\n", + av_get_media_type_string(type)); + return AVERROR(ENOMEM); + } + + /* Copy codec parameters from input stream to output codec context */ + if ((ret = avcodec_parameters_to_context(*dec_ctx, st->codecpar)) < 0) { + fprintf(stderr, "Failed to copy %s codec parameters to decoder context\n", + av_get_media_type_string(type)); + return ret; + } + + /* Init the decoders */ + if ((ret = avcodec_open2(*dec_ctx, dec, &opts)) < 0) { + fprintf(stderr, "Failed to open %s codec\n", + av_get_media_type_string(type)); + return ret; + } + *stream_idx = stream_index; + } + + return 0; +} + +static int get_format_from_sample_fmt(const char **fmt, + enum AVSampleFormat sample_fmt) +{ + int i; + struct sample_fmt_entry { + enum AVSampleFormat sample_fmt; const char *fmt_be, *fmt_le; + } sample_fmt_entries[] = { + { AV_SAMPLE_FMT_U8, "u8", "u8" }, + { AV_SAMPLE_FMT_S16, "s16be", "s16le" }, + { AV_SAMPLE_FMT_S32, "s32be", "s32le" }, + { AV_SAMPLE_FMT_FLT, "f32be", "f32le" }, + { AV_SAMPLE_FMT_DBL, "f64be", "f64le" }, + }; + *fmt = NULL; + + for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) { + struct sample_fmt_entry *entry = &sample_fmt_entries[i]; + if (sample_fmt == entry->sample_fmt) { + *fmt = AV_NE(entry->fmt_be, entry->fmt_le); + return 0; + } + } + + fprintf(stderr, + "sample format %s is not supported as output format\n", + av_get_sample_fmt_name(sample_fmt)); + return -1; +} + +int main (int argc, char **argv) +{ + int ret = 0; + + if (argc != 4) { + fprintf(stderr, "usage: %s input_file video_output_file audio_output_file\n" + "API example program to show how to read frames from an input file.\n" + "This program reads frames from a file, decodes them, and writes decoded\n" + "video frames to a rawvideo file named video_output_file, and decoded\n" + "audio frames to a rawaudio file named audio_output_file.\n", + argv[0]); + exit(1); + } + src_filename = argv[1]; + video_dst_filename = argv[2]; + audio_dst_filename = argv[3]; + + /* open input file, and allocate format context */ + if (avformat_open_input(&fmt_ctx, src_filename, NULL, NULL) < 0) { + fprintf(stderr, "Could not open source file %s\n", src_filename); + exit(1); + } + + /* retrieve stream information */ + if (avformat_find_stream_info(fmt_ctx, NULL) < 0) { + fprintf(stderr, "Could not find stream information\n"); + exit(1); + } + + if (open_codec_context(&video_stream_idx, &video_dec_ctx, fmt_ctx, AVMEDIA_TYPE_VIDEO) >= 0) { + video_stream = fmt_ctx->streams[video_stream_idx]; + + video_dst_file = fopen(video_dst_filename, "wb"); + if (!video_dst_file) { + fprintf(stderr, "Could not open destination file %s\n", video_dst_filename); + ret = 1; + goto end; + } + + /* allocate image where the decoded image will be put */ + width = video_dec_ctx->width; + height = video_dec_ctx->height; + pix_fmt = video_dec_ctx->pix_fmt; + ret = av_image_alloc(video_dst_data, video_dst_linesize, + width, height, pix_fmt, 1); + if (ret < 0) { + fprintf(stderr, "Could not allocate raw video buffer\n"); + goto end; + } + video_dst_bufsize = ret; + } + + if (open_codec_context(&audio_stream_idx, &audio_dec_ctx, fmt_ctx, AVMEDIA_TYPE_AUDIO) >= 0) { + audio_stream = fmt_ctx->streams[audio_stream_idx]; + audio_dst_file = fopen(audio_dst_filename, "wb"); + if (!audio_dst_file) { + fprintf(stderr, "Could not open destination file %s\n", audio_dst_filename); + ret = 1; + goto end; + } + } + + /* dump input information to stderr */ + av_dump_format(fmt_ctx, 0, src_filename, 0); + + if (!audio_stream && !video_stream) { + fprintf(stderr, "Could not find audio or video stream in the input, aborting\n"); + ret = 1; + goto end; + } + + frame = av_frame_alloc(); + if (!frame) { + fprintf(stderr, "Could not allocate frame\n"); + ret = AVERROR(ENOMEM); + goto end; + } + + /* initialize packet, set data to NULL, let the demuxer fill it */ + av_init_packet(&pkt); + pkt.data = NULL; + pkt.size = 0; + + if (video_stream) + printf("Demuxing video from file '%s' into '%s'\n", src_filename, video_dst_filename); + if (audio_stream) + printf("Demuxing audio from file '%s' into '%s'\n", src_filename, audio_dst_filename); + + /* read frames from the file */ + while (av_read_frame(fmt_ctx, &pkt) >= 0) { + // check if the packet belongs to a stream we are interested in, otherwise + // skip it + if (pkt.stream_index == video_stream_idx) + ret = decode_packet(video_dec_ctx, &pkt); + else if (pkt.stream_index == audio_stream_idx) + ret = decode_packet(audio_dec_ctx, &pkt); + av_packet_unref(&pkt); + if (ret < 0) + break; + } + + /* flush the decoders */ + if (video_dec_ctx) + decode_packet(video_dec_ctx, NULL); + if (audio_dec_ctx) + decode_packet(audio_dec_ctx, NULL); + + printf("Demuxing succeeded.\n"); + + if (video_stream) { + printf("Play the output video file with the command:\n" + "ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n", + av_get_pix_fmt_name(pix_fmt), width, height, + video_dst_filename); + } + + if (audio_stream) { + enum AVSampleFormat sfmt = audio_dec_ctx->sample_fmt; + int n_channels = audio_dec_ctx->channels; + const char *fmt; + + if (av_sample_fmt_is_planar(sfmt)) { + const char *packed = av_get_sample_fmt_name(sfmt); + printf("Warning: the sample format the decoder produced is planar " + "(%s). This example will output the first channel only.\n", + packed ? packed : "?"); + sfmt = av_get_packed_sample_fmt(sfmt); + n_channels = 1; + } + + if ((ret = get_format_from_sample_fmt(&fmt, sfmt)) < 0) + goto end; + + printf("Play the output audio file with the command:\n" + "ffplay -f %s -ac %d -ar %d %s\n", + fmt, n_channels, audio_dec_ctx->sample_rate, + audio_dst_filename); + } + +end: + avcodec_free_context(&video_dec_ctx); + avcodec_free_context(&audio_dec_ctx); + avformat_close_input(&fmt_ctx); + if (video_dst_file) + fclose(video_dst_file); + if (audio_dst_file) + fclose(audio_dst_file); + av_frame_free(&frame); + av_free(video_dst_data[0]); + + return ret < 0; +} diff --git a/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/encode_audio.c b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/encode_audio.c new file mode 100644 index 000000000..ab3586be7 --- /dev/null +++ b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/encode_audio.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2001 Fabrice Bellard + * + * 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. + */ + +/** + * @file + * audio encoding with libavcodec API example. + * + * @example encode_audio.c + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include + +/* check that a given sample format is supported by the encoder */ +static int check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt) +{ + const enum AVSampleFormat *p = codec->sample_fmts; + + while (*p != AV_SAMPLE_FMT_NONE) { + if (*p == sample_fmt) + return 1; + p++; + } + return 0; +} + +/* just pick the highest supported samplerate */ +static int select_sample_rate(const AVCodec *codec) +{ + const int *p; + int best_samplerate = 0; + + if (!codec->supported_samplerates) + return 44100; + + p = codec->supported_samplerates; + while (*p) { + if (!best_samplerate || abs(44100 - *p) < abs(44100 - best_samplerate)) + best_samplerate = *p; + p++; + } + return best_samplerate; +} + +/* select layout with the highest channel count */ +static int select_channel_layout(const AVCodec *codec) +{ + const uint64_t *p; + uint64_t best_ch_layout = 0; + int best_nb_channels = 0; + + if (!codec->channel_layouts) + return AV_CH_LAYOUT_STEREO; + + p = codec->channel_layouts; + while (*p) { + int nb_channels = av_get_channel_layout_nb_channels(*p); + + if (nb_channels > best_nb_channels) { + best_ch_layout = *p; + best_nb_channels = nb_channels; + } + p++; + } + return best_ch_layout; +} + +static void encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, + FILE *output) +{ + int ret; + + /* send the frame for encoding */ + ret = avcodec_send_frame(ctx, frame); + if (ret < 0) { + fprintf(stderr, "Error sending the frame to the encoder\n"); + exit(1); + } + + /* read all the available output packets (in general there may be any + * number of them */ + while (ret >= 0) { + ret = avcodec_receive_packet(ctx, pkt); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) + return; + else if (ret < 0) { + fprintf(stderr, "Error encoding audio frame\n"); + exit(1); + } + + fwrite(pkt->data, 1, pkt->size, output); + av_packet_unref(pkt); + } +} + +int main(int argc, char **argv) +{ + const char *filename; + const AVCodec *codec; + AVCodecContext *c= NULL; + AVFrame *frame; + AVPacket *pkt; + int i, j, k, ret; + FILE *f; + uint16_t *samples; + float t, tincr; + + if (argc <= 1) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 0; + } + filename = argv[1]; + + /* find the MP2 encoder */ + codec = avcodec_find_encoder(AV_CODEC_ID_MP2); + if (!codec) { + fprintf(stderr, "Codec not found\n"); + exit(1); + } + + c = avcodec_alloc_context3(codec); + if (!c) { + fprintf(stderr, "Could not allocate audio codec context\n"); + exit(1); + } + + /* put sample parameters */ + c->bit_rate = 64000; + + /* check that the encoder supports s16 pcm input */ + c->sample_fmt = AV_SAMPLE_FMT_S16; + if (!check_sample_fmt(codec, c->sample_fmt)) { + fprintf(stderr, "Encoder does not support sample format %s", + av_get_sample_fmt_name(c->sample_fmt)); + exit(1); + } + + /* select other audio parameters supported by the encoder */ + c->sample_rate = select_sample_rate(codec); + c->channel_layout = select_channel_layout(codec); + c->channels = av_get_channel_layout_nb_channels(c->channel_layout); + + /* open it */ + if (avcodec_open2(c, codec, NULL) < 0) { + fprintf(stderr, "Could not open codec\n"); + exit(1); + } + + f = fopen(filename, "wb"); + if (!f) { + fprintf(stderr, "Could not open %s\n", filename); + exit(1); + } + + /* packet for holding encoded output */ + pkt = av_packet_alloc(); + if (!pkt) { + fprintf(stderr, "could not allocate the packet\n"); + exit(1); + } + + /* frame containing input raw audio */ + frame = av_frame_alloc(); + if (!frame) { + fprintf(stderr, "Could not allocate audio frame\n"); + exit(1); + } + + frame->nb_samples = c->frame_size; + frame->format = c->sample_fmt; + frame->channel_layout = c->channel_layout; + + /* allocate the data buffers */ + ret = av_frame_get_buffer(frame, 0); + if (ret < 0) { + fprintf(stderr, "Could not allocate audio data buffers\n"); + exit(1); + } + + /* encode a single tone sound */ + t = 0; + tincr = 2 * M_PI * 440.0 / c->sample_rate; + for (i = 0; i < 200; i++) { + /* make sure the frame is writable -- makes a copy if the encoder + * kept a reference internally */ + ret = av_frame_make_writable(frame); + if (ret < 0) + exit(1); + samples = (uint16_t*)frame->data[0]; + + for (j = 0; j < c->frame_size; j++) { + samples[2*j] = (int)(sin(t) * 10000); + + for (k = 1; k < c->channels; k++) + samples[2*j + k] = samples[2*j]; + t += tincr; + } + encode(c, frame, pkt, f); + } + + /* flush the encoder */ + encode(c, NULL, pkt, f); + + fclose(f); + + av_frame_free(&frame); + av_packet_free(&pkt); + avcodec_free_context(&c); + + return 0; +} diff --git a/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/encode_video.c b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/encode_video.c new file mode 100644 index 000000000..908eb203d --- /dev/null +++ b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/encode_video.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2001 Fabrice Bellard + * + * 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. + */ + +/** + * @file + * video encoding with libavcodec API example + * + * @example encode_video.c + */ + +#include +#include +#include + +#include + +#include +#include + +static void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt, + FILE *outfile) +{ + int ret; + + /* send the frame to the encoder */ + if (frame) + printf("Send frame %3"PRId64"\n", frame->pts); + + ret = avcodec_send_frame(enc_ctx, frame); + if (ret < 0) { + fprintf(stderr, "Error sending a frame for encoding\n"); + exit(1); + } + + while (ret >= 0) { + ret = avcodec_receive_packet(enc_ctx, pkt); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) + return; + else if (ret < 0) { + fprintf(stderr, "Error during encoding\n"); + exit(1); + } + + printf("Write packet %3"PRId64" (size=%5d)\n", pkt->pts, pkt->size); + fwrite(pkt->data, 1, pkt->size, outfile); + av_packet_unref(pkt); + } +} + +int main(int argc, char **argv) +{ + const char *filename, *codec_name; + const AVCodec *codec; + AVCodecContext *c= NULL; + int i, ret, x, y; + FILE *f; + AVFrame *frame; + AVPacket *pkt; + uint8_t endcode[] = { 0, 0, 1, 0xb7 }; + + if (argc <= 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(0); + } + filename = argv[1]; + codec_name = argv[2]; + + /* find the mpeg1video encoder */ + codec = avcodec_find_encoder_by_name(codec_name); + if (!codec) { + fprintf(stderr, "Codec '%s' not found\n", codec_name); + exit(1); + } + + c = avcodec_alloc_context3(codec); + if (!c) { + fprintf(stderr, "Could not allocate video codec context\n"); + exit(1); + } + + pkt = av_packet_alloc(); + if (!pkt) + exit(1); + + /* put sample parameters */ + c->bit_rate = 400000; + /* resolution must be a multiple of two */ + c->width = 352; + c->height = 288; + /* frames per second */ + c->time_base = (AVRational){1, 25}; + c->framerate = (AVRational){25, 1}; + + /* emit one intra frame every ten frames + * check frame pict_type before passing frame + * to encoder, if frame->pict_type is AV_PICTURE_TYPE_I + * then gop_size is ignored and the output of encoder + * will always be I frame irrespective to gop_size + */ + c->gop_size = 10; + c->max_b_frames = 1; + c->pix_fmt = AV_PIX_FMT_YUV420P; + + if (codec->id == AV_CODEC_ID_H264) + av_opt_set(c->priv_data, "preset", "slow", 0); + + /* open it */ + ret = avcodec_open2(c, codec, NULL); + if (ret < 0) { + fprintf(stderr, "Could not open codec: %s\n", av_err2str(ret)); + exit(1); + } + + f = fopen(filename, "wb"); + if (!f) { + fprintf(stderr, "Could not open %s\n", filename); + exit(1); + } + + frame = av_frame_alloc(); + if (!frame) { + fprintf(stderr, "Could not allocate video frame\n"); + exit(1); + } + frame->format = c->pix_fmt; + frame->width = c->width; + frame->height = c->height; + + ret = av_frame_get_buffer(frame, 0); + if (ret < 0) { + fprintf(stderr, "Could not allocate the video frame data\n"); + exit(1); + } + + /* encode 1 second of video */ + for (i = 0; i < 25; i++) { + fflush(stdout); + + /* make sure the frame data is writable */ + ret = av_frame_make_writable(frame); + if (ret < 0) + exit(1); + + /* prepare a dummy image */ + /* Y */ + for (y = 0; y < c->height; y++) { + for (x = 0; x < c->width; x++) { + frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3; + } + } + + /* Cb and Cr */ + for (y = 0; y < c->height/2; y++) { + for (x = 0; x < c->width/2; x++) { + frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2; + frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5; + } + } + + frame->pts = i; + + /* encode the image */ + encode(c, frame, pkt, f); + } + + /* flush the encoder */ + encode(c, NULL, pkt, f); + + /* add sequence end code to have a real MPEG file */ + if (codec->id == AV_CODEC_ID_MPEG1VIDEO || codec->id == AV_CODEC_ID_MPEG2VIDEO) + fwrite(endcode, 1, sizeof(endcode), f); + fclose(f); + + avcodec_free_context(&c); + av_frame_free(&frame); + av_packet_free(&pkt); + + return 0; +} diff --git a/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/extract_mvs.c b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/extract_mvs.c new file mode 100644 index 000000000..de31ccd2b --- /dev/null +++ b/ThirdParty/ffmpeg/arm/share/ffmpeg/examples/extract_mvs.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2012 Stefano Sabatini + * Copyright (c) 2014 Clément Bœsch + * + * 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 +#include + +static AVFormatContext *fmt_ctx = NULL; +static AVCodecContext *video_dec_ctx = NULL; +static AVStream *video_stream = NULL; +static const char *src_filename = NULL; + +static int video_stream_idx = -1; +static AVFrame *frame = NULL; +static int video_frame_count = 0; + +static int decode_packet(const AVPacket *pkt) +{ + int ret = avcodec_send_packet(video_dec_ctx, pkt); + if (ret < 0) { + fprintf(stderr, "Error while sending a packet to the decoder: %s\n", av_err2str(ret)); + return ret; + } + + while (ret >= 0) { + ret = avcodec_receive_frame(video_dec_ctx, frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } else if (ret < 0) { + fprintf(stderr, "Error while receiving a frame from the decoder: %s\n", av_err2str(ret)); + return ret; + } + + if (ret >= 0) { + int i; + AVFrameSideData *sd; + + video_frame_count++; + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_MOTION_VECTORS); + if (sd) { + const AVMotionVector *mvs = (const AVMotionVector *)sd->data; + for (i = 0; i < sd->size / sizeof(*mvs); i++) { + const AVMotionVector *mv = &mvs[i]; + printf("%d,%2d,%2d,%2d,%4d,%4d,%4d,%4d,0x%"PRIx64"\n", + video_frame_count, mv->source, + mv->w, mv->h, mv->src_x, mv->src_y, + mv->dst_x, mv->dst_y, mv->flags); + } + } + av_frame_unref(frame); + } + } + + return 0; +} + +static int open_codec_context(AVFormatContext *fmt_ctx, enum AVMediaType type) +{ + int ret; + AVStream *st; + AVCodecContext *dec_ctx = NULL; + AVCodec *dec = NULL; + AVDictionary *opts = NULL; + + ret = av_find_best_stream(fmt_ctx, type, -1, -1, &dec, 0); + if (ret < 0) { + fprintf(stderr, "Could not find %s stream in input file '%s'\n", + av_get_media_type_string(type), src_filename); + return ret; + } else { + int stream_idx = ret; + st = fmt_ctx->streams[stream_idx]; + + dec_ctx = avcodec_alloc_context3(dec); + if (!dec_ctx) { + fprintf(stderr, "Failed to allocate codec\n"); + return AVERROR(EINVAL); + } + + ret = avcodec_parameters_to_context(dec_ctx, st->codecpar); + if (ret < 0) { + fprintf(stderr, "Failed to copy codec parameters to codec context\n"); + return ret; + } + + /* Init the video decoder */ + av_dict_set(&opts, "flags2", "+export_mvs", 0); + if ((ret = avcodec_open2(dec_ctx, dec, &opts)) < 0) { + fprintf(stderr, "Failed to open %s codec\n", + av_get_media_type_string(type)); + return ret; + } + + video_stream_idx = stream_idx; + video_stream = fmt_ctx->streams[video_stream_idx]; + video_dec_ctx = dec_ctx; + } + + return 0; +} + +int main(int argc, char **argv) +{ + int ret = 0; + AVPacket pkt = { 0 }; + + if (argc != 2) { + fprintf(stderr, "Usage: %s