wav converter for mp3
This commit is contained in:
parent
88b21877ca
commit
961a2152b3
|
|
@ -73,6 +73,9 @@ namespace HeavenStudio
|
||||||
public event Action<double> onBeatChanged;
|
public event Action<double> onBeatChanged;
|
||||||
public event Action<RiqEntity, RiqEntity> onSectionChange;
|
public event Action<RiqEntity, RiqEntity> onSectionChange;
|
||||||
public event Action<double> onBeatPulse;
|
public event Action<double> onBeatPulse;
|
||||||
|
public event Action<double> onPlay;
|
||||||
|
public event Action<double> onPause;
|
||||||
|
public event Action<double> onUnPause;
|
||||||
|
|
||||||
public int BeatmapEntities()
|
public int BeatmapEntities()
|
||||||
{
|
{
|
||||||
|
|
@ -143,9 +146,6 @@ namespace HeavenStudio
|
||||||
eventCaller = this.gameObject.AddComponent<EventCaller>();
|
eventCaller = this.gameObject.AddComponent<EventCaller>();
|
||||||
eventCaller.GamesHolder = GamesHolder.transform;
|
eventCaller.GamesHolder = GamesHolder.transform;
|
||||||
eventCaller.Init();
|
eventCaller.Init();
|
||||||
Conductor.instance.SetBpm(120f);
|
|
||||||
Conductor.instance.SetVolume(100f);
|
|
||||||
Conductor.instance.firstBeatOffset = Beatmap.data.offset;
|
|
||||||
|
|
||||||
// note: serialize this shit in the inspector //
|
// note: serialize this shit in the inspector //
|
||||||
GameObject textbox = Instantiate(Resources.Load<GameObject>("Prefabs/Common/Textbox"));
|
GameObject textbox = Instantiate(Resources.Load<GameObject>("Prefabs/Common/Textbox"));
|
||||||
|
|
@ -177,6 +177,9 @@ namespace HeavenStudio
|
||||||
}
|
}
|
||||||
|
|
||||||
SortEventsList();
|
SortEventsList();
|
||||||
|
Conductor.instance.SetBpm(Beatmap.TempoChanges[0]["tempo"]);
|
||||||
|
Conductor.instance.SetVolume(Beatmap.VolumeChanges[0]["volume"]);
|
||||||
|
Conductor.instance.firstBeatOffset = Beatmap.data.offset;
|
||||||
|
|
||||||
if (Beatmap.Entities.Count >= 1)
|
if (Beatmap.Entities.Count >= 1)
|
||||||
{
|
{
|
||||||
|
|
@ -231,6 +234,7 @@ namespace HeavenStudio
|
||||||
|
|
||||||
public void NewRemix()
|
public void NewRemix()
|
||||||
{
|
{
|
||||||
|
Debug.Log("Creating new remix");
|
||||||
AudioLoadDone = false;
|
AudioLoadDone = false;
|
||||||
Beatmap = new("1", "HeavenStudio");
|
Beatmap = new("1", "HeavenStudio");
|
||||||
Beatmap.data.properties = Minigames.propertiesModel;
|
Beatmap.data.properties = Minigames.propertiesModel;
|
||||||
|
|
@ -238,7 +242,6 @@ namespace HeavenStudio
|
||||||
Beatmap.AddNewVolumeChange(0, 100f);
|
Beatmap.AddNewVolumeChange(0, 100f);
|
||||||
Beatmap.data.offset = 0f;
|
Beatmap.data.offset = 0f;
|
||||||
Conductor.instance.musicSource.clip = null;
|
Conductor.instance.musicSource.clip = null;
|
||||||
RiqFileHandler.UnlockCache();
|
|
||||||
RiqFileHandler.WriteRiq(Beatmap);
|
RiqFileHandler.WriteRiq(Beatmap);
|
||||||
AudioLoadDone = true;
|
AudioLoadDone = true;
|
||||||
}
|
}
|
||||||
|
|
@ -680,6 +683,11 @@ namespace HeavenStudio
|
||||||
Minigame miniGame = currentGameO?.GetComponent<Minigame>();
|
Minigame miniGame = currentGameO?.GetComponent<Minigame>();
|
||||||
if (miniGame != null)
|
if (miniGame != null)
|
||||||
miniGame.OnPlay(beat);
|
miniGame.OnPlay(beat);
|
||||||
|
onPlay?.Invoke(beat);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
onUnPause?.Invoke(beat);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playMode)
|
if (playMode)
|
||||||
|
|
@ -693,17 +701,21 @@ namespace HeavenStudio
|
||||||
public void Pause()
|
public void Pause()
|
||||||
{
|
{
|
||||||
Conductor.instance.Pause();
|
Conductor.instance.Pause();
|
||||||
// Util.SoundByte.PauseOneShots();
|
Util.SoundByte.PauseOneShots();
|
||||||
|
onPause?.Invoke(Conductor.instance.songPositionInBeatsAsDouble);
|
||||||
canInput = false;
|
canInput = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop(double beat, bool restart = false, float restartDelay = 0f)
|
public void Stop(double beat, bool restart = false, float restartDelay = 0f)
|
||||||
{
|
{
|
||||||
// I feel like I should standardize the names
|
// I feel like I should standardize the names
|
||||||
SkillStarManager.instance.KillStar();
|
if (Conductor.instance.isPlaying)
|
||||||
TimingAccuracyDisplay.instance.StopStarFlash();
|
{
|
||||||
GoForAPerfect.instance.Disable();
|
SkillStarManager.instance.KillStar();
|
||||||
SectionMedalsManager.instance.OnRemixEnd(endBeat, currentSection);
|
TimingAccuracyDisplay.instance.StopStarFlash();
|
||||||
|
GoForAPerfect.instance.Disable();
|
||||||
|
SectionMedalsManager.instance.OnRemixEnd(endBeat, currentSection);
|
||||||
|
}
|
||||||
|
|
||||||
Minigame miniGame = currentGameO.GetComponent<Minigame>();
|
Minigame miniGame = currentGameO.GetComponent<Minigame>();
|
||||||
if (miniGame != null)
|
if (miniGame != null)
|
||||||
|
|
@ -739,22 +751,27 @@ namespace HeavenStudio
|
||||||
private IEnumerator WaitReadyAndPlayCo(double beat, float delay = 1f, bool discord = true)
|
private IEnumerator WaitReadyAndPlayCo(double beat, float delay = 1f, bool discord = true)
|
||||||
{
|
{
|
||||||
WaitUntil yieldOverlays = new WaitUntil(() => OverlaysManager.OverlaysReady);
|
WaitUntil yieldOverlays = new WaitUntil(() => OverlaysManager.OverlaysReady);
|
||||||
WaitUntil yieldBeatmap = new WaitUntil(() => Beatmap != null && Beatmap.Entities.Count > 0);
|
WaitUntil yieldBeatmap = new WaitUntil(() => Beatmap != null && BeatmapEntities() > 0);
|
||||||
WaitUntil yieldAudio = new WaitUntil(() => AudioLoadDone || (ChartLoadError && !GlobalGameManager.IsShowingDialog));
|
WaitUntil yieldAudio = new WaitUntil(() => AudioLoadDone || (ChartLoadError && !GlobalGameManager.IsShowingDialog));
|
||||||
WaitUntil yieldGame = null;
|
WaitUntil yieldGame = null;
|
||||||
List<Minigames.Minigame> gamesToPreload = SeekAheadAndPreload(beat, 4f);
|
List<Minigames.Minigame> gamesToPreload = SeekAheadAndPreload(beat, 4f);
|
||||||
|
Debug.Log($"Preloading {gamesToPreload.Count} games");
|
||||||
if (gamesToPreload.Count > 0)
|
if (gamesToPreload.Count > 0)
|
||||||
{
|
{
|
||||||
yieldGame = new WaitUntil(() => gamesToPreload.All(x => x.AssetsLoaded));
|
yieldGame = new WaitUntil(() => gamesToPreload.All(x => x.AssetsLoaded));
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for overlays to be ready
|
// wait for overlays to be ready
|
||||||
|
Debug.Log("waiting for overlays");
|
||||||
yield return yieldOverlays;
|
yield return yieldOverlays;
|
||||||
// wait for beatmap to be loaded
|
// wait for beatmap to be loaded
|
||||||
|
Debug.Log("waiting for beatmap");
|
||||||
yield return yieldBeatmap;
|
yield return yieldBeatmap;
|
||||||
//wait for audio clip to be loaded
|
//wait for audio clip to be loaded
|
||||||
|
Debug.Log("waiting for audio");
|
||||||
yield return yieldAudio;
|
yield return yieldAudio;
|
||||||
//wait for games to be loaded
|
//wait for games to be loaded
|
||||||
|
Debug.Log("waiting for minigames");
|
||||||
if (yieldGame != null)
|
if (yieldGame != null)
|
||||||
yield return yieldGame;
|
yield return yieldGame;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -414,7 +414,6 @@ namespace HeavenStudio.Editor
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
RiqFileHandler.UnlockCache();
|
|
||||||
RiqFileHandler.WriteRiq(GameManager.instance.Beatmap);
|
RiqFileHandler.WriteRiq(GameManager.instance.Beatmap);
|
||||||
RiqFileHandler.PackRiq(path, true);
|
RiqFileHandler.PackRiq(path, true);
|
||||||
Debug.Log("Packed RIQ successfully!");
|
Debug.Log("Packed RIQ successfully!");
|
||||||
|
|
@ -465,7 +464,6 @@ namespace HeavenStudio.Editor
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
RiqFileHandler.UnlockCache();
|
|
||||||
string tmpDir = RiqFileHandler.ExtractRiq(path);
|
string tmpDir = RiqFileHandler.ExtractRiq(path);
|
||||||
Debug.Log("Imported RIQ successfully!");
|
Debug.Log("Imported RIQ successfully!");
|
||||||
LoadRemix();
|
LoadRemix();
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ using System.Threading.Tasks;
|
||||||
using Cysharp.Threading.Tasks;
|
using Cysharp.Threading.Tasks;
|
||||||
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityEngine.Networking;
|
||||||
using DG.Tweening;
|
using DG.Tweening;
|
||||||
|
|
||||||
using HeavenStudio.Util;
|
using HeavenStudio.Util;
|
||||||
|
|
@ -32,6 +33,7 @@ namespace HeavenStudio
|
||||||
public static void InitPreprocessor()
|
public static void InitPreprocessor()
|
||||||
{
|
{
|
||||||
RiqBeatmap.OnUpdateBeatmap += PreProcessBeatmap;
|
RiqBeatmap.OnUpdateBeatmap += PreProcessBeatmap;
|
||||||
|
RiqFileHandler.AudioConverter = JukeboxAudioConverter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Dictionary<string, object> propertiesModel = new()
|
public static Dictionary<string, object> propertiesModel = new()
|
||||||
|
|
@ -142,6 +144,35 @@ namespace HeavenStudio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string JukeboxAudioConverter(string filePath, AudioType audioType, string specificType)
|
||||||
|
{
|
||||||
|
if (audioType == AudioType.MPEG)
|
||||||
|
{
|
||||||
|
Debug.Log($"mp3 loaded, Converting {filePath} to wav...");
|
||||||
|
// convert mp3 to wav
|
||||||
|
// import the mp3 as an audioclip
|
||||||
|
string url = "file://" + filePath;
|
||||||
|
using (var www = UnityWebRequestMultimedia.GetAudioClip(url, audioType))
|
||||||
|
{
|
||||||
|
www.SendWebRequest();
|
||||||
|
while (!www.isDone) { }
|
||||||
|
if (www.result == UnityWebRequest.Result.ConnectionError)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Could not load audio file {filePath}! Error: {www.error}");
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
AudioClip clip = DownloadHandlerAudioClip.GetContent(www);
|
||||||
|
string fileName = Path.GetFileNameWithoutExtension(filePath);
|
||||||
|
Debug.Log(fileName);
|
||||||
|
SavWav.Save("/RIQCache/" + fileName, clip, true);
|
||||||
|
filePath = Path.Combine(Application.temporaryCachePath, "/RIQCache/" + fileName + ".wav");
|
||||||
|
|
||||||
|
clip = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// processes an riq beatmap after it is loaded
|
/// processes an riq beatmap after it is loaded
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ namespace HeavenStudio.Common
|
||||||
public void OnRemixEnd(double beat, RiqEntity lastSection)
|
public void OnRemixEnd(double beat, RiqEntity lastSection)
|
||||||
{
|
{
|
||||||
if (!PersistentDataManager.gameSettings.isMedalOn) return;
|
if (!PersistentDataManager.gameSettings.isMedalOn) return;
|
||||||
if (PersistentDataManager.gameSettings.isMedalOn && isMedalsStarted)
|
if (PersistentDataManager.gameSettings.isMedalOn && isMedalsStarted && lastSection != null)
|
||||||
{
|
{
|
||||||
GameManager.instance.DoSectionCompletion(beat, isMedalsEligible, lastSection["sectionName"], 1);
|
GameManager.instance.DoSectionCompletion(beat, isMedalsEligible, lastSection["sectionName"], 1);
|
||||||
GameObject medal = Instantiate(isMedalsEligible ? MedalOkPrefab : MedalMissPrefab, MedalsHolder.transform);
|
GameObject medal = Instantiate(isMedalsEligible ? MedalOkPrefab : MedalMissPrefab, MedalsHolder.transform);
|
||||||
|
|
|
||||||
187
Assets/Scripts/Util/SavWav.cs
Normal file
187
Assets/Scripts/Util/SavWav.cs
Normal file
|
|
@ -0,0 +1,187 @@
|
||||||
|
|
||||||
|
// Copyright (c) 2012 Calvin Rien
|
||||||
|
// http://the.darktable.com
|
||||||
|
//
|
||||||
|
// This software is provided 'as-is', without any express or implied warranty. In
|
||||||
|
// no event will the authors be held liable for any damages arising from the use
|
||||||
|
// of this software.
|
||||||
|
//
|
||||||
|
// Permission is granted to anyone to use this software for any purpose,
|
||||||
|
// including commercial applications, and to alter it and redistribute it freely,
|
||||||
|
// subject to the following restrictions:
|
||||||
|
//
|
||||||
|
// 1. The origin of this software must not be misrepresented; you must not claim
|
||||||
|
// that you wrote the original software. If you use this software in a product,
|
||||||
|
// an acknowledgment in the product documentation would be appreciated but is not
|
||||||
|
// required.
|
||||||
|
//
|
||||||
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
// misrepresented as being the original software.
|
||||||
|
//
|
||||||
|
// 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
//
|
||||||
|
// =============================================================================
|
||||||
|
//
|
||||||
|
// derived from Gregorio Zanon's script
|
||||||
|
// http://forum.unity3d.com/threads/119295-Writing-AudioListener.GetOutputData-to-wav-problem?p=806734&viewfull=1#post806734
|
||||||
|
|
||||||
|
//
|
||||||
|
// Fork by R-WebsterNoble
|
||||||
|
// Optimized! Now 20 times quicker
|
||||||
|
// Easy to get byte[] instead of saving file
|
||||||
|
|
||||||
|
// Note: GetWav() with trimming returns the full buffer with a load of zeros on the end.
|
||||||
|
// Use the length out parameter to know where the data stops.
|
||||||
|
//
|
||||||
|
|
||||||
|
// RHeavenStudio
|
||||||
|
// modified to use temporary cache path instead of persistent data path
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public static class SavWav
|
||||||
|
{
|
||||||
|
private const uint HeaderSize = 44;
|
||||||
|
private const float RescaleFactor = 32767; //to convert float to Int16
|
||||||
|
|
||||||
|
public static void Save(string filename, AudioClip clip, bool trim = false)
|
||||||
|
{
|
||||||
|
if (!filename.ToLower().EndsWith(".wav"))
|
||||||
|
{
|
||||||
|
filename += ".wav";
|
||||||
|
}
|
||||||
|
|
||||||
|
var filepath = Path.Combine(Application.temporaryCachePath, filename);
|
||||||
|
|
||||||
|
// Make sure directory exists if user is saving to sub dir.
|
||||||
|
if (!Directory.Exists(Path.GetDirectoryName(filepath)))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(filepath));
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var fileStream = new FileStream(filepath, FileMode.Create))
|
||||||
|
using (var writer = new BinaryWriter(fileStream))
|
||||||
|
{
|
||||||
|
var wav = GetWav(clip, out var length, trim);
|
||||||
|
writer.Write(wav, 0, (int)length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] GetWav(AudioClip clip, out uint length, bool trim = false)
|
||||||
|
{
|
||||||
|
var data = ConvertAndWrite(clip, out length, out var samples, trim);
|
||||||
|
|
||||||
|
WriteHeader(data, clip, length, samples);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] ConvertAndWrite(AudioClip clip, out uint length, out uint samplesAfterTrimming, bool trim)
|
||||||
|
{
|
||||||
|
var samples = new float[clip.samples * clip.channels];
|
||||||
|
|
||||||
|
clip.GetData(samples, 0);
|
||||||
|
|
||||||
|
var sampleCount = samples.Length;
|
||||||
|
|
||||||
|
var start = 0;
|
||||||
|
var end = sampleCount - 1;
|
||||||
|
|
||||||
|
if (trim)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < sampleCount; i++)
|
||||||
|
{
|
||||||
|
if ((short)(samples[i] * RescaleFactor) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
start = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = sampleCount -1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if ((short)(samples[i] * RescaleFactor) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
end = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var buffer = new byte[(sampleCount * 2) + HeaderSize];
|
||||||
|
|
||||||
|
var p = HeaderSize;
|
||||||
|
for (var i = start; i <= end; i++)
|
||||||
|
{
|
||||||
|
var value = (short) (samples[i] * RescaleFactor);
|
||||||
|
buffer[p++] = (byte) (value >> 0);
|
||||||
|
buffer[p++] = (byte) (value >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
length = p;
|
||||||
|
samplesAfterTrimming = (uint) (end - start + 1);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddDataToBuffer(byte[] buffer, ref uint offset, byte[] addBytes)
|
||||||
|
{
|
||||||
|
foreach (var b in addBytes)
|
||||||
|
{
|
||||||
|
buffer[offset++] = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteHeader(byte[] stream, AudioClip clip, uint length, uint samples)
|
||||||
|
{
|
||||||
|
var hz = (uint)clip.frequency;
|
||||||
|
var channels = (ushort)clip.channels;
|
||||||
|
|
||||||
|
var offset = 0u;
|
||||||
|
|
||||||
|
var riff = Encoding.UTF8.GetBytes("RIFF");
|
||||||
|
AddDataToBuffer(stream, ref offset, riff);
|
||||||
|
|
||||||
|
var chunkSize = BitConverter.GetBytes(length - 8);
|
||||||
|
AddDataToBuffer(stream, ref offset, chunkSize);
|
||||||
|
|
||||||
|
var wave = Encoding.UTF8.GetBytes("WAVE");
|
||||||
|
AddDataToBuffer(stream, ref offset, wave);
|
||||||
|
|
||||||
|
var fmt = Encoding.UTF8.GetBytes("fmt ");
|
||||||
|
AddDataToBuffer(stream, ref offset, fmt);
|
||||||
|
|
||||||
|
var subChunk1 = BitConverter.GetBytes(16u);
|
||||||
|
AddDataToBuffer(stream, ref offset, subChunk1);
|
||||||
|
|
||||||
|
const ushort two = 2;
|
||||||
|
const ushort one = 1;
|
||||||
|
|
||||||
|
var audioFormat = BitConverter.GetBytes(one);
|
||||||
|
AddDataToBuffer(stream, ref offset, audioFormat);
|
||||||
|
|
||||||
|
var numChannels = BitConverter.GetBytes(channels);
|
||||||
|
AddDataToBuffer(stream, ref offset, numChannels);
|
||||||
|
|
||||||
|
var sampleRate = BitConverter.GetBytes(hz);
|
||||||
|
AddDataToBuffer(stream, ref offset, sampleRate);
|
||||||
|
|
||||||
|
var byteRate = BitConverter.GetBytes(hz * channels * 2); // sampleRate * bytesPerSample*number of channels, here 44100*2*2
|
||||||
|
AddDataToBuffer(stream, ref offset, byteRate);
|
||||||
|
|
||||||
|
var blockAlign = (ushort)(channels * 2);
|
||||||
|
AddDataToBuffer(stream, ref offset, BitConverter.GetBytes(blockAlign));
|
||||||
|
|
||||||
|
ushort bps = 16;
|
||||||
|
var bitsPerSample = BitConverter.GetBytes(bps);
|
||||||
|
AddDataToBuffer(stream, ref offset, bitsPerSample);
|
||||||
|
|
||||||
|
var dataString = Encoding.UTF8.GetBytes("data");
|
||||||
|
AddDataToBuffer(stream, ref offset, dataString);
|
||||||
|
|
||||||
|
var subChunk2 = BitConverter.GetBytes(samples * 2);
|
||||||
|
AddDataToBuffer(stream, ref offset, subChunk2);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Assets/Scripts/Util/SavWav.cs.meta
Normal file
11
Assets/Scripts/Util/SavWav.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5a24cd489c15c51408c78bb5c145360e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
"com.unity.nuget.newtonsoft-json": "3.2.1",
|
"com.unity.nuget.newtonsoft-json": "3.2.1",
|
||||||
"jillejr.newtonsoft.json-for-unity.converters": "1.5.1"
|
"jillejr.newtonsoft.json-for-unity.converters": "1.5.1"
|
||||||
},
|
},
|
||||||
"hash": "33fa051b5a6954a365df23038085caca9a844cab"
|
"hash": "4628637dd95ff16553a55e0cdb08f6c4fbce2906"
|
||||||
},
|
},
|
||||||
"com.sr4dev.unity-spriteassist": {
|
"com.sr4dev.unity-spriteassist": {
|
||||||
"version": "https://github.com/sr4dev/Unity-SpriteAssist.git?path=Assets/SpriteAssist",
|
"version": "https://github.com/sr4dev/Unity-SpriteAssist.git?path=Assets/SpriteAssist",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue