diff --git a/Assets/Plugins/NAudio.dll b/Assets/Plugins/NAudio.dll new file mode 100644 index 0000000..9425d40 Binary files /dev/null and b/Assets/Plugins/NAudio.dll differ diff --git a/Assets/Plugins/NAudio.dll.meta b/Assets/Plugins/NAudio.dll.meta new file mode 100644 index 0000000..186f540 --- /dev/null +++ b/Assets/Plugins/NAudio.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 2c0b0ac3e281b3f4c87f02a42e65baad +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/Version2.unity b/Assets/Scenes/Version2.unity index 77dec44..8aecf6e 100644 --- a/Assets/Scenes/Version2.unity +++ b/Assets/Scenes/Version2.unity @@ -249,6 +249,83 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3821828} m_CullTransparentMesh: 1 +--- !u!1 &4067202 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4067203} + - component: {fileID: 4067205} + - component: {fileID: 4067204} + m_Layer: 5 + m_Name: Content + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4067203 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4067202} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1566898430} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 1} +--- !u!114 &4067204 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4067202} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_ChildAlignment: 0 + m_Spacing: 0 + m_ChildForceExpandWidth: 1 + m_ChildForceExpandHeight: 1 + m_ChildControlWidth: 1 + m_ChildControlHeight: 0 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 +--- !u!114 &4067205 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4067202} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalFit: 0 + m_VerticalFit: 2 --- !u!1 &18660878 GameObject: m_ObjectHideFlags: 0 @@ -2160,6 +2237,7 @@ RectTransform: - {fileID: 1696082999} - {fileID: 1339030790} - {fileID: 755403585} + - {fileID: 829429200} m_Father: {fileID: 1300114664} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -3313,6 +3391,132 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 354788360} m_CullTransparentMesh: 1 +--- !u!1 &354822016 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 354822017} + - component: {fileID: 354822020} + - component: {fileID: 354822019} + - component: {fileID: 354822018} + m_Layer: 5 + m_Name: Scrollbar Vertical + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &354822017 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 354822016} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 2119296308} + m_Father: {fileID: 1678768230} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 0} + m_Pivot: {x: 1, y: 1} +--- !u!114 &354822018 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 354822016} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2a4db7a114972834c8e4117be1d82ba3, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1141489498} + m_HandleRect: {fileID: 1141489497} + m_Direction: 2 + m_Value: 0 + m_Size: 1 + m_NumberOfSteps: 0 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &354822019 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 354822016} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &354822020 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 354822016} + m_CullTransparentMesh: 1 --- !u!1 &399621053 GameObject: m_ObjectHideFlags: 0 @@ -3446,6 +3650,42 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 399621053} m_CullTransparentMesh: 1 +--- !u!1 &406861037 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 406861038} + m_Layer: 5 + m_Name: Sliding Area + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &406861038 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 406861037} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1958159930} + m_Father: {fileID: 523153148} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -20, y: -20} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &418000888 GameObject: m_ObjectHideFlags: 0 @@ -5198,6 +5438,132 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 504748474} m_CullTransparentMesh: 1 +--- !u!1 &523153147 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 523153148} + - component: {fileID: 523153151} + - component: {fileID: 523153150} + - component: {fileID: 523153149} + m_Layer: 5 + m_Name: Scrollbar Vertical + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &523153148 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 523153147} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 406861038} + m_Father: {fileID: 931406426} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 0} + m_Pivot: {x: 1, y: 1} +--- !u!114 &523153149 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 523153147} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2a4db7a114972834c8e4117be1d82ba3, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1958159931} + m_HandleRect: {fileID: 1958159930} + m_Direction: 2 + m_Value: 0 + m_Size: 1 + m_NumberOfSteps: 0 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &523153150 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 523153147} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &523153151 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 523153147} + m_CullTransparentMesh: 1 --- !u!1 &538060552 GameObject: m_ObjectHideFlags: 0 @@ -5481,6 +5847,140 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 544177723} m_CullTransparentMesh: 1 +--- !u!1 &544380814 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 544380815} + - component: {fileID: 544380817} + - component: {fileID: 544380816} + m_Layer: 5 + m_Name: Text (TMP) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &544380815 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 544380814} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 695996365} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &544380816 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 544380814} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: LiveSounds + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4281479730 + m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 24 + m_fontSizeBase: 24 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!222 &544380817 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 544380814} + m_CullTransparentMesh: 1 --- !u!1 &553103594 GameObject: m_ObjectHideFlags: 0 @@ -6369,6 +6869,139 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 685507282} m_CullTransparentMesh: 1 +--- !u!1 &695996364 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 695996365} + - component: {fileID: 695996368} + - component: {fileID: 695996367} + - component: {fileID: 695996366} + m_Layer: 5 + m_Name: LiveSounds + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &695996365 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 695996364} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 544380815} + m_Father: {fileID: 1200264883} + m_RootOrder: 8 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &695996366 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 695996364} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 695996367} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1564959567} + m_TargetAssemblyTypeName: UmaViewerUI, Assembly-CSharp + m_MethodName: ToggleUIPanel + m_Mode: 2 + m_Arguments: + m_ObjectArgument: {fileID: 829429199} + m_ObjectArgumentAssemblyTypeName: UnityEngine.GameObject, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!114 &695996367 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 695996364} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &695996368 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 695996364} + m_CullTransparentMesh: 1 --- !u!1 &696629172 GameObject: m_ObjectHideFlags: 0 @@ -7810,6 +8443,43 @@ MonoBehaviour: m_EditorClassIdentifier: m_HorizontalFit: 0 m_VerticalFit: 2 +--- !u!1 &829429199 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 829429200} + m_Layer: 5 + m_Name: LiveSound + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &829429200 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 829429199} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1678768230} + - {fileID: 931406426} + m_Father: {fileID: 242004464} + m_RootOrder: 8 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &835845605 GameObject: m_ObjectHideFlags: 0 @@ -8720,6 +9390,114 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &931406425 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 931406426} + - component: {fileID: 931406428} + - component: {fileID: 931406427} + - component: {fileID: 931406429} + m_Layer: 5 + m_Name: LiveCharaSound List + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &931406426 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 931406425} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1537165047} + - {fileID: 523153148} + m_Father: {fileID: 829429200} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 398.68, y: -30} + m_SizeDelta: {x: 255, y: -60} + m_Pivot: {x: 0, y: 0.5} +--- !u!114 &931406427 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 931406425} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 0.392} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &931406428 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 931406425} + m_CullTransparentMesh: 1 +--- !u!114 &931406429 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 931406425} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1aa08ab6e0800fa44ae55d278d1423e3, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Content: {fileID: 1297525977} + m_Horizontal: 0 + m_Vertical: 1 + m_MovementType: 2 + m_Elasticity: 0.1 + m_Inertia: 1 + m_DecelerationRate: 0.135 + m_ScrollSensitivity: 90 + m_Viewport: {fileID: 1537165047} + m_HorizontalScrollbar: {fileID: 0} + m_VerticalScrollbar: {fileID: 523153149} + m_HorizontalScrollbarVisibility: 2 + m_VerticalScrollbarVisibility: 2 + m_HorizontalScrollbarSpacing: -3 + m_VerticalScrollbarSpacing: -3 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] --- !u!1 &952551663 GameObject: m_ObjectHideFlags: 0 @@ -9664,6 +10442,81 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: -20, y: -20} m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &1141489496 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1141489497} + - component: {fileID: 1141489499} + - component: {fileID: 1141489498} + m_Layer: 5 + m_Name: Handle + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1141489497 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1141489496} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 2119296308} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1141489498 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1141489496} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1141489499 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1141489496} + m_CullTransparentMesh: 1 --- !u!1 &1148283644 GameObject: m_ObjectHideFlags: 0 @@ -10007,7 +10860,7 @@ MonoBehaviour: m_HandleRect: {fileID: 1882365342} m_Direction: 2 m_Value: 1 - m_Size: 0.66019416 + m_Size: 0.6601942 m_NumberOfSteps: 0 m_OnValueChanged: m_PersistentCalls: @@ -10163,6 +11016,7 @@ RectTransform: - {fileID: 2011080537} - {fileID: 1259881231} - {fileID: 886856742} + - {fileID: 695996365} m_Father: {fileID: 733646225} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -10806,6 +11660,83 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1286086648} m_CullTransparentMesh: 1 +--- !u!1 &1297525976 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1297525977} + - component: {fileID: 1297525979} + - component: {fileID: 1297525978} + m_Layer: 5 + m_Name: Content + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1297525977 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1297525976} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1537165047} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 1} +--- !u!114 &1297525978 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1297525976} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_ChildAlignment: 0 + m_Spacing: 0 + m_ChildForceExpandWidth: 1 + m_ChildForceExpandHeight: 1 + m_ChildControlWidth: 1 + m_ChildControlHeight: 0 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 +--- !u!114 &1297525979 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1297525976} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalFit: 0 + m_VerticalFit: 2 --- !u!1 &1300114663 GameObject: m_ObjectHideFlags: 0 @@ -12535,6 +13466,96 @@ MonoBehaviour: m_EditorClassIdentifier: m_HorizontalFit: 0 m_VerticalFit: 2 +--- !u!1 &1537165046 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1537165047} + - component: {fileID: 1537165050} + - component: {fileID: 1537165049} + - component: {fileID: 1537165048} + m_Layer: 5 + m_Name: Viewport + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1537165047 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1537165046} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1297525977} + m_Father: {fileID: 931406426} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -17, y: 0} + m_Pivot: {x: 0, y: 1} +--- !u!114 &1537165048 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1537165046} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3} + m_Name: + m_EditorClassIdentifier: + m_ShowMaskGraphic: 0 +--- !u!114 &1537165049 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1537165046} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1537165050 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1537165046} + m_CullTransparentMesh: 1 --- !u!1 &1558324556 GameObject: m_ObjectHideFlags: 0 @@ -12715,6 +13736,8 @@ MonoBehaviour: SceneList: {fileID: 835845609} FacialList: {fileID: 1684693617} LiveList: {fileID: 1970310175} + LiveSoundList: {fileID: 1678768233} + LiveCharaSoundList: {fileID: 931406429} SSWidth: {fileID: 0} SSHeight: {fileID: 0} TogglablePanels: @@ -12726,6 +13749,7 @@ MonoBehaviour: - {fileID: 1696082998} - {fileID: 1339030789} - {fileID: 755403584} + - {fileID: 829429199} UmaContainerPrefab: {fileID: 648541853701480401, guid: 1b51367a8eeaf0146b7bff4005281a2c, type: 3} UmaContainerSliderPrefab: {fileID: 648541853701480401, guid: 75962a0f68455494db9858ad95fb4ae8, @@ -12756,7 +13780,98 @@ MonoBehaviour: CurrentUMAContainer: {fileID: 0} CurrentLiveContainer: {fileID: 0} CurrentOtherContainer: {fileID: 0} + CurrentAudioSource: {fileID: 0} OverrideController: {fileID: 22100000, guid: 130057757529b0f40a4fdc08bf0861ef, type: 2} +--- !u!1 &1566898429 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1566898430} + - component: {fileID: 1566898433} + - component: {fileID: 1566898432} + - component: {fileID: 1566898431} + m_Layer: 5 + m_Name: Viewport + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1566898430 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1566898429} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4067203} + m_Father: {fileID: 1678768230} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -17, y: 0} + m_Pivot: {x: 0, y: 1} +--- !u!114 &1566898431 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1566898429} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3} + m_Name: + m_EditorClassIdentifier: + m_ShowMaskGraphic: 0 +--- !u!114 &1566898432 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1566898429} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1566898433 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1566898429} + m_CullTransparentMesh: 1 --- !u!1 &1576308332 GameObject: m_ObjectHideFlags: 0 @@ -13937,6 +15052,114 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1677453984} m_CullTransparentMesh: 1 +--- !u!1 &1678768229 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1678768230} + - component: {fileID: 1678768232} + - component: {fileID: 1678768231} + - component: {fileID: 1678768233} + m_Layer: 5 + m_Name: LiveSound List + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1678768230 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1678768229} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1566898430} + - {fileID: 354822017} + m_Father: {fileID: 829429200} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 0, y: -30} + m_SizeDelta: {x: 398.6807, y: -60} + m_Pivot: {x: 0, y: 0.5} +--- !u!114 &1678768231 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1678768229} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 0.392} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1678768232 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1678768229} + m_CullTransparentMesh: 1 +--- !u!114 &1678768233 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1678768229} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1aa08ab6e0800fa44ae55d278d1423e3, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Content: {fileID: 4067203} + m_Horizontal: 0 + m_Vertical: 1 + m_MovementType: 2 + m_Elasticity: 0.1 + m_Inertia: 1 + m_DecelerationRate: 0.135 + m_ScrollSensitivity: 90 + m_Viewport: {fileID: 1566898430} + m_HorizontalScrollbar: {fileID: 0} + m_VerticalScrollbar: {fileID: 354822018} + m_HorizontalScrollbarVisibility: 2 + m_VerticalScrollbarVisibility: 2 + m_HorizontalScrollbarSpacing: -3 + m_VerticalScrollbarSpacing: -3 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] --- !u!1 &1684693613 GameObject: m_ObjectHideFlags: 0 @@ -16024,6 +17247,81 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1949291610} m_CullTransparentMesh: 1 +--- !u!1 &1958159929 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1958159930} + - component: {fileID: 1958159932} + - component: {fileID: 1958159931} + m_Layer: 5 + m_Name: Handle + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1958159930 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1958159929} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 406861038} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1958159931 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1958159929} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1958159932 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1958159929} + m_CullTransparentMesh: 1 --- !u!1 &1970310173 GameObject: m_ObjectHideFlags: 0 @@ -17011,6 +18309,42 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2108058672} m_CullTransparentMesh: 1 +--- !u!1 &2119296307 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2119296308} + m_Layer: 5 + m_Name: Sliding Area + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2119296308 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2119296307} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1141489497} + m_Father: {fileID: 354822017} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -20, y: -20} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &2126459760 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/Audio.meta b/Assets/Scripts/Audio.meta new file mode 100644 index 0000000..7cdc4ad --- /dev/null +++ b/Assets/Scripts/Audio.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e9030d1ab7c7dce4eb3d45b93c88e3c2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/ClHcaSharp.meta b/Assets/Scripts/Audio/ClHcaSharp.meta new file mode 100644 index 0000000..a025c4b --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7fc4162661dc2f8469c9c2f7f36d6357 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/ClHcaSharp/Ath.cs b/Assets/Scripts/Audio/ClHcaSharp/Ath.cs new file mode 100644 index 0000000..8f9600c --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/Ath.cs @@ -0,0 +1,115 @@ +using System; +using static ClHcaSharp.Constants; + +namespace ClHcaSharp +{ + internal static class Ath + { + private static readonly byte[] baseCurve = new byte[656] + { + 0x78, 0x5F, 0x56, 0x51, 0x4E, 0x4C, 0x4B, 0x49, 0x48, 0x48, 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, + 0x44, 0x44, 0x44, 0x44, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, + 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, + 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, + 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, + 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43, + 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, + 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x4A, 0x4A, 0x4A, 0x4A, + 0x4A, 0x4A, 0x4A, 0x4A, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, + 0x4C, 0x4D, 0x4D, 0x4D, 0x4D, 0x4D, 0x4D, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4F, 0x4F, 0x4F, + 0x4F, 0x4F, 0x4F, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, + 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x56, + 0x56, 0x56, 0x56, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x5A, + 0x5A, 0x5A, 0x5A, 0x5B, 0x5B, 0x5B, 0x5B, 0x5C, 0x5C, 0x5C, 0x5D, 0x5D, 0x5D, 0x5D, 0x5E, 0x5E, + 0x5E, 0x5F, 0x5F, 0x5F, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, 0x63, + 0x63, 0x64, 0x64, 0x64, 0x65, 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x69, + 0x69, 0x6A, 0x6A, 0x6A, 0x6B, 0x6B, 0x6B, 0x6C, 0x6C, 0x6D, 0x6D, 0x6D, 0x6E, 0x6E, 0x6F, 0x6F, + 0x70, 0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x76, 0x76, + 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x7A, 0x7A, 0x7B, 0x7B, 0x7C, 0x7C, 0x7D, 0x7D, 0x7E, + 0x7E, 0x7F, 0x7F, 0x80, 0x80, 0x81, 0x81, 0x82, 0x83, 0x83, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, + 0x87, 0x88, 0x88, 0x89, 0x89, 0x8A, 0x8A, 0x8B, 0x8C, 0x8C, 0x8D, 0x8D, 0x8E, 0x8F, 0x8F, 0x90, + 0x90, 0x91, 0x92, 0x92, 0x93, 0x94, 0x94, 0x95, 0x95, 0x96, 0x97, 0x97, 0x98, 0x99, 0x99, 0x9A, + 0x9B, 0x9B, 0x9C, 0x9D, 0x9D, 0x9E, 0x9F, 0xA0, 0xA0, 0xA1, 0xA2, 0xA2, 0xA3, 0xA4, 0xA5, 0xA5, + 0xA6, 0xA7, 0xA7, 0xA8, 0xA9, 0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAE, 0xAF, 0xB0, 0xB1, 0xB1, + 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, + 0xC0, 0xC1, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, + 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, + 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xED, 0xEE, + 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFF, 0xFF + }; + + public static byte[] Init(int type, int sampleRate) + { + return type switch + { + 0 => Init0(), + 1 => Init1(sampleRate), + _ => throw new ArgumentException("Invalid ATH type."), + }; + } + + private static byte[] Init0() + { + byte[] athCurve = new byte[SamplesPerSubframe]; + for (int i = 0; i < athCurve.Length; i++) + { + athCurve[i] = 0; + } + + return athCurve; + } + + private static byte[] Init1(int sampleRate) + { + byte[] athCurve = new byte[SamplesPerSubframe]; + int acc = 0; + + for (int i = 0; i < athCurve.Length; i++) + { + acc += sampleRate; + int index = acc >> 13; + + if (index >= 654) + { + Fill(athCurve, i, SamplesPerSubframe - i,0xFF); + break; + } + athCurve[i] = baseCurve[index]; + } + + return athCurve; + } + + public static void Fill(T[] array, int start, int end, T value) + { + if (array == null) + { + throw new ArgumentNullException("array"); + } + if (start < 0 || start >= end) + { + throw new ArgumentOutOfRangeException("fromIndex"); + } + if (end >= array.Length) + { + throw new ArgumentOutOfRangeException("toIndex"); + } + for (int i = start; i < end; i++) + { + array[i] = value; + } + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Audio/ClHcaSharp/Ath.cs.meta b/Assets/Scripts/Audio/ClHcaSharp/Ath.cs.meta new file mode 100644 index 0000000..aa03b9a --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/Ath.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a39556ddea0390c469c2d1363dedb0c0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/ClHcaSharp/BitReader.cs b/Assets/Scripts/Audio/ClHcaSharp/BitReader.cs new file mode 100644 index 0000000..e0fc9b2 --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/BitReader.cs @@ -0,0 +1,107 @@ +namespace ClHcaSharp +{ + internal class BitReader + { + private static readonly uint[] mask32 = new uint[] + { + 0xFFFFFFFF, 0x7FFFFFFF, 0x3FFFFFFF, 0x1FFFFFFF, + 0x0FFFFFFF, 0x07FFFFFF, 0x03FFFFFF, 0x01FFFFFF + }; + + private static readonly uint[] mask24 = new uint[] + { + 0xFFFFFF, 0x7FFFFF, 0x3FFFFF, 0x1FFFFF, + 0x0FFFFF, 0x07FFFF, 0x03FFFF, 0x01FFFF + }; + + private static readonly uint[] mask16 = new uint[] + { + 0xFFFF, 0x7FFF, 0x3FFF, 0x1FFF, + 0x0FFF, 0x07FF, 0x03FF, 0x01FF + }; + + private static readonly uint[] mask8 = new uint[] + { + 0xFF, 0x7F, 0x3F, 0x1F, + 0x0F, 0x07, 0x03, 0x01 + }; + + private readonly byte[] data; + private readonly int dataSize; + private int currentBit; + + public BitReader(byte[] data) + { + this.data = data; + dataSize = data.Length * 8; + } + + public int Bit => currentBit; + + public int Peek(int bits) + { + int bit = currentBit; + int bitsRemaining = bit & 7; + int size = dataSize; + + uint value = 0; + + if (bit + bits <= size) + { + int bitOffset = bits + bitsRemaining; + int bitLeft = size - bit; + + if (bitLeft >= 32 && bitOffset >= 25) + { + int dataOffset = bit >> 3; + value = data[dataOffset]; + value = (value << 8) | data[dataOffset + 1]; + value = (value << 8) | data[dataOffset + 2]; + value = (value << 8) | data[dataOffset + 3]; + value &= mask32[bitsRemaining]; + value >>= 32 - bitsRemaining - bits; + } + else if (bitLeft >= 24 && bitOffset >= 17) + { + int dataOffset = bit >> 3; + value = data[dataOffset]; + value = (value << 8) | data[dataOffset + 1]; + value = (value << 8) | data[dataOffset + 2]; + value &= mask24[bitsRemaining]; + value >>= 24 - bitsRemaining - bits; + } + else if (bitLeft >= 16 && bitOffset >= 9) + { + int dataOffset = bit >> 3; + value = data[dataOffset]; + value = (value << 8) | data[dataOffset + 1]; + value &= mask16[bitsRemaining]; + value >>= 16 - bitsRemaining - bits; + } + else + { + int dataOffset = bit >> 3; + value = data[dataOffset]; + value &= mask8[bitsRemaining]; + value >>= 8 - bitsRemaining - bits; + } + + return (int)value; + } + + return (int)value; + } + + public int Read(int bits) + { + int value = Peek(bits); + currentBit += bits; + return value; + } + + public void Skip(int bits) + { + currentBit += bits; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Audio/ClHcaSharp/BitReader.cs.meta b/Assets/Scripts/Audio/ClHcaSharp/BitReader.cs.meta new file mode 100644 index 0000000..cdb8f4c --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/BitReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cc08b7eb89558ea4d88cb74e8379b9bb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/ClHcaSharp/Channel.cs b/Assets/Scripts/Audio/ClHcaSharp/Channel.cs new file mode 100644 index 0000000..34c42c3 --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/Channel.cs @@ -0,0 +1,29 @@ +using static ClHcaSharp.Constants; + +namespace ClHcaSharp +{ + internal class Channel + { + public ChannelType Type { get; set; } + public int CodedCount { get; set; } + + public byte[] Intensity { get; } = new byte[Subframes]; + public byte[] ScaleFactors { get; } = new byte[SamplesPerSubframe]; + public byte[] Resolution { get; } = new byte[SamplesPerSubframe]; + public byte[] Noises { get; } = new byte[SamplesPerSubframe]; + public int NoiseCount { get; set; } + public int ValidCount { get; set; } + + public float[] Gain { get; } = new float[SamplesPerSubframe]; + public float[][] Spectra { get; } = + JaggedArray.CreateJaggedArray( + Subframes, SamplesPerSubframe); + public float[] Temp { get; } = new float[SamplesPerSubframe]; + public float[] Dct { get; } = new float[SamplesPerSubframe]; + public float[] ImdctPrevious { get; } = new float[SamplesPerSubframe]; + + public float[][] Wave { get; } = + JaggedArray.CreateJaggedArray( + Subframes, SamplesPerSubframe); + } +} \ No newline at end of file diff --git a/Assets/Scripts/Audio/ClHcaSharp/Channel.cs.meta b/Assets/Scripts/Audio/ClHcaSharp/Channel.cs.meta new file mode 100644 index 0000000..782f2b0 --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/Channel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 498326e4dde9bc442b3552cb8902aabb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/ClHcaSharp/Cipher.cs b/Assets/Scripts/Audio/ClHcaSharp/Cipher.cs new file mode 100644 index 0000000..29cfa4f --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/Cipher.cs @@ -0,0 +1,139 @@ +using System; + +namespace ClHcaSharp +{ + internal static class Cipher + { + public static void Decrypt(byte[] cipherData, byte[] data) + { + for (int i = 0; i < data.Length; i++) + { + data[i] = cipherData[data[i]]; + } + } + + public static byte[] Init(int type, ulong keyCode) + { + if (type == 56 && keyCode == 0) type = 0; + + return type switch + { + 0 => Init0(), + 1 => Init1(), + 56 => Init56(keyCode), + _ => throw new ArgumentException("Invalid cipher type."), + }; + } + + private static byte[] Init0() + { + byte[] cipherTable = new byte[256]; + + for (int i = 0; i < 256; i++) + { + cipherTable[i] = (byte)i; + } + + return cipherTable; + } + + private static byte[] Init1() + { + byte[] cipherTable = new byte[256]; + + const int mul = 13; + const int add = 11; + + uint v = 0; + for (int i = 1; i < 256 - 1; i++) + { + v = (v * mul + add) & 0xFF; + if (v == 0 || v == 0xFF) + v = (v * mul + add) & 0xFF; + cipherTable[i] = (byte)v; + } + cipherTable[0] = 0; + cipherTable[0xFF] = 0xFF; + + return cipherTable; + } + + private static void Init56CreateTable(byte[] table, byte key) + { + int mul = ((key & 1) << 3) | 5; + int add = (key & 0xE) | 1; + + key >>= 4; + + for (int i = 0; i < 16; i++) + { + key = (byte)((key * mul + add) & 0xF); + table[i] = key; + } + } + + private static byte[] Init56(ulong keyCode) + { + byte[] cipherTable = new byte[256]; + + byte[] kc = new byte[8]; + byte[] seed = new byte[16]; + byte[] baseTable = new byte[256]; + byte[] baseTableR = new byte[16]; + byte[] baseTableC = new byte[16]; + + if (keyCode != 0) keyCode--; + + for (int r = 0; r < (8 - 1); r++) + { + kc[r] = (byte)(keyCode & 0xFF); + keyCode >>= 8; + } + + seed[0x00] = kc[1]; + seed[0x01] = (byte)(kc[1] ^ kc[6]); + seed[0x02] = (byte)(kc[2] ^ kc[3]); + seed[0x03] = kc[2]; + seed[0x04] = (byte)(kc[2] ^ kc[1]); + seed[0x05] = (byte)(kc[3] ^ kc[4]); + seed[0x06] = kc[3]; + seed[0x07] = (byte)(kc[3] ^ kc[2]); + seed[0x08] = (byte)(kc[4] ^ kc[5]); + seed[0x09] = kc[4]; + seed[0x0A] = (byte)(kc[4] ^ kc[3]); + seed[0x0B] = (byte)(kc[5] ^ kc[6]); + seed[0x0C] = kc[5]; + seed[0x0D] = (byte)(kc[5] ^ kc[4]); + seed[0x0E] = (byte)(kc[6] ^ kc[1]); + seed[0x0F] = kc[6]; + + Init56CreateTable(baseTableR, kc[0]); + + for (int r = 0; r < 16; r++) + { + byte nb; + Init56CreateTable(baseTableC, seed[r]); + nb = (byte)(baseTableR[r] << 4); + for (int c = 0; c < 16; c++) + { + baseTable[r * 16 + c] = (byte)(nb | baseTableC[c]); + } + } + + uint x = 0; + uint pos = 1; + + for (int i = 0; i < 256; i++) + { + x = (x + 17) & 0xFF; + if (baseTable[x] != 0 && baseTable[x] != 0xFF) + cipherTable[pos++] = baseTable[x]; + } + + cipherTable[0] = 0; + cipherTable[0xFF] = 0xFF; + + return cipherTable; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Audio/ClHcaSharp/Cipher.cs.meta b/Assets/Scripts/Audio/ClHcaSharp/Cipher.cs.meta new file mode 100644 index 0000000..16efa8e --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/Cipher.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2df350f56f9f56749b44a838345aa543 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/ClHcaSharp/Crc.cs b/Assets/Scripts/Audio/ClHcaSharp/Crc.cs new file mode 100644 index 0000000..cade072 --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/Crc.cs @@ -0,0 +1,37 @@ +namespace ClHcaSharp +{ + internal static class Crc + { + private static readonly ushort[] crcMaskTable = new ushort[] + { + 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, + 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, + 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, + 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, + 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, + 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, + 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, + 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, + 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 + }; + + public static ushort Crc16Checksum(byte[] data) + { + ushort sum = 0; + + for (int i = 0; i < data.Length; i++) + { + sum = (ushort)((sum << 8) ^ crcMaskTable[(sum >> 8) ^ data[i]]); + } + + return sum; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Audio/ClHcaSharp/Crc.cs.meta b/Assets/Scripts/Audio/ClHcaSharp/Crc.cs.meta new file mode 100644 index 0000000..55b2e7f --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/Crc.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 12364682399d45244bee9579676101b4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/ClHcaSharp/Definitions.cs b/Assets/Scripts/Audio/ClHcaSharp/Definitions.cs new file mode 100644 index 0000000..cbb1ab6 --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/Definitions.cs @@ -0,0 +1,42 @@ +using Assets.Scripts.Audio.CriWareFormats.Common; +using System; + +namespace ClHcaSharp +{ + internal static class Constants + { + public const int Version101 = 0x0101; + public const int Version102 = 0x0102; + public const int Version103 = 0x0103; + public const int Version200 = 0x0200; + public const int Version300 = 0x0300; + + public const int MinFrameSize = 0x8; + public const int MaxFrameSize = 0xFFFF; + + public const int Mask = 0x7F7F7F7F; + public const int Subframes = 8; + public const int SamplesPerSubframe = 128; + public const int SamplesPerFrame = Subframes * SamplesPerSubframe; + public const int MdctBits = 7; + + public const int MinChannels = 1; + public const int MaxChannels = 16; + public const int MinSampleRate = 1; + public const int MaxSampleRate = 0x7FFFFF; + + public const int DefaultRandom = 1; + + public static readonly float MsStereoRatio = BinaryPrimitives.Int32BitsToSingle(0x3F3504F3); + + + } + + + public enum ChannelType + { + Discrete = 0, + StereoPrimary = 1, + StereoSecondary = 2 + } +} \ No newline at end of file diff --git a/Assets/Scripts/Audio/ClHcaSharp/Definitions.cs.meta b/Assets/Scripts/Audio/ClHcaSharp/Definitions.cs.meta new file mode 100644 index 0000000..3456ddc --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/Definitions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9097dc5440eaa4241a8902e43ba0d270 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/ClHcaSharp/HcaContext.cs b/Assets/Scripts/Audio/ClHcaSharp/HcaContext.cs new file mode 100644 index 0000000..406a10e --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/HcaContext.cs @@ -0,0 +1,53 @@ +namespace ClHcaSharp +{ + internal class HcaContext + { + public int Version { get; set; } + public int HeaderSize { get; set; } + + public int ChannelCount { get; set; } + public int SampleRate { get; set; } + public int FrameCount { get; set; } + public int EncoderDelay { get; set; } + public int EncoderPadding { get; set; } + + public int FrameSize { get; set; } + public int MinResolution { get; set; } + public int MaxResolution { get; set; } + public int TrackCount { get; set; } + public int ChannelConfig { get; set; } + public int StereoType { get; set; } + public int TotalBandCount { get; set; } + public int BaseBandCount { get; set; } + public int StereoBandCount { get; set; } + public int BandsPerHfrGroup { get; set; } + public int MsStereo { get; set; } + public int Reserved { get; set; } + + public int VbrMaxFrameSize { get; set; } + public int VbrNoiseLevel { get; set; } + + public int AthType { get; set; } + + public int LoopStartFrame { get; set; } + public int LoopEndFrame { get; set; } + public int LoopStartDelay { get; set; } + public int LoopEndPadding { get; set; } + public bool LoopFlag { get; set; } + + public int CiphType { get; set; } + public ulong KeyCode { get; set; } + + public float RvaVolume { get; set; } + + public int CommentLength { get; set; } + public string Comment { get; set; } + + public int HfrGroupCount { get; set; } + public byte[] AthCurve { get; set; } + public byte[] CipherTable { get; set; } + + public int Random { get; set; } + public Channel[] Channels { get; set; } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Audio/ClHcaSharp/HcaContext.cs.meta b/Assets/Scripts/Audio/ClHcaSharp/HcaContext.cs.meta new file mode 100644 index 0000000..0b94c39 --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/HcaContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 58df776bbf57b404fac50ddf009fff77 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/ClHcaSharp/HcaDecoder.cs b/Assets/Scripts/Audio/ClHcaSharp/HcaDecoder.cs new file mode 100644 index 0000000..3676a73 --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/HcaDecoder.cs @@ -0,0 +1,732 @@ +using System; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using static ClHcaSharp.Constants; +using static ClHcaSharp.Header; +using static ClHcaSharp.Tables; + +namespace ClHcaSharp +{ + public class HcaDecoder + { + private readonly HcaContext hca; + + public HcaDecoder(Stream hcaStream, ulong key) + { + hca = new HcaContext(); + + InitializeTables(); + + DecodeHeader(hca, hcaStream); + SetKey(key); + } + + public HcaInfo GetInfo() + { + HcaInfo info = new HcaInfo() + { + Version = hca.Version, + HeaderSize = hca.HeaderSize, + SamplingRate = hca.SampleRate, + ChannelCount = hca.ChannelCount, + BlockSize = hca.FrameSize, + BlockCount = hca.FrameCount, + EncoderDelay = hca.EncoderDelay, + EncoderPadding = hca.EncoderPadding, + LoopEnabled = hca.LoopFlag, + LoopStartBlock = hca.LoopStartFrame, + LoopEndBlock = hca.LoopEndFrame, + LoopStartDelay = hca.LoopStartDelay, + LoopEndPadding = hca.LoopEndPadding, + SamplesPerBlock = SamplesPerFrame, + Comment = hca.Comment, + EncryptionEnabled = hca.CiphType == 56 + }; + return info; + } + + public void SetKey(ulong key) + { + hca.KeyCode = key; + hca.CipherTable = Cipher.Init(hca.CiphType, hca.KeyCode); + } + + public int TestBlock(byte[] data) + { + const int frameSamples = Subframes * SamplesPerSubframe; + const float scale = 32768.0f; + + int status; + int clips = 0; + int blanks = 0; + int[] channelBlanks = new int[MaxChannels]; + + byte[] buffer = data; + + { + bool isEmpty = true; + + for (int i = 0x02; i < data.Length - 0x02; i++) + { + if (buffer[i] != 0) + { + isEmpty = false; + break; + } + } + + if (isEmpty) return 0; + } + + status = DecodeBlockUnpack(data); + if (status < 0) return -1; + + { + int bitsMax = hca.FrameSize * 8; + int byteStart; + + if (status + 14 > bitsMax) + return -1; + //throw new Exception("BitReader error."); + + byteStart = (status / 8) + (status % 8 > 0 ? 0x01 : 0); + + for (int i = byteStart; i < hca.FrameSize - 0x02; i++) + { + if (buffer[i] != 0) + return -1; + } + } + + DecodeBlockTransform(); + for (int ch = 0; ch < hca.ChannelCount; ch++) + { + for (int sf = 0; sf < Subframes; sf++) + { + for (int s = 0; s < SamplesPerSubframe; s++) + { + float fSample = hca.Channels[ch].Wave[sf][s]; + + if (fSample > 1.0f || fSample < -1.0f) + clips++; + else + { + int pSample = (int)(fSample * scale); + if (pSample == 0 || pSample == -1) + { + blanks++; + channelBlanks[ch]++; + } + } + } + } + } + + if (clips == 1) + clips++; + if (clips > 1) + return clips; + + if (blanks == hca.ChannelCount * frameSamples) + return 0; + + if (hca.ChannelCount >= 2) + { + if (channelBlanks[0] == frameSamples && channelBlanks[1] != frameSamples) + return 3; + } + + return 1; + } + + public void ReadSamples16(short[][] samples) + { + for (int subframe = 0; subframe < Subframes; subframe++) + { + for (int sample = 0; sample < SamplesPerSubframe; sample++) + { + for (int channel = 0; channel < hca.ChannelCount; channel++) + { + float f = hca.Channels[channel].Wave[subframe][sample]; + int sInt = (int)(32768 * f); + if (sInt > 32767) + sInt = 32767; + else if (sInt < -32767) + sInt = -32767; + samples[channel][(SamplesPerSubframe * subframe) + sample] = (short)sInt; + } + } + } + } + + public void ReadSamples16(short[] samples) + { + int sampleIndex = 0; + for (int subframe = 0; subframe < Subframes; subframe++) + { + for (int sample = 0; sample < SamplesPerSubframe; sample++) + { + for (int channel = 0; channel < hca.ChannelCount; channel++) + { + float f = hca.Channels[channel].Wave[subframe][sample]; + int sInt = (int)(32768 * f); + if (sInt > 32767) + sInt = 32767; + else if (sInt < -32767) + sInt = -32767; + samples[sampleIndex++] = (short)sInt; + } + } + } + } + + public float[] ReadSamples16() + { + List data = new List(); + for (int subframe = 0; subframe < Subframes; subframe++) + { + for (int sample = 0; sample < SamplesPerSubframe; sample++) + { + for (int channel = 0; channel < hca.ChannelCount; channel++) + { + float f = hca.Channels[channel].Wave[subframe][sample]; + data.Add(f); + } + } + } + return data.ToArray(); + } + + public void DecodeReset() + { + if (hca != null) + { + hca.Random = DefaultRandom; + + for (int i = 0; i < hca.ChannelCount; i++) + { + Channel channel = hca.Channels[i]; + + Array.Clear(channel.ImdctPrevious, 0, channel.ImdctPrevious.Length); + } + } + } + + public int DecodeBlock(byte[] data) + { + int result = DecodeBlockUnpack(data); + + if (result < 0) return result; + + DecodeBlockTransform(); + + return result; + } + + private int DecodeBlockUnpack(byte[] data) + { + if (data.Length < hca.FrameSize) + throw new ArgumentException("Data is less than expected frame size."); + + BitReader bitReader = new BitReader(data); + + ushort sync = (ushort)bitReader.Read(16); + if (sync != 0xFFFF) throw new InvalidDataException("Sync error."); + + if (Crc.Crc16Checksum(data) > 0) throw new InvalidDataException("Checksum error."); + + Cipher.Decrypt(hca.CipherTable, data); + + int frameAcceptableNoiseLevel = bitReader.Read(9); + int frameEvaluationBoundary = bitReader.Read(7); + + int packedNoiseLevel = (frameAcceptableNoiseLevel << 8) - frameEvaluationBoundary; + + for (int channel = 0; channel < hca.ChannelCount; channel++) + { + UnpackScaleFactors(hca.Channels[channel], bitReader, hca.HfrGroupCount, hca.Version); + UnpackIntensity(hca.Channels[channel], bitReader, hca.HfrGroupCount, hca.Version); + CalculateResolution(hca.Channels[channel], packedNoiseLevel, hca.AthCurve, hca.MinResolution, hca.MaxResolution); + CalculateGain(hca.Channels[channel]); + } + + for (int subframe = 0; subframe < Subframes; subframe++) + { + for (int channel = 0; channel < hca.ChannelCount; channel++) + { + DequantizeCoefficients(hca.Channels[channel], bitReader, subframe); + } + } + + return bitReader.Bit; + } + + private void DecodeBlockTransform() + { + for (int subframe = 0; subframe < Subframes; subframe++) + { + for (int channel = 0; channel < hca.ChannelCount; channel++) + { + int random = hca.Random; + ReconstructNoise(hca.Channels[channel], hca.MinResolution, hca.MsStereo, ref random, subframe); + hca.Random = random; + ReconstructHighFrequency(hca.Channels[channel], hca.HfrGroupCount, hca.BandsPerHfrGroup, + hca.StereoBandCount, hca.BaseBandCount, hca.TotalBandCount, hca.Version, subframe); + } + + if (hca.StereoBandCount > 0) + { + for (int ch = 0; ch < hca.ChannelCount - 1; ch++) + { + ApplyIntensityStereo(hca.Channels, ch * 2, subframe, hca.BaseBandCount, hca.TotalBandCount); + ApplyMsStereo(hca.Channels, ch * 2, hca.MsStereo, hca.BaseBandCount, hca.TotalBandCount, subframe); + } + } + + for (int channel = 0; channel < hca.ChannelCount; channel++) + { + ImdctTransform(hca.Channels[channel], subframe); + } + } + } + + private static void UnpackScaleFactors(Channel channel, BitReader bitReader, int hfrGroupCount, int version) + { + int csCount = channel.CodedCount; + int extraCount; + byte deltaBits = (byte)bitReader.Read(3); + + if (channel.Type == ChannelType.StereoSecondary || hfrGroupCount <= 0 || version <= Version200) + extraCount = 0; + else + { + extraCount = hfrGroupCount; + csCount += extraCount; + + if (csCount > SamplesPerSubframe) + return; + //throw new InvalidDataException("Invalid scale count."); + } + + if (deltaBits >= 6) + { + for (int i = 0; i < csCount; i++) + { + channel.ScaleFactors[i] = (byte)bitReader.Read(6); + } + } + else if (deltaBits > 0) + { + byte expectedDelta = (byte)((1 << deltaBits) - 1); + byte value = (byte)bitReader.Read(6); + + channel.ScaleFactors[0] = value; + for (int i = 1; i < csCount; i++) + { + byte delta = (byte)bitReader.Read(deltaBits); + + if (delta == expectedDelta) + value = (byte)bitReader.Read(6); + else + { + int scaleFactorTest = value + (delta - (expectedDelta >> 1)); + if (scaleFactorTest < 0 || scaleFactorTest >= 64) + return; + //throw new InvalidDataException("Invalid scale factor."); + + value = (byte)(value - (expectedDelta >> 1) + delta); + value = (byte)(value & 0x3F); + } + + channel.ScaleFactors[i] = value; + } + } + else + { + for (int i = 0; i < SamplesPerSubframe; i++) + { + channel.ScaleFactors[i] = 0; + } + } + + for (int i = 0; i < extraCount; i++) + { + channel.ScaleFactors[SamplesPerSubframe - 1 - i] = channel.ScaleFactors[csCount - i]; + } + } + + private static void UnpackIntensity(Channel channel, BitReader bitReader, int hfrGroupCount, int version) + { + if (channel.Type == ChannelType.StereoSecondary) + { + if (version <= Version200) + { + byte value = (byte)bitReader.Peek(4); + + channel.Intensity[0] = value; + if (value < 15) + { + bitReader.Skip(4); + for (int i = 1; i < Subframes; i++) + { + channel.Intensity[i] = (byte)bitReader.Read(4); + } + } + } + else + { + byte value = (byte)bitReader.Peek(4); + + if (value < 15) + { + bitReader.Skip(4); + + byte deltaBits = (byte)bitReader.Read(2); + + channel.Intensity[0] = value; + if (deltaBits == 3) + { + for (int i = 1; i < Subframes; i++) + { + channel.Intensity[i] = (byte)bitReader.Read(4); + } + } + else + { + byte bMax = (byte)((2 << deltaBits) - 1); + byte bits = (byte)(deltaBits + 1); + + for (int i = 1; i < Subframes; i++) + { + byte delta = (byte)bitReader.Read(bits); + if (delta == bMax) + value = (byte)bitReader.Read(4); + else + { + value = (byte)(value - (bMax >> 1) + delta); + if (value > 15) + return; + //throw new InvalidDataException("Intensity value out of range."); + } + + channel.Intensity[i] = value; + } + } + } + else + { + bitReader.Skip(4); + for (int i = 0; i < Subframes; i++) + { + channel.Intensity[i] = 7; + } + } + } + } + else + { + if (version <= Version200) + { + byte[] hfrScales = channel.ScaleFactors; + int hfrScalesOffset = 128 - hfrGroupCount; + + for (int i = 0; i < hfrGroupCount; i++) + { + hfrScales[hfrScalesOffset + i] = (byte)bitReader.Read(6); + } + } + } + } + + private static void CalculateResolution(Channel channel, int packedNoiseLevel, byte[] athCurve, int minResolution, int maxResolution) + { + int crCount = channel.CodedCount; + int noiseCount = 0; + int validCount = 0; + + for (int i = 0; i < crCount; i++) + { + byte newResolution = 0; + byte scaleFactor = channel.ScaleFactors[i]; + + if (scaleFactor > 0) + { + int noiseLevel = athCurve[i] + ((packedNoiseLevel + i) >> 8); + int curvePosition = noiseLevel + 1 - ((5 * scaleFactor) >> 1); + + if (curvePosition < 0) + newResolution = 15; + else if (curvePosition <= 65) + newResolution = InvertTable[curvePosition]; + else + newResolution = 0; + + if (newResolution > maxResolution) + newResolution = (byte)maxResolution; + else if (newResolution < minResolution) + newResolution = (byte)minResolution; + + if (newResolution < 1) + { + channel.Noises[noiseCount] = (byte)i; + noiseCount++; + } + else + { + channel.Noises[SamplesPerSubframe - 1 - validCount] = (byte)i; + validCount++; + } + } + channel.Resolution[i] = newResolution; + } + + channel.NoiseCount = noiseCount; + channel.ValidCount = validCount; + + Array.Clear(channel.Resolution, crCount, SamplesPerSubframe - crCount); + } + + private static void CalculateGain(Channel channel) + { + int cgCount = channel.CodedCount; + for (int i = 0; i < cgCount; i++) + { + float scaleFactorScale = DequantizerScalingTable[channel.ScaleFactors[i]]; + float resolutionScale = DequantizerRangeTable[channel.Resolution[i]]; + channel.Gain[i] = scaleFactorScale * resolutionScale; + } + } + + private static void DequantizeCoefficients(Channel channel, BitReader bitReader, int subframe) + { + int ccCount = channel.CodedCount; + + for (int i = 0; i < ccCount; i++) + { + float qc; + byte resolution = channel.Resolution[i]; + byte bits = MaxBitTable[resolution]; + int code = bitReader.Read(bits); + + if (resolution > 7) + { + int signedCode = (1 - ((code & 1) << 1)) * (code >> 1); + if (signedCode == 0) + bitReader.Skip(-1); + qc = signedCode; + } + else + { + int index = (resolution << 4) + code; + int skip = ReadBitTable[index] - bits; + bitReader.Skip(skip); + qc = ReadValueTable[index]; + } + + channel.Spectra[subframe][i] = channel.Gain[i] * qc; + } + + Array.Clear(channel.Spectra[subframe], ccCount, SamplesPerSubframe - ccCount); + } + + private static void ReconstructNoise(Channel channel, int minResolution, int msStereo, ref int random, int subframe) + { + if (minResolution > 0) return; + if (channel.ValidCount <= 0 || channel.NoiseCount <= 0) return; + if (msStereo != 0 && channel.Type == ChannelType.StereoPrimary) return; + + for (int i = 0; i < channel.NoiseCount; i++) + { + random = 0x343FD * random + 0x269EC3; + + int randomIndex = SamplesPerSubframe - channel.ValidCount + (((random & 0x7FFF) * channel.ValidCount) >> 15); + + int noiseIndex = channel.Noises[i]; + int validIndex = channel.Noises[randomIndex]; + + int sfNoise = channel.ScaleFactors[noiseIndex]; + int sfValid = channel.ScaleFactors[validIndex]; + int scIndex = (sfNoise - sfValid + 62) & ~((sfNoise - sfValid + 62) >> 31); + + channel.Spectra[subframe][noiseIndex] = ScaleConversionTable[scIndex] * channel.Spectra[subframe][validIndex]; + } + } + + private static void ReconstructHighFrequency(Channel channel, int hfrGroupCount, int bandsPerHfrGroup, + int stereoBandCount, int baseBandCount, int totalBandCount, int version, int subframe) + { + if (bandsPerHfrGroup == 0) return; + if (channel.Type == ChannelType.StereoSecondary) return; + + int groupLimit; + int startBand = stereoBandCount + baseBandCount; + int highBand = startBand; + int lowBand = startBand - 1; + + int hfrScalesOffset = 128 - hfrGroupCount; + byte[] hfrScales = channel.ScaleFactors; + + if (version <= Version200) + groupLimit = hfrGroupCount; + else + { + groupLimit = hfrGroupCount >= 0 ? hfrGroupCount : hfrGroupCount + 1; + groupLimit >>= 1; + } + + for (int group = 0; group < hfrGroupCount; group++) + { + int lowBandSub = group < groupLimit ? 1 : 0; + + for (int i = 0; i < bandsPerHfrGroup; i++) + { + if (highBand >= totalBandCount || lowBand < 0) break; + + int scIndex = hfrScales[hfrScalesOffset + group]; + scIndex &= ~(scIndex >> 31); + + channel.Spectra[subframe][highBand] = ScaleConversionTable[scIndex] * channel.Spectra[subframe][lowBand]; + + highBand++; + lowBand -= lowBandSub; + } + } + + channel.Spectra[subframe][highBand - 1] = 0.0f; + } + + private static void ApplyIntensityStereo(Channel[] channelPair, int channelOffset, int subframe, int baseBandCount, int totalBandCount) + { + if (channelPair[channelOffset + 0].Type != ChannelType.StereoPrimary) return; + + float ratioL = IntensityRatioTable[channelPair[channelOffset + 1].Intensity[subframe]]; + float ratioR = 2.0f - ratioL; + float[] spectraL = channelPair[channelOffset + 0].Spectra[subframe]; + float[] spectraR = channelPair[channelOffset + 1].Spectra[subframe]; + + for (int band = baseBandCount; band < totalBandCount; band++) + { + float coefL = spectraL[band] * ratioL; + float coefR = spectraR[band] * ratioR; + spectraL[band] = coefL; + spectraR[band] = coefR; + } + } + + private static void ApplyMsStereo(Channel[] channelPair, int channelOffset, int msStereo, int baseBandCount, int totalBandCount, int subframe) + { + if (msStereo != 0) return; + if (channelPair[channelOffset + 0].Type != ChannelType.StereoPrimary) return; + + float ratio = MsStereoRatio; + float[] spectraL = channelPair[channelOffset + 0].Spectra[subframe]; + float[] spectraR = channelPair[channelOffset + 1].Spectra[subframe]; + + for (int band = baseBandCount; band < totalBandCount; band++) + { + float coefL = (spectraL[band] + spectraR[band]) * ratio; + float coefR = (spectraL[band] - spectraR[band]) * ratio; + spectraL[band] = coefL; + spectraR[band] = coefR; + } + } + + private static void ImdctTransform(Channel channel, int subframe) + { + const int size = SamplesPerSubframe; + const int half = SamplesPerSubframe / 2; + const int mdctBits = MdctBits; + + int count1 = 1; + int count2 = half; + float[] temp1 = channel.Spectra[subframe]; + float[] temp2 = channel.Temp; + int temp1Index = 0; + + for (int i = 0; i < mdctBits; i++) + { + float[] swap; + float[] d = temp2; + int d1Index = 0; + int d2Index = count2; + + for (int j = 0; j < count1; j++) + { + for (int k = 0; k < count2; k++) + { + float a = temp1[temp1Index++]; + float b = temp1[temp1Index++]; + d[d1Index++] = a + b; + d[d2Index++] = a - b; + } + + d1Index += count2; + d2Index += count2; + } + + temp1Index -= SamplesPerSubframe; + swap = temp1; + temp1 = temp2; + temp2 = swap; + + count1 <<= 1; + count2 >>= 1; + } + + count1 = half; + count2 = 1; + temp1 = channel.Temp; + temp2 = channel.Spectra[subframe]; + + for (int i = 0; i < mdctBits; i++) + { + int sinTableIndex = 0; + int cosTableIndex = 0; + + float[] swap; + float[] d = temp2; + float[] s = temp1; + int d1Index = 0; + int d2Index = count2 * 2 - 1; + int s1Index = 0; + int s2Index = count2; + + for (int j = 0; j < count1; j++) + { + for (int k = 0; k < count2; k++) + { + float a = s[s1Index++]; + float b = s[s2Index++]; + float sin = SinTable[i][sinTableIndex++]; + float cos = CosTable[i][cosTableIndex++]; + d[d1Index++] = a * sin - b * cos; + d[d2Index--] = a * cos + b * sin; + } + + s1Index += count2; + s2Index += count2; + d1Index += count2; + d2Index += count2 * 3; + } + + swap = temp1; + temp1 = temp2; + temp2 = swap; + + count1 >>= 1; + count2 <<= 1; + } + + float[] dct = channel.Spectra[subframe]; + float[] prev = channel.ImdctPrevious; + for (int i = 0; i < half; i++) + { + channel.Wave[subframe][i] = ImdctWindow[i] * dct[i + half] + prev[i]; + channel.Wave[subframe][i + half] = ImdctWindow[i + half] * dct[size - 1 - i] - prev[i + half]; + channel.ImdctPrevious[i] = ImdctWindow[size - 1 - i] * dct[half - i - 1]; + channel.ImdctPrevious[i + half] = ImdctWindow[half - i - 1] * dct[i]; + } + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Audio/ClHcaSharp/HcaDecoder.cs.meta b/Assets/Scripts/Audio/ClHcaSharp/HcaDecoder.cs.meta new file mode 100644 index 0000000..86fac35 --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/HcaDecoder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ac4b2f835ba632b46a03cf7190f73045 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/ClHcaSharp/HcaInfo.cs b/Assets/Scripts/Audio/ClHcaSharp/HcaInfo.cs new file mode 100644 index 0000000..6c593cb --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/HcaInfo.cs @@ -0,0 +1,26 @@ +namespace ClHcaSharp +{ + public class HcaInfo + { + public int Version { get; set; } + public int HeaderSize { get; set; } + public int SamplingRate { get; set; } + public int ChannelCount { get; set; } + public int BlockSize { get; set; } + public int BlockCount { get; set; } + public int EncoderDelay { get; set; } + public int EncoderPadding { get; set; } + public bool LoopEnabled { get; set; } + public int LoopStartBlock { get; set; } + public int LoopEndBlock { get; set; } + public int LoopStartDelay { get; set; } + public int LoopEndPadding { get; set; } + public int SamplesPerBlock { get; set; } + public string Comment { get; set; } + public bool EncryptionEnabled { get; set; } + + public int SampleCount => BlockCount * SamplesPerBlock - EncoderDelay - EncoderPadding; + public int LoopStartSample => LoopStartBlock * SamplesPerBlock - EncoderDelay + LoopStartDelay; + public int LoopEndSample => LoopEndBlock * SamplesPerBlock - EncoderDelay + (SamplesPerBlock - LoopEndPadding); + } +} \ No newline at end of file diff --git a/Assets/Scripts/Audio/ClHcaSharp/HcaInfo.cs.meta b/Assets/Scripts/Audio/ClHcaSharp/HcaInfo.cs.meta new file mode 100644 index 0000000..c32348e --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/HcaInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 951e0209b6d7c7e4e9d47d922cd7b490 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/ClHcaSharp/Header.cs b/Assets/Scripts/Audio/ClHcaSharp/Header.cs new file mode 100644 index 0000000..e13f9e5 --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/Header.cs @@ -0,0 +1,329 @@ +using System; +using System.IO; +using System.Text; +using static ClHcaSharp.Constants; + +namespace ClHcaSharp +{ + internal static class Header + { + public static bool IsHeaderValid(Stream hcaStream, out int headerSize) + { + BitReader bitReader = new BitReader(new BinaryReader(hcaStream).ReadBytes(8)); + + headerSize = 0; + if ((bitReader.Peek(32) & Mask) == StringToUInt32("HCA")) + { + bitReader.Skip(32 + 16); + headerSize = bitReader.Read(16); + + return true; + } + + return false; + } + + public static void DecodeHeader(HcaContext hca, Stream hcaStream) + { + if (!IsHeaderValid(hcaStream, out int headerSize)) + throw new InvalidDataException("Invalid HCA header."); + + BinaryReader binaryReader = new BinaryReader(hcaStream); + binaryReader.BaseStream.Position = 0; + + BitReader bitReader = new BitReader(binaryReader.ReadBytes((int)headerSize)); + + if ((bitReader.Peek(32) & Mask) == StringToUInt32("HCA")) + { + bitReader.Skip(32); + hca.Version = bitReader.Read(16); + hca.HeaderSize = bitReader.Read(16); + + headerSize -= 8; + } + else throw new InvalidDataException("Not an HCA file."); + + if (headerSize >= 16 && (bitReader.Peek(32) & Mask) == StringToUInt32("fmt")) + { + bitReader.Skip(32); + hca.ChannelCount = bitReader.Read(8); + hca.SampleRate = bitReader.Read(24); + hca.FrameCount = bitReader.Read(32); + hca.EncoderDelay = bitReader.Read(16); + hca.EncoderPadding = bitReader.Read(16); + + if (!(hca.ChannelCount >= MinChannels && hca.ChannelCount <= MaxChannels)) + throw new InvalidDataException("Invalid channel count."); + + if (hca.FrameCount == 0) + throw new InvalidDataException("Frame count is zero."); + + if (!(hca.SampleRate >= MinSampleRate && hca.SampleRate <= MaxSampleRate)) + throw new InvalidDataException("Invalid sample rate."); + + headerSize -= 16; + } + else throw new InvalidDataException("No format chunk."); + + if (headerSize >= 16 && (bitReader.Peek(32) & Mask) == StringToUInt32("comp")) + { + bitReader.Skip(32); + hca.FrameSize = bitReader.Read(16); + hca.MinResolution = bitReader.Read(8); + hca.MaxResolution = bitReader.Read(8); + hca.TrackCount = bitReader.Read(8); + hca.ChannelConfig = bitReader.Read(8); + hca.TotalBandCount = bitReader.Read(8); + hca.BaseBandCount = bitReader.Read(8); + hca.StereoBandCount = bitReader.Read(8); + hca.BandsPerHfrGroup = bitReader.Read(8); + hca.MsStereo = bitReader.Read(8); + hca.Reserved = bitReader.Read(8); + + headerSize -= 16; + } + else if (headerSize >= 12 && (bitReader.Peek(32) & Mask) == StringToUInt32("dec")) + { + bitReader.Skip(32); + hca.FrameSize = bitReader.Read(16); + hca.MinResolution = bitReader.Read(8); + hca.MaxResolution = bitReader.Read(8); + hca.TotalBandCount = bitReader.Read(8) + 1; + hca.BaseBandCount = bitReader.Read(8) + 1; + hca.TrackCount = bitReader.Read(4); + hca.ChannelConfig = bitReader.Read(4); + hca.StereoType = bitReader.Read(8); + + if (hca.StereoType == 0) hca.BaseBandCount = hca.TotalBandCount; + hca.StereoBandCount = hca.TotalBandCount - hca.BaseBandCount; + hca.BandsPerHfrGroup = 0; + + headerSize -= 12; + } + else throw new InvalidDataException("No compression or decode chunk."); + + if (headerSize >= 8 && (bitReader.Peek(32) & Mask) == StringToUInt32("vbr")) + { + bitReader.Skip(32); + hca.VbrMaxFrameSize = bitReader.Read(16); + hca.VbrNoiseLevel = bitReader.Read(16); + + if (!(hca.FrameSize == 0 && hca.VbrMaxFrameSize > 8 && hca.VbrMaxFrameSize <= 511)) + throw new InvalidDataException("Invalid frame size."); + + headerSize -= 8; + } + else + { + hca.VbrMaxFrameSize = 0; + hca.VbrNoiseLevel = 0; + } + + if (headerSize >= 6 && (bitReader.Peek(32) & Mask) == StringToUInt32("ath")) + { + bitReader.Skip(32); + hca.AthType = bitReader.Read(16); + } + else hca.AthType = (hca.Version < Version200) ? 1 : 0; + + if (headerSize >= 16 && (bitReader.Peek(32) & Mask) == StringToUInt32("loop")) + { + bitReader.Skip(32); + hca.LoopStartFrame = bitReader.Read(32); + hca.LoopEndFrame = bitReader.Read(32); + hca.LoopStartDelay = bitReader.Read(16); + hca.LoopEndPadding = bitReader.Read(16); + + hca.LoopFlag = true; + + if (!(hca.LoopStartFrame >= 0 && hca.LoopStartFrame <= hca.LoopEndFrame + && hca.LoopEndFrame < hca.FrameCount)) + throw new InvalidDataException("Invalid loop frames."); + + headerSize -= 16; + } + else + { + hca.LoopStartFrame = 0; + hca.LoopEndFrame = 0; + hca.LoopStartDelay = 0; + hca.LoopEndPadding = 0; + + hca.LoopFlag = false; + } + + if (headerSize >= 6 && (bitReader.Peek(32) & Mask) == StringToUInt32("ciph")) + { + bitReader.Skip(32); + hca.CiphType = bitReader.Read(16); + + if (!(hca.CiphType == 0 || hca.CiphType == 1 || hca.CiphType == 56)) + throw new InvalidDataException("Invalid cipher type."); + + headerSize -= 6; + } + + if (headerSize >= 8 && (bitReader.Peek(32) & Mask) == StringToUInt32("rva")) + { + bitReader.Skip(32); + int rvaVolumeInt = bitReader.Read(32); + hca.RvaVolume = BitConverter.ToSingle(BitConverter.GetBytes(rvaVolumeInt),0); + + headerSize -= 8; + } + else hca.RvaVolume = 1.0f; + + if (headerSize >= 5 && (bitReader.Peek(32) & Mask) == StringToUInt32("comm")) + { + bitReader.Skip(32); + hca.CommentLength = bitReader.Read(8); + + if (hca.CommentLength > headerSize) throw new InvalidDataException("Comment string out of bounds."); + + StringBuilder commentStringBuilder = new StringBuilder(); + + for (int i = 0; i < hca.CommentLength; i++) + { + commentStringBuilder.Append(bitReader.Read(8)); + } + + hca.Comment = commentStringBuilder.ToString(); + + //headerSize -= 5 + hca.CommentLength; + } + else hca.CommentLength = 0; + + // IDE0059 + //if (headerSize >= 4 && (bitReader.Peek(32) & Mask) == StringToUInt32("pad")) + //{ + // headerSize -= (headerSize - 2); + //} + + if (hca.FrameSize < MinFrameSize || hca.FrameSize > MaxFrameSize) + throw new InvalidDataException("Invalid frame size."); + + if (hca.Version <= Version200) + { + if (hca.MinResolution != 1 || hca.MaxResolution != 15) + throw new InvalidDataException("Incompatible resolution."); + } + + if (hca.TrackCount == 0) hca.TrackCount = 1; + + if (hca.TrackCount > hca.ChannelCount) throw new InvalidDataException("Invalid track count."); + + if (hca.TotalBandCount > SamplesPerSubframe || + hca.BaseBandCount > SamplesPerSubframe || + hca.StereoBandCount > SamplesPerSubframe || + hca.BaseBandCount + hca.StereoBandCount > SamplesPerSubframe || + hca.BandsPerHfrGroup > SamplesPerSubframe) + throw new InvalidDataException("Invalid bands."); + + hca.HfrGroupCount = HeaderCeil2( + hca.TotalBandCount - hca.BaseBandCount - hca.StereoBandCount, + hca.BandsPerHfrGroup); + + hca.AthCurve = Ath.Init(hca.AthType, hca.SampleRate); + hca.CipherTable = Cipher.Init(hca.CiphType, hca.KeyCode); + + int channelsPerTrack = hca.ChannelCount / hca.TrackCount; + ChannelType[] channelTypes = new ChannelType[channelsPerTrack]; + + for (int i = 0; i < channelTypes.Length; i++) + { + channelTypes[i] = ChannelType.Discrete; + } + + if (hca.StereoBandCount > 0 && channelsPerTrack > 1) + { + for (int i = 0; i < hca.TrackCount; i++) + { + switch (channelsPerTrack) + { + case 2: + case 3: + channelTypes[0] = ChannelType.StereoPrimary; + channelTypes[1] = ChannelType.StereoSecondary; + break; + + case 4: + channelTypes[0] = ChannelType.StereoPrimary; + channelTypes[1] = ChannelType.StereoSecondary; + if (hca.ChannelConfig == 0) + { + channelTypes[2] = ChannelType.StereoPrimary; + channelTypes[3] = ChannelType.StereoSecondary; + } + break; + + case 5: + channelTypes[0] = ChannelType.StereoPrimary; + channelTypes[1] = ChannelType.StereoSecondary; + if (hca.ChannelConfig <= 2) + { + channelTypes[3] = ChannelType.StereoPrimary; + channelTypes[4] = ChannelType.StereoSecondary; + } + break; + + case 6: + case 7: + channelTypes[0] = ChannelType.StereoPrimary; + channelTypes[1] = ChannelType.StereoSecondary; + channelTypes[4] = ChannelType.StereoPrimary; + channelTypes[5] = ChannelType.StereoSecondary; + break; + + case 8: + channelTypes[0] = ChannelType.StereoPrimary; + channelTypes[1] = ChannelType.StereoSecondary; + channelTypes[4] = ChannelType.StereoPrimary; + channelTypes[5] = ChannelType.StereoSecondary; + channelTypes[6] = ChannelType.StereoPrimary; + channelTypes[7] = ChannelType.StereoSecondary; + break; + + default: + break; + } + } + } + + hca.Channels = new Channel[hca.ChannelCount]; + for (int i = 0; i < hca.ChannelCount; i++) + { + hca.Channels[i] = new Channel + { + Type = channelTypes[i], + CodedCount = + channelTypes[i] != ChannelType.StereoSecondary ? + hca.BaseBandCount + hca.StereoBandCount : + hca.BaseBandCount + }; + } + + hca.Random = DefaultRandom; + + if (hca.MsStereo > 0) throw new InvalidDataException(); + if (hca.HfrGroupCount > 0 && hca.Version == Version300) + throw new InvalidDataException(); + } + + private static int HeaderCeil2(int a, int b) + { + if (b < 1) return 0; + return a / b + ((a % b) > 0 ? 1 : 0); + } + + private static uint StringToUInt32(string value) + { + uint result = 0; + int bytePos = 3; + for (int i = 0; i < value.Length; i++) + { + result |= (uint)(value[i] << 8 * bytePos--); + } + return result; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Audio/ClHcaSharp/Header.cs.meta b/Assets/Scripts/Audio/ClHcaSharp/Header.cs.meta new file mode 100644 index 0000000..78eceff --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/Header.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 366eb88aab2749c40bdd2255f763b075 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/ClHcaSharp/JaggedArray.cs b/Assets/Scripts/Audio/ClHcaSharp/JaggedArray.cs new file mode 100644 index 0000000..5396a8d --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/JaggedArray.cs @@ -0,0 +1,28 @@ +using System; + +namespace ClHcaSharp +{ + internal static class JaggedArray + { + public static T CreateJaggedArray(params int[] lengths) + { + return (T)InitJaggedArray(typeof(T).GetElementType(), lengths, 0); + } + + private static object InitJaggedArray(Type type, int[] lengths, int arrayIndex) + { + Array array = Array.CreateInstance(type, lengths[arrayIndex]); + Type subElementType = type.GetElementType(); + + if (type.HasElementType) + { + for (int i = 0; i < lengths[arrayIndex]; i++) + { + array.SetValue(InitJaggedArray(subElementType, lengths, arrayIndex + 1), i); + } + } + + return array; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Audio/ClHcaSharp/JaggedArray.cs.meta b/Assets/Scripts/Audio/ClHcaSharp/JaggedArray.cs.meta new file mode 100644 index 0000000..1628a56 --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/JaggedArray.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b866b2adde0b7ae4dbeaeb482c11f042 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/ClHcaSharp/Tables.cs b/Assets/Scripts/Audio/ClHcaSharp/Tables.cs new file mode 100644 index 0000000..c33c4da --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/Tables.cs @@ -0,0 +1,335 @@ +using Assets.Scripts.Audio.CriWareFormats.Common; +using System; + +namespace ClHcaSharp +{ + internal static class Tables + { + public static void InitializeTables() + { + for (int i = 0; i < DequantizerScalingTableHex.Length; i++) + { + DequantizerScalingTable[i] = BinaryPrimitives.Int32BitsToSingle((int)DequantizerScalingTableHex[i]); + } + + for (int i = 0; i < DequantizerRangeTableHex.Length; i++) + { + DequantizerRangeTable[i] = BinaryPrimitives.Int32BitsToSingle((int)DequantizerRangeTableHex[i]); + } + + for (int i = 0; i < ScaleConversionTableHex.Length; i++) + { + ScaleConversionTable[i] = BinaryPrimitives.Int32BitsToSingle((int)ScaleConversionTableHex[i]); + } + + for (int i = 0; i < IntensityRatioTableHex.Length; i++) + { + IntensityRatioTable[i] = BinaryPrimitives.Int32BitsToSingle((int)IntensityRatioTableHex[i]); + } + + for (int i = 0; i < SinTableHex.Length; i++) + { + SinTable[i] = new float[64]; + + for (int j = 0; j < SinTableHex[i].Length; j++) + { + SinTable[i][j] = BinaryPrimitives.Int32BitsToSingle((int)SinTableHex[i][j]); + } + } + + for (int i = 0; i < CosTableHex.Length; i++) + { + CosTable[i] = new float[64]; + + for (int j = 0; j < CosTableHex[i].Length; j++) + { + CosTable[i][j] = BinaryPrimitives.Int32BitsToSingle((int)CosTableHex[i][j]); + } + } + + for (int i = 0; i < ImdctWindowHex.Length; i++) + { + ImdctWindow[i] = BinaryPrimitives.Int32BitsToSingle((int)ImdctWindowHex[i]); + } + } + + public static readonly byte[] InvertTable = new byte[66] + { + 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, + 12, 12, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 9, + 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 7, 6, 6, 5, 4, + 4, 4, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1 + }; + + public static readonly uint[] DequantizerScalingTableHex = new uint[64] + { + 0x342A8D26, 0x34633F89, 0x3497657D, 0x34C9B9BE, 0x35066491, 0x353311C4, 0x356E9910, 0x359EF532, + 0x35D3CCF1, 0x360D1ADF, 0x363C034A, 0x367A83B3, 0x36A6E595, 0x36DE60F5, 0x371426FF, 0x3745672A, + 0x37838359, 0x37AF3B79, 0x37E97C38, 0x381B8D3A, 0x384F4319, 0x388A14D5, 0x38B7FBF0, 0x38F5257D, + 0x3923520F, 0x39599D16, 0x3990FA4D, 0x39C12C4D, 0x3A00B1ED, 0x3A2B7A3A, 0x3A647B6D, 0x3A9837F0, + 0x3ACAD226, 0x3B071F62, 0x3B340AAF, 0x3B6FE4BA, 0x3B9FD228, 0x3BD4F35B, 0x3C0DDF04, 0x3C3D08A4, + 0x3C7BDFED, 0x3CA7CD94, 0x3CDF9613, 0x3D14F4F0, 0x3D467991, 0x3D843A29, 0x3DB02F0E, 0x3DEAC0C7, + 0x3E1C6573, 0x3E506334, 0x3E8AD4C6, 0x3EB8FBAF, 0x3EF67A41, 0x3F243516, 0x3F5ACB94, 0x3F91C3D3, + 0x3FC238D2, 0x400164D2, 0x402C6897, 0x4065B907, 0x40990B88, 0x40CBEC15, 0x4107DB35, 0x413504F3 + }; + + public static readonly float[] DequantizerScalingTable = new float[64]; + + + public static readonly uint[] DequantizerRangeTableHex = new uint[16] + { + 0x3F800000, 0x3F2AAAAB, 0x3ECCCCCD, 0x3E924925, 0x3E638E39, 0x3E3A2E8C, 0x3E1D89D9, 0x3E088889, + 0x3D842108, 0x3D020821, 0x3C810204, 0x3C008081, 0x3B804020, 0x3B002008, 0x3A801002, 0x3A000801 + }; + + public static readonly float[] DequantizerRangeTable = new float[16]; + + public static readonly byte[] MaxBitTable = new byte[16] + { + 0, 2, 3, 3, 4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12 + }; + + public static readonly byte[] ReadBitTable = new byte[128] + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 + }; + + public static readonly float[] ReadValueTable = new float[128] + { + +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, + +0.0f, +0.0f, +1.0f, -1.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, + +0.0f, +0.0f, +1.0f, +1.0f, -1.0f, -1.0f, +2.0f, -2.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, + +0.0f, +0.0f, +1.0f, -1.0f, +2.0f, -2.0f, +3.0f, -3.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, +0.0f, + +0.0f, +0.0f, +1.0f, +1.0f, -1.0f, -1.0f, +2.0f, +2.0f, -2.0f, -2.0f, +3.0f, +3.0f, -3.0f, -3.0f, +4.0f, -4.0f, + +0.0f, +0.0f, +1.0f, +1.0f, -1.0f, -1.0f, +2.0f, +2.0f, -2.0f, -2.0f, +3.0f, -3.0f, +4.0f, -4.0f, +5.0f, -5.0f, + +0.0f, +0.0f, +1.0f, +1.0f, -1.0f, -1.0f, +2.0f, -2.0f, +3.0f, -3.0f, +4.0f, -4.0f, +5.0f, -5.0f, +6.0f, -6.0f, + +0.0f, +0.0f, +1.0f, -1.0f, +2.0f, -2.0f, +3.0f, -3.0f, +4.0f, -4.0f, +5.0f, -5.0f, +6.0f, -6.0f, +7.0f, -7.0f + }; + + public static readonly uint[] ScaleConversionTableHex = new uint[128] + { + 0x00000000, 0x32A0B051, 0x32D61B5E, 0x330EA43A, 0x333E0F68, 0x337D3E0C, 0x33A8B6D5, 0x33E0CCDF, + 0x3415C3FF, 0x34478D75, 0x3484F1F6, 0x34B123F6, 0x34EC0719, 0x351D3EDA, 0x355184DF, 0x358B95C2, + 0x35B9FCD2, 0x35F7D0DF, 0x36251958, 0x365BFBB8, 0x36928E72, 0x36C346CD, 0x370218AF, 0x372D583F, + 0x3766F85B, 0x3799E046, 0x37CD078C, 0x3808980F, 0x38360094, 0x38728177, 0x38A18FAF, 0x38D744FD, + 0x390F6A81, 0x393F179A, 0x397E9E11, 0x39A9A15B, 0x39E2055B, 0x3A16942D, 0x3A48A2D8, 0x3A85AAC3, + 0x3AB21A32, 0x3AED4F30, 0x3B1E196E, 0x3B52A81E, 0x3B8C57CA, 0x3BBAFF5B, 0x3BF9295A, 0x3C25FED7, + 0x3C5D2D82, 0x3C935A2B, 0x3CC4563F, 0x3D02CD87, 0x3D2E4934, 0x3D68396A, 0x3D9AB62B, 0x3DCE248C, + 0x3E0955EE, 0x3E36FD92, 0x3E73D290, 0x3EA27043, 0x3ED87039, 0x3F1031DC, 0x3F40213B, 0x3F800000, + 0x3FAA8D26, 0x3FE33F89, 0x4017657D, 0x4049B9BE, 0x40866491, 0x40B311C4, 0x40EE9910, 0x411EF532, + 0x4153CCF1, 0x418D1ADF, 0x41BC034A, 0x41FA83B3, 0x4226E595, 0x425E60F5, 0x429426FF, 0x42C5672A, + 0x43038359, 0x432F3B79, 0x43697C38, 0x439B8D3A, 0x43CF4319, 0x440A14D5, 0x4437FBF0, 0x4475257D, + 0x44A3520F, 0x44D99D16, 0x4510FA4D, 0x45412C4D, 0x4580B1ED, 0x45AB7A3A, 0x45E47B6D, 0x461837F0, + 0x464AD226, 0x46871F62, 0x46B40AAF, 0x46EFE4BA, 0x471FD228, 0x4754F35B, 0x478DDF04, 0x47BD08A4, + 0x47FBDFED, 0x4827CD94, 0x485F9613, 0x4894F4F0, 0x48C67991, 0x49043A29, 0x49302F0E, 0x496AC0C7, + 0x499C6573, 0x49D06334, 0x4A0AD4C6, 0x4A38FBAF, 0x4A767A41, 0x4AA43516, 0x4ADACB94, 0x4B11C3D3, + 0x4B4238D2, 0x4B8164D2, 0x4BAC6897, 0x4BE5B907, 0x4C190B88, 0x4C4BEC15, 0x00000000, 0x00000000 + }; + + public static float[] ScaleConversionTable = new float[128]; + + public static readonly uint[] IntensityRatioTableHex = new uint[16] + { + 0x40000000, 0x3FEDB6DB, 0x3FDB6DB7, 0x3FC92492, 0x3FB6DB6E, 0x3FA49249, 0x3F924925, 0x3F800000, + 0x3F5B6DB7, 0x3F36DB6E, 0x3F124925, 0x3EDB6DB7, 0x3E924925, 0x3E124925, 0x00000000, 0x00000000 + }; + + public static float[] IntensityRatioTable = new float[16]; + + public static readonly uint[][] SinTableHex = new uint[7][] + { + new uint[64] + { + 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, + 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, + 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, + 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, + 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, + 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, + 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, + 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75, 0x3DA73D75 + }, + new uint[64] + { + 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, + 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, + 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, + 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, + 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, + 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, + 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, + 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31, 0x3F7B14BE, 0x3F54DB31 + }, + new uint[64] + { + 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, + 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, + 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, + 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, + 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, + 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, + 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, + 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403, 0x3F7EC46D, 0x3F74FA0B, 0x3F61C598, 0x3F45E403 + }, + new uint[64] + { + 0x3F7FB10F, 0x3F7D3AAC, 0x3F7853F8, 0x3F710908, 0x3F676BD8, 0x3F5B941A, 0x3F4D9F02, 0x3F3DAEF9, + 0x3F7FB10F, 0x3F7D3AAC, 0x3F7853F8, 0x3F710908, 0x3F676BD8, 0x3F5B941A, 0x3F4D9F02, 0x3F3DAEF9, + 0x3F7FB10F, 0x3F7D3AAC, 0x3F7853F8, 0x3F710908, 0x3F676BD8, 0x3F5B941A, 0x3F4D9F02, 0x3F3DAEF9, + 0x3F7FB10F, 0x3F7D3AAC, 0x3F7853F8, 0x3F710908, 0x3F676BD8, 0x3F5B941A, 0x3F4D9F02, 0x3F3DAEF9, + 0x3F7FB10F, 0x3F7D3AAC, 0x3F7853F8, 0x3F710908, 0x3F676BD8, 0x3F5B941A, 0x3F4D9F02, 0x3F3DAEF9, + 0x3F7FB10F, 0x3F7D3AAC, 0x3F7853F8, 0x3F710908, 0x3F676BD8, 0x3F5B941A, 0x3F4D9F02, 0x3F3DAEF9, + 0x3F7FB10F, 0x3F7D3AAC, 0x3F7853F8, 0x3F710908, 0x3F676BD8, 0x3F5B941A, 0x3F4D9F02, 0x3F3DAEF9, + 0x3F7FB10F, 0x3F7D3AAC, 0x3F7853F8, 0x3F710908, 0x3F676BD8, 0x3F5B941A, 0x3F4D9F02, 0x3F3DAEF9 + }, + new uint[64] + { + 0x3F7FEC43, 0x3F7F4E6D, 0x3F7E1324, 0x3F7C3B28, 0x3F79C79D, 0x3F76BA07, 0x3F731447, 0x3F6ED89E, + 0x3F6A09A7, 0x3F64AA59, 0x3F5EBE05, 0x3F584853, 0x3F514D3D, 0x3F49D112, 0x3F41D870, 0x3F396842, + 0x3F7FEC43, 0x3F7F4E6D, 0x3F7E1324, 0x3F7C3B28, 0x3F79C79D, 0x3F76BA07, 0x3F731447, 0x3F6ED89E, + 0x3F6A09A7, 0x3F64AA59, 0x3F5EBE05, 0x3F584853, 0x3F514D3D, 0x3F49D112, 0x3F41D870, 0x3F396842, + 0x3F7FEC43, 0x3F7F4E6D, 0x3F7E1324, 0x3F7C3B28, 0x3F79C79D, 0x3F76BA07, 0x3F731447, 0x3F6ED89E, + 0x3F6A09A7, 0x3F64AA59, 0x3F5EBE05, 0x3F584853, 0x3F514D3D, 0x3F49D112, 0x3F41D870, 0x3F396842, + 0x3F7FEC43, 0x3F7F4E6D, 0x3F7E1324, 0x3F7C3B28, 0x3F79C79D, 0x3F76BA07, 0x3F731447, 0x3F6ED89E, + 0x3F6A09A7, 0x3F64AA59, 0x3F5EBE05, 0x3F584853, 0x3F514D3D, 0x3F49D112, 0x3F41D870, 0x3F396842 + }, + new uint[64] + { + 0x3F7FFB11, 0x3F7FD397, 0x3F7F84AB, 0x3F7F0E58, 0x3F7E70B0, 0x3F7DABCC, 0x3F7CBFC9, 0x3F7BACCD, + 0x3F7A7302, 0x3F791298, 0x3F778BC5, 0x3F75DEC6, 0x3F740BDD, 0x3F721352, 0x3F6FF573, 0x3F6DB293, + 0x3F6B4B0C, 0x3F68BF3C, 0x3F660F88, 0x3F633C5A, 0x3F604621, 0x3F5D2D53, 0x3F59F26A, 0x3F5695E5, + 0x3F531849, 0x3F4F7A1F, 0x3F4BBBF8, 0x3F47DE65, 0x3F43E200, 0x3F3FC767, 0x3F3B8F3B, 0x3F373A23, + 0x3F7FFB11, 0x3F7FD397, 0x3F7F84AB, 0x3F7F0E58, 0x3F7E70B0, 0x3F7DABCC, 0x3F7CBFC9, 0x3F7BACCD, + 0x3F7A7302, 0x3F791298, 0x3F778BC5, 0x3F75DEC6, 0x3F740BDD, 0x3F721352, 0x3F6FF573, 0x3F6DB293, + 0x3F6B4B0C, 0x3F68BF3C, 0x3F660F88, 0x3F633C5A, 0x3F604621, 0x3F5D2D53, 0x3F59F26A, 0x3F5695E5, + 0x3F531849, 0x3F4F7A1F, 0x3F4BBBF8, 0x3F47DE65, 0x3F43E200, 0x3F3FC767, 0x3F3B8F3B, 0x3F373A23 + }, + new uint[64] + { + 0x3F7FFEC4, 0x3F7FF4E6, 0x3F7FE129, 0x3F7FC38F, 0x3F7F9C18, 0x3F7F6AC7, 0x3F7F2F9D, 0x3F7EEA9D, + 0x3F7E9BC9, 0x3F7E4323, 0x3F7DE0B1, 0x3F7D7474, 0x3F7CFE73, 0x3F7C7EB0, 0x3F7BF531, 0x3F7B61FC, + 0x3F7AC516, 0x3F7A1E84, 0x3F796E4E, 0x3F78B47B, 0x3F77F110, 0x3F772417, 0x3F764D97, 0x3F756D97, + 0x3F748422, 0x3F73913F, 0x3F7294F8, 0x3F718F57, 0x3F708066, 0x3F6F6830, 0x3F6E46BE, 0x3F6D1C1D, + 0x3F6BE858, 0x3F6AAB7B, 0x3F696591, 0x3F6816A8, 0x3F66BECC, 0x3F655E0B, 0x3F63F473, 0x3F628210, + 0x3F6106F2, 0x3F5F8327, 0x3F5DF6BE, 0x3F5C61C7, 0x3F5AC450, 0x3F591E6A, 0x3F577026, 0x3F55B993, + 0x3F53FAC3, 0x3F5233C6, 0x3F5064AF, 0x3F4E8D90, 0x3F4CAE79, 0x3F4AC77F, 0x3F48D8B3, 0x3F46E22A, + 0x3F44E3F5, 0x3F42DE29, 0x3F40D0DA, 0x3F3EBC1B, 0x3F3CA003, 0x3F3A7CA4, 0x3F385216, 0x3F36206C + } + }; + + public static float[][] SinTable = new float[7][]; + + public static readonly uint[][] CosTableHex = new uint[7][] + { + new uint[64] + { + 0xBD0A8BD4, 0x3D0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, + 0x3D0A8BD4, 0xBD0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, + 0x3D0A8BD4, 0xBD0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, + 0xBD0A8BD4, 0x3D0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, + 0x3D0A8BD4, 0xBD0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, + 0xBD0A8BD4, 0x3D0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, + 0xBD0A8BD4, 0x3D0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, + 0x3D0A8BD4, 0xBD0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4, 0x3D0A8BD4, 0x3D0A8BD4, 0xBD0A8BD4 + }, + new uint[64] + { + 0xBE47C5C2, 0xBF0E39DA, 0x3E47C5C2, 0x3F0E39DA, 0x3E47C5C2, 0x3F0E39DA, 0xBE47C5C2, 0xBF0E39DA, + 0x3E47C5C2, 0x3F0E39DA, 0xBE47C5C2, 0xBF0E39DA, 0xBE47C5C2, 0xBF0E39DA, 0x3E47C5C2, 0x3F0E39DA, + 0x3E47C5C2, 0x3F0E39DA, 0xBE47C5C2, 0xBF0E39DA, 0xBE47C5C2, 0xBF0E39DA, 0x3E47C5C2, 0x3F0E39DA, + 0xBE47C5C2, 0xBF0E39DA, 0x3E47C5C2, 0x3F0E39DA, 0x3E47C5C2, 0x3F0E39DA, 0xBE47C5C2, 0xBF0E39DA, + 0x3E47C5C2, 0x3F0E39DA, 0xBE47C5C2, 0xBF0E39DA, 0xBE47C5C2, 0xBF0E39DA, 0x3E47C5C2, 0x3F0E39DA, + 0xBE47C5C2, 0xBF0E39DA, 0x3E47C5C2, 0x3F0E39DA, 0x3E47C5C2, 0x3F0E39DA, 0xBE47C5C2, 0xBF0E39DA, + 0xBE47C5C2, 0xBF0E39DA, 0x3E47C5C2, 0x3F0E39DA, 0x3E47C5C2, 0x3F0E39DA, 0xBE47C5C2, 0xBF0E39DA, + 0x3E47C5C2, 0x3F0E39DA, 0xBE47C5C2, 0xBF0E39DA, 0xBE47C5C2, 0xBF0E39DA, 0x3E47C5C2, 0x3F0E39DA + }, + new uint[64] + { + 0xBDC8BD36, 0xBE94A031, 0xBEF15AEA, 0xBF226799, 0x3DC8BD36, 0x3E94A031, 0x3EF15AEA, 0x3F226799, + 0x3DC8BD36, 0x3E94A031, 0x3EF15AEA, 0x3F226799, 0xBDC8BD36, 0xBE94A031, 0xBEF15AEA, 0xBF226799, + 0x3DC8BD36, 0x3E94A031, 0x3EF15AEA, 0x3F226799, 0xBDC8BD36, 0xBE94A031, 0xBEF15AEA, 0xBF226799, + 0xBDC8BD36, 0xBE94A031, 0xBEF15AEA, 0xBF226799, 0x3DC8BD36, 0x3E94A031, 0x3EF15AEA, 0x3F226799, + 0x3DC8BD36, 0x3E94A031, 0x3EF15AEA, 0x3F226799, 0xBDC8BD36, 0xBE94A031, 0xBEF15AEA, 0xBF226799, + 0xBDC8BD36, 0xBE94A031, 0xBEF15AEA, 0xBF226799, 0x3DC8BD36, 0x3E94A031, 0x3EF15AEA, 0x3F226799, + 0xBDC8BD36, 0xBE94A031, 0xBEF15AEA, 0xBF226799, 0x3DC8BD36, 0x3E94A031, 0x3EF15AEA, 0x3F226799, + 0x3DC8BD36, 0x3E94A031, 0x3EF15AEA, 0x3F226799, 0xBDC8BD36, 0xBE94A031, 0xBEF15AEA, 0xBF226799 + }, + new uint[64] + { + 0xBD48FB30, 0xBE164083, 0xBE78CFCC, 0xBEAC7CD4, 0xBEDAE880, 0xBF039C3D, 0xBF187FC0, 0xBF2BEB4A, + 0x3D48FB30, 0x3E164083, 0x3E78CFCC, 0x3EAC7CD4, 0x3EDAE880, 0x3F039C3D, 0x3F187FC0, 0x3F2BEB4A, + 0x3D48FB30, 0x3E164083, 0x3E78CFCC, 0x3EAC7CD4, 0x3EDAE880, 0x3F039C3D, 0x3F187FC0, 0x3F2BEB4A, + 0xBD48FB30, 0xBE164083, 0xBE78CFCC, 0xBEAC7CD4, 0xBEDAE880, 0xBF039C3D, 0xBF187FC0, 0xBF2BEB4A, + 0x3D48FB30, 0x3E164083, 0x3E78CFCC, 0x3EAC7CD4, 0x3EDAE880, 0x3F039C3D, 0x3F187FC0, 0x3F2BEB4A, + 0xBD48FB30, 0xBE164083, 0xBE78CFCC, 0xBEAC7CD4, 0xBEDAE880, 0xBF039C3D, 0xBF187FC0, 0xBF2BEB4A, + 0xBD48FB30, 0xBE164083, 0xBE78CFCC, 0xBEAC7CD4, 0xBEDAE880, 0xBF039C3D, 0xBF187FC0, 0xBF2BEB4A, + 0x3D48FB30, 0x3E164083, 0x3E78CFCC, 0x3EAC7CD4, 0x3EDAE880, 0x3F039C3D, 0x3F187FC0, 0x3F2BEB4A + }, + new uint[64] + { + 0xBCC90AB0, 0xBD96A905, 0xBDFAB273, 0xBE2F10A2, 0xBE605C13, 0xBE888E93, 0xBEA09AE5, 0xBEB8442A, + 0xBECF7BCA, 0xBEE63375, 0xBEFC5D27, 0xBF08F59B, 0xBF13682A, 0xBF1D7FD1, 0xBF273656, 0xBF3085BB, + 0x3CC90AB0, 0x3D96A905, 0x3DFAB273, 0x3E2F10A2, 0x3E605C13, 0x3E888E93, 0x3EA09AE5, 0x3EB8442A, + 0x3ECF7BCA, 0x3EE63375, 0x3EFC5D27, 0x3F08F59B, 0x3F13682A, 0x3F1D7FD1, 0x3F273656, 0x3F3085BB, + 0x3CC90AB0, 0x3D96A905, 0x3DFAB273, 0x3E2F10A2, 0x3E605C13, 0x3E888E93, 0x3EA09AE5, 0x3EB8442A, + 0x3ECF7BCA, 0x3EE63375, 0x3EFC5D27, 0x3F08F59B, 0x3F13682A, 0x3F1D7FD1, 0x3F273656, 0x3F3085BB, + 0xBCC90AB0, 0xBD96A905, 0xBDFAB273, 0xBE2F10A2, 0xBE605C13, 0xBE888E93, 0xBEA09AE5, 0xBEB8442A, + 0xBECF7BCA, 0xBEE63375, 0xBEFC5D27, 0xBF08F59B, 0xBF13682A, 0xBF1D7FD1, 0xBF273656, 0xBF3085BB + }, + new uint[64] + { + 0xBC490E90, 0xBD16C32C, 0xBD7B2B74, 0xBDAFB680, 0xBDE1BC2E, 0xBE09CF86, 0xBE22ABB6, 0xBE3B6ECF, + 0xBE541501, 0xBE6C9A7F, 0xBE827DC0, 0xBE8E9A22, 0xBE9AA086, 0xBEA68F12, 0xBEB263EF, 0xBEBE1D4A, + 0xBEC9B953, 0xBED53641, 0xBEE0924F, 0xBEEBCBBB, 0xBEF6E0CB, 0xBF00E7E4, 0xBF064B82, 0xBF0B9A6B, + 0xBF10D3CD, 0xBF15F6D9, 0xBF1B02C6, 0xBF1FF6CB, 0xBF24D225, 0xBF299415, 0xBF2E3BDE, 0xBF32C8C9, + 0x3C490E90, 0x3D16C32C, 0x3D7B2B74, 0x3DAFB680, 0x3DE1BC2E, 0x3E09CF86, 0x3E22ABB6, 0x3E3B6ECF, + 0x3E541501, 0x3E6C9A7F, 0x3E827DC0, 0x3E8E9A22, 0x3E9AA086, 0x3EA68F12, 0x3EB263EF, 0x3EBE1D4A, + 0x3EC9B953, 0x3ED53641, 0x3EE0924F, 0x3EEBCBBB, 0x3EF6E0CB, 0x3F00E7E4, 0x3F064B82, 0x3F0B9A6B, + 0x3F10D3CD, 0x3F15F6D9, 0x3F1B02C6, 0x3F1FF6CB, 0x3F24D225, 0x3F299415, 0x3F2E3BDE, 0x3F32C8C9 + }, + new uint[64] + { + 0xBBC90F88, 0xBC96C9B6, 0xBCFB49BA, 0xBD2FE007, 0xBD621469, 0xBD8A200A, 0xBDA3308C, 0xBDBC3AC3, + 0xBDD53DB9, 0xBDEE3876, 0xBE039502, 0xBE1008B7, 0xBE1C76DE, 0xBE28DEFC, 0xBE354098, 0xBE419B37, + 0xBE4DEE60, 0xBE5A3997, 0xBE667C66, 0xBE72B651, 0xBE7EE6E1, 0xBE8586CE, 0xBE8B9507, 0xBE919DDD, + 0xBE97A117, 0xBE9D9E78, 0xBEA395C5, 0xBEA986C4, 0xBEAF713A, 0xBEB554EC, 0xBEBB31A0, 0xBEC1071E, + 0xBEC6D529, 0xBECC9B8B, 0xBED25A09, 0xBED8106B, 0xBEDDBE79, 0xBEE363FA, 0xBEE900B7, 0xBEEE9479, + 0xBEF41F07, 0xBEF9A02D, 0xBEFF17B2, 0xBF0242B1, 0xBF04F484, 0xBF07A136, 0xBF0A48AD, 0xBF0CEAD0, + 0xBF0F8784, 0xBF121EB0, 0xBF14B039, 0xBF173C07, 0xBF19C200, 0xBF1C420C, 0xBF1EBC12, 0xBF212FF9, + 0xBF239DA9, 0xBF26050A, 0xBF286605, 0xBF2AC082, 0xBF2D1469, 0xBF2F61A5, 0xBF31A81D, 0xBF33E7BC + } + }; + + public static float[][] CosTable = new float[7][]; + + public static readonly uint[] ImdctWindowHex = new uint[128] + { + 0x3A3504F0, 0x3B0183B8, 0x3B70C538, 0x3BBB9268, 0x3C04A809, 0x3C308200, 0x3C61284C, 0x3C8B3F17, + 0x3CA83992, 0x3CC77FBD, 0x3CE91110, 0x3D0677CD, 0x3D198FC4, 0x3D2DD35C, 0x3D434643, 0x3D59ECC1, + 0x3D71CBA8, 0x3D85741E, 0x3D92A413, 0x3DA078B4, 0x3DAEF522, 0x3DBE1C9E, 0x3DCDF27B, 0x3DDE7A1D, + 0x3DEFB6ED, 0x3E00D62B, 0x3E0A2EDA, 0x3E13E72A, 0x3E1E00B1, 0x3E287CF2, 0x3E335D55, 0x3E3EA321, + 0x3E4A4F75, 0x3E56633F, 0x3E62DF37, 0x3E6FC3D1, 0x3E7D1138, 0x3E8563A2, 0x3E8C72B7, 0x3E93B561, + 0x3E9B2AEF, 0x3EA2D26F, 0x3EAAAAAB, 0x3EB2B222, 0x3EBAE706, 0x3EC34737, 0x3ECBD03D, 0x3ED47F46, + 0x3EDD5128, 0x3EE6425C, 0x3EEF4EFF, 0x3EF872D7, 0x3F00D4A9, 0x3F0576CA, 0x3F0A1D3B, 0x3F0EC548, + 0x3F136C25, 0x3F180EF2, 0x3F1CAAC2, 0x3F213CA2, 0x3F25C1A5, 0x3F2A36E7, 0x3F2E9998, 0x3F32E705, + 0xBF371C9E, 0xBF3B37FE, 0xBF3F36F2, 0xBF431780, 0xBF46D7E6, 0xBF4A76A4, 0xBF4DF27C, 0xBF514A6F, + 0xBF547DC5, 0xBF578C03, 0xBF5A74EE, 0xBF5D3887, 0xBF5FD707, 0xBF6250DA, 0xBF64A699, 0xBF66D908, + 0xBF68E90E, 0xBF6AD7B1, 0xBF6CA611, 0xBF6E5562, 0xBF6FE6E7, 0xBF715BEF, 0xBF72B5D1, 0xBF73F5E6, + 0xBF751D89, 0xBF762E13, 0xBF7728D7, 0xBF780F20, 0xBF78E234, 0xBF79A34C, 0xBF7A5397, 0xBF7AF439, + 0xBF7B8648, 0xBF7C0ACE, 0xBF7C82C8, 0xBF7CEF26, 0xBF7D50CB, 0xBF7DA88E, 0xBF7DF737, 0xBF7E3D86, + 0xBF7E7C2A, 0xBF7EB3CC, 0xBF7EE507, 0xBF7F106C, 0xBF7F3683, 0xBF7F57CA, 0xBF7F74B6, 0xBF7F8DB6, + 0xBF7FA32E, 0xBF7FB57B, 0xBF7FC4F6, 0xBF7FD1ED, 0xBF7FDCAD, 0xBF7FE579, 0xBF7FEC90, 0xBF7FF22E, + 0xBF7FF688, 0xBF7FF9D0, 0xBF7FFC32, 0xBF7FFDDA, 0xBF7FFEED, 0xBF7FFF8F, 0xBF7FFFDF, 0xBF7FFFFC + }; + + public static float[] ImdctWindow = new float[128]; + } +} \ No newline at end of file diff --git a/Assets/Scripts/Audio/ClHcaSharp/Tables.cs.meta b/Assets/Scripts/Audio/ClHcaSharp/Tables.cs.meta new file mode 100644 index 0000000..b813531 --- /dev/null +++ b/Assets/Scripts/Audio/ClHcaSharp/Tables.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 50f6f92edea4fac4f9241320ffa4d4c3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/CriWareFormats.meta b/Assets/Scripts/Audio/CriWareFormats.meta new file mode 100644 index 0000000..4274f81 --- /dev/null +++ b/Assets/Scripts/Audio/CriWareFormats.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0d19d8ce2b0439342b93e9026e190de3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/CriWareFormats/AcbNameLoader.cs b/Assets/Scripts/Audio/CriWareFormats/AcbNameLoader.cs new file mode 100644 index 0000000..8d0e6ea --- /dev/null +++ b/Assets/Scripts/Audio/CriWareFormats/AcbNameLoader.cs @@ -0,0 +1,702 @@ +using CriWareFormats.Common; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CriWareFormats +{ + struct CueName + { + public ushort CueIndex; + public string Name; + } + + struct Cue + { + public byte ReferenceType; + public ushort ReferenceIndex; + } + + struct BlockSequence + { + public ushort NumTracks; + public uint TrackIndexOffset; + public uint TrackIndexSize; + public ushort NumBlocks; + public uint BlockIndexOffset; + public uint BlockIndexSize; + } + + struct Block + { + public ushort NumTracks; + public uint TrackIndexOffset; + public uint TrackIndexSize; + } + + struct Sequence + { + public ushort NumTracks; + public uint TrackIndexOffset; + public uint TrackIndexSize; + public byte Type; + } + + struct Track + { + public ushort EventIndex; + } + + struct TrackCommand + { + public uint CommandOffset; + public uint CommandSize; + } + + struct Synth + { + public byte Type; + public uint ReferenceItemsOffset; + public uint ReferenceItemsSize; + } + + struct Waveform + { + public ushort Id; + public ushort PortNo; + public byte Streaming; + } + + public class AcbNameLoader + { + private Stream acbStream; + + private UtfTable header; + private UtfTable cueNames; + + private BinaryReaderEndian cueReader; + private BinaryReaderEndian cueNameReader; + private BinaryReaderEndian blockSequenceReader; + private BinaryReaderEndian blockReader; + private BinaryReaderEndian sequenceReader; + private BinaryReaderEndian trackReader; + private BinaryReaderEndian trackCommandReader; + private BinaryReaderEndian synthReader; + private BinaryReaderEndian waveformReader; + + private Cue[] cue; + private CueName[] cueName; + private BlockSequence[] blockSequence; + private Block[] block; + private Sequence[] sequence; + private Track[] track; + private TrackCommand[] trackCommand; + private Synth[] synth; + private Waveform[] waveform; + + private int cueRows; + private int cueNameRows; + private int blockSequenceRows; + private int blockRows; + private int sequenceRows; + private int trackRows; + private int trackCommandRows; + private int synthRows; + private int waveFormRows; + + private bool isMemory; + private int targetWaveId; + private int targetPort; + + private int synthDepth; + private int sequenceDepth; + + private short cueNameIndex; + private string cueNameName; + private int awbNameCount; + private List awbNameList; + private string name; + + public AcbNameLoader(Stream acb) + { + acbStream = acb; + header = new UtfTable(acb, (uint)acbStream.Position); + } + + bool OpenUtfSubtable(out BinaryReaderEndian tableReader, out UtfTable table, string tableName, out int rows) + { + if (!header.Query(0, tableName, out VLData data)) + throw new ArgumentException("Error reading table."); + + tableReader = new BinaryReaderEndian(acbStream); + table = new UtfTable(tableReader.BaseStream, data.Offset, out rows, out string _); + + return true; + } + + void AddAcbName(byte streaming) + { + if (cueNameName.Length == 0) + return; + + for (int i = 0; i < awbNameCount; i++) + { + if (awbNameList[i] == cueNameIndex) + return; + } + + if (awbNameCount > 0) + { + name += "; "; + name += cueNameName; + } + else + name = cueNameName; + + if (streaming == 2 && isMemory) + name += " [pre]"; + + awbNameList.Add(cueNameIndex); + awbNameCount++; + } + + void PreloadAcbWaveForm() + { + ref int rows = ref waveFormRows; + + if (rows != 0) return; + if (!OpenUtfSubtable(out waveformReader, out UtfTable table, "WaveformTable", out rows)) + throw new Exception("Failure opening Waveform table."); + if (rows == 0) return; + + waveform = new Waveform[rows]; + + int cId = table.GetColumn("Id"); + int cMemoryAwbId = table.GetColumn("MemoryAwbId"); + int cStreamAwbId = table.GetColumn("StreamAwbId"); + int cStreamAwbPortNo = table.GetColumn("StreamAwbPortNo"); + int cStreaming = table.GetColumn("Streaming"); + + for (int i = 0; i < rows; i++) + { + ref Waveform r = ref waveform[i]; + + if (!table.Query(i, cId, out r.Id)) + { + if (isMemory) + { + table.Query(i, cMemoryAwbId, out r.Id); + waveform[i].PortNo = 0xFFFF; + } + else + { + table.Query(i, cStreamAwbId, out r.Id); + table.Query(i, cStreamAwbPortNo, out r.PortNo); + } + } + else + waveform[i].PortNo = 0xFFFF; + + table.Query(i, cStreaming, out r.Streaming); + } + } + + void LoadAcbWaveForm(ushort index) + { + PreloadAcbWaveForm(); + + if (index > waveFormRows) + throw new ArgumentOutOfRangeException(nameof(index)); + + ref Waveform r = ref waveform[index]; + + if (r.Id != targetWaveId) + return; + + if (targetPort >= 0 && r.PortNo != 0xFFFF && r.PortNo != targetPort) + return; + + if ((isMemory && r.Streaming == 1) || (!isMemory && r.Streaming == 0)) + return; + + AddAcbName(r.Streaming); + + return; + } + + void PreloadAcbSynth() + { + ref int rows = ref synthRows; + + if (rows != 0) return; + if (!OpenUtfSubtable(out synthReader, out UtfTable table, "SynthTable", out rows)) + throw new Exception("Failure opening Synth table."); + if (rows == 0) return; + + synth = new Synth[rows]; + + int cType = table.GetColumn("Type"); + int cReferenceItems = table.GetColumn("ReferenceItems"); + + for (int i = 0; i < rows; i++) + { + ref Synth r = ref synth[i]; + + table.Query(i, cType, out r.Type); + table.Query(i, cReferenceItems, out r.ReferenceItemsOffset, out r.ReferenceItemsSize); + } + } + + void LoadAcbSynth(ushort index) + { + PreloadAcbSynth(); + + if (index > synthRows) + throw new ArgumentOutOfRangeException(nameof(index)); + + ref Synth r = ref synth[index]; + + synthDepth++; + + if (synthDepth > 3) + throw new Exception("Synth depth too high"); + + int count = (int)(r.ReferenceItemsSize / 4); + for (int i = 0; i < count; i++) + { + synthReader.BaseStream.Position = r.ReferenceItemsOffset + i * 4; + + ushort itemType = synthReader.ReadUInt16BE(); + ushort itemIndex = synthReader.ReadUInt16BE(); + + switch (itemType) + { + case 0: + count = 0; + break; + + case 1: + LoadAcbWaveForm(itemIndex); + break; + + case 2: + LoadAcbSynth(itemIndex); + break; + + case 3: + LoadAcbSequence(itemIndex); + break; + + case 6: + default: + count = 0; + break; + } + } + + synthDepth--; + } + + void LoadAcbCommandTlvs(BinaryReaderEndian reader, uint commandOffset, uint commandSize) + { + uint pos = 0; + uint maxPos = commandSize; + + while (pos < maxPos) + { + reader.BaseStream.Position = commandOffset + pos; + + ushort tlvCode = reader.ReadUInt16BE(); + ushort tlvSize = reader.ReadByte(); + + pos += 3; + + switch (tlvCode) + { + case 2000: + case 2003: + if (tlvSize < 4) + break; + + reader.BaseStream.Position = commandOffset + pos; + + ushort tlvType = reader.ReadUInt16BE(); + ushort tlvIndex = reader.ReadUInt16BE(); + + switch (tlvType) + { + case 2: + LoadAcbSynth(tlvIndex); + break; + + case 3: + LoadAcbSequence(tlvIndex); + break; + + default: + maxPos = 0; + break; + } + + break; + + default: + break; + } + + pos += tlvSize; + } + } + + void PreloadAcbTrackCommand() + { + ref int rows = ref trackCommandRows; + + if (rows != 0) return; + if (!OpenUtfSubtable(out trackCommandReader, out UtfTable table, "TrackEventTable", out rows)) + { + if (!OpenUtfSubtable(out trackCommandReader, out table, "CommandTable", out rows)) + throw new Exception("Failure opening Command table."); + } + if (rows == 0) return; + + trackCommand = new TrackCommand[rows]; + + int cCommand = table.GetColumn("Command"); + + for (int i = 0; i < rows; i++) + { + ref TrackCommand r = ref trackCommand[i]; + + table.Query(i, cCommand, out r.CommandOffset, out r.CommandSize); + } + } + + void LoadAcbTrackCommand(ushort index) + { + PreloadAcbTrackCommand(); + + if (index > trackCommandRows) + throw new ArgumentOutOfRangeException(nameof(index)); + + LoadAcbCommandTlvs( + trackCommandReader, + trackCommand[index].CommandOffset, + trackCommand[index].CommandSize); + } + + void PreloadAcbTrack() + { + ref int rows = ref trackRows; + + if (rows != 0) return; + if (!OpenUtfSubtable(out trackReader, out UtfTable table, "TrackTable", out rows)) + throw new Exception("Failure opening Track table."); + if (rows == 0) return; + + track = new Track[rows]; + + int cEventIndex = table.GetColumn("EventIndex"); + + for (int i = 0; i < rows; i++) + { + ref Track r = ref track[i]; + + table.Query(i, cEventIndex, out r.EventIndex); + } + } + + void LoadAcbTrack(ushort index) + { + PreloadAcbTrack(); + + if (index > trackRows) + throw new ArgumentOutOfRangeException(nameof(index)); + + ref Track r = ref track[index]; + + if (r.EventIndex == 65535) + return; + + LoadAcbTrackCommand(r.EventIndex); + } + + void PreloadAcbSequence() + { + ref int rows = ref sequenceRows; + + if (rows != 0) return; + if (!OpenUtfSubtable(out sequenceReader, out UtfTable table, "SequenceTable", out rows)) + throw new Exception("Failure opening Sequence table."); + if (rows == 0) return; + + sequence = new Sequence[rows]; + + int cNumTracks = table.GetColumn("NumTracks"); + int cTrackIndex = table.GetColumn("TrackIndex"); + int cType = table.GetColumn("Type"); + + for (int i = 0; i < rows; i++) + { + ref Sequence r = ref sequence[i]; + + table.Query(i, cNumTracks, out r.NumTracks); + table.Query(i, cTrackIndex, out r.TrackIndexOffset, out r.TrackIndexSize); + table.Query(i, cType, out r.Type); + } + } + + void LoadAcbSequence(uint index) + { + PreloadAcbSequence(); + + if (index > sequenceRows) + throw new ArgumentOutOfRangeException(nameof(index)); + + ref Sequence r = ref sequence[index]; + + sequenceDepth++; + + if (sequenceDepth > 3) + throw new Exception("Sequence depth too high."); + + if (r.NumTracks * 2 > r.TrackIndexSize) + throw new Exception("Wrong Sequence.TrackIndex size."); + + switch (r.Type) + { + default: + for (int i = 0; i < r.NumTracks; i++) + { + sequenceReader.BaseStream.Position = r.TrackIndexOffset + i * 2; + + short trackIndexIndex = sequenceReader.ReadInt16BE(); + LoadAcbTrack((ushort)trackIndexIndex); + } + break; + } + + sequenceDepth--; + } + + void PreloadAcbBlock() + { + ref int rows = ref blockRows; + + if (rows != 0) return; + if (!OpenUtfSubtable(out blockReader, out UtfTable table, "BlockTable", out rows)) + throw new Exception("Failure opening Block table."); + if (rows == 0) return; + + block = new Block[rows]; + + int cNumTracks = table.GetColumn("NumTracks"); + int cTrackIndex = table.GetColumn("TrackIndex"); + + for (int i = 0; i < rows; i++) + { + ref Block r = ref block[i]; + table.Query(i, cNumTracks, out r.NumTracks); + table.Query(i, cTrackIndex, out VLData data); + r.TrackIndexOffset = data.Offset; + r.TrackIndexSize = data.Size; + } + } + + void LoadAcbBlock(ushort index) + { + PreloadAcbBlock(); + + if (index > blockRows) + throw new ArgumentOutOfRangeException(nameof(index)); + + ref Block r = ref block[index]; + + if (r.NumTracks * 2 > r.TrackIndexSize) + throw new Exception("Wrong Block.TrackIndex size."); + + for (int i = 0; i < r.NumTracks; i++) + { + blockReader.BaseStream.Position = r.TrackIndexOffset + i * 2; + + short trackIndexIndex = blockReader.ReadInt16BE(); + LoadAcbTrack((ushort)trackIndexIndex); + } + } + + void PreloadAcbBlockSequence() + { + ref int rows = ref blockSequenceRows; + + if (rows != 0) return; + if (!OpenUtfSubtable(out blockSequenceReader, out UtfTable table, "BlockSequenceTable", out rows)) + throw new Exception("Failure opening BlockSequence table."); + if (rows == 0) return; + + blockSequence = new BlockSequence[rows]; + + int cNumTracks = table.GetColumn("NumTracks"); + int cTrackIndex = table.GetColumn("TrackIndex"); + int cNumBlocks = table.GetColumn("NumBlocks"); + int cBlockIndex = table.GetColumn("BlockIndex"); + + for (int i = 0; i < rows; i++) + { + ref BlockSequence r = ref blockSequence[i]; + + table.Query(i, cNumTracks, out r.NumTracks); + table.Query(i, cTrackIndex, out r.TrackIndexOffset, out r.TrackIndexSize); + table.Query(i, cNumBlocks, out r.NumBlocks); + table.Query(i, cBlockIndex, out r.BlockIndexOffset, out r.BlockIndexSize); + } + } + + void LoadAcbBlockSequence(ushort index) + { + PreloadAcbBlockSequence(); + + if (index > blockSequenceRows) + throw new ArgumentOutOfRangeException(nameof(index)); + + ref BlockSequence r = ref blockSequence[index]; + + if (r.NumTracks * 2 > r.TrackIndexSize) + throw new Exception("Wrong BlockSequence.TrackIndex size."); + + for (int i = 0; i < r.NumTracks; i++) + { + blockSequenceReader.BaseStream.Position = r.TrackIndexOffset + i * 2; + + short trackIndexIndex = blockSequenceReader.ReadInt16(); + LoadAcbTrack((ushort)trackIndexIndex); + } + + if (r.NumBlocks * 2 > r.BlockIndexSize) + throw new Exception("Wrong BlockSequence.BlockIndex size."); + + for (int i = 0; i < r.NumBlocks; i++) + { + blockSequenceReader.BaseStream.Position = r.BlockIndexOffset + i * 2; + + short blockIndexIndex = blockSequenceReader.ReadInt16(); + LoadAcbBlock((ushort)blockIndexIndex); + } + } + + void PreloadAcbCue() + { + ref int rows = ref cueRows; + + if (rows != 0) return; + if (!OpenUtfSubtable(out cueReader, out UtfTable table, "CueTable", out rows)) + throw new Exception("Failure opening Cue table."); + if (rows == 0) return; + + cue = new Cue[rows]; + + int cReferenceType = table.GetColumn("ReferenceType"); + int cReferenceIndex = table.GetColumn("ReferenceIndex"); + + for (int i = 0; i < rows; i++) + { + ref Cue r = ref cue[i]; + + table.Query(i, cReferenceType, out r.ReferenceType); + table.Query(i, cReferenceIndex, out r.ReferenceIndex); + } + } + + void LoadAcbCue(ushort index) + { + PreloadAcbCue(); + + if (index > cueRows) + throw new ArgumentOutOfRangeException(nameof(index)); + + ref Cue r = ref cue[index]; + + switch (r.ReferenceType) + { + case 1: + LoadAcbWaveForm(r.ReferenceIndex); + break; + + case 2: + LoadAcbSynth(r.ReferenceIndex); + break; + + case 3: + LoadAcbSequence(r.ReferenceIndex); + break; + + case 8: + LoadAcbBlockSequence(r.ReferenceIndex); + break; + + default: + break; + } + } + + void PreloadAcbCueName() + { + ref UtfTable table = ref cueNames; + ref int rows = ref cueNameRows; + + if (rows != 0) return; + if (!OpenUtfSubtable(out cueNameReader, out table, "CueNameTable", out rows)) + throw new Exception("Failure opening CueName table."); + if (rows == 0) return; + + cueName = new CueName[rows]; + + int cCueIndex = table.GetColumn("CueIndex"); + int cCueName = table.GetColumn("CueName"); + + for (int i = 0; i < rows; i++) + { + ref CueName r = ref cueName[i]; + + table.Query(i, cCueIndex, out r.CueIndex); + table.Query(i, cCueName, out r.Name); + } + } + + void LoadAcbCueName(ushort index) + { + PreloadAcbCueName(); + + if (index > cueNameRows) + throw new ArgumentOutOfRangeException(nameof(index)); + + ref CueName r = ref cueName[index]; + + cueNameIndex = (short)index; + cueNameName = r.Name; + + LoadAcbCue(r.CueIndex); + } + + public string LoadWaveName(int waveId, int port, bool memory) + { + targetWaveId = waveId; + targetPort = port; + isMemory = memory; + + name = ""; + awbNameCount = 0; + awbNameList = new List(); + + PreloadAcbCueName(); + for (ushort i = 0; i < cueNameRows; i++) + { + LoadAcbCueName(i); + } + + return name; + } + } +} diff --git a/Assets/Scripts/Audio/CriWareFormats/AcbNameLoader.cs.meta b/Assets/Scripts/Audio/CriWareFormats/AcbNameLoader.cs.meta new file mode 100644 index 0000000..c6f507e --- /dev/null +++ b/Assets/Scripts/Audio/CriWareFormats/AcbNameLoader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 612a4d57772b3ad429b40eafbe7d0e3a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/CriWareFormats/AcbReader.cs b/Assets/Scripts/Audio/CriWareFormats/AcbReader.cs new file mode 100644 index 0000000..16910e4 --- /dev/null +++ b/Assets/Scripts/Audio/CriWareFormats/AcbReader.cs @@ -0,0 +1,57 @@ +using CriWareFormats.Common; +using System; +using System.Collections.Generic; +using System.IO; + +namespace CriWareFormats +{ + public sealed class AcbReader : IDisposable + { + private readonly Stream outerStream; + private readonly long positionOffset; + private readonly uint awbOffset; + private readonly uint awbLength; + + private readonly AcbNameLoader acbNameLoader; + + public AcbReader(Stream acbStream) : this(acbStream, 0) { } + + public AcbReader(Stream acbStream, long offset) + { + outerStream = acbStream; + positionOffset = offset; + + acbStream.Position = positionOffset; + + UtfTable utfTable = new UtfTable(acbStream, (uint)offset, out int rows, out string name); + + if (rows != 1 || !name.Equals("Header")) + throw new InvalidDataException("No Header table."); + + if (!utfTable.Query(0, "AwbFile", out VLData awbValueData)) + throw new InvalidDataException("No embedded AWB file."); + + awbOffset = awbValueData.Offset; + awbLength = awbValueData.Size; + + outerStream.Position = positionOffset; + acbNameLoader = new AcbNameLoader(outerStream); + } + + public AwbReader GetAwb() + { + return new AwbReader(new SpliceStream(outerStream, awbOffset, awbLength), true); + } + + public string GetWaveName(int waveId, int port, bool memory) + { + outerStream.Position = positionOffset; + return acbNameLoader.LoadWaveName(waveId, port, memory); + } + + public void Dispose() + { + outerStream.Dispose(); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Audio/CriWareFormats/AcbReader.cs.meta b/Assets/Scripts/Audio/CriWareFormats/AcbReader.cs.meta new file mode 100644 index 0000000..ce71d6a --- /dev/null +++ b/Assets/Scripts/Audio/CriWareFormats/AcbReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 407dbae8a66935f499cad063a996b044 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/CriWareFormats/AwbReader.cs b/Assets/Scripts/Audio/CriWareFormats/AwbReader.cs new file mode 100644 index 0000000..8cd828a --- /dev/null +++ b/Assets/Scripts/Audio/CriWareFormats/AwbReader.cs @@ -0,0 +1,139 @@ +using CriWareFormats.Common; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace CriWareFormats +{ + internal class Row + { + public string Name { get; set; } + public ushort Id { get; set; } + } + + public struct Wave + { + public int WaveId { get; set; } + public long Offset { get; set; } + public long Length { get; set; } + } + + public sealed class AwbReader : IDisposable + { + private readonly BinaryReader binaryReader; + + private readonly byte offsetSize; + private readonly int totalSubsongs; + private readonly ushort alignment; + private readonly ushort subkey; + + private readonly List cueNames; + private readonly List waves; + + public AwbReader(Stream awbStream, bool isEmbedded) : this(awbStream, 0) + { + IsEmbedded = isEmbedded; + } + + public AwbReader(Stream awbStream) : this(awbStream, 0) { } + + public AwbReader(Stream awbStream, long positionOffset) + { + binaryReader = new BinaryReader(awbStream); + + binaryReader.BaseStream.Position = positionOffset; + + if (!binaryReader.ReadChars(4).SequenceEqual("AFS2".ToCharArray())) + throw new InvalidDataException("Incorrect magic."); + + binaryReader.BaseStream.Position += 0x1; + offsetSize = binaryReader.ReadByte(); + binaryReader.BaseStream.Position += 0x2; + totalSubsongs = binaryReader.ReadInt32(); + alignment = binaryReader.ReadUInt16(); + subkey = binaryReader.ReadUInt16(); + + waves = new List(totalSubsongs); + + for (int subsong = 1; subsong <= totalSubsongs; subsong++) + { + long offset = 0x10; + + long waveIdOffset = offset + (subsong - 1) * 0x2; + + binaryReader.BaseStream.Position = waveIdOffset; + + int waveId = binaryReader.ReadUInt16(); + + offset += totalSubsongs * 0x2; + + long subfileOffset = 0; + long subfileNext = 0; + long fileSize = binaryReader.BaseStream.Length; + + offset += (subsong - 1) * offsetSize; + + binaryReader.BaseStream.Position = offset; + + switch (offsetSize) + { + case 0x4: + subfileOffset = binaryReader.ReadUInt32(); + subfileNext = binaryReader.ReadUInt32(); + break; + + case 0x2: + subfileOffset = binaryReader.ReadUInt16(); + subfileNext = binaryReader.ReadUInt16(); + break; + + default: + Fail(); + break; + } + + subfileOffset += subfileOffset % alignment > 0 ? + alignment - subfileOffset % alignment : 0; + subfileNext += subfileNext % alignment > 0 && subfileNext < fileSize ? + alignment - subfileNext % alignment : 0; + long subfileSize = subfileNext - subfileOffset; + + waves.Add(new Wave() + { + WaveId = waveId, + Offset = subfileOffset, + Length = subfileSize + }); + + //if (CueNames.Count > 0) Console.Write("{0,-36}", CueNames.Where(row => row.Index == waveId).First().Name); + //else Console.Write(waveId); + + //Console.WriteLine(subfileOffset); + } + + cueNames = new List(); + } + + public ushort Subkey => subkey; + + public bool IsEmbedded { get; } + + public List Waves => waves; + + public Stream GetWaveSubfileStream(Wave wave) + { + return new SpliceStream(binaryReader.BaseStream, wave.Offset, wave.Length); + } + + private static void Fail() + { + throw new Exception("Failure reading AWB file."); + } + + public void Dispose() + { + binaryReader.Dispose(); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Audio/CriWareFormats/AwbReader.cs.meta b/Assets/Scripts/Audio/CriWareFormats/AwbReader.cs.meta new file mode 100644 index 0000000..c08d940 --- /dev/null +++ b/Assets/Scripts/Audio/CriWareFormats/AwbReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0e1fffc1a1dfe27459a35520448ccaf0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/CriWareFormats/Common.meta b/Assets/Scripts/Audio/CriWareFormats/Common.meta new file mode 100644 index 0000000..a74cd00 --- /dev/null +++ b/Assets/Scripts/Audio/CriWareFormats/Common.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 13c5ca40b071c634bb7e480a0479ad81 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/CriWareFormats/Common/BinaryPrimitives.cs b/Assets/Scripts/Audio/CriWareFormats/Common/BinaryPrimitives.cs new file mode 100644 index 0000000..8079b17 --- /dev/null +++ b/Assets/Scripts/Audio/CriWareFormats/Common/BinaryPrimitives.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Assets.Scripts.Audio.CriWareFormats.Common +{ + public class BinaryPrimitives + { + internal static ushort ReverseEndianness(ushort le) + { + byte[] tmp = BitConverter.GetBytes(le); + Array.Reverse(tmp); + return BitConverter.ToUInt16(tmp,0); + } + + internal static int ReverseEndianness(int le) + { + byte[] tmp = BitConverter.GetBytes(le); + Array.Reverse(tmp); + return BitConverter.ToInt32(tmp, 0); + } + + internal static uint ReverseEndianness(uint le) + { + byte[] tmp = BitConverter.GetBytes(le); + Array.Reverse(tmp); + return BitConverter.ToUInt32(tmp, 0); + } + + internal static long ReverseEndianness(long le) + { + byte[] tmp = BitConverter.GetBytes(le); + Array.Reverse(tmp); + return BitConverter.ToInt64(tmp, 0); + } + + internal static ulong ReverseEndianness(ulong le) + { + byte[] tmp = BitConverter.GetBytes(le); + Array.Reverse(tmp); + return BitConverter.ToUInt64(tmp, 0); + } + + public static float Int32BitsToSingle(int m) + { + return BitConverter.ToSingle(BitConverter.GetBytes(m), 0); + } + } +} diff --git a/Assets/Scripts/Audio/CriWareFormats/Common/BinaryPrimitives.cs.meta b/Assets/Scripts/Audio/CriWareFormats/Common/BinaryPrimitives.cs.meta new file mode 100644 index 0000000..4cad7fd --- /dev/null +++ b/Assets/Scripts/Audio/CriWareFormats/Common/BinaryPrimitives.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 35c4bac6c1dd0664b975a22cf7ea445a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/CriWareFormats/Common/BinaryReaderEndian.cs b/Assets/Scripts/Audio/CriWareFormats/Common/BinaryReaderEndian.cs new file mode 100644 index 0000000..b9f08f4 --- /dev/null +++ b/Assets/Scripts/Audio/CriWareFormats/Common/BinaryReaderEndian.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime; +using Assets.Scripts.Audio.CriWareFormats.Common; + +namespace CriWareFormats.Common +{ + public class BinaryReaderEndian : BinaryReader + + { + public BinaryReaderEndian(Stream input) : base(input) + { + } + + public short ReadInt16BE() + { + ushort le = base.ReadUInt16(); + return (short)BinaryPrimitives.ReverseEndianness(le); + } + + public ushort ReadUInt16BE() + { + ushort le = base.ReadUInt16(); + return BinaryPrimitives.ReverseEndianness(le); + } + + public int ReadInt32BE() + { + uint le = base.ReadUInt32(); + return (int)BinaryPrimitives.ReverseEndianness(le); + } + + public uint ReadUInt32BE() + { + uint le = base.ReadUInt32(); + return BinaryPrimitives.ReverseEndianness(le); + } + + public long ReadInt64BE() + { + ulong le = base.ReadUInt64(); + return (long)BinaryPrimitives.ReverseEndianness(le); + } + + public ulong ReadUInt64BE() + { + ulong le = base.ReadUInt64(); + return BinaryPrimitives.ReverseEndianness(le); + } + + public float ReadSingleBE() + { + float le = base.ReadSingle(); + byte[] floatBytes = BitConverter.GetBytes(le); + byte[] reversed = new byte[4]; + for (int i = 0; i < 4; i++) + { + reversed[i] = floatBytes[3 - i]; + } + return BitConverter.ToSingle(reversed, 0); + } + + + } +} diff --git a/Assets/Scripts/Audio/CriWareFormats/Common/BinaryReaderEndian.cs.meta b/Assets/Scripts/Audio/CriWareFormats/Common/BinaryReaderEndian.cs.meta new file mode 100644 index 0000000..ea14afa --- /dev/null +++ b/Assets/Scripts/Audio/CriWareFormats/Common/BinaryReaderEndian.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f17047d6b7698144588e1d5181473cf9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/CriWareFormats/Common/Endian.cs b/Assets/Scripts/Audio/CriWareFormats/Common/Endian.cs new file mode 100644 index 0000000..6f7bc0a --- /dev/null +++ b/Assets/Scripts/Audio/CriWareFormats/Common/Endian.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CriWareFormats.Common +{ + public static class Endian + { + public static ushort Reverse(ushort value) + { + return (ushort)(value << 8 | value >> 8); + } + + public static uint Reverse(uint value) + { + value = (value & 0x00FF00FFu) << 8 | (value & 0xFF00FF00u) >> 8; + return (value & 0x0000FFFFu) << 16 | (value & 0xFFFF0000u) >> 16; + } + + public static ulong Reverse(ulong value) + { + value = (value & 0x00FF00FF00FF00FFu) << 8 | (value & 0xFF00FF00FF00FF00u) >> 8; + value = (value & 0x0000FFFF0000FFFFu) << 16 | (value & 0xFFFF0000FFFF0000u) >> 16; + return value << 32 | value >> 32; + } + } +} diff --git a/Assets/Scripts/Audio/CriWareFormats/Common/Endian.cs.meta b/Assets/Scripts/Audio/CriWareFormats/Common/Endian.cs.meta new file mode 100644 index 0000000..8403b84 --- /dev/null +++ b/Assets/Scripts/Audio/CriWareFormats/Common/Endian.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dfc4d0df7cc8c294ead855ed1ccc762d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/CriWareFormats/SpliceStream.cs b/Assets/Scripts/Audio/CriWareFormats/SpliceStream.cs new file mode 100644 index 0000000..9965fad --- /dev/null +++ b/Assets/Scripts/Audio/CriWareFormats/SpliceStream.cs @@ -0,0 +1,116 @@ +using System; +using System.IO; + +namespace CriWareFormats +{ + public class SpliceStream : Stream + { + private readonly Stream innerStream; + private readonly long realPosition; + + private long internalPosition; + + private readonly object positionLock = new object(); + + public SpliceStream(Stream sourceStream, long offset, long length) + { + innerStream = sourceStream; + realPosition = offset; + internalPosition = 0; + + Length = length; + } + + public override bool CanRead => true; + + public override bool CanSeek => true; + + public override bool CanWrite => false; + + public override long Length { get; } + + public override long Position + { + get + { + lock (positionLock) + { + return internalPosition; + } + } + set + { + lock (positionLock) + { + long checkValue = value + realPosition; + if (value < 0 || value >= Length) throw new ArgumentOutOfRangeException(nameof(value)); + internalPosition = value; + + if (innerStream.CanSeek) innerStream.Position = checkValue; + } + } + } + + public override void Flush() + { + throw new NotImplementedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + lock (positionLock) + { + if (innerStream.CanRead) + { + long restore = innerStream.Position; + innerStream.Position = realPosition + internalPosition; + int read = innerStream.Read(buffer, offset, count); + internalPosition += read; + innerStream.Position = restore; + return read; + } + else return 0; + } + } + + public override long Seek(long offset, SeekOrigin origin) + { + lock (positionLock) + { + switch (origin) + { + case SeekOrigin.Begin: + if (offset >= Length) throw new ArgumentOutOfRangeException(nameof(offset)); + internalPosition = offset; + break; + + case SeekOrigin.Current: + if (internalPosition + offset >= Length) throw new ArgumentOutOfRangeException(nameof(offset)); + internalPosition += offset; + break; + + case SeekOrigin.End: + if (internalPosition - offset < 0) throw new ArgumentOutOfRangeException(nameof(offset)); + internalPosition = Length; + internalPosition -= offset; + break; + + default: + break; + } + + return internalPosition; + } + } + + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + } +} diff --git a/Assets/Scripts/Audio/CriWareFormats/SpliceStream.cs.meta b/Assets/Scripts/Audio/CriWareFormats/SpliceStream.cs.meta new file mode 100644 index 0000000..0af8e81 --- /dev/null +++ b/Assets/Scripts/Audio/CriWareFormats/SpliceStream.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5e2b71da36bdac14fae41451686dac14 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/CriWareFormats/UtfTable.cs b/Assets/Scripts/Audio/CriWareFormats/UtfTable.cs new file mode 100644 index 0000000..087e4c6 --- /dev/null +++ b/Assets/Scripts/Audio/CriWareFormats/UtfTable.cs @@ -0,0 +1,435 @@ +using CriWareFormats.Common; +using System; +using System.IO; +using System.Linq; +using System.Text; + +namespace CriWareFormats +{ + [Flags] + public enum ColumnFlag : byte + { + Name = 0x10, + Default = 0x20, + Row = 0x40, + Undefined = 0x80 + } + + public enum ColumnType : byte + { + Byte = 0x00, + SByte = 0x01, + UInt16 = 0x02, + Int16 = 0x03, + UInt32 = 0x04, + Int32 = 0x05, + UInt64 = 0x06, + Int64 = 0x07, + Float = 0x08, + Double = 0x09, + String = 0x0A, + VLData = 0x0B, + UInt128 = 0x0C, + Undefined = 0xFF + } + + public struct Column + { + public ColumnFlag Flag; + public ColumnType Type; + public string Name; + public uint Offset; + + public override string ToString() + { + return Name; + } + } + + public struct VLData + { + public uint Offset; + public uint Size; + + public override string ToString() + { + return $"Offset: {Offset}, Length: {Size}"; + } + } + + public struct Result + { + public ColumnType Type; + public object Value; + } + + public sealed class UtfTable + { + private readonly BinaryReaderEndian binaryReader; + private readonly uint tableOffset; + + private readonly uint tableSize; + private readonly ushort version; + private readonly ushort rowsOffset; + private readonly uint stringsOffset; + private readonly uint dataOffset; + private readonly uint nameOffset; + + private readonly ushort columns; + private readonly ushort rowWidth; + private readonly uint rows; + + private readonly byte[] schemaBuffer; + private readonly Column[] schema; + + private readonly uint schemaOffset; + private readonly uint schemaSize; + private readonly uint rowsSize; + private readonly uint dataSize; + private readonly uint stringsSize; + + private readonly byte[] stringTable; + private readonly string tableName; + + public UtfTable(Stream utfTableStream, out int utfTableRows, out string utfTableRowName) : + this(utfTableStream, 0, out utfTableRows, out utfTableRowName) + { } + + public UtfTable(Stream utfTableStream, uint offset) : + this(utfTableStream, offset, out int _, out string _) + { } + + public UtfTable(Stream utfTableStream, uint offset, out int utfTableRows, out string rowName) + { + binaryReader = new BinaryReaderEndian(utfTableStream); + tableOffset = offset; + + binaryReader.BaseStream.Position = offset; + + if (!binaryReader.ReadChars(4).SequenceEqual("@UTF".ToCharArray())) + throw new InvalidDataException("Incorrect magic."); + tableSize = binaryReader.ReadUInt32BE() + 0x08; + version = binaryReader.ReadUInt16BE(); + rowsOffset = (ushort)(binaryReader.ReadUInt16BE() + 0x08); + stringsOffset = binaryReader.ReadUInt32BE() + 0x08; + dataOffset = binaryReader.ReadUInt32BE() + 0x08; + nameOffset = binaryReader.ReadUInt32BE(); + columns = binaryReader.ReadUInt16BE(); + rowWidth = binaryReader.ReadUInt16BE(); + rows = binaryReader.ReadUInt32BE(); + + schemaOffset = 0x20; + schemaSize = rowsOffset - schemaOffset; + rowsSize = stringsOffset - rowsOffset; + stringsSize = dataOffset - stringsOffset; + dataSize = tableSize - dataOffset; + + if (version != 0x00 && version != 0x01) + throw new InvalidDataException("Unknown @UTF version."); + if (tableOffset + tableSize > utfTableStream.Length) + throw new InvalidDataException("Table size exceeds bounds of file."); + if (rowsOffset > tableSize || stringsOffset > tableSize || dataOffset > tableSize) + throw new InvalidDataException("Offsets out of bounds."); + if (stringsSize <= 0 || nameOffset > stringsSize) + throw new InvalidDataException("Invalid string table size."); + if (Columns <= 0) + throw new InvalidDataException("Table has no columns."); + + schemaBuffer = new byte[schemaSize]; + binaryReader.BaseStream.Position = tableOffset + schemaOffset; + if (binaryReader.Read(schemaBuffer, 0, (int)schemaSize) != schemaSize) + throw new InvalidDataException("Failed to read schema."); + + stringTable = new byte[stringsSize]; + binaryReader.BaseStream.Position = tableOffset + stringsOffset; + if (binaryReader.Read(stringTable, 0, (int)stringsSize) != stringsSize) + throw new InvalidDataException("Failed to read string table."); + + uint columnOffset = 0; + uint schemaPos = 0; + + tableName = GetStringFromTable(nameOffset); + + schema = new Column[columns]; + + BinaryReaderEndian bytesReader = new BinaryReaderEndian(new MemoryStream(schemaBuffer) { Position = 0 }); + for (int i = 0; i < columns; i++) + { + bytesReader.BaseStream.Position = schemaPos; + + byte info = bytesReader.ReadByte(); + uint nameOffset = bytesReader.ReadUInt32BE(); + + if (nameOffset > stringsSize) + throw new InvalidDataException("String offset out of bounds."); + schemaPos += 0x1 + 0x4; + + bytesReader.BaseStream.Position = schemaPos; + + schema[i] = new Column() + { + Flag = (ColumnFlag)(info & 0xF0), + Type = (ColumnType)(info & 0x0F), + Name = "", + Offset = 0 + }; + + if (schema[i].Flag == 0 || + !schema[i].Flag.HasFlag(ColumnFlag.Name) || + schema[i].Flag.HasFlag(ColumnFlag.Default) && schema[i].Flag.HasFlag(ColumnFlag.Row) || + schema[i].Flag.HasFlag(ColumnFlag.Undefined)) + throw new InvalidDataException("Unknown column flag combo found."); + + uint valueSize; + switch (schema[i].Type) + { + case ColumnType.Byte: + case ColumnType.SByte: + valueSize = 0x1; + break; + case ColumnType.UInt16: + case ColumnType.Int16: + valueSize = 0x2; + break; + case ColumnType.UInt32: + case ColumnType.Int32: + case ColumnType.Float: + case ColumnType.String: + valueSize = 0x4; + break; + case ColumnType.UInt64: + case ColumnType.Int64: + case ColumnType.VLData: + valueSize = 0x8; + break; + default: + throw new InvalidDataException("Invalid column type."); + }; + + if (schema[i].Flag.HasFlag(ColumnFlag.Name)) + schema[i].Name = GetStringFromTable(nameOffset); + + if (schema[i].Flag.HasFlag(ColumnFlag.Default)) + { + schema[i].Offset = schemaPos; + schemaPos += valueSize; + + bytesReader.BaseStream.Position = schemaPos; + + } + + if (schema[i].Flag.HasFlag(ColumnFlag.Row)) + { + schema[i].Offset = columnOffset; + columnOffset += valueSize; + } + } + + utfTableRows = (int)rows; + rowName = GetStringFromTable(nameOffset); + + bytesReader.Dispose(); + } + + public ushort Columns => columns; + + public uint Rows => rows; + + public Column[] Schema => schema; + + public string TableName => tableName; + + public int GetColumn(string columnName) + { + for (int i = 0; i < columns; i++) + { + Column column = schema[i]; + + if (column.Name is null || !column.Name.Equals(columnName)) + continue; + + return i; + } + + return -1; + } + + private bool Query(int row, int column, out Result result) + { + result = new Result(); + + if (row >= rows || row < 0) + //throw new ArgumentOutOfRangeException(nameof(row)); + return false; + if (column >= columns || column < 0) + //throw new ArgumentOutOfRangeException(nameof(column)); + return false; + + Column col = schema[column]; + uint dataOffset = 0; + BinaryReaderEndian bytesReader = null; + + result.Type = col.Type; + + if (col.Flag.HasFlag(ColumnFlag.Default)) + { + if (schemaBuffer != null) + { + bytesReader = new BinaryReaderEndian(new MemoryStream(schemaBuffer)); + bytesReader.BaseStream.Position = col.Offset; + } + else + { + dataOffset = tableOffset + schemaOffset + col.Offset; + } + } + else if (col.Flag.HasFlag(ColumnFlag.Row)) + { + dataOffset = (uint)(tableOffset + rowsOffset + row * rowWidth + col.Offset); + } + else + throw new InvalidDataException("Invalid flag."); + + binaryReader.BaseStream.Position = dataOffset; + + switch (col.Type) + { + case ColumnType.Byte: + result.Value = (bytesReader != null) ? bytesReader.ReadByte() : binaryReader.ReadByte(); + break; + case ColumnType.SByte: + result.Value = (bytesReader != null) ? bytesReader.ReadSByte() : binaryReader.ReadSByte(); + break; + case ColumnType.UInt16: + result.Value = (bytesReader != null) ? bytesReader.ReadUInt16BE() : binaryReader.ReadUInt16BE(); + break; + case ColumnType.Int16: + result.Value = (bytesReader != null) ? bytesReader.ReadInt16BE() : binaryReader.ReadInt16BE(); + break; + case ColumnType.UInt32: + result.Value = (bytesReader != null) ? bytesReader.ReadUInt32BE() : binaryReader.ReadUInt32BE(); + break; + case ColumnType.Int32: + result.Value = (bytesReader != null) ? bytesReader.ReadInt32BE() : binaryReader.ReadInt32BE(); + break; + case ColumnType.UInt64: + result.Value = (bytesReader != null) ? bytesReader.ReadUInt64BE() : binaryReader.ReadUInt64BE(); + break; + case ColumnType.Int64: + result.Value = (bytesReader != null) ? bytesReader.ReadInt64BE() : binaryReader.ReadInt64BE(); + break; + case ColumnType.Float: + result.Value = (bytesReader != null) ? bytesReader.ReadSingleBE() : binaryReader.ReadSingleBE(); + break; + //case ColumnType.Double: + // break; + case ColumnType.String: + uint nameOffset = (bytesReader != null) ? bytesReader.ReadUInt32BE() : binaryReader.ReadUInt32BE(); + if (nameOffset > stringsSize) + throw new InvalidDataException("Name offset out of bounds."); + result.Value = GetStringFromTable(nameOffset); + break; + case ColumnType.VLData: + if ((bytesReader != null)) + { + result.Value = new VLData() + { + Offset = bytesReader.ReadUInt32BE(), + Size = bytesReader.ReadUInt32BE() + }; + } + else + { + result.Value = new VLData() + { + Offset = binaryReader.ReadUInt32BE(), + Size = binaryReader.ReadUInt32BE() + }; + } + break; + //case ColumnType.UInt128: + // break; + default: + return false; + } + + return true; + } + + public bool Query(int row, int column, out T value) + { + bool valid = Query(row, column, out Result result); + + bool enumParseResult = Enum.TryParse(typeof(T).Name, out ColumnType type); + + if (!valid || !enumParseResult || result.Type != type) + { + value = default; + return false; + } + + value = (T)result.Value; + + if (value is VLData vlData) + { + vlData.Offset += tableOffset + dataOffset; + value = (T)(object)vlData; + } + + return true; + } + + public bool Query(int row, string columnName, out T value) => + Query(row, GetColumn(columnName), out value); + + public bool Query(int row, int column, out uint offset, out uint size) + { + if (!Query(row, column, out VLData data)) + { + offset = 0; + size = 0; + + return false; + } + + offset = data.Offset; + size = data.Size; + + return true; + } + + public bool Query(int row, string columnName, out uint offset, out uint size) + => Query(row, GetColumn(columnName), out offset, out size); + + private string GetStringFromTable(uint offset) + { + if (offset > stringsSize) + throw new InvalidDataException("Invalid string offset."); + + StringBuilder stringBuilder = new StringBuilder(); + + int i = 0; + while (i < stringsSize) + { + stringBuilder.Append((char)stringTable[offset + i++]); + if (stringTable[offset + i] == '\0') break; + } + + return stringBuilder.ToString(); + } + + public UtfTable OpenSubtable(string tableName) + { + if (!Query(0, tableName, out VLData tableValueData)) + throw new ArgumentException("Subtable does not exist."); + + binaryReader.BaseStream.Position = tableValueData.Offset; + + return new UtfTable( + binaryReader.BaseStream, + tableValueData.Offset, + out int _, + out string _); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Audio/CriWareFormats/UtfTable.cs.meta b/Assets/Scripts/Audio/CriWareFormats/UtfTable.cs.meta new file mode 100644 index 0000000..eb7d199 --- /dev/null +++ b/Assets/Scripts/Audio/CriWareFormats/UtfTable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d662e0678754d5945b5223b3ab889993 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/HcaWaveStream.cs b/Assets/Scripts/Audio/HcaWaveStream.cs new file mode 100644 index 0000000..b8280cd --- /dev/null +++ b/Assets/Scripts/Audio/HcaWaveStream.cs @@ -0,0 +1,172 @@ +using ClHcaSharp; +using NAudio.Wave; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UmaMusumeAudio +{ + public class HcaWaveStream : WaveStream + { + private readonly Stream hcaFileStream; + private readonly BinaryReader hcaFileReader; + private readonly HcaDecoder decoder; + private readonly HcaInfo info; + private readonly long dataStart; + private readonly object positionLock = new object(); + + private readonly short[][] sampleBuffer; + + private long samplePosition; + + public HcaWaveStream(Stream hcaFile, ulong key) + { + hcaFileStream = hcaFile; + hcaFileReader = new BinaryReader(hcaFile); + decoder = new HcaDecoder(hcaFile, key); + info = decoder.GetInfo(); + dataStart = hcaFile.Position; + + sampleBuffer = new short[info.ChannelCount][]; + for (int i = 0; i < info.ChannelCount; i++) + { + sampleBuffer[i] = new short[info.SamplesPerBlock]; + } + + Loop = info.LoopEnabled; + LoopStartSample = info.LoopStartSample; + LoopEndSample = info.LoopEndSample; + + WaveFormat = new WaveFormat(info.SamplingRate, info.ChannelCount); + + samplePosition = info.EncoderDelay; + FillBuffer(samplePosition); + } + + public HcaInfo Info => Info1; + + public bool Loop { get; set; } + + public long LoopStartSample { get; set; } + + public long LoopEndSample { get; set; } + + public override WaveFormat WaveFormat { get; } + + public override long Length => Info1.SampleCount * Info1.ChannelCount * sizeof(short); + + public override long Position + { + get + { + lock (positionLock) + { + return (samplePosition - Info1.EncoderDelay) * Info1.ChannelCount * sizeof(short); + } + } + set + { + lock (positionLock) + { + samplePosition = value / Info1.ChannelCount / sizeof(short); + samplePosition += Info1.EncoderDelay; + + if (Position < Length) FillBuffer(samplePosition); + } + } + } + + public HcaInfo Info1 => info; + + public short[][] SampleBuffer => sampleBuffer; + + public HcaDecoder Decoder => decoder; + + public override int Read(byte[] buffer, int offset, int count) + { + lock (positionLock) + { + int read = 0; + + for (int i = 0; i < count / Info1.ChannelCount / sizeof(short); i++) + { + if (samplePosition - Info1.EncoderDelay >= LoopEndSample && Loop) + { + samplePosition = LoopStartSample + Info1.EncoderDelay; + FillBuffer(samplePosition); + } + else if (Position >= Length) break; + + if (samplePosition % Info1.SamplesPerBlock == 0) FillBuffer(samplePosition); + + for (int j = 0; j < Info1.ChannelCount; j++) + { + int bufferOffset = (i * Info1.ChannelCount + j) * sizeof(short); + buffer[offset + bufferOffset] = (byte)SampleBuffer[j][samplePosition % Info1.SamplesPerBlock]; + buffer[offset + bufferOffset + 1] = (byte)(SampleBuffer[j][samplePosition % Info1.SamplesPerBlock] >> 8); + + read += sizeof(short); + } + + samplePosition++; + } + + return read; + } + } + + public void ResetLoop() + { + Loop = Info1.LoopEnabled; + LoopStartSample = Info1.LoopStartSample; + LoopEndSample = Info1.LoopEndSample; + } + + private void FillBuffer(long sample) + { + int block = (int)(sample / Info1.SamplesPerBlock); + FillBuffer(block); + } + + private void FillBuffer(int block) + { + if (block >= 0) hcaFileStream.Position = dataStart + block * Info1.BlockSize; + + if (hcaFileStream.Position < hcaFileStream.Length) + { + byte[] blockBytes = hcaFileReader.ReadBytes(Info1.BlockSize); + if (blockBytes.Length > 0) + { + Decoder.DecodeBlock(blockBytes); + Decoder.ReadSamples16(SampleBuffer); + } + } + } + + //private void FillBuffer() + //{ + // if (hcaFileStream.Position >= hcaFileStream.Length) + // hcaFileStream.Position = dataStart; + + // byte[] blockBytes = hcaFileReader.ReadBytes(info.BlockSize); + // if (blockBytes.Length > 0) + // { + // decoder.DecodeBlock(blockBytes); + // decoder.ReadSamples16(sampleBuffer); + // } + // else + // { + // for (int i = 0; i < sampleBuffer.Length; i++) + // { + // for (int j = 0; j < sampleBuffer[i].Length; j++) + // { + // sampleBuffer[i][j] = 0; + // } + // } + // } + //} + } +} diff --git a/Assets/Scripts/Audio/HcaWaveStream.cs.meta b/Assets/Scripts/Audio/HcaWaveStream.cs.meta new file mode 100644 index 0000000..31393b7 --- /dev/null +++ b/Assets/Scripts/Audio/HcaWaveStream.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9d2df19f53207594cbf031ad1620c2ea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/UmaWaveStream.cs b/Assets/Scripts/Audio/UmaWaveStream.cs new file mode 100644 index 0000000..e402246 --- /dev/null +++ b/Assets/Scripts/Audio/UmaWaveStream.cs @@ -0,0 +1,54 @@ +using NAudio.Wave; +using System; +using UnityEngine; +using System.IO; +using CriWareFormats; +using ClHcaSharp; + +namespace UmaMusumeAudio +{ + public class UmaWaveStream : WaveStream + { + /* + * Open Umamusume > umamusume_Data > resources.assets with a hex editor. + * Check the results when finding the string "StreamingAssets". + * The key should be near a string named "cri_auth". + */ + private const ulong umaMusumeKey = 75923756697503; + + private readonly HcaWaveStream hcaWaveStream; + + public UmaWaveStream(AwbReader awbReader, int waveId) + { + Stream awbSubfile = awbReader.GetWaveSubfileStream(awbReader.Waves.Find((wave) => wave.WaveId == waveId)); + hcaWaveStream = new HcaWaveStream(awbSubfile, MixKey(umaMusumeKey, awbReader.Subkey)); + } + + public override WaveFormat WaveFormat => hcaWaveStream.WaveFormat; + + public bool Loop { get => hcaWaveStream.Loop; set { hcaWaveStream.Loop = value; } } + + public long LoopStartSample { get => hcaWaveStream.LoopStartSample; set { hcaWaveStream.LoopStartSample = value; } } + + public long LoopEndSample { get => hcaWaveStream.LoopEndSample; set { hcaWaveStream.LoopEndSample = value; } } + + public override long Length => hcaWaveStream.Length; + + public override long Position { get => hcaWaveStream.Position; set => hcaWaveStream.Position = value; } + + public short[][] SampleBuffer => hcaWaveStream.SampleBuffer; + + public HcaDecoder Decoder => hcaWaveStream.Decoder; + public HcaInfo Info => hcaWaveStream.Info; + + public override int Read(byte[] buffer, int offset, int count) + { + return hcaWaveStream.Read(buffer, offset, count); + } + + public void ResetLoop() => hcaWaveStream.ResetLoop(); + + private static ulong MixKey(ulong key, ushort subkey) => + key * (((ulong)subkey << 16) | ((ushort)~subkey + 2u)); + } +} diff --git a/Assets/Scripts/Audio/UmaWaveStream.cs.meta b/Assets/Scripts/Audio/UmaWaveStream.cs.meta new file mode 100644 index 0000000..adf3202 --- /dev/null +++ b/Assets/Scripts/Audio/UmaWaveStream.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d159c340b0cc08c47bfafb5e5641a630 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Audio/WAV.cs b/Assets/Scripts/Audio/WAV.cs new file mode 100644 index 0000000..f35f700 --- /dev/null +++ b/Assets/Scripts/Audio/WAV.cs @@ -0,0 +1,118 @@ +public class WAV +{ + + // convert two bytes to one float in the range -1 to 1 + static float bytesToFloat(byte firstByte, byte secondByte) + { + // convert two bytes to one short (little endian) + short s = (short)((secondByte << 8) | firstByte); + // convert to range from -1 to (just below) 1 + return s / 32768.0F; + } + + static int bytesToInt(byte[] bytes, int offset = 0) + { + int value = 0; + for (int i = 0; i < 4; i++) + { + value |= ((int)bytes[offset + i]) << (i * 8); + } + return value; + } + // properties + public float[] LeftChannel { get; internal set; } + public float[] RightChannel { get; internal set; } + public float[] StereoChannel { get; internal set; } + public int ChannelCount { get; internal set; } + public int SampleCount { get; internal set; } + public int Frequency { get; internal set; } + + public WAV(byte[] wav) + { + + // Determine if mono or stereo + ChannelCount = wav[22]; // Forget byte 23 as 99.999% of WAVs are 1 or 2 channels + + // Get the frequency + Frequency = bytesToInt(wav, 24); + + // Get past all the other sub chunks to get to the data subchunk: + int pos = 12; // First Subchunk ID from 12 to 16 + + // Keep iterating until we find the data chunk (i.e. 64 61 74 61 ...... (i.e. 100 97 116 97 in decimal)) + while (!(wav[pos] == 100 && wav[pos + 1] == 97 && wav[pos + 2] == 116 && wav[pos + 3] == 97)) + { + pos += 4; + int chunkSize = wav[pos] + wav[pos + 1] * 256 + wav[pos + 2] * 65536 + wav[pos + 3] * 16777216; + pos += 4 + chunkSize; + } + pos += 8; + + // Pos is now positioned to start of actual sound data. + SampleCount = (wav.Length - pos) / 2; // 2 bytes per sample (16 bit sound mono) + if (ChannelCount == 2) SampleCount /= 2; // 4 bytes per sample (16 bit stereo) + + // Allocate memory (right will be null if only mono sound) + LeftChannel = new float[SampleCount]; + if (ChannelCount == 2) RightChannel = new float[SampleCount]; + else RightChannel = null; + + // Write to double array/s: + int i = 0; + while (pos < wav.Length) + { + LeftChannel[i] = bytesToFloat(wav[pos], wav[pos + 1]); + pos += 2; + if (ChannelCount == 2) + { + RightChannel[i] = bytesToFloat(wav[pos], wav[pos + 1]); + pos += 2; + } + i++; + } + + //Merge left and right channels for stereo sound + if (ChannelCount == 2) + { + StereoChannel = new float[SampleCount * 2]; + //Current position in our left and right channels + int channelPos = 0; + //After we've changed two values for our Stereochannel, we want to increase our channelPos + short posChange = 0; + + for (int index = 0; index < (SampleCount * 2); index++) + { + + if (index % 2 == 0) + { + StereoChannel[index] = LeftChannel[channelPos]; + posChange++; + } + else + { + StereoChannel[index] = RightChannel[channelPos]; + posChange++; + } + //Two values have been changed, so update our channelPos + if (posChange % 2 == 0) + { + if (channelPos < SampleCount) + { + channelPos++; + //Reset the counter for next iterations + posChange = 0; + } + } + } + } + else + { + StereoChannel = null; + } + } + + public override string ToString() + { + return string.Format("[WAV: LeftChannel={0}, RightChannel={1}, ChannelCount={2}, SampleCount={3}, Frequency={4}]", LeftChannel, RightChannel, ChannelCount, SampleCount, Frequency); + } +} \ No newline at end of file diff --git a/Assets/Scripts/Audio/WAV.cs.meta b/Assets/Scripts/Audio/WAV.cs.meta new file mode 100644 index 0000000..fb63974 --- /dev/null +++ b/Assets/Scripts/Audio/WAV.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f0f51605a8223f8489a6c386d8d06198 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/UmaViewerBuilder.cs b/Assets/Scripts/UmaViewerBuilder.cs index 9cbd1b6..ada8b15 100644 --- a/Assets/Scripts/UmaViewerBuilder.cs +++ b/Assets/Scripts/UmaViewerBuilder.cs @@ -1,10 +1,15 @@ -using Gallop; +using CriWareFormats; +using Gallop; +using NAudio.Wave; +using NAudio.Wave.SampleProviders; using Newtonsoft.Json.Linq; using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; +using UmaMusumeAudio; using UnityEditor; using UnityEngine; @@ -21,7 +26,7 @@ public class UmaViewerBuilder : MonoBehaviour public UmaContainer CurrentUMAContainer; public UmaContainer CurrentLiveContainer; public UmaContainer CurrentOtherContainer; - + public AudioSource CurrentAudioSource; public AnimatorOverrideController OverrideController; @@ -337,10 +342,10 @@ public class UmaViewerBuilder : MonoBehaviour public void LoadLive(int id) { - var asset = UmaViewerMain.Instance.AbList.FirstOrDefault(a => a.Name.EndsWith("cutt_son"+id)); + var asset = UmaViewerMain.Instance.AbList.FirstOrDefault(a => a.Name.EndsWith("cutt_son" + id)); if (CurrentLiveContainer != null) { - Destroy(CurrentLiveContainer); + Destroy(CurrentLiveContainer.gameObject); } UnloadAllBundle(); CurrentLiveContainer = new GameObject(Path.GetFileName(asset.Name)).AddComponent(); @@ -348,6 +353,47 @@ public class UmaViewerBuilder : MonoBehaviour } + public void LoadLiveSound(int songid, UmaDatabaseEntry SongAwb) + { + if (CurrentAudioSource) + { + Destroy(CurrentAudioSource.gameObject); + } + AudioClip clip = LoadAudio(SongAwb); + if (clip != null) + { + CurrentAudioSource = new GameObject("SoundController").AddComponent(); + CurrentAudioSource.clip = clip; + CurrentAudioSource.Play(); + } + } + + public AudioClip LoadAudio(UmaDatabaseEntry awb) + { + UmaViewerUI.Instance.LoadedAssetsAdd(awb); + + string awbPath = $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "Low"}\\Cygames\\umamusume\\dat\\{awb.Url.Substring(0, 2)}\\{awb.Url}"; + if (!File.Exists(awbPath)) return null; + + FileStream awbFile = File.OpenRead(awbPath); + AwbReader awbReader = new AwbReader(awbFile); + + + var stream = new UmaWaveStream(awbReader, 0); + MemoryStream outputStream = new MemoryStream(); + WaveFileWriter.WriteWavFileToStream(outputStream, stream); + + WAV wav = new WAV(outputStream.ToArray()); + AudioClip clip = AudioClip.Create(Path.GetFileName(awb.Name).Replace(".awb", ""),wav.SampleCount,wav.ChannelCount,wav.Frequency,false); + clip.SetData(wav.ChannelCount > 1 ? wav.StereoChannel : wav.LeftChannel, 0); + + awbReader.Dispose(); + awbFile.Dispose(); + outputStream.Dispose(); + + return clip; + } + private void RecursiveLoadAsset(UmaDatabaseEntry entry, bool IsSubAsset = false) { if (!string.IsNullOrEmpty(entry.Prerequisites)) @@ -397,17 +443,17 @@ public class UmaViewerBuilder : MonoBehaviour Type aType = asset.GetType(); if (aType == typeof(AnimationClip)) { - if (CurrentUMAContainer&&CurrentUMAContainer.Body) + if (CurrentUMAContainer && CurrentUMAContainer.Body) { AnimationClip bbb = asset as AnimationClip; bbb.wrapMode = WrapMode.Loop; CurrentUMAContainer.OverrideController["clip"] = bbb; CurrentUMAContainer.UmaAnimator.Play("clip", 0, 0); - + } - if(!CurrentLiveContainer) - UnloadBundle(bundle, false); + if (!CurrentLiveContainer) + UnloadBundle(bundle, false); } else if (aType == typeof(GameObject)) { @@ -678,7 +724,7 @@ public class UmaViewerBuilder : MonoBehaviour private void LoadProp(GameObject go) { - var prop = Instantiate(go, (go.name.Contains("Cutt_son")?CurrentLiveContainer:CurrentOtherContainer).transform); + var prop = Instantiate(go, (go.name.Contains("Cutt_son") ? CurrentLiveContainer : CurrentOtherContainer).transform); foreach (Renderer r in prop.GetComponentsInChildren()) { foreach (Material m in r.sharedMaterials) diff --git a/Assets/Scripts/UmaViewerUI.cs b/Assets/Scripts/UmaViewerUI.cs index ddf9808..775c275 100644 --- a/Assets/Scripts/UmaViewerUI.cs +++ b/Assets/Scripts/UmaViewerUI.cs @@ -30,6 +30,8 @@ public class UmaViewerUI : MonoBehaviour,FaceLoadCallBack public ScrollRect FacialList; public ScrollRect LiveList; + public ScrollRect LiveSoundList; + public ScrollRect LiveCharaSoundList; //settings public TMPro.TMP_InputField SSWidth, SSHeight; @@ -117,7 +119,11 @@ public class UmaViewerUI : MonoBehaviour,FaceLoadCallBack { Destroy(ui.gameObject); } - foreach (FacialMorph morph in target.EyeBrowMorphs) + List tempMorph = new List(); + tempMorph.AddRange(target.EyeBrowMorphs); + tempMorph.AddRange(target.EyeMorphs); + tempMorph.AddRange(target.MouthMorphs); + foreach (FacialMorph morph in tempMorph) { var container = Instantiate(UmaContainerSliderPrefab, FacialList.content).GetComponent(); container.Name.text = morph.name; @@ -126,24 +132,6 @@ public class UmaViewerUI : MonoBehaviour,FaceLoadCallBack container.Slider.minValue = 0; container.Slider.onValueChanged.AddListener((a) => { morph.Weight = a;}); } - foreach (FacialMorph morph in target.EyeMorphs) - { - var container = Instantiate(UmaContainerSliderPrefab, FacialList.content).GetComponent(); - container.Name.text = morph.name; - container.Slider.value = morph.Weight; - container.Slider.maxValue = 1; - container.Slider.minValue = 0; - container.Slider.onValueChanged.AddListener((a) => { morph.Weight = a; }); - } - foreach (FacialMorph morph in target.MouthMorphs) - { - var container = Instantiate(UmaContainerSliderPrefab, FacialList.content).GetComponent(); - container.Name.text = morph.name; - container.Slider.value = morph.Weight; - container.Slider.maxValue = 1; - container.Slider.minValue = 0; - container.Slider.onValueChanged.AddListener((a) => { morph.Weight = a; }); - } } public void LoadLivePanels() @@ -159,7 +147,17 @@ public class UmaViewerUI : MonoBehaviour,FaceLoadCallBack Builder.LoadLive(live.MusicId); }); + var CharaContainer = Instantiate(UmaContainerNoTMPPrefab, LiveSoundList.content).GetComponent(); + CharaContainer.GetComponentInChildren().text = " " + live.MusicId + " " + live.songName; + var CharaimageInstance1 = container.GetComponent(); + CharaContainer.Button.onClick.AddListener(() => { + HighlightChildImage(LiveList.content, imageInstance1); + ListLiveSounds(live.MusicId); + }); + } + + } public void LoadMiniModelPanels() @@ -263,6 +261,32 @@ public class UmaViewerUI : MonoBehaviour,FaceLoadCallBack } } + void ListLiveSounds(int songid) + { + for (int i = LiveCharaSoundList.content.childCount - 1; i >= 0; i--) + { + Destroy(LiveCharaSoundList.content.GetChild(i).gameObject); + } + string nameVar = $"snd_bgm_live_{songid}_chara"; + foreach (var entry in Main.AbList.Where(a => a.Name.Contains(nameVar) && a.Name.EndsWith("awb"))) + { + var container = Instantiate(UmaContainerPrefab, LiveCharaSoundList.content).GetComponent(); + string[] split = entry.Name.Split('_'); + string name = split[split.Length - 2] + getCharaName(split[split.Length - 2]) + " " + split[split.Length - 1].Replace(".awb",""); + container.Name.text = name; + container.Button.onClick.AddListener(() => { + HighlightChildImage(LiveCharaSoundList.content, container.GetComponent()); + Builder.LoadLiveSound(songid, entry); + }); + } + } + + string getCharaName(string id) + { + var entry = Main.Characters.FirstOrDefault(a => a.Id.ToString().Equals(id)); + return (entry == null) ? id.ToString() : entry.Name; + } + public static string GetCostumeName(string costumeId) { switch (costumeId) diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset index 4086f3f..37bbbeb 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/ProjectSettings/ProjectSettings.asset @@ -161,7 +161,6 @@ PlayerSettings: m_ColorGamuts: 00000000 targetPixelDensity: 30 resolutionScalingMode: 0 - resetResolutionOnWindowResize: 0 androidSupportedAspectRatio: 1 androidMaxAspectRatio: 2.1 applicationIdentifier: @@ -514,9 +513,7 @@ PlayerSettings: switchPlayerConnectionEnabled: 1 switchUseNewStyleFilepaths: 0 switchUseMicroSleepForYield: 1 - switchEnableRamDiskSupport: 0 switchMicroSleepForYieldTime: 25 - switchRamDiskSpaceSize: 12 ps4NPAgeRating: 12 ps4NPTitleSecret: ps4NPTrophyPackPath: @@ -631,7 +628,8 @@ PlayerSettings: gcIncremental: 0 assemblyVersionValidation: 1 gcWBarrierValidation: 0 - apiCompatibilityLevelPerPlatform: {} + apiCompatibilityLevelPerPlatform: + Standalone: 6 m_RenderingPath: 1 m_MobileRenderingPath: 1 metroPackageName: Template_3D @@ -661,7 +659,6 @@ PlayerSettings: metroFTAName: metroFTAFileTypes: [] metroProtocolName: - vcxProjDefaultLanguage: XboxOneProductId: XboxOneUpdateKey: XboxOneSandboxId: diff --git a/app.config b/app.config new file mode 100644 index 0000000..d4ff09b --- /dev/null +++ b/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file