Skip to content

Commit

Permalink
Merge pull request #135 from Kylemc1413/feature/zenject
Browse files Browse the repository at this point in the history
Zenjectify
Meivyn authored Mar 19, 2024
2 parents 986212c + 765786b commit 7c52d22
Showing 27 changed files with 682 additions and 625 deletions.
23 changes: 17 additions & 6 deletions source/SongCore/Collections.cs
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@ public static class Collections
internal static readonly ConcurrentDictionary<string, string> LevelHashDictionary = new ConcurrentDictionary<string, string>();
internal static readonly ConcurrentDictionary<string, List<string>> HashLevelDictionary = new ConcurrentDictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
internal static readonly ConcurrentDictionary<string, string> LevelPathDictionary = new ConcurrentDictionary<string, string>();
internal static readonly ConcurrentDictionary<string, StandardLevelInfoSaveData> LevelSaveDataDictionary = new ConcurrentDictionary<string, StandardLevelInfoSaveData>();

internal static BeatmapLevelPack? WipLevelPack;
internal static ConcurrentDictionary<string, ExtraSongData> CustomSongsData = new ConcurrentDictionary<string, ExtraSongData>();
@@ -44,6 +45,17 @@ public static List<string> levelIDsForHash(string hash)
return HashLevelDictionary.TryGetValue(hash, out var songs) ? songs : new List<string>();
}

public static string GetCustomLevelPath(string levelID)
{
return LevelPathDictionary.TryGetValue(levelID, out var path) ? path : string.Empty;
}

public static StandardLevelInfoSaveData? GetStandardLevelInfoSaveData(string levelID)
{
LevelSaveDataDictionary.TryGetValue(levelID, out var standardLevelInfoSaveData);
return standardLevelInfoSaveData;
}

internal static void AddExtraSongData(string hash, string path, string rawSongData)
{
if (!CustomSongsData.ContainsKey(hash))
@@ -135,7 +147,7 @@ public static void RegisterCapability(string capability)
return null;
}

public static SeperateSongFolder AddSeperateSongFolder(string name, string folderPath, FolderLevelPack pack, Sprite? image = null, bool wip = false, bool cachezips = false)
public static SeparateSongFolder AddSeparateSongFolder(string name, string folderPath, FolderLevelPack pack, Sprite? image = null, bool wip = false, bool cachezips = false)
{
UI.BasicUI.GetIcons();
if (!Directory.Exists(folderPath))
@@ -152,14 +164,13 @@ public static SeperateSongFolder AddSeperateSongFolder(string name, string folde
}

var entry = new SongFolderEntry(name, folderPath, pack, "", wip, cachezips);
var seperateSongFolder = new ModSeperateSongFolder(entry, image == null ? UI.BasicUI.FolderIcon! : image);
var separateSongFolder = new ModSeparateSongFolder(entry, image == null ? UI.BasicUI.FolderIcon! : image);

Loader.SeperateSongFolders.Add(seperateSongFolder);
return seperateSongFolder;
Loader.SeparateSongFolders.Add(separateSongFolder);
return separateSongFolder;
}


public static void DeregisterizeCapability(string capability)
public static void DeregisterCapability(string capability)
{
_capabilities.Remove(capability);
}
12 changes: 0 additions & 12 deletions source/SongCore/Data/SongData.cs
Original file line number Diff line number Diff line change
@@ -9,18 +9,6 @@

namespace SongCore.Data
{
public class SongData
{
public string RawSongData;
public StandardLevelInfoSaveData SaveData;

public SongData(string rawSongData, StandardLevelInfoSaveData saveData)
{
RawSongData = rawSongData;
SaveData = saveData;
}
}

[Serializable]
public class ExtraSongData
{
32 changes: 16 additions & 16 deletions source/SongCore/Data/SongFolderEntries.cs
Original file line number Diff line number Diff line change
@@ -32,15 +32,15 @@ public SongFolderEntry(string name, string path, FolderLevelPack pack, string im
}
}

public class SeperateSongFolder
public class SeparateSongFolder
{
public readonly ConcurrentDictionary<string, BeatmapLevel> Levels = new ConcurrentDictionary<string, BeatmapLevel>();

public SongFolderEntry SongFolderEntry { get; private set; }
public SongCoreCustomBeatmapLevelPack LevelPack { get; private set; } = null;
public SeperateSongFolder? CacheFolder { get; private set; }
public SeparateSongFolder? CacheFolder { get; private set; }

public SeperateSongFolder(SongFolderEntry folderEntry, SeperateSongFolder? cacheFolder = null)
public SeparateSongFolder(SongFolderEntry folderEntry, SeparateSongFolder? cacheFolder = null)
{
SongFolderEntry = folderEntry;
CacheFolder = cacheFolder;
@@ -61,15 +61,15 @@ public SeperateSongFolder(SongFolderEntry folderEntry, SeperateSongFolder? cache
}
catch
{
Logging.Logger.Info($"Failed to Load Image For Separate Folder \"{folderEntry.Name}\"");
Logging.Logger.Info($"Failed to load image for separate folder \"{folderEntry.Name}\"");
}
}

LevelPack = new SongCoreCustomBeatmapLevelPack(CustomLevelLoader.kCustomLevelPackPrefixId + folderEntry.Name, folderEntry.Name, image, Levels.Values.ToArray());
}
}

public SeperateSongFolder(SongFolderEntry folderEntry, UnityEngine.Sprite image)
public SeparateSongFolder(SongFolderEntry folderEntry, UnityEngine.Sprite image)
{
SongFolderEntry = folderEntry;
if (folderEntry.Pack == FolderLevelPack.NewPack)
@@ -78,9 +78,9 @@ public SeperateSongFolder(SongFolderEntry folderEntry, UnityEngine.Sprite image)
}
}

public static List<SeperateSongFolder> ReadSeperateFoldersFromFile(string filePath)
public static List<SeparateSongFolder> ReadSeparateFoldersFromFile(string filePath)
{
var result = new List<SeperateSongFolder>();
var result = new List<SeparateSongFolder>();
try
{
XDocument file = XDocument.Load(filePath);
@@ -123,41 +123,41 @@ public static List<SeperateSongFolder> ReadSeperateFoldersFromFile(string filePa
// Console.WriteLine(" " + entry.Pack);
// Console.WriteLine(" " + entry.WIP);

SeperateSongFolder? cachedSeperate = null;
SeparateSongFolder? cachedSeparate = null;
if (zipCaching)
{
var cachePack = (FolderLevelPack) pack == FolderLevelPack.CustomWIPLevels ? FolderLevelPack.CachedWIPLevels : FolderLevelPack.NewPack;

SongFolderEntry cachedSongFolderEntry = new SongFolderEntry($"Cached {name}", Path.Combine(path, "Cache"), cachePack, imagePath, isWIP, false);
cachedSeperate = new SeperateSongFolder(cachedSongFolderEntry);
cachedSeparate = new SeparateSongFolder(cachedSongFolderEntry);
}

var seperate = new SeperateSongFolder(entry, cachedSeperate);
var seperate = new SeparateSongFolder(entry, cachedSeparate);
result.Add(seperate);
if (cachedSeperate != null)
if (cachedSeparate != null)
{
result.Add(cachedSeperate);
result.Add(cachedSeparate);
}
}
}
catch
{
Logging.Logger.Warn("Error Reading folders.xml! Make sure the file is properly formatted.");
Logging.Logger.Warn("Error reading folders.xml! Make sure the file is properly formatted.");
}

return result;
}
}

public class ModSeperateSongFolder : SeperateSongFolder
public class ModSeparateSongFolder : SeparateSongFolder
{
public bool AlwaysShow { get; set; } = true;

public ModSeperateSongFolder(SongFolderEntry folderEntry) : base(folderEntry)
public ModSeparateSongFolder(SongFolderEntry folderEntry) : base(folderEntry)
{
}

public ModSeperateSongFolder(SongFolderEntry folderEntry, UnityEngine.Sprite image) : base(folderEntry, image)
public ModSeparateSongFolder(SongFolderEntry folderEntry, UnityEngine.Sprite image) : base(folderEntry, image)
{
}
}
6 changes: 3 additions & 3 deletions source/SongCore/Data/folders.xml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<!--
<!--
Syntax for folder entries (Entries with name Example will be skipped)
<folder>
<Name> Name to be used for the folder entry, is used for level pack name if set to be a new pack i.e. SecondSongFolder </Name>
<Path> Full path to the folder to be used i.e. C:\Program Files (x86)\Steam\steamapps\common\Beat Saber\SecondSongFolder </Path>
<Pack> either 0, 1, or 2. 0 means the songs will be put in the main custom levels pack, 1 means it will be put in the wip levels pack, and 2 means it will be put into a new pack </Pack>
<ImagePath> If Pack is 2, will attempt to load this image to use as the pack cover. Use the Full Path to the Image </ImagePath>
<WIP> If Pack is 2, songs in this folder will be treated as WIP and only be playable in practice mode. Use True/False for the value </WIP>
<CacheZIPs> If set to true, SongCore will unzip the archives in this folder and show them as a seperate level pack in game </CacheZIPs>
<CacheZIPs> If set to true, SongCore will unzip the archives in this folder and show them as a separate level pack in game </CacheZIPs>
</folder>
Example Entry:
<folder>
<Name>SecondSongFolder</Name>
25 changes: 25 additions & 0 deletions source/SongCore/HarmonyPatches/AllowNegativeNjsValuesPatch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using SiraUtil.Affinity;

namespace SongCore.HarmonyPatches
{
internal class AllowNegativeNjsValuesPatch : IAffinity
{
private readonly BeatmapBasicData _beatmapBasicData;

private AllowNegativeNjsValuesPatch(BeatmapBasicData beatmapBasicData)
{
_beatmapBasicData = beatmapBasicData;
}

[AffinityPatch(typeof(BeatmapObjectSpawnMovementData), nameof(BeatmapObjectSpawnMovementData.Init))]
[AffinityPrefix]
private void ForceNegativeStartNoteJumpMovementSpeed(ref float startNoteJumpMovementSpeed)
{
var noteJumpMovementSpeed = _beatmapBasicData.noteJumpMovementSpeed;
if (noteJumpMovementSpeed < 0)
{
startNoteJumpMovementSpeed = noteJumpMovementSpeed;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using SiraUtil.Affinity;
using SongCore.Utilities;

namespace SongCore.HarmonyPatches
{
internal class BeatmapLevelDifficultyDataPatches : IAffinity
{
private readonly bool? _showRotationNoteSpawnLines;
private readonly bool? _oneSaber;

private BeatmapLevelDifficultyDataPatches(BeatmapLevel beatmapLevel, BeatmapKey beatmapKey)
{
var difficultyData = Collections.RetrieveDifficultyData(beatmapLevel, beatmapKey);
if (difficultyData != null)
{
_showRotationNoteSpawnLines = difficultyData._showRotationNoteSpawnLines;
_oneSaber = difficultyData._oneSaber;
}
}

[AffinityPatch(typeof(BeatLineManager), nameof(BeatLineManager.HandleNoteWasSpawned))]
[AffinityPrefix]
private bool ShowOrHideRotationNoteSpawnLines()
{
return _showRotationNoteSpawnLines ?? true;
}

[AffinityPatch(typeof(SaberManager.InitData), "ctor", AffinityMethodType.Constructor, null, typeof(bool), typeof(SaberType))]
private void ForceOneSaber(SaberManager.InitData __instance)
{
if (_oneSaber.HasValue)
{
Accessors.OneSaberModeAccessor(ref __instance) = _oneSaber.Value;
}
}
}
}
13 changes: 13 additions & 0 deletions source/SongCore/HarmonyPatches/BindBeatmapLevelPatch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using HarmonyLib;

namespace SongCore.HarmonyPatches
{
[HarmonyPatch(typeof(GameplayCoreInstaller), nameof(GameplayCoreInstaller.InstallBindings))]
internal class BindBeatmapLevelPatch
{
private static void Postfix(GameplayCoreInstaller __instance)
{
__instance.Container.Bind<BeatmapLevel>().FromInstance(__instance._sceneSetupData.beatmapLevel).AsSingle();
}
}
}
87 changes: 87 additions & 0 deletions source/SongCore/HarmonyPatches/CosmeticCharacteristicsPatch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BGLib.Polyglot;
using HMUI;
using SiraUtil.Affinity;
using SongCore.Utilities;
using UnityEngine;

namespace SongCore.HarmonyPatches
{
internal class CosmeticCharacteristicsPatch : IAffinity
{
private readonly StandardLevelDetailViewController _standardLevelDetailViewController;

private CosmeticCharacteristicsPatch(StandardLevelDetailViewController standardLevelDetailViewController)
{
_standardLevelDetailViewController = standardLevelDetailViewController;
}

[AffinityPatch(typeof(BeatmapCharacteristicSegmentedControlController), nameof(BeatmapCharacteristicSegmentedControlController.SetData))]
private void SetCosmeticCharacteristic(BeatmapCharacteristicSegmentedControlController __instance, BeatmapCharacteristicSO selectedBeatmapCharacteristic)
{
if (!Plugin.Configuration.DisplayCustomCharacteristics)
{
return;
}

var beatmapLevel = _standardLevelDetailViewController._beatmapLevel;
if (beatmapLevel.hasPrecalculatedData)
{
return;
}

var extraSongData = Collections.RetrieveExtraSongData(Hashing.GetCustomLevelHash(beatmapLevel)!);
if (extraSongData?._characteristicDetails == null || extraSongData._characteristicDetails.Length == 0)
{
return;
}

var segmentedControl = __instance._segmentedControl;
var dataItems = segmentedControl._dataItems;
var newDataItems = new List<IconSegmentedControl.DataItem>();
var i = 0;
var cellIndex = 0;
foreach (var dataItem in dataItems)
{
var beatmapCharacteristic = __instance._beatmapCharacteristics[i];
var serializedName = beatmapCharacteristic.serializedName;
var characteristicDetails = extraSongData._characteristicDetails.FirstOrDefault(c => c._beatmapCharacteristicName == serializedName);

if (characteristicDetails != null)
{
Sprite? icon = null;

var customLevelPath = Collections.GetCustomLevelPath(beatmapLevel.levelID);
if (characteristicDetails._characteristicIconFilePath != null && !string.IsNullOrEmpty(customLevelPath))
{
icon = Utils.LoadSpriteFromFile(Path.Combine(customLevelPath, characteristicDetails._characteristicIconFilePath));
}

if (icon == null)
{
icon = beatmapCharacteristic.icon;
}

var label = characteristicDetails._characteristicLabel ?? Localization.Get(beatmapCharacteristic.descriptionLocalizationKey);
newDataItems.Add(new IconSegmentedControl.DataItem(icon, label));
}
else
{
newDataItems.Add(dataItem);
}

if (beatmapCharacteristic == selectedBeatmapCharacteristic)
{
cellIndex = i;
}

i++;
}

segmentedControl.SetData(newDataItems.ToArray());
segmentedControl.SelectCellWithNumber(cellIndex);
}
}
}
Loading

0 comments on commit 7c52d22

Please sign in to comment.