├── Docs ├── Images │ ├── l1.png │ ├── l2.png │ ├── l3.png │ └── window.png └── Localization.md ├── UmamusumeExplorer ├── Resources │ ├── FileIcon.ico │ ├── NextIcon.png │ ├── PlayIcon.png │ ├── PrevIcon.png │ ├── StopIcon.png │ ├── FolderIcon.ico │ ├── PauseIcon.png │ ├── ProgramIcon.ico │ ├── NextBankIcon.png │ └── PrevBankIcon.png ├── Music │ ├── SongLength.cs │ ├── Live │ │ ├── CharacterPosition.cs │ │ ├── TrackMode.cs │ │ ├── LyricsTrigger.cs │ │ ├── PartTrigger.cs │ │ └── LiveConfigurationManager.cs │ ├── Race │ │ ├── Pattern.cs │ │ └── Bgm.cs │ └── SampleProviders │ │ ├── IEventSampleProvider.cs │ │ ├── IExtendedSampleProvider.cs │ │ ├── ExtendedSampleProvider.cs │ │ ├── PanSampleProvider.cs │ │ └── TestRunningBgmSampleProvider.cs ├── Controls │ ├── IAudioTrack.cs │ ├── AptitudeRank.cs │ ├── SkillBackground.cs │ ├── DoubleBufferedTableLayoutPanel.cs │ ├── TrackComboBoxItem.cs │ ├── BgmComboBoxItem.cs │ ├── SkillEvolutionConditionItem.cs │ ├── AudioSource.cs │ ├── CostumeComboBoxItem.cs │ ├── CharaComboBoxItem.cs │ ├── CircleControl.cs │ ├── RarityComboBoxItem.cs │ ├── CharacterInfoForm.cs │ ├── IncompleteExportForm.cs │ ├── ProgressLabel.cs │ ├── HighQualityPictureBox.cs │ ├── SongItemsPanel.cs │ ├── SkillEvolutionsForm.cs │ ├── HighlightPictureBox.cs │ ├── SupportCardsControl.cs │ ├── CharacterItemsPanel.cs │ ├── InstallationSelectForm.cs │ ├── SupportCardItemsPanel.cs │ ├── AtomAudioTrack.cs │ ├── AtomAudioSource.cs │ ├── ConfigureLoopForm.cs │ ├── SongsControl.cs │ ├── JukeboxItemsPanel.cs │ ├── SkillEvolutionConditionContainer.cs │ ├── BorderlessAeroForm.cs │ ├── ControlHelpers.cs │ ├── CharacterSelectForm.Designer.cs │ ├── SkillEvolutionDetailsForm.cs │ ├── SongsControl.Designer.cs │ ├── SupportCardsControl.Designer.cs │ ├── CharacterSelectForm.cs │ ├── SkillEvolutionConditionItem.Designer.cs │ ├── SkillColorGenerator.cs │ ├── RankedLabel.cs │ ├── StatusDisplayLabel.resx │ ├── SkillLarge.cs │ ├── SkillSmall.cs │ ├── CharacterPositionControl.cs │ ├── MultiStateButton.cs │ ├── RankedLabel.resx │ ├── UnitSetupForm.Designer.cs │ ├── CharacterPositionControl.Designer.cs │ ├── SkillEvolutionConditionContainer.Designer.cs │ ├── CharacterVoiceListItemControl.cs │ └── IncompleteExportForm.Designer.cs ├── MainForm.cs ├── Utility │ ├── LoadingProgress.cs │ ├── CsvReader.cs │ ├── LivePermissionDataHelper.cs │ └── TextHelpers.cs ├── Assets │ ├── ImagePointerContainer.cs │ ├── PinnedBitmap.cs │ ├── EncryptedAssetStream.cs │ ├── LoadingForm.cs │ ├── LoadingForm.Designer.cs │ └── AssetTables.cs ├── Program.cs ├── Pages │ ├── SupportCardInfoControl.cs │ ├── JukeboxControl.cs │ ├── LiveMusicPlayerControl.cs │ ├── CharacterInfoControl.cs │ ├── LiveMusicPlayerControl.Designer.cs │ ├── SupportCardInfoControl.Designer.cs │ └── CharacterInfoControl.Designer.cs ├── SplashForm.cs ├── UmamusumeExplorer.csproj ├── SplashForm.Designer.cs └── UmaApplicationContext.cs ├── Tools ├── Extractor │ ├── Properties │ │ └── launchSettings.json │ ├── Extractor.csproj │ ├── GameFile.cs │ └── Program.cs ├── SqlCreateStatementToClass │ ├── SqlCreateStatementToClass.csproj │ └── Program.cs ├── PlayerCli │ ├── GameFile.cs │ ├── PlayerCli.csproj │ └── UmaWaveStream.cs └── LiveMusicPlayerCli │ ├── LiveMusicPlayerCli.csproj │ ├── PanSampleProvider.cs │ ├── AssetTables.cs │ └── PartTrigger.cs ├── UmamusumeData ├── Tables │ ├── LivePermissionData.cs │ ├── TextData.cs │ ├── AvailableSkillSet.cs │ ├── SkillUpgradeDescription.cs │ ├── SingleModeSkillNeedPoint.cs │ ├── SkillUpgradeCondition.cs │ ├── GachaAvailable.cs │ ├── CardData.cs │ ├── CharacterSystemText.cs │ ├── SupportCardData.cs │ ├── JukeboxMusicData.cs │ ├── LiveData.cs │ ├── SkillSet.cs │ ├── CardRarityData.cs │ └── RaceBgm.cs ├── UmamusumeData.csproj ├── ManifestEntryKind.cs ├── DataDirectories │ ├── DataDirectory.cs │ ├── SteamDataDirectory.cs │ ├── DefaultDataDirectory.cs │ └── DmmDataDirectory.cs ├── AssetBundleGroup.cs └── ManifestEntry.cs ├── .gitmodules ├── UmamusumeAudio ├── UmamusumeAudio.csproj └── UmaWaveStream.cs ├── README.md ├── .github └── workflows │ └── build.yml └── .gitattributes /Docs/Images/l1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarshmallowAndroid/UmamusumeExplorer/HEAD/Docs/Images/l1.png -------------------------------------------------------------------------------- /Docs/Images/l2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarshmallowAndroid/UmamusumeExplorer/HEAD/Docs/Images/l2.png -------------------------------------------------------------------------------- /Docs/Images/l3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarshmallowAndroid/UmamusumeExplorer/HEAD/Docs/Images/l3.png -------------------------------------------------------------------------------- /Docs/Images/window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarshmallowAndroid/UmamusumeExplorer/HEAD/Docs/Images/window.png -------------------------------------------------------------------------------- /UmamusumeExplorer/Resources/FileIcon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarshmallowAndroid/UmamusumeExplorer/HEAD/UmamusumeExplorer/Resources/FileIcon.ico -------------------------------------------------------------------------------- /UmamusumeExplorer/Resources/NextIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarshmallowAndroid/UmamusumeExplorer/HEAD/UmamusumeExplorer/Resources/NextIcon.png -------------------------------------------------------------------------------- /UmamusumeExplorer/Resources/PlayIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarshmallowAndroid/UmamusumeExplorer/HEAD/UmamusumeExplorer/Resources/PlayIcon.png -------------------------------------------------------------------------------- /UmamusumeExplorer/Resources/PrevIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarshmallowAndroid/UmamusumeExplorer/HEAD/UmamusumeExplorer/Resources/PrevIcon.png -------------------------------------------------------------------------------- /UmamusumeExplorer/Resources/StopIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarshmallowAndroid/UmamusumeExplorer/HEAD/UmamusumeExplorer/Resources/StopIcon.png -------------------------------------------------------------------------------- /UmamusumeExplorer/Resources/FolderIcon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarshmallowAndroid/UmamusumeExplorer/HEAD/UmamusumeExplorer/Resources/FolderIcon.ico -------------------------------------------------------------------------------- /UmamusumeExplorer/Resources/PauseIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarshmallowAndroid/UmamusumeExplorer/HEAD/UmamusumeExplorer/Resources/PauseIcon.png -------------------------------------------------------------------------------- /UmamusumeExplorer/Resources/ProgramIcon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarshmallowAndroid/UmamusumeExplorer/HEAD/UmamusumeExplorer/Resources/ProgramIcon.ico -------------------------------------------------------------------------------- /UmamusumeExplorer/Resources/NextBankIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarshmallowAndroid/UmamusumeExplorer/HEAD/UmamusumeExplorer/Resources/NextBankIcon.png -------------------------------------------------------------------------------- /UmamusumeExplorer/Resources/PrevBankIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarshmallowAndroid/UmamusumeExplorer/HEAD/UmamusumeExplorer/Resources/PrevBankIcon.png -------------------------------------------------------------------------------- /UmamusumeExplorer/Music/SongLength.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Music 2 | { 3 | public enum SongLength 4 | { 5 | ShortVersion, 6 | GameSizeVersion 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Tools/Extractor/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Extractor": { 4 | "commandName": "Project", 5 | "commandLineArgs": "D:\\umamusume_" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/IAudioTrack.cs: -------------------------------------------------------------------------------- 1 | using NAudio.Wave; 2 | 3 | namespace UmamusumeExplorer.Controls 4 | { 5 | public interface IAudioTrack 6 | { 7 | public string Name { get; } 8 | 9 | public WaveStream WaveStream { get; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Music/Live/CharacterPosition.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Music.Live 2 | { 3 | internal struct CharacterPosition(int characterId, int position) 4 | { 5 | public int CharacterId = characterId; 6 | public int Position = position; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/AptitudeRank.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Controls 2 | { 3 | public enum AptitudeRank 4 | { 5 | Unknown, 6 | G, 7 | F, 8 | E, 9 | D, 10 | C, 11 | B, 12 | A, 13 | S 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/SkillBackground.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Controls 2 | { 3 | public enum SkillBackground 4 | { 5 | Rarity1 = 1, 6 | Rarity2 = 2, 7 | Rarity3 = 3, 8 | Rarity4 = 4, 9 | Rarity5 = 5, 10 | Evolution = 6 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Music/Race/Pattern.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Music.Race 2 | { 3 | internal class Pattern 4 | { 5 | public int BgmTime { get; set; } 6 | 7 | public string BgmCueName { get; set; } = ""; 8 | 9 | public string BgmCuesheetName { get; set; } = ""; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/DoubleBufferedTableLayoutPanel.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Controls 2 | { 3 | internal class DoubleBufferedTableLayoutPanel : TableLayoutPanel 4 | { 5 | public DoubleBufferedTableLayoutPanel() 6 | { 7 | DoubleBuffered = true; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/TrackComboBoxItem.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Controls 2 | { 3 | record TrackComboBoxItem(string TrackName, int Index, IAudioTrack Track) 4 | { 5 | public override string ToString() 6 | { 7 | return $"{Index}: {TrackName}"; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Tools/SqlCreateStatementToClass/SqlCreateStatementToClass.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Music/Live/TrackMode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace UmamusumeExplorer.Music.Live 8 | { 9 | internal enum TrackMode 10 | { 11 | Main, 12 | Extra, 13 | Merge 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/BgmComboBoxItem.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeData.Tables; 2 | 3 | namespace UmamusumeExplorer.Controls 4 | { 5 | internal class BgmComboBoxItem 6 | { 7 | public RaceBgm? RaceBgm { get; set; } 8 | 9 | public override string ToString() 10 | { 11 | return RaceBgm?.Id.ToString() ?? ""; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tools/Extractor/Extractor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | Extractor.Program 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /UmamusumeData/Tables/LivePermissionData.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | 3 | namespace UmamusumeData.Tables 4 | { 5 | [Table("live_permission_data")] 6 | public class LivePermissionData 7 | { 8 | [Column("music_id"), NotNull, PrimaryKey] 9 | public int MusicId { get; set; } 10 | 11 | [Column("chara_id"), NotNull, PrimaryKey] 12 | public int CharaId { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /UmamusumeExplorer/MainForm.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeExplorer.Controls; 2 | 3 | namespace UmamusumeExplorer 4 | { 5 | partial class MainForm : Form 6 | { 7 | public MainForm() 8 | { 9 | InitializeComponent(); 10 | } 11 | 12 | private void MainForm_FormClosing(object sender, FormClosingEventArgs e) 13 | { 14 | ControlHelpers.CloseAllForms(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Utility/LoadingProgress.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Utility 2 | { 3 | class LoadingProgress : IProgress 4 | { 5 | private readonly Action reportProgress; 6 | 7 | public LoadingProgress(Action action) 8 | { 9 | reportProgress = action; 10 | } 11 | 12 | public void Report(int value) 13 | { 14 | reportProgress.Invoke(value); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /UmamusumeData/UmamusumeData.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ClHcaSharp"] 2 | path = ClHcaSharp 3 | url = https://github.com/MarshmallowAndroid/ClHcaSharp 4 | [submodule "CriWareTools"] 5 | path = CriWareTools 6 | url = https://github.com/MarshmallowAndroid/CriWareTools 7 | [submodule "CriWareLibrary"] 8 | path = CriWareLibrary 9 | url = https://github.com/MarshmallowAndroid/CriWareLibrary 10 | [submodule "AssetsTools.NET"] 11 | path = AssetsTools.NET 12 | url = https://github.com/nesrak1/AssetsTools.NET.git 13 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Music/SampleProviders/IEventSampleProvider.cs: -------------------------------------------------------------------------------- 1 | using NAudio.Wave; 2 | 3 | namespace UmamusumeExplorer.Music.SampleProviders 4 | { 5 | // Sample provider interface that uses events to signal the end of sample modification (fades, delays, etc.) 6 | interface IEventSampleProvider : ISampleProvider 7 | { 8 | public delegate void SampleProvideEventHandler(ISampleProvider source); 9 | public event SampleProvideEventHandler OnSampleProviderEvent; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /UmamusumeData/ManifestEntryKind.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace UmamusumeData 8 | { 9 | public enum ManifestEntryKind : byte 10 | { 11 | Default = 0x0, 12 | AssetManifest = 0x1, 13 | PlatformManifest = 0x2, 14 | RootManifest = 0x3, 15 | Master = 0xA, 16 | Sound = 0xB, 17 | Movie = 0xC, 18 | Font = 0xD 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/SkillEvolutionConditionItem.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeData; 2 | using UmamusumeData.Tables; 3 | using UmamusumeExplorer.Assets; 4 | 5 | namespace UmamusumeExplorer.Controls 6 | { 7 | public partial class SkillEvolutionConditionItem : UserControl 8 | { 9 | public SkillEvolutionConditionItem(SkillUpgradeCondition condition) 10 | { 11 | InitializeComponent(); 12 | 13 | conditionLabel.Text = AssetTables.GetText(TextCategory.SkillUpgrade1, condition.Id); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /UmamusumeData/Tables/TextData.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | 3 | namespace UmamusumeData.Tables 4 | { 5 | [Table("text_data")] 6 | public class TextData 7 | { 8 | [Column("id"), NotNull] 9 | public int Id { get; set; } 10 | 11 | [Column("category"), NotNull, PrimaryKey] 12 | public int Category { get; set; } 13 | 14 | [Column("index"), NotNull, PrimaryKey] 15 | public int Index { get; set; } 16 | 17 | [Column("text"), NotNull] 18 | public string Text { get; set; } = ""; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Assets/ImagePointerContainer.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace UmamusumeExplorer.Assets 4 | { 5 | class ImagePointerContainer 6 | { 7 | public ImagePointerContainer(GCHandle imageHandle, int width, int height) 8 | { 9 | ImageHandle = imageHandle; 10 | Width = width; 11 | Height = height; 12 | } 13 | 14 | public GCHandle ImageHandle { get; } 15 | 16 | public int Width { get; } 17 | 18 | public int Height { get; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Music/SampleProviders/IExtendedSampleProvider.cs: -------------------------------------------------------------------------------- 1 | using NAudio.Wave; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace UmamusumeExplorer.Music.SampleProviders 9 | { 10 | internal interface IExtendedSampleProvider : ISampleProvider 11 | { 12 | public long Position { get; set; } 13 | 14 | public long Length { get; } 15 | 16 | public TimeSpan CurrentTime { get; } 17 | 18 | public TimeSpan TotalTime { get; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /UmamusumeData/Tables/AvailableSkillSet.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | 3 | namespace UmamusumeData.Tables 4 | { 5 | [Table("available_skill_set")] 6 | public class AvailableSkillSet 7 | { 8 | [Column("id"), NotNull, PrimaryKey] 9 | public int Id { get; set; } 10 | 11 | [Column("available_skill_set_id"), NotNull] 12 | public int AvailableSkillSetId { get; set; } 13 | 14 | [Column("skill_id"), NotNull] 15 | public int SkillId { get; set; } 16 | 17 | [Column("need_rank"), NotNull] 18 | public int NeedRank { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/AudioSource.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Controls 2 | { 3 | public abstract class AudioSource 4 | { 5 | private IAudioTrack[]? tracks; 6 | 7 | public abstract string Name { get; } 8 | 9 | public abstract int TrackCount { get; } 10 | 11 | public IAudioTrack[] Tracks 12 | { 13 | get 14 | { 15 | tracks ??= InitializeTracks(); 16 | return tracks; 17 | } 18 | } 19 | 20 | protected abstract IAudioTrack[] InitializeTracks(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/CostumeComboBoxItem.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeData; 2 | using UmamusumeData.Tables; 3 | using UmamusumeExplorer.Assets; 4 | 5 | namespace UmamusumeExplorer.Controls 6 | { 7 | class CostumeComboBoxItem 8 | { 9 | public CostumeComboBoxItem(CardData cardData) 10 | { 11 | CardData = cardData; 12 | } 13 | 14 | public CardData CardData { get; } 15 | 16 | public override string ToString() 17 | { 18 | return AssetTables.GetText(TextCategory.MasterCardTitleName, CardData.Id); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UmamusumeAudio/UmamusumeAudio.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/CharaComboBoxItem.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeData; 2 | using UmamusumeData.Tables; 3 | using UmamusumeExplorer.Assets; 4 | 5 | namespace UmamusumeExplorer.Controls 6 | { 7 | class CharaComboBoxItem 8 | { 9 | public CharaComboBoxItem(CharaData chara) 10 | { 11 | CharaData = chara; 12 | } 13 | 14 | public CharaData CharaData { get; } 15 | 16 | public override string ToString() 17 | { 18 | return CharaData.Id.ToString() + ": " + AssetTables.GetText(TextCategory.MasterCharaName, CharaData.Id); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UmamusumeData/Tables/SkillUpgradeDescription.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | 3 | namespace UmamusumeData.Tables 4 | { 5 | [Table("skill_upgrade_description")] 6 | public class SkillUpgradeDescription 7 | { 8 | [Column("id"), NotNull, PrimaryKey] 9 | public int Id { get; set; } 10 | 11 | [Column("card_id"), NotNull, Unique] 12 | public int CardId { get; set; } 13 | 14 | [Column("rank"), NotNull] 15 | public int Rank { get; set; } 16 | 17 | [Column("skill_id"), NotNull, Unique] 18 | public int SkillId { get; set; } 19 | 20 | [Column("start_date"), NotNull] 21 | public int StartDate { get; set; } 22 | } 23 | } -------------------------------------------------------------------------------- /UmamusumeData/Tables/SingleModeSkillNeedPoint.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | 3 | namespace UmamusumeData.Tables 4 | { 5 | [Table("single_mode_skill_need_point")] 6 | public class SingleModeSkillNeedPoint 7 | { 8 | [Column("id"), NotNull, PrimaryKey] 9 | public int Id { get; set; } 10 | 11 | [Column("need_skill_point"), NotNull] 12 | public int NeedSkillPoint { get; set; } 13 | 14 | [Column("status_type"), NotNull] 15 | public int StatusType { get; set; } 16 | 17 | [Column("status_value"), NotNull] 18 | public int StatusValue { get; set; } 19 | 20 | [Column("solvable_type"), NotNull] 21 | public int SolvableType { get; set; } 22 | } 23 | } -------------------------------------------------------------------------------- /Tools/Extractor/GameFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using SQLite; 7 | 8 | namespace Extractor 9 | { 10 | [Table("a")] 11 | public class GameFile 12 | { 13 | [PrimaryKey] 14 | [Column("i")] 15 | public int Id { get; set; } 16 | 17 | [Column("n"), NotNull] 18 | public string Name { get; set; } 19 | 20 | [Column("l"), NotNull] 21 | public string Length { get; set; } 22 | 23 | [Column("h"), NotNull] 24 | public string Hash { get; set; } 25 | 26 | [Column("m")] 27 | public string Category { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/CircleControl.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing.Drawing2D; 2 | using Rectangle = System.Drawing.Rectangle; 3 | 4 | namespace UmamusumeExplorer.Controls 5 | { 6 | internal class CircleControl : Label 7 | { 8 | public CircleControl() : base() 9 | { 10 | } 11 | 12 | protected override void OnPaint(PaintEventArgs e) 13 | { 14 | e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; 15 | 16 | Rectangle clip = e.ClipRectangle; 17 | clip.Width -= 1; 18 | clip.Height -= 1; 19 | 20 | e.Graphics.FillEllipse(new SolidBrush(ForeColor), clip); 21 | 22 | base.OnPaint(e); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Tools/PlayerCli/GameFile.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | 3 | namespace PlayerCli 4 | { 5 | [Table("a")] 6 | public class GameFile 7 | { 8 | [PrimaryKey] 9 | [Column("i")] 10 | public int Id { get; set; } 11 | 12 | [Column("n"), NotNull] 13 | public string Name { get; set; } 14 | 15 | [Column("l"), NotNull] 16 | public string Length { get; set; } 17 | 18 | [Column("h"), NotNull] 19 | public string Hash { get; set; } 20 | 21 | public string BaseName 22 | { 23 | get 24 | { 25 | int lastSlash = Name.LastIndexOf('/'); 26 | return Name[(lastSlash + 1)..]; 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/RarityComboBoxItem.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using UmamusumeData.Tables; 3 | 4 | namespace UmamusumeExplorer.Controls 5 | { 6 | class RarityComboBoxItem 7 | { 8 | public RarityComboBoxItem(CardRarityData cardRarityData) 9 | { 10 | CardRarityData = cardRarityData; 11 | } 12 | 13 | public CardRarityData CardRarityData { get; } 14 | 15 | public override string ToString() 16 | { 17 | StringBuilder starsString = new(); 18 | for (int i = 0; i < CardRarityData.Rarity; i++) 19 | { 20 | starsString.Append('★'); 21 | } 22 | 23 | return starsString.ToString(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Docs/Localization.md: -------------------------------------------------------------------------------- 1 | # Localization 2 | This project is built on Windows Forms, which has support for localization built into the 3 | Visual Studio designer via different resource (`.resx`) files for each language. 4 | 5 | To generate localized resources for a form or user control: 6 | 7 | 1. In your forked repository, open the form in the designer and check if `Localizable` is set to `True` 8 | 9 | ![](Images/l1.png) 10 | 2. Set the `Language` property to the language you want to localize the form in 11 | 12 | ![](Images/l2.png) 13 | 3. Change the text properties of the controls to your localized text 14 | 15 | ![](Images/l3.png) 16 | 17 | Commit the changes of the resource files (or the designer code in some cases) and make a new pull request. 18 | -------------------------------------------------------------------------------- /Tools/PlayerCli/PlayerCli.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | PlayerCli.Program 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /UmamusumeData/DataDirectories/DataDirectory.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeData.DataDirectories 2 | { 3 | public abstract class DataDirectory 4 | { 5 | public abstract string Name { get; } 6 | 7 | public string DataDirectoryPath { get; protected set; } = ""; 8 | 9 | public static bool CheckDirectory(string basePath) 10 | { 11 | if (string.IsNullOrWhiteSpace(basePath)) return false; 12 | if (!Directory.Exists(Path.Combine(basePath, "dat"))) return false; 13 | if (!File.Exists(Path.Combine(basePath, "meta"))) return false; 14 | if (!File.Exists(Path.Combine(basePath, "master", "master.mdb"))) return false; 15 | 16 | return true; 17 | } 18 | 19 | public abstract bool Exists(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Utility/CsvReader.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace UmamusumeExplorer.Utility 4 | { 5 | internal class CsvReader : StreamReader 6 | { 7 | public CsvReader(Stream stream) : base(stream) { } 8 | 9 | public string ReadCsvLine() 10 | { 11 | StringBuilder stringBuilder = new(); 12 | bool withinQuotes = false; 13 | 14 | int read; 15 | while ((read = Read()) != -1) 16 | { 17 | char c = (char)read; 18 | 19 | if (c == '"') 20 | withinQuotes = !withinQuotes; 21 | 22 | if (c == '\n' && !withinQuotes) break; 23 | 24 | stringBuilder.Append(c); 25 | } 26 | 27 | return stringBuilder.ToString(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/CharacterInfoForm.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeData; 2 | using UmamusumeData.Tables; 3 | using UmamusumeExplorer.Assets; 4 | 5 | namespace UmamusumeExplorer.Controls 6 | { 7 | partial class CharacterInfoForm : Form 8 | { 9 | public CharacterInfoForm(CharaData chara) 10 | { 11 | InitializeComponent(); 12 | 13 | cardInfoControl.CharaData = chara; 14 | voiceLinesControl.CharaId = chara.Id; 15 | songsControl.CharaId = chara.Id; 16 | supportCardsControl.CharaId = chara.Id; 17 | 18 | Text = AssetTables.GetText(TextCategory.MasterCharaName, chara.Id); 19 | } 20 | 21 | private void CharacterInfoForm_FormClosing(object sender, FormClosingEventArgs e) 22 | { 23 | voiceLinesControl.StopAllPlayback(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tools/LiveMusicPlayerCli/LiveMusicPlayerCli.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /UmamusumeData/AssetBundleGroup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace UmamusumeData 8 | { 9 | [Flags] 10 | public enum AssetBundleGroup : int 11 | { 12 | Default = 0x00, 13 | DeleteOnLogin = 0x01, 14 | DownloadLogin = 0x02, 15 | Tutorial = 0x04, 16 | HomeLogin = 0x08, 17 | RequiredTutorialStart = 0x10, 18 | DelayRelease = 0x20, 19 | RealFanfare = 0x40, 20 | WithRealFanfare = 0x80, 21 | DataManagementTarget = 0x100, 22 | DeleteOnLoginWithConditionStory = 0x10000, 23 | DeleteOnLoginWithConditionEvent = 0x20000, 24 | DeleteOnLoginWithConditionLive = 0x40000, 25 | DeleteOnLoginWithConditionRace = 0x80000, 26 | DeleteOnLoginWithConditionOthers = 0x100000 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /UmamusumeData/Tables/SkillUpgradeCondition.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | 3 | namespace UmamusumeData.Tables 4 | { 5 | [Table("skill_upgrade_condition")] 6 | public class SkillUpgradeCondition 7 | { 8 | [Column("id"), NotNull, PrimaryKey] 9 | public int Id { get; set; } 10 | 11 | [Column("upgrade_type"), NotNull] 12 | public int UpgradeType { get; set; } 13 | 14 | [Column("description_id"), NotNull, Unique] 15 | public int DescriptionId { get; set; } 16 | 17 | [Column("num"), NotNull, Unique] 18 | public int Num { get; set; } 19 | 20 | [Column("sub_num"), NotNull, Unique] 21 | public int SubNum { get; set; } 22 | 23 | [Column("timing_type"), NotNull] 24 | public int TimingType { get; set; } 25 | 26 | [Column("count_type"), NotNull] 27 | public int CountType { get; set; } 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/IncompleteExportForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | 11 | namespace UmamusumeExplorer.Controls 12 | { 13 | public partial class IncompleteExportForm : Form 14 | { 15 | public IncompleteExportForm(IDictionary failedFiles) 16 | { 17 | InitializeComponent(); 18 | 19 | foreach (var file in failedFiles) 20 | { 21 | failedFilesList.Items.Add(new ListViewItem([file.Key.Name, file.Value.Message])); 22 | } 23 | } 24 | 25 | private void OkButton_Click(object sender, EventArgs e) 26 | { 27 | Close(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/ProgressLabel.cs: -------------------------------------------------------------------------------- 1 | using Color = System.Drawing.Color; 2 | using Rectangle = System.Drawing.Rectangle; 3 | 4 | namespace UmamusumeExplorer.Controls 5 | { 6 | internal class ProgressLabel : Label 7 | { 8 | private float progress; 9 | 10 | public float Progress 11 | { 12 | get => progress; 13 | set 14 | { 15 | progress = value; 16 | Refresh(); 17 | } 18 | } 19 | 20 | protected override void OnPaint(PaintEventArgs e) 21 | { 22 | //e.Graphics.Clear(BackColor); 23 | Rectangle drawRectangle = e.ClipRectangle; 24 | drawRectangle.Width = (int)(drawRectangle.Width * progress); 25 | e.Graphics.FillRectangle(new SolidBrush(Color.LightGreen), drawRectangle); 26 | 27 | base.OnPaint(e); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Program.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer 2 | { 3 | static class Program 4 | { 5 | /// 6 | /// The main entry point for the application. 7 | /// 8 | [STAThread] 9 | static void Main() 10 | { 11 | Application.SetHighDpiMode(HighDpiMode.SystemAware); 12 | Application.EnableVisualStyles(); 13 | Application.SetCompatibleTextRenderingDefault(false); 14 | AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; 15 | Application.Run(new UmaApplicationContext()); 16 | } 17 | 18 | private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) 19 | { 20 | MessageBox.Show(e.ExceptionObject.ToString(), "Unhandled Exception", MessageBoxButtons.OK, MessageBoxIcon.Stop); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/HighQualityPictureBox.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing.Drawing2D; 2 | 3 | namespace UmamusumeExplorer.Controls 4 | { 5 | internal class HighQualityPictureBox : PictureBox 6 | { 7 | protected override void OnPaint(PaintEventArgs pe) 8 | { 9 | pe.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; 10 | pe.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; 11 | pe.Graphics.SmoothingMode = SmoothingMode.AntiAlias; 12 | base.OnPaint(pe); 13 | } 14 | 15 | protected override void OnPaintBackground(PaintEventArgs pe) 16 | { 17 | pe.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; 18 | pe.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; 19 | pe.Graphics.SmoothingMode = SmoothingMode.AntiAlias; 20 | base.OnPaintBackground(pe); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /UmamusumeData/Tables/GachaAvailable.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | 3 | namespace UmamusumeData.Tables 4 | { 5 | [Table("gacha_available")] 6 | public class GachaAvailable 7 | { 8 | [Column("gacha_id"), NotNull, PrimaryKey] 9 | public int GachaId { get; set; } 10 | 11 | [Column("card_id"), NotNull, PrimaryKey] 12 | public int CardId { get; set; } 13 | 14 | [Column("card_type"), NotNull] 15 | public int CardType { get; set; } 16 | 17 | [Column("rarity"), NotNull] 18 | public int Rarity { get; set; } 19 | 20 | [Column("odds"), NotNull] 21 | public int Odds { get; set; } 22 | 23 | [Column("is_pickup"), NotNull] 24 | public int IsPickup { get; set; } 25 | 26 | [Column("recommend_order"), NotNull] 27 | public int RecommendOrder { get; set; } 28 | 29 | [Column("is_prize_selectable"), NotNull] 30 | public int IsPrizeSelectable { get; set; } 31 | } 32 | } -------------------------------------------------------------------------------- /UmamusumeData/DataDirectories/SteamDataDirectory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.Json; 6 | using System.Text.Json.Nodes; 7 | using System.Text.Json.Serialization; 8 | using System.Threading.Tasks; 9 | 10 | namespace UmamusumeData.DataDirectories 11 | { 12 | internal class SteamJapanDataDirectory : DataDirectory 13 | { 14 | private static readonly string programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); 15 | private static readonly string dataDirectory = Path.Combine( 16 | programFilesX86, "Steam", "steamapps", "common", 17 | "UmamusumePrettyDerby_Jpn", "UmamusumePrettyDerby_Jpn_Data", "Persistent"); 18 | 19 | public override string Name => "Steam (Japan)"; 20 | 21 | public override bool Exists() 22 | { 23 | return CheckDirectory(dataDirectory); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/SongItemsPanel.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeData; 2 | using UmamusumeData.Tables; 3 | using UmamusumeExplorer.Assets; 4 | 5 | namespace UmamusumeExplorer.Controls 6 | { 7 | internal class SongItemsPanel : ItemsPanel 8 | { 9 | public override bool ProcessItem(LiveData liveData, ref Control? jacket) 10 | { 11 | if (liveData.HasLive == 0) return false; 12 | 13 | jacket = new PictureBox() 14 | { 15 | BackgroundImage = GameAssets.GetJacket(liveData.MusicId, 'l')?.Bitmap, 16 | BackgroundImageLayout = ImageLayout.Zoom, 17 | Cursor = Cursors.Hand, 18 | Height = 128, 19 | Width = 128, 20 | Tag = liveData 21 | }; 22 | 23 | ToolTip toolTip = new(); 24 | toolTip.SetToolTip(jacket, $"{liveData.MusicId}: {AssetTables.GetText(TextCategory.MasterLiveTitle, liveData.MusicId)}"); 25 | 26 | return true; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/SkillEvolutionsForm.cs: -------------------------------------------------------------------------------- 1 | using System.Data; 2 | using UmamusumeData.Tables; 3 | 4 | namespace UmamusumeExplorer.Controls 5 | { 6 | public partial class SkillEvolutionsForm : Form 7 | { 8 | public SkillEvolutionsForm(SkillData skill, IEnumerable evolutionSkills) 9 | { 10 | InitializeComponent(); 11 | 12 | toEvolveSkill.Skill = skill; 13 | 14 | evolvedSkill1.Skill = skill; 15 | evolvedSkill1.EvolutionSkill = evolutionSkills.First(); 16 | 17 | SkillData? evolutionSkill2 = evolutionSkills.ElementAtOrDefault(1); 18 | if (evolutionSkill2 is not null) 19 | { 20 | evolvedSkill2.Skill = skill; 21 | evolvedSkill2.EvolutionSkill = evolutionSkill2; 22 | } 23 | else 24 | evolvedSkill2.Visible = false; 25 | } 26 | 27 | private void CloseButton_Click(object sender, EventArgs e) 28 | { 29 | Close(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/HighlightPictureBox.cs: -------------------------------------------------------------------------------- 1 | using Color = System.Drawing.Color; 2 | 3 | namespace UmamusumeExplorer.Controls 4 | { 5 | internal class HighlightPictureBox : HighQualityPictureBox 6 | { 7 | private bool highlight = false; 8 | private readonly bool marked; 9 | private Image? image; 10 | 11 | public HighlightPictureBox(bool isMarked) 12 | { 13 | marked = isMarked; 14 | image = Image; 15 | } 16 | 17 | public bool ShowHighlight 18 | { 19 | get => highlight; 20 | set 21 | { 22 | if (marked && value) 23 | { 24 | BackColor = Color.LightGreen; 25 | Image = null; 26 | } 27 | else 28 | { 29 | BackColor = Color.FromKnownColor(KnownColor.Transparent); 30 | Image = image; 31 | } 32 | 33 | highlight = value; 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/SupportCardsControl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | using UmamusumeData.Tables; 11 | using UmamusumeExplorer.Assets; 12 | 13 | namespace UmamusumeExplorer.Controls 14 | { 15 | public partial class SupportCardsControl : UserControl 16 | { 17 | private readonly IEnumerable supportCardDatas = AssetTables.SupportCardDatas; 18 | 19 | public SupportCardsControl() 20 | { 21 | InitializeComponent(); 22 | 23 | supportCardItemsPanel.Indeterminate = true; 24 | supportCardItemsPanel.Filter = (item) => item.CharaId == CharaId; 25 | } 26 | 27 | public int CharaId { get; set; } 28 | 29 | private void SupportCardsControl_Load(object sender, EventArgs e) 30 | { 31 | supportCardItemsPanel.Items = supportCardDatas; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Utility/LivePermissionDataHelper.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeData; 2 | using UmamusumeData.Tables; 3 | using UmamusumeExplorer.Assets; 4 | 5 | namespace UmamusumeExplorer.Utility 6 | { 7 | static class LivePermissionDataHelper 8 | { 9 | public static IEnumerable GetLivePermissionData(int musicId) 10 | { 11 | List livePermissionData = AssetTables.LivePermissionDatas.Where(lpd => lpd.MusicId == musicId).ToList(); 12 | 13 | var matches = UmaDataHelper.GetManifestEntries(ga => ga.BaseName.StartsWith($"snd_bgm_live_{musicId}_chara_") && ga.BaseName.EndsWith(".awb")); 14 | foreach (var audioAsset in matches) 15 | { 16 | int charaId = int.Parse(audioAsset.BaseName.Remove(0, $"snd_bgm_live_{musicId}_chara_".Length)[..4]); 17 | 18 | if (livePermissionData is not List list) continue; 19 | list.Add(new LivePermissionData() { MusicId = musicId, CharaId = charaId }); 20 | } 21 | 22 | return livePermissionData; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/CharacterItemsPanel.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeData; 2 | using UmamusumeData.Tables; 3 | using UmamusumeExplorer.Assets; 4 | using UmamusumeExplorer.Controls; 5 | 6 | namespace UmamusumeExplorer.Controls 7 | { 8 | internal class CharacterItemsPanel : ItemsPanel 9 | { 10 | public override bool ProcessItem(CharaData charaData, ref Control? characterPictureBox) 11 | { 12 | bool playable = AssetTables.CardDatas.Any(cd => cd.CharaId == charaData.Id); 13 | characterPictureBox = new HighlightPictureBox(playable) 14 | { 15 | BackgroundImage = GameAssets.GetCharaIcon(charaData.Id)?.Bitmap, 16 | BackgroundImageLayout = ImageLayout.Stretch, 17 | Cursor = Cursors.Hand, 18 | Height = 108, 19 | Width = 98, 20 | Tag = charaData 21 | }; 22 | 23 | ToolTip toolTip = new(); 24 | toolTip.SetToolTip(characterPictureBox, $"{charaData.Id}: {AssetTables.GetText(TextCategory.MasterCharaName, charaData.Id)}"); 25 | 26 | return true; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Music/SampleProviders/ExtendedSampleProvider.cs: -------------------------------------------------------------------------------- 1 | using NAudio.Wave; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace UmamusumeExplorer.Music.SampleProviders 9 | { 10 | internal class ExtendedSampleProvider : IExtendedSampleProvider 11 | { 12 | private readonly WaveStream source; 13 | private readonly ISampleProvider sampleProvider; 14 | 15 | public ExtendedSampleProvider(WaveStream waveStream) 16 | { 17 | source = waveStream; 18 | sampleProvider = waveStream.ToSampleProvider(); 19 | } 20 | 21 | public WaveFormat WaveFormat => sampleProvider.WaveFormat; 22 | 23 | public long Position { get => source.Position; set => source.Position = value; } 24 | 25 | public long Length => source.Length; 26 | 27 | public TimeSpan CurrentTime => source.CurrentTime; 28 | 29 | public TimeSpan TotalTime => source.TotalTime; 30 | 31 | public int Read(float[] buffer, int offset, int count) => sampleProvider.Read(buffer, offset, count); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Pages/SupportCardInfoControl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | using UmamusumeData.Tables; 11 | using UmamusumeExplorer.Assets; 12 | using UmamusumeExplorer.Controls; 13 | 14 | namespace UmamusumeExplorer.Pages 15 | { 16 | public partial class SupportCardInfoControl : UserControl 17 | { 18 | private readonly IEnumerable supportCardDatas = AssetTables.SupportCardDatas; 19 | 20 | public SupportCardInfoControl() 21 | { 22 | InitializeComponent(); 23 | } 24 | 25 | private void SupportCardInfoControl_Load(object sender, EventArgs e) 26 | { 27 | supportCardItemsPanel.Items = supportCardDatas; 28 | } 29 | 30 | private void MarkLimitedCheckBox_CheckedChanged(object sender, EventArgs e) 31 | { 32 | foreach (HighlightPictureBox hpb in supportCardItemsPanel.Controls) 33 | { 34 | hpb.ShowHighlight = markLimitedCheckBox.Checked; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tools/LiveMusicPlayerCli/PanSampleProvider.cs: -------------------------------------------------------------------------------- 1 | using NAudio.Wave; 2 | using System; 3 | 4 | namespace LiveMusicPlayerCli 5 | { 6 | class PanSampleProvider : ISampleProvider 7 | { 8 | private readonly ISampleProvider sampleProvider; 9 | 10 | public PanSampleProvider(ISampleProvider source) 11 | { 12 | sampleProvider = source; 13 | 14 | if (source.WaveFormat.Channels != 2) 15 | throw new Exception("Must be stereo."); 16 | } 17 | 18 | public WaveFormat WaveFormat => sampleProvider.WaveFormat; 19 | 20 | public float Pan { get; set; } 21 | 22 | public int Read(float[] buffer, int offset, int count) 23 | { 24 | int read = sampleProvider.Read(buffer, offset, count); 25 | 26 | float left; 27 | float right; 28 | 29 | int bufferIndex = 0; 30 | for (int i = offset; i < count / 2; i++) 31 | { 32 | left = Pan <= 0 ? 1.0F : (1.0F - Pan); 33 | right = Pan >= 0 ? 1.0F : (Pan + 1.0F); 34 | 35 | buffer[bufferIndex++] *= left; 36 | buffer[bufferIndex++] *= right; 37 | } 38 | 39 | return read; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Umamusume Explorer 2 | [![CI](https://github.com/MarshmallowAndroid/UmamusumeExplorer/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/MarshmallowAndroid/UmamusumeExplorer/actions/workflows/build.yml) 3 | 4 | A GUI utility for viewing certain assets from the game ウマ娘 プリティーダービー (Umamusume: Pretty Derby) developed by Cygames, Inc. 5 | 6 | DISCLAIMER: You must have your own installation of the game from DMM Games or Steam to use this utility. 7 | Game data such as stats are not modified in any way, and can be viewed with any SQLite database viewer. 8 | 9 | ## Functions included 10 | 11 | * File browser with search and export 12 | * Audio player with export 13 | * Live music player with export 14 | * Race music simulator 15 | * Character info 16 | * 5 main stats (speed, stamina, etc.) for each costume and its rarity 17 | * Skill view 18 | 19 | ![Screenshot](Docs/Images/window.png) 20 | 21 | ## Credits 22 | 23 | * [sqlite-net](https://github.com/praeclarum/sqlite-net) and [SQLite3MultipleCiphers](https://github.com/utelle/SQLite3MultipleCiphers) for SQLite functionality 24 | * [vgmstream](https://github.com/vgmstream/vgmstream) for the CRIWARE code 25 | * [AssetsTools.NET](https://github.com/nesrak1/AssetsTools.NET) for reading Unity assets 26 | -------------------------------------------------------------------------------- /UmamusumeData/DataDirectories/DefaultDataDirectory.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeData.DataDirectories 2 | { 3 | internal class DefaultDataDirectory : DataDirectory 4 | { 5 | private static readonly string appData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); 6 | private static readonly string cygamesDirectory = Path.Combine(appData + "Low", "Cygames"); 7 | private static readonly string umamusumeDirectory = Path.Combine(cygamesDirectory, "umamusume"); 8 | private static readonly string umamusumeJpnDirectory = Path.Combine(cygamesDirectory, "UmamusumePrettyDerby_Jpn"); 9 | 10 | public override string Name => "Default (AppData)"; 11 | 12 | public override bool Exists() 13 | { 14 | string[] dataDirectories = 15 | { 16 | umamusumeDirectory, 17 | umamusumeJpnDirectory 18 | }; 19 | 20 | foreach (var dataDirectory in dataDirectories) 21 | { 22 | if (CheckDirectory(dataDirectory)) 23 | { 24 | DataDirectoryPath = dataDirectory; 25 | return true; 26 | } 27 | } 28 | 29 | return false; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | workflow_dispatch: 9 | jobs: 10 | build: 11 | runs-on: windows-latest 12 | steps: 13 | - name: Checkout branch and submodules 14 | uses: actions/checkout@v4.2.2 15 | with: 16 | submodules: 'recursive' 17 | - name: Setup MSBuild 18 | uses: microsoft/setup-msbuild@v2 19 | with: 20 | msbuild-architecture: x64 21 | - name: Generate AssetsTools.NET texture decoder CMake config 22 | run: cmake -S .\AssetsTools.NET -B .\AssetsTools.NET\build 23 | - name: Build AssetsTools.NET texture decoder 24 | run: cmake --build .\AssetsTools.NET\build 25 | - name: Build .NET project 26 | run: dotnet publish .\UmamusumeExplorer --no-self-contained -r win-x64 -c Release -p:PublishSingleFile=true 27 | - name: Zip up files 28 | run: Compress-Archive .\UmamusumeExplorer\bin\Release\net8.0-windows\win-x64\publish\* build-win-x64.zip 29 | - name: Upload build artifact 30 | uses: actions/upload-artifact@v4.4.3 31 | with: 32 | name: build-win-x64 33 | path: .\UmamusumeExplorer\bin\Release\net8.0-windows\win-x64\publish\ 34 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Pages/JukeboxControl.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeData.Tables; 2 | using UmamusumeExplorer.Assets; 3 | using UmamusumeExplorer.Music; 4 | 5 | namespace UmamusumeExplorer.Pages 6 | { 7 | partial class JukeboxControl : UserControl 8 | { 9 | private readonly IEnumerable? jukeboxMusicDatas = AssetTables.JukeboxMusicDatas?.OrderBy(l => l.Sort); 10 | 11 | public JukeboxControl() 12 | { 13 | InitializeComponent(); 14 | 15 | shortVersionRadioButton.Checked = true; 16 | shortVersionRadioButton.Tag = SongLength.ShortVersion; 17 | gameSizeVersionRadioButton.Tag = SongLength.GameSizeVersion; 18 | } 19 | 20 | private void LiveMusicPlayerSongSelectControl_Load(object sender, EventArgs e) 21 | { 22 | jukeboxItemsPanel.Items = jukeboxMusicDatas; 23 | } 24 | 25 | private void RadioBuiton_CheckedChanegd(object sender, EventArgs e) 26 | { 27 | if (sender is not RadioButton radioButton) return; 28 | if (radioButton.Tag is not null) 29 | { 30 | if (radioButton.Checked) 31 | jukeboxItemsPanel.CurrentSongLength = (SongLength)radioButton.Tag; 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Utility/TextHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using UmamusumeData; 7 | using UmamusumeData.Tables; 8 | using UmamusumeExplorer.Assets; 9 | 10 | namespace UmamusumeExplorer.Utility 11 | { 12 | internal static class TextHelpers 13 | { 14 | public static string GetCharaName(int id, bool includeId = false, bool includeEnglishName = false) 15 | { 16 | StringBuilder charaNameBuilder = new(); 17 | 18 | if (includeId) charaNameBuilder.Append($"{id:d4}: "); 19 | string charaName = AssetTables.TextDatas.First(td => td.Index == id && td.Category == (int)TextCategory.MasterCharaName).Text; 20 | charaNameBuilder.Append(charaName); 21 | if (includeEnglishName) 22 | { 23 | string? englishName = AssetTables.TextDatas.FirstOrDefault(td => td.Index == id && td.Category == (int)TextCategory.CharaName_En)?.Text; 24 | if (englishName is not null) 25 | { 26 | charaNameBuilder.Append($" ({englishName})"); 27 | } 28 | } 29 | 30 | return charaNameBuilder.ToString(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Music/SampleProviders/PanSampleProvider.cs: -------------------------------------------------------------------------------- 1 | using NAudio.Wave; 2 | 3 | namespace UmamusumeExplorer.Music.SampleProviders 4 | { 5 | class PanSampleProvider : ISampleProvider 6 | { 7 | private readonly ISampleProvider sampleProvider; 8 | 9 | public PanSampleProvider(ISampleProvider source) 10 | { 11 | sampleProvider = source; 12 | 13 | if (source.WaveFormat.Channels != 2) 14 | throw new Exception("Must be stereo."); 15 | } 16 | 17 | public WaveFormat WaveFormat => sampleProvider.WaveFormat; 18 | 19 | public float Pan { get; set; } 20 | 21 | public int Read(float[] buffer, int offset, int count) 22 | { 23 | int read = sampleProvider.Read(buffer, offset, count); 24 | 25 | float left; 26 | float right; 27 | 28 | int bufferIndex = 0; 29 | for (int i = offset; i < count / 2; i++) 30 | { 31 | left = Pan <= 0 ? 1.0F : (1.0F - Pan) / 2.0F; 32 | right = Pan >= 0 ? 1.0F : (Pan + 1.0F) / 2.0F; 33 | 34 | buffer[bufferIndex++] *= left; 35 | buffer[bufferIndex++] *= right; 36 | } 37 | 38 | return read; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Assets/PinnedBitmap.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace UmamusumeExplorer.Assets 4 | { 5 | class PinnedBitmap : IDisposable 6 | { 7 | private readonly GCHandle bitmapHandle; 8 | private readonly Bitmap bitmap; 9 | 10 | public PinnedBitmap(byte[] imageBytes, int width, int height) 11 | { 12 | bitmapHandle = GCHandle.Alloc(imageBytes, GCHandleType.Pinned); 13 | bitmap = new Bitmap( 14 | width, 15 | height, 16 | width * 4, 17 | System.Drawing.Imaging.PixelFormat.Format32bppArgb, 18 | bitmapHandle.AddrOfPinnedObject()); 19 | } 20 | 21 | public PinnedBitmap(nint imageDataHandle, int width, int height) 22 | { 23 | bitmap = new Bitmap( 24 | width, 25 | height, 26 | width * 4, 27 | System.Drawing.Imaging.PixelFormat.Format32bppArgb, 28 | imageDataHandle); 29 | } 30 | 31 | public Bitmap Bitmap => bitmap; 32 | 33 | public void Dispose() 34 | { 35 | bitmap.Dispose(); 36 | 37 | if (bitmapHandle.IsAllocated) 38 | bitmapHandle.Free(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/InstallationSelectForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | using UmamusumeData.DataDirectories; 11 | 12 | namespace UmamusumeExplorer.Controls 13 | { 14 | public partial class InstallationSelectForm : Form 15 | { 16 | private readonly IEnumerable dataDirectories; 17 | 18 | public InstallationSelectForm(IEnumerable dataDirectories) 19 | { 20 | InitializeComponent(); 21 | 22 | foreach (var item in dataDirectories) 23 | { 24 | installationListBox.Items.Add(item.Name); 25 | } 26 | 27 | this.dataDirectories = dataDirectories; 28 | } 29 | 30 | public string Path { get; private set; } = ""; 31 | 32 | private void OkButton_Click(object sender, EventArgs e) 33 | { 34 | Path = dataDirectories.ElementAt(installationListBox.SelectedIndex).DataDirectoryPath; 35 | DialogResult = DialogResult.OK; 36 | } 37 | 38 | private void cancelButton_Click(object sender, EventArgs e) 39 | { 40 | DialogResult = DialogResult.Cancel; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/SupportCardItemsPanel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using UmamusumeData; 7 | using UmamusumeData.Tables; 8 | using UmamusumeExplorer.Assets; 9 | using UmamusumeExplorer.Controls; 10 | 11 | namespace UmamusumeExplorer.Controls 12 | { 13 | internal class SupportCardItemsPanel : ItemsPanel 14 | { 15 | public override bool ProcessItem(SupportCardData supportCardData, ref Control? supportCardPictureBox) 16 | { 17 | bool limited = !AssetTables.GachaAvailables.Any(ga => ga.CardType == 2 && ga.CardId == supportCardData.Id); 18 | supportCardPictureBox = new HighlightPictureBox(limited) 19 | { 20 | BackgroundImage = GameAssets.GetSupportCardIcon(supportCardData.Id)?.Bitmap, 21 | BackgroundImageLayout = ImageLayout.Stretch, 22 | Cursor = Cursors.Hand, 23 | Height = 166, 24 | Width = 127, 25 | Tag = supportCardData 26 | }; 27 | 28 | ToolTip toolTip = new(); 29 | toolTip.SetToolTip(supportCardPictureBox, $"{supportCardData.Id}: {AssetTables.GetText(TextCategory.MasterSupportCardTitleName, supportCardData.Id)}"); 30 | 31 | return true; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/AtomAudioTrack.cs: -------------------------------------------------------------------------------- 1 | using CriWareLibrary; 2 | using NAudio.Wave; 3 | using UmamusumeAudio; 4 | 5 | namespace UmamusumeExplorer.Controls 6 | { 7 | internal class AtomAudioTrack : IAudioTrack 8 | { 9 | private readonly FileStream acbFile; 10 | private readonly AwbReader awbReader; 11 | private readonly int index; 12 | 13 | public AtomAudioTrack(string acbPath, string awbPath, int waveIndex) 14 | { 15 | acbFile = File.OpenRead(acbPath); 16 | 17 | AcbParser acbNameLoader = new(acbFile); 18 | 19 | bool hasMemory = false; 20 | if (string.IsNullOrEmpty(awbPath)) 21 | { 22 | AcbReader acbReader = new(acbFile); 23 | awbReader = acbReader.GetAwb(); 24 | 25 | hasMemory = acbReader.HasMemoryAwb; 26 | } 27 | else 28 | { 29 | awbReader = new(File.OpenRead(awbPath)); 30 | } 31 | 32 | index = waveIndex; 33 | 34 | Name = acbNameLoader.LoadWaveName(waveIndex, 0, hasMemory); 35 | } 36 | 37 | public string Name { get; } 38 | 39 | public WaveStream WaveStream 40 | { 41 | get 42 | { 43 | return new UmaWaveStream(awbReader, awbReader.Waves[index].WaveId); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/AtomAudioSource.cs: -------------------------------------------------------------------------------- 1 | using CriWareLibrary; 2 | 3 | namespace UmamusumeExplorer.Controls 4 | { 5 | internal class AtomAudioSource : AudioSource 6 | { 7 | private readonly string acb; 8 | private readonly string awb; 9 | 10 | public AtomAudioSource(string name, string acbPath, string awbPath) 11 | { 12 | Name = name; 13 | acb = acbPath; 14 | awb = awbPath; 15 | 16 | using var acbReader = new AcbReader(File.OpenRead(acbPath)); 17 | 18 | if (string.IsNullOrEmpty(awbPath) && acbReader.HasMemoryAwb) 19 | { 20 | TrackCount = acbReader.GetAwb().Waves.Count; 21 | } 22 | else 23 | { 24 | if (string.IsNullOrEmpty(awbPath)) return; 25 | 26 | using var awbReader = new AwbReader(File.OpenRead(awbPath)); 27 | TrackCount = awbReader.Waves.Count; 28 | } 29 | } 30 | 31 | public override string Name { get; } 32 | 33 | public override int TrackCount { get; } 34 | 35 | protected override IAudioTrack[] InitializeTracks() 36 | { 37 | AtomAudioTrack[] tracks = new AtomAudioTrack[TrackCount]; 38 | 39 | for (int i = 0; i < TrackCount; i++) 40 | { 41 | tracks[i] = new AtomAudioTrack(acb, awb, i); 42 | } 43 | 44 | return tracks; 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /UmamusumeExplorer/Pages/LiveMusicPlayerControl.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeData.Tables; 2 | using UmamusumeExplorer.Assets; 3 | using UmamusumeExplorer.Controls; 4 | using UmamusumeExplorer.Music; 5 | 6 | namespace UmamusumeExplorer.Pages 7 | { 8 | partial class LiveMusicPlayerControl : UserControl 9 | { 10 | private readonly IEnumerable? liveDatas = AssetTables.LiveDatas?.OrderBy(l => l.Sort); 11 | 12 | public LiveMusicPlayerControl() 13 | { 14 | InitializeComponent(); 15 | 16 | songItemsPanel.ItemClick += (s, e) => 17 | { 18 | if (s is not PictureBox pictureBox) return; 19 | if (pictureBox.Tag is not LiveData liveData) return; 20 | 21 | if (liveData is not null) 22 | { 23 | MusicManager liveManager = new(liveData); 24 | 25 | if (liveManager.SetupLive(this)) 26 | ControlHelpers.ShowFormCenter(new PlayerForm(liveManager), this); 27 | } 28 | }; 29 | } 30 | 31 | private void LiveMusicPlayerSongSelectControl_Load(object sender, EventArgs e) 32 | { 33 | songItemsPanel.Items = liveDatas; 34 | } 35 | 36 | private void LinkLabelDownload_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) 37 | { 38 | ControlHelpers.ShowFormCenter(new DownloadWorkaroundForm(), this); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Music/Race/Bgm.cs: -------------------------------------------------------------------------------- 1 | using CriWareLibrary; 2 | using UmamusumeAudio; 3 | using UmamusumeData; 4 | 5 | namespace UmamusumeExplorer.Music.Race 6 | { 7 | internal class Bgm : IDisposable 8 | { 9 | private readonly AwbReader awbReader; 10 | 11 | public Bgm(IEnumerable entryList, string cuesheetName, string cueName) 12 | { 13 | string lower = cuesheetName.ToLower(); 14 | string awbPath = UmaDataHelper.GetPath(entryList, lower + ".awb"); 15 | string acbPath = UmaDataHelper.GetPath(entryList, lower + ".acb"); 16 | 17 | awbReader = new(File.OpenRead(awbPath)); 18 | 19 | AcbReader acbReader = new(File.OpenRead(acbPath)); 20 | 21 | int targetWaveId = -1; 22 | foreach (var wave in awbReader.Waves) 23 | { 24 | string waveName = acbReader.GetWaveName(wave.WaveId, 0, false); 25 | 26 | if (waveName.Contains(cueName)) 27 | { 28 | targetWaveId = wave.WaveId; 29 | break; 30 | } 31 | } 32 | 33 | acbReader.Dispose(); 34 | 35 | UmaWaveStream = new UmaWaveStream(awbReader, targetWaveId); 36 | } 37 | 38 | public UmaWaveStream UmaWaveStream { get; } 39 | 40 | public void Dispose() 41 | { 42 | GC.SuppressFinalize(this); 43 | awbReader.Dispose(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Music/Live/LyricsTrigger.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace UmamusumeExplorer.Music.Live 4 | { 5 | class LyricsTrigger 6 | { 7 | public int TimeMs { get; } 8 | 9 | public string Lyrics { get; } 10 | 11 | public LyricsTrigger(string lyricsCsvLine) 12 | { 13 | string[] columns = Split(lyricsCsvLine, ','); 14 | TimeMs = int.Parse(columns[0]); 15 | Lyrics = columns[1] 16 | .Replace("[COMMA]", ",") 17 | .Replace("&&", "&") 18 | .Replace("\\n", "\n"); 19 | } 20 | 21 | private string[] Split(string toSplit, char splitChar) 22 | { 23 | List splitStrings = new(); 24 | StringBuilder splitStringBuilder = new(); 25 | 26 | bool withinQuotes = false; 27 | 28 | for (int i = 0; i < toSplit.Length; i++) 29 | { 30 | char c = toSplit[i]; 31 | 32 | if (c == '"') withinQuotes = !withinQuotes; 33 | 34 | if (!withinQuotes && c == splitChar) 35 | { 36 | splitStrings.Add(splitStringBuilder.ToString()); 37 | splitStringBuilder.Clear(); 38 | } 39 | else 40 | splitStringBuilder.Append(c); 41 | } 42 | 43 | splitStrings.Add(splitStringBuilder.ToString()); 44 | return splitStrings.ToArray(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/ConfigureLoopForm.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeAudio; 2 | 3 | namespace UmamusumeExplorer.Controls 4 | { 5 | partial class ConfigureLoopForm : Form 6 | { 7 | private readonly UmaWaveStream hcaWaveStream; 8 | 9 | public ConfigureLoopForm(UmaWaveStream waveStream) 10 | { 11 | InitializeComponent(); 12 | 13 | hcaWaveStream = waveStream; 14 | } 15 | 16 | private void LoopModifyForm_Load(object sender, EventArgs e) 17 | { 18 | LoadValues(); 19 | } 20 | 21 | private void OKButton_Click(object sender, EventArgs e) 22 | { 23 | hcaWaveStream.Loop = loopEnabledCheckBox.Checked; 24 | hcaWaveStream.LoopStartSample = (long)startSampleNumericUpDown.Value; 25 | hcaWaveStream.LoopEndSample = (long)endSampleNumericUpDown.Value; 26 | 27 | Close(); 28 | } 29 | 30 | private void ResetButton_Click(object sender, EventArgs e) 31 | { 32 | hcaWaveStream.ResetLoop(); 33 | LoadValues(); 34 | } 35 | 36 | private void LoadValues() 37 | { 38 | loopEnabledCheckBox.Checked = hcaWaveStream.Loop; 39 | startSampleNumericUpDown.Value = hcaWaveStream.LoopStartSample; 40 | endSampleNumericUpDown.Value = hcaWaveStream.LoopEndSample; 41 | } 42 | 43 | private void CancelButton_Click(object sender, EventArgs e) 44 | { 45 | Close(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /UmamusumeData/Tables/CardData.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | 3 | namespace UmamusumeData.Tables 4 | { 5 | [Table("card_data")] 6 | public class CardData 7 | { 8 | [Column("id"), NotNull, PrimaryKey] 9 | public int Id { get; set; } 10 | 11 | [Column("chara_id"), NotNull] 12 | public int CharaId { get; set; } 13 | 14 | [Column("default_rarity"), NotNull] 15 | public int DefaultRarity { get; set; } 16 | 17 | [Column("limited_chara"), NotNull] 18 | public int LimitedChara { get; set; } 19 | 20 | [Column("available_skill_set_id"), NotNull] 21 | public int AvailableSkillSetId { get; set; } 22 | 23 | [Column("talent_speed"), NotNull] 24 | public int TalentSpeed { get; set; } 25 | [Column("talent_stamina"), NotNull] 26 | public int TalentStamina { get; set; } 27 | [Column("talent_pow"), NotNull] 28 | public int TalentPow { get; set; } 29 | [Column("talent_guts"), NotNull] 30 | public int TalentGuts { get; set; } 31 | [Column("talent_wiz"), NotNull] 32 | public int TalentWiz { get; set; } 33 | 34 | [Column("talent_group_id"), NotNull] 35 | public int TalentGroupId { get; set; } 36 | 37 | [Column("bg_id"), NotNull] 38 | public int BgId { get; set; } 39 | 40 | [Column("get_piece_id"), NotNull] 41 | public int GetPieceId { get; set; } 42 | 43 | [Column("running_style"), NotNull] 44 | public int RunningStyle { get; set; } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /UmamusumeData/Tables/CharacterSystemText.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | 3 | namespace UmamusumeData.Tables 4 | { 5 | [Table("character_system_text")] 6 | public class CharacterSystemText 7 | { 8 | [Column("character_id"), NotNull, PrimaryKey] 9 | public int CharacterId { get; set; } 10 | 11 | [Column("voice_id"), NotNull, PrimaryKey] 12 | public int VoiceId { get; set; } 13 | 14 | [Column("text"), NotNull] 15 | public string Text { get; set; } = ""; 16 | 17 | [Column("cue_sheet"), NotNull] 18 | public string CueSheet { get; set; } = ""; 19 | 20 | [Column("cue_id"), NotNull] 21 | public int CueId { get; set; } 22 | 23 | [Column("motion_set"), NotNull] 24 | public int MotionSet { get; set; } 25 | 26 | [Column("scene"), NotNull] 27 | public int Scene { get; set; } 28 | 29 | [Column("use_gallery"), NotNull] 30 | public int UseGallery { get; set; } 31 | 32 | [Column("card_id"), NotNull] 33 | public int CardId { get; set; } 34 | 35 | [Column("lip_sync_data"), NotNull] 36 | public string LipSyncData { get; set; } = ""; 37 | 38 | [Column("gender"), NotNull] 39 | public int Gender { get; set; } 40 | 41 | [Column("motion_second_set"), NotNull] 42 | public int MotionSecondSet { get; set; } 43 | 44 | [Column("motion_second_start"), NotNull] 45 | public int MotionSecondStart { get; set; } 46 | 47 | [Column("start_date"), NotNull] 48 | public int StartDate { get; set; } 49 | } 50 | } -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/SongsControl.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeData.Tables; 2 | using UmamusumeExplorer.Assets; 3 | using UmamusumeExplorer.Music; 4 | using UmamusumeExplorer.Utility; 5 | 6 | namespace UmamusumeExplorer.Controls 7 | { 8 | public partial class SongsControl : UserControl 9 | { 10 | private readonly IEnumerable liveDatas = AssetTables.LiveDatas; 11 | 12 | public SongsControl() 13 | { 14 | InitializeComponent(); 15 | 16 | songItemsPanel.Indeterminate = true; 17 | songItemsPanel.Filter = (item) => 18 | { 19 | IEnumerable livePermissionData = LivePermissionDataHelper.GetLivePermissionData(item.MusicId); 20 | return livePermissionData.FirstOrDefault(lpd => lpd.CharaId == CharaId) is not null; 21 | }; 22 | songItemsPanel.ItemClick += (s, e) => 23 | { 24 | if (s is not PictureBox pictureBox) return; 25 | if (pictureBox.Tag is not LiveData liveData) return; 26 | 27 | if (liveData is not null) 28 | { 29 | MusicManager liveManager = new(liveData); 30 | 31 | if (liveManager.SetupLive(this)) 32 | ControlHelpers.ShowFormCenter(new PlayerForm(liveManager), this); 33 | } 34 | }; 35 | } 36 | 37 | public int CharaId { get; set; } 38 | 39 | private void SongsControl_Load(object sender, EventArgs e) 40 | { 41 | if (liveDatas is not null) 42 | songItemsPanel.Items = liveDatas; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/JukeboxItemsPanel.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeData; 2 | using UmamusumeData.Tables; 3 | using UmamusumeExplorer.Assets; 4 | using UmamusumeExplorer.Music; 5 | 6 | namespace UmamusumeExplorer.Controls 7 | { 8 | internal class JukeboxItemsPanel : ItemsPanel 9 | { 10 | public SongLength CurrentSongLength { get; set; } 11 | 12 | public override bool ProcessItem(JukeboxMusicData jukeboxMusicData, ref Control? jacket) 13 | { 14 | jacket = new PictureBox() 15 | { 16 | BackgroundImage = GameAssets.GetJacket(jukeboxMusicData.MusicId, 'l')?.Bitmap, 17 | BackgroundImageLayout = ImageLayout.Zoom, 18 | Cursor = Cursors.Hand, 19 | Height = 128, 20 | Width = 128, 21 | Tag = jukeboxMusicData 22 | }; 23 | 24 | jacket.Click += (s, e) => 25 | { 26 | if (s is not PictureBox pictureBox) return; 27 | if (pictureBox.Tag is not JukeboxMusicData jukeBoxData) return; 28 | if (jukeBoxData is not null) 29 | { 30 | MusicManager jukeBoxManager = new(jukeBoxData); 31 | 32 | if (jukeBoxManager.SetupJukeBoxMusic(CurrentSongLength)) 33 | ControlHelpers.ShowFormCenter(new PlayerForm(jukeBoxManager), this); 34 | } 35 | }; 36 | 37 | ToolTip toolTip = new(); 38 | toolTip.SetToolTip(jacket, $"{jukeboxMusicData.MusicId}: {AssetTables.GetText(TextCategory.MasterLiveTitle, jukeboxMusicData.MusicId)}"); 39 | 40 | return true; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Pages/CharacterInfoControl.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeData.Tables; 2 | using UmamusumeExplorer.Assets; 3 | using UmamusumeExplorer.Controls; 4 | 5 | namespace UmamusumeExplorer.Pages 6 | { 7 | partial class CharacterInfoControl : UserControl 8 | { 9 | private readonly IEnumerable charaDatas = AssetTables.CharaDatas; 10 | 11 | public CharacterInfoControl() 12 | { 13 | InitializeComponent(); 14 | 15 | characterItemsPanel.ItemClick += (s, e) => 16 | { 17 | if (s is not PictureBox pictureBox) return; 18 | if (pictureBox.Tag is not CharaData charaData) return; 19 | 20 | CharacterInfoForm details = new(charaData); 21 | ControlHelpers.ShowFormCenter(details, this); 22 | }; 23 | } 24 | 25 | private void CharacterInfoControl_Load(object sender, EventArgs e) 26 | { 27 | characterItemsPanel.Items = charaDatas; 28 | 29 | foreach (var item in charaDatas) 30 | { 31 | charaListComboBox.Items.Add(new CharaComboBoxItem(item)); 32 | } 33 | } 34 | 35 | private void GoButton_Click(object sender, EventArgs e) 36 | { 37 | if (charaListComboBox.SelectedItem is CharaComboBoxItem item) ControlHelpers.ShowFormCenter(new CharacterInfoForm(item.CharaData), this); 38 | } 39 | 40 | private void ShowPlayableCheckBox_CheckedChanged(object sender, EventArgs e) 41 | { 42 | foreach (HighlightPictureBox hpb in characterItemsPanel.Controls) 43 | { 44 | hpb.ShowHighlight = markPlayableCheckBox.Checked; 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /UmamusumeData/ManifestEntry.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | 3 | namespace UmamusumeData 4 | { 5 | [Table("a")] 6 | public class ManifestEntry 7 | { 8 | [Column("i"), PrimaryKey] 9 | public int Id { get; set; } 10 | 11 | [Column("n"), NotNull] 12 | public string Name { get; set; } = ""; 13 | 14 | [Column("d")] 15 | public string Dependencies { get; set; } = ""; 16 | 17 | [Column("g"), NotNull] 18 | public AssetBundleGroup Group { get; set; } 19 | 20 | [Column("l"), NotNull] 21 | public long Length { get; set; } 22 | 23 | [Column("c"), NotNull] 24 | public long Checksum { get; set; } 25 | 26 | [Column("h"), NotNull] 27 | public string HashName { get; set; } = ""; 28 | 29 | [Column("m"), NotNull] 30 | public string Manifest { get; set; } = ""; 31 | 32 | [Column("k"), NotNull] 33 | public ManifestEntryKind Kind { get; set; } 34 | 35 | [Column("s"), NotNull] 36 | public byte State { get; set; } 37 | 38 | [Column("p"), NotNull] 39 | public int Priority { get; set; } 40 | 41 | [Column("e"), NotNull] 42 | public long EncryptionKey { get; set; } 43 | 44 | public string BaseName 45 | { 46 | get 47 | { 48 | int lastSlash = Name.LastIndexOf('/'); 49 | return Name[(lastSlash + 1)..]; 50 | } 51 | } 52 | 53 | public override bool Equals(object? obj) 54 | { 55 | return (obj as ManifestEntry)?.HashName == HashName; 56 | } 57 | 58 | public override int GetHashCode() 59 | { 60 | return HashName.GetHashCode(); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Tools/LiveMusicPlayerCli/AssetTables.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using UmamusumeData; 7 | using UmamusumeData.Tables; 8 | 9 | namespace LiveMusicPlayerCli 10 | { 11 | internal static class AssetTables 12 | { 13 | public static IEnumerable AudioAssetEntries { get; } = UmaDataHelper.GetManifestEntries(ga => ga.Name.StartsWith("sound/")); 14 | 15 | public static IEnumerable CharaNameTextDatas { get; } = UmaDataHelper.GetMasterDatabaseRows(td => td.Category == 170); 16 | public static IEnumerable CharaNameKatakanaTextDatas { get; } = UmaDataHelper.GetMasterDatabaseRows(td => td.Category == 182); 17 | public static IEnumerable CharaVoiceNameTextDatas { get; } = UmaDataHelper.GetMasterDatabaseRows(td => td.Category == 7); 18 | public static IEnumerable CharaCostumeNameTextDatas { get; } = UmaDataHelper.GetMasterDatabaseRows(td => td.Category == 5); 19 | 20 | public static IEnumerable LiveDatas { get; } = UmaDataHelper.GetMasterDatabaseRows(); 21 | public static IEnumerable LivePermissionDatas { get; } = UmaDataHelper.GetMasterDatabaseRows(); 22 | 23 | public static IEnumerable LiveNameTextDatas { get; } = UmaDataHelper.GetMasterDatabaseRows(td => td.Category == 16); 24 | public static IEnumerable LiveInfoTextDatas { get; } = UmaDataHelper.GetMasterDatabaseRows(td => td.Category == 17); 25 | 26 | public static string GetText(IEnumerable textDatas, int index) 27 | { 28 | return textDatas.First(td => td.Index == index).Text; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Music/SampleProviders/TestRunningBgmSampleProvider.cs: -------------------------------------------------------------------------------- 1 | using NAudio.Wave; 2 | using NAudio.Wave.SampleProviders; 3 | using UmamusumeExplorer.Music.Race; 4 | 5 | namespace UmamusumeExplorer.Music.SampleProviders 6 | { 7 | internal class TestRunningBgmSampleProvider : ISampleProvider 8 | { 9 | private readonly FadeInOutSampleProvider first; 10 | private readonly ISampleProvider second; 11 | private readonly int fadeSampleCount; 12 | 13 | private bool lastSpurtTriggered = false; 14 | private int fadePosition = 0; 15 | 16 | public TestRunningBgmSampleProvider(Bgm firstPattern, Bgm secondPattern) 17 | { 18 | first = new(firstPattern.UmaWaveStream.ToSampleProvider()); 19 | second = secondPattern.UmaWaveStream.ToSampleProvider(); 20 | 21 | fadeSampleCount = 1000 * (WaveFormat.SampleRate / 1000); 22 | } 23 | 24 | public WaveFormat WaveFormat => first.WaveFormat; 25 | 26 | public void LastSpurt() 27 | { 28 | first.BeginFadeOut(500); 29 | lastSpurtTriggered = true; 30 | } 31 | 32 | public int Read(float[] buffer, int offset, int count) 33 | { 34 | first.Read(buffer, offset, count); 35 | 36 | if (fadePosition >= fadeSampleCount) 37 | { 38 | float[] buffer2 = new float[count]; 39 | second.Read(buffer2, 0, count); 40 | 41 | for (int i = 0; i < count; i++) 42 | { 43 | buffer[i] += buffer2[i]; 44 | } 45 | } 46 | 47 | if (lastSpurtTriggered && fadePosition < fadeSampleCount) 48 | { 49 | fadePosition += count; 50 | } 51 | 52 | return count; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Tools/PlayerCli/UmaWaveStream.cs: -------------------------------------------------------------------------------- 1 | using NAudio.Wave; 2 | using System; 3 | using System.IO; 4 | using CriWareLibrary; 5 | 6 | namespace PlayerCli 7 | { 8 | public class UmaWaveStream : WaveStream 9 | { 10 | private const ulong umaMusumeKey = 75923756697503; 11 | 12 | private readonly HcaWaveStream hcaWaveStream; 13 | 14 | public UmaWaveStream(AwbReader awbReader, int waveId) 15 | { 16 | Stream awbSubfile = awbReader.GetWaveSubfileStream(awbReader.Waves.Find((wave) => wave.WaveId == waveId)); 17 | hcaWaveStream = new(awbSubfile, MixKey(umaMusumeKey, awbReader.Subkey)); 18 | } 19 | 20 | public override WaveFormat WaveFormat => hcaWaveStream.WaveFormat; 21 | 22 | public bool Loop { get => hcaWaveStream.Loop; set { hcaWaveStream.Loop = value; } } 23 | 24 | public long LoopStartSample { get => hcaWaveStream.LoopStartSample; set { hcaWaveStream.LoopStartSample = value; } } 25 | 26 | public long LoopEndSample { get => hcaWaveStream.LoopEndSample; set { hcaWaveStream.LoopEndSample = value; } } 27 | 28 | public override long Length => hcaWaveStream.Length; 29 | 30 | public override long Position { get => hcaWaveStream.Position; set => hcaWaveStream.Position = value; } 31 | 32 | public override int Read(byte[] buffer, int offset, int count) 33 | { 34 | return hcaWaveStream.Read(buffer, offset, count); 35 | } 36 | 37 | public void ResetLoop() => hcaWaveStream.ResetLoop(); 38 | 39 | private static ulong MixKey(ulong key, ushort subkey) => 40 | key * (((ulong)subkey << 16) | ((ushort)~subkey + 2u)); 41 | 42 | protected override void Dispose(bool disposing) 43 | { 44 | hcaWaveStream.Dispose(); 45 | 46 | base.Dispose(disposing); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /UmamusumeAudio/UmaWaveStream.cs: -------------------------------------------------------------------------------- 1 | using NAudio.Wave; 2 | using System; 3 | using System.IO; 4 | using CriWareLibrary; 5 | 6 | namespace UmamusumeAudio 7 | { 8 | public class UmaWaveStream : WaveStream 9 | { 10 | private const ulong umaMusumeKey = 75923756697503; 11 | 12 | private readonly HcaWaveStream hcaWaveStream; 13 | 14 | public UmaWaveStream(AwbReader awbReader, int waveId) 15 | { 16 | Stream awbSubfile = awbReader.GetWaveSubfileStream(awbReader.Waves.Find((wave) => wave.WaveId == waveId)); 17 | hcaWaveStream = new(awbSubfile, MixKey(umaMusumeKey, awbReader.Subkey)); 18 | } 19 | 20 | public override WaveFormat WaveFormat => hcaWaveStream.WaveFormat; 21 | 22 | public bool Loop { get => hcaWaveStream.Loop; set { hcaWaveStream.Loop = value; } } 23 | 24 | public long LoopStartSample { get => hcaWaveStream.LoopStartSample; set { hcaWaveStream.LoopStartSample = value; } } 25 | 26 | public long LoopEndSample { get => hcaWaveStream.LoopEndSample; set { hcaWaveStream.LoopEndSample = value; } } 27 | 28 | public override long Length => hcaWaveStream.Length; 29 | 30 | public override long Position { get => hcaWaveStream.Position; set => hcaWaveStream.Position = value; } 31 | 32 | public override int Read(byte[] buffer, int offset, int count) 33 | { 34 | return hcaWaveStream.Read(buffer, offset, count); 35 | } 36 | 37 | public void ResetLoop() => hcaWaveStream.ResetLoop(); 38 | 39 | private static ulong MixKey(ulong key, ushort subkey) => 40 | key * (((ulong)subkey << 16) | ((ushort)~subkey + 2u)); 41 | 42 | protected override void Dispose(bool disposing) 43 | { 44 | hcaWaveStream.Dispose(); 45 | 46 | base.Dispose(disposing); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /UmamusumeData/Tables/SupportCardData.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | 3 | namespace UmamusumeData.Tables 4 | { 5 | [Table("support_card_data")] 6 | public class SupportCardData 7 | { 8 | [Column("id"), NotNull, PrimaryKey] 9 | public int Id { get; set; } 10 | 11 | [Column("chara_id"), NotNull] 12 | public int CharaId { get; set; } 13 | 14 | [Column("rarity"), NotNull] 15 | public int Rarity { get; set; } 16 | 17 | [Column("exchange_item_id"), NotNull] 18 | public int ExchangeItemId { get; set; } 19 | 20 | [Column("effect_table_id"), NotNull] 21 | public int EffectTableId { get; set; } 22 | 23 | [Column("unique_effect_id"), NotNull] 24 | public int UniqueEffectId { get; set; } 25 | 26 | [Column("command_type"), NotNull] 27 | public int CommandType { get; set; } 28 | 29 | [Column("command_id"), NotNull] 30 | public int CommandId { get; set; } 31 | 32 | [Column("support_card_type"), NotNull] 33 | public int SupportCardType { get; set; } 34 | 35 | [Column("skill_set_id"), NotNull] 36 | public int SkillSetId { get; set; } 37 | 38 | [Column("detail_pos_x"), NotNull] 39 | public int DetailPosX { get; set; } 40 | 41 | [Column("detail_pos_y"), NotNull] 42 | public int DetailPosY { get; set; } 43 | 44 | [Column("detail_scale"), NotNull] 45 | public int DetailScale { get; set; } 46 | 47 | [Column("detail_rot_z"), NotNull] 48 | public int DetailRotZ { get; set; } 49 | 50 | [Column("start_date"), NotNull] 51 | public int StartDate { get; set; } 52 | 53 | [Column("outing_max"), NotNull] 54 | public int OutingMax { get; set; } 55 | 56 | [Column("effect_id"), NotNull] 57 | public int EffectId { get; set; } 58 | } 59 | } -------------------------------------------------------------------------------- /UmamusumeData/DataDirectories/DmmDataDirectory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.Json; 6 | using System.Text.Json.Nodes; 7 | using System.Text.Json.Serialization; 8 | using System.Threading.Tasks; 9 | 10 | namespace UmamusumeData.DataDirectories 11 | { 12 | internal class DmmDataDirectory : DataDirectory 13 | { 14 | private static readonly string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); 15 | private static readonly string dmmDirectory = Path.Combine(appData, "dmmgameplayer5"); 16 | private static readonly string dmmConfigPath = Path.Combine(dmmDirectory, "dmmgame.cnf"); 17 | 18 | public override string Name => "DMM Games"; 19 | 20 | public override bool Exists() 21 | { 22 | if (!File.Exists(dmmConfigPath)) return false; 23 | 24 | string configJson = File.ReadAllText(dmmConfigPath); 25 | 26 | JsonNode? json = JsonNode.Parse(configJson); 27 | JsonArray? contents = json?["contents"]?.AsArray(); 28 | 29 | if (contents is null) return false; 30 | 31 | foreach (var product in contents) 32 | { 33 | if (product?["productId"]?.ToString() != "umamusume") continue; 34 | string? installDir = product?["detail"]?["path"]?.ToString(); 35 | if (installDir is null) return false; 36 | string dataDir = Path.Combine(installDir, "umamusume_Data", "Persistent"); 37 | if (CheckDirectory(dataDir)) 38 | { 39 | DataDirectoryPath = dataDir; 40 | return true; 41 | } 42 | else 43 | return false; 44 | } 45 | 46 | return false; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /UmamusumeExplorer/SplashForm.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeData; 2 | using UmamusumeExplorer.Assets; 3 | using UmamusumeExplorer.Controls; 4 | 5 | namespace UmamusumeExplorer 6 | { 7 | public partial class SplashForm : BorderlessAeroForm 8 | { 9 | private Thread? loadThread; 10 | private bool loadSuccess = false; 11 | 12 | public SplashForm() 13 | { 14 | InitializeComponent(); 15 | SetBorderlessAndAdjust(); 16 | } 17 | 18 | public bool LoadAndClose() 19 | { 20 | loadThread = new(() => 21 | { 22 | try 23 | { 24 | UmaDataHelper.Initialize(); 25 | AssetTables.UpdateProgress += UpdateProgress; 26 | AssetTables.Initialize(); 27 | AssetTables.UpdateProgress -= UpdateProgress; 28 | Invoke(() => Close()); 29 | loadSuccess = true; 30 | } 31 | catch (Exception e) 32 | { 33 | string message = e.InnerException?.Message ?? e.Message; 34 | MessageBox.Show($"Error reading tables.\n\nMessage:\n{message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 35 | Application.Exit(); 36 | } 37 | }); 38 | ShowDialog(); 39 | 40 | return loadSuccess; 41 | } 42 | 43 | private void UpdateProgress(int progress, string name) 44 | { 45 | Invoke(() => 46 | { 47 | loadingProgressBar.Value = progress; 48 | if (progress < 100) 49 | loadingLabel.Text = $"Loading {name}..."; 50 | else 51 | loadingLabel.Text = "Starting..."; 52 | }); 53 | } 54 | 55 | private void SplashForm_Load(object sender, EventArgs e) 56 | { 57 | loadThread?.Start(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/SkillEvolutionConditionContainer.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeData; 2 | using UmamusumeData.Tables; 3 | using UmamusumeExplorer.Assets; 4 | 5 | namespace UmamusumeExplorer.Controls 6 | { 7 | public partial class SkillEvolutionConditionContainer : UserControl 8 | { 9 | private readonly IEnumerable conditions; 10 | private readonly int defaultHeight; 11 | 12 | public SkillEvolutionConditionContainer(IEnumerable upgradeConditions) 13 | { 14 | InitializeComponent(); 15 | 16 | SkillUpgradeCondition firstCondition = upgradeConditions.First(); 17 | 18 | conditionNumberLabel.Text = $"Condition {firstCondition.Num}"; 19 | conditions = upgradeConditions; 20 | 21 | defaultHeight = conditionNumberLabel.Top + conditionNumberLabel.Height + conditionNumberLabel.Margin.Vertical; 22 | Height = defaultHeight; 23 | 24 | if (upgradeConditions.Count() == 1) 25 | { 26 | conditionDescription.Text = AssetTables.GetText(TextCategory.SkillUpgrade1, firstCondition.Id); 27 | conditionsPanel.Height = 0; 28 | } 29 | } 30 | 31 | private void SkillEvolutionConditionMultiple_Load(object sender, EventArgs e) 32 | { 33 | if (conditions.Count() > 1) 34 | { 35 | conditionsPanel.Height = 0; 36 | foreach (var condition in conditions) 37 | { 38 | SkillEvolutionConditionItem conditionSingle = new(condition); 39 | conditionSingle.Width = conditionsPanel.Width - conditionSingle.Margin.Horizontal; 40 | conditionsPanel.Controls.Add(conditionSingle); 41 | 42 | conditionsPanel.Height += conditionSingle.Height + conditionSingle.Margin.Vertical; 43 | } 44 | 45 | Height += conditionsPanel.Height + conditionsPanel.Margin.Bottom; 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/BorderlessAeroForm.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace UmamusumeExplorer.Controls 4 | { 5 | public class BorderlessAeroForm : Form 6 | { 7 | public void SetBorderlessAndAdjust() 8 | { 9 | FormBorderStyle = FormBorderStyle.FixedSingle; 10 | 11 | Size newSize = Size; 12 | newSize.Height -= SystemInformation.CaptionHeight + (SystemInformation.FrameBorderSize.Height + SystemInformation.VerticalResizeBorderThickness - 1) * 2; 13 | newSize.Width -= (SystemInformation.FrameBorderSize.Width + SystemInformation.HorizontalResizeBorderThickness - 1) * 2; 14 | 15 | Size = newSize; 16 | } 17 | 18 | protected override void WndProc(ref Message m) 19 | { 20 | switch (m.Msg) 21 | { 22 | case 0x0083: // WM_NCCALCSIZE 23 | { 24 | NCCALCSIZE_PARAMS ncp = Marshal.PtrToStructure(m.LParam); 25 | 26 | ncp.rgrc[0].left += 1; 27 | ncp.rgrc[0].top += 1; 28 | ncp.rgrc[0].right += 1; 29 | ncp.rgrc[0].bottom += 1; 30 | 31 | Marshal.StructureToPtr(ncp, m.LParam, true); 32 | 33 | m.Result = 1; 34 | } 35 | break; 36 | default: 37 | base.WndProc(ref m); 38 | break; 39 | } 40 | } 41 | 42 | [StructLayout(LayoutKind.Sequential)] 43 | struct RECT 44 | { 45 | public int left; 46 | public int top; 47 | public int right; 48 | public int bottom; 49 | } 50 | 51 | [StructLayout(LayoutKind.Sequential)] 52 | struct NCCALCSIZE_PARAMS 53 | { 54 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] 55 | public RECT[] rgrc; 56 | public nint lppos; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /UmamusumeData/Tables/JukeboxMusicData.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | 3 | namespace UmamusumeData.Tables 4 | { 5 | [Table("jukebox_music_data")] 6 | public class JukeboxMusicData 7 | { 8 | [Column("music_id"), NotNull, PrimaryKey] 9 | public int MusicId { get; set; } 10 | 11 | [Column("sort"), NotNull] 12 | public int Sort { get; set; } 13 | 14 | [Column("condition_type"), NotNull] 15 | public int ConditionType { get; set; } 16 | 17 | [Column("is_hidden"), NotNull] 18 | public int IsHidden { get; set; } 19 | 20 | [Column("version_type"), NotNull] 21 | public int VersionType { get; set; } 22 | 23 | [Column("request_type"), NotNull] 24 | public int RequestType { get; set; } 25 | 26 | [Column("lamp_color"), NotNull] 27 | public int LampColor { get; set; } 28 | 29 | [Column("lamp_animation"), NotNull] 30 | public int LampAnimation { get; set; } 31 | 32 | [Column("name_texture_length"), NotNull] 33 | public int NameTextureLength { get; set; } 34 | 35 | [Column("song_type"), NotNull] 36 | public int SongType { get; set; } 37 | 38 | [Column("bgm_cue_name_short"), NotNull] 39 | public string BgmCueNameShort { get; set; } = ""; 40 | 41 | [Column("bgm_cuesheet_name_short"), NotNull] 42 | public string BgmCuesheetNameShort { get; set; } = ""; 43 | 44 | [Column("bgm_cue_name_gamesize"), NotNull] 45 | public string BgmCueNameGamesize { get; set; } = ""; 46 | 47 | [Column("bgm_cuesheet_name_gamesize"), NotNull] 48 | public string BgmCuesheetNameGamesize { get; set; } = ""; 49 | 50 | [Column("short_length"), NotNull] 51 | public int ShortLength { get; set; } 52 | 53 | [Column("alter_jacket"), NotNull] 54 | public int AlterJacket { get; set; } 55 | 56 | [Column("start_date"), NotNull] 57 | public int StartDate { get; set; } 58 | 59 | [Column("end_date"), NotNull] 60 | public int EndDate { get; set; } 61 | } 62 | } -------------------------------------------------------------------------------- /UmamusumeData/Tables/LiveData.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | 3 | namespace UmamusumeData.Tables 4 | { 5 | [Table("live_data")] 6 | public class LiveData 7 | { 8 | [Column("music_id"), NotNull, PrimaryKey] 9 | public int MusicId { get; set; } 10 | 11 | [Column("sort"), NotNull] 12 | public int Sort { get; set; } 13 | 14 | [Column("music_type"), NotNull] 15 | public int MusicType { get; set; } 16 | 17 | [Column("title_color_top"), NotNull] 18 | public string TitleColorTop { get; set; } = ""; 19 | 20 | [Column("title_color_bottom"), NotNull] 21 | public string TitleColorBottom { get; set; } = ""; 22 | 23 | [Column("condition_type"), NotNull] 24 | public int ConditionType { get; set; } 25 | 26 | [Column("song_chara_type"), NotNull] 27 | public int SongCharaType { get; set; } 28 | 29 | [Column("live_member_number"), NotNull] 30 | public int LiveMemberNumber { get; set; } 31 | 32 | [Column("default_main_dress"), NotNull] 33 | public int DefaultMainDress { get; set; } 34 | 35 | [Column("default_main_dress_color"), NotNull] 36 | public int DefaultMainDressColor { get; set; } 37 | 38 | [Column("default_mob_dress"), NotNull] 39 | public int DefaultMobDress { get; set; } 40 | 41 | [Column("default_mob_dress_color"), NotNull] 42 | public int DefaultMobDressColor { get; set; } 43 | 44 | [Column("backdancer_order"), NotNull] 45 | public int BackdancerOrder { get; set; } 46 | 47 | [Column("backdancer_dress"), NotNull] 48 | public int BackdancerDress { get; set; } 49 | 50 | [Column("backdancer_dress_color"), NotNull] 51 | public int BackdancerDressColor { get; set; } 52 | 53 | [Column("has_live"), NotNull] 54 | public int HasLive { get; set; } 55 | 56 | [Column("start_date"), NotNull] 57 | public int StartDate { get; set; } 58 | 59 | [Column("end_date"), NotNull] 60 | public int EndDate { get; set; } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/ControlHelpers.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Controls 2 | { 3 | static class ControlHelpers 4 | { 5 | private static List
OpenedForms { get; } = new(); 6 | 7 | public static void CloseAllForms() 8 | { 9 | OpenedForms.ForEach(form => 10 | { 11 | form.FormClosed -= FormClosed; 12 | form.Close(); 13 | }); 14 | 15 | OpenedForms.Clear(); 16 | } 17 | 18 | public static DialogResult ShowFormDialogCenter(Form form, Control source) 19 | { 20 | SetNewLocation(form, source); 21 | Form parentForm = GetParentForm(source); 22 | form.Icon = parentForm.Icon; 23 | return form.ShowDialog(parentForm); 24 | } 25 | 26 | public static void ShowFormCenter(Form form, Control source) 27 | { 28 | SetNewLocation(form, source); 29 | form.Icon = GetParentForm(source).Icon; 30 | form.FormClosed += FormClosed; 31 | form.Show(); 32 | OpenedForms.Add(form); 33 | } 34 | 35 | private static void SetNewLocation(Form form, Control source) 36 | { 37 | Form parentForm = GetParentForm(source); 38 | 39 | int left = parentForm.Left; 40 | int top = parentForm.Top; 41 | 42 | left += parentForm.Width / 2 - form.Width / 2; 43 | top += parentForm.Height / 2 - form.Height / 2; 44 | 45 | form.Left = left; 46 | form.Top = top; 47 | } 48 | 49 | public static Form GetParentForm(Control? control) 50 | { 51 | if (control is Form parentForm) 52 | { 53 | return parentForm; 54 | } 55 | else return GetParentForm(control?.Parent); 56 | } 57 | 58 | private static void FormClosed(object? sender, FormClosedEventArgs e) 59 | { 60 | if (sender is not Form form) return; 61 | OpenedForms.Remove(form); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /UmamusumeExplorer/UmamusumeExplorer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WinExe 5 | net8.0-windows 6 | enable 7 | true 8 | enable 9 | UmamusumeExplorer.Program 10 | Resources\ProgramIcon.ico 11 | 1.3.6.0 12 | 1.3.6.0 13 | Umamusume Explorer 14 | Umamusume Explorer 15 | Jacob Tarun 16 | en-US 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | True 40 | True 41 | Resources.resx 42 | 43 | 44 | 45 | 46 | 47 | ResXFileCodeGenerator 48 | Resources.Designer.cs 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/CharacterSelectForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Controls 2 | { 3 | partial class CharacterSelectForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CharacterSelectForm)); 32 | characterItemsPanel = new CharacterItemsPanel(); 33 | SuspendLayout(); 34 | // 35 | // characterItemsPanel 36 | // 37 | resources.ApplyResources(characterItemsPanel, "characterItemsPanel"); 38 | characterItemsPanel.Items = null; 39 | characterItemsPanel.Name = "characterItemsPanel"; 40 | // 41 | // CharacterSelectForm 42 | // 43 | resources.ApplyResources(this, "$this"); 44 | AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 45 | Controls.Add(characterItemsPanel); 46 | Name = "CharacterSelectForm"; 47 | Load += CharacterSelectForm_Load; 48 | ResumeLayout(false); 49 | } 50 | 51 | #endregion 52 | 53 | private CharacterItemsPanel characterItemsPanel; 54 | } 55 | } -------------------------------------------------------------------------------- /UmamusumeData/Tables/SkillSet.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | 3 | namespace UmamusumeData.Tables 4 | { 5 | [Table("skill_set")] 6 | public class SkillSet 7 | { 8 | [Column("id"), NotNull, PrimaryKey] 9 | public int Id { get; set; } 10 | 11 | [Column("skill_id1"), NotNull] 12 | public int SkillId1 { get; set; } 13 | 14 | [Column("skill_level1"), NotNull] 15 | public int SkillLevel1 { get; set; } 16 | 17 | [Column("skill_id2"), NotNull] 18 | public int SkillId2 { get; set; } 19 | 20 | [Column("skill_level2"), NotNull] 21 | public int SkillLevel2 { get; set; } 22 | 23 | [Column("skill_id3"), NotNull] 24 | public int SkillId3 { get; set; } 25 | 26 | [Column("skill_level3"), NotNull] 27 | public int SkillLevel3 { get; set; } 28 | 29 | [Column("skill_id4"), NotNull] 30 | public int SkillId4 { get; set; } 31 | 32 | [Column("skill_level4"), NotNull] 33 | public int SkillLevel4 { get; set; } 34 | 35 | [Column("skill_id5"), NotNull] 36 | public int SkillId5 { get; set; } 37 | 38 | [Column("skill_level5"), NotNull] 39 | public int SkillLevel5 { get; set; } 40 | 41 | [Column("skill_id6"), NotNull] 42 | public int SkillId6 { get; set; } 43 | 44 | [Column("skill_level6"), NotNull] 45 | public int SkillLevel6 { get; set; } 46 | 47 | [Column("skill_id7"), NotNull] 48 | public int SkillId7 { get; set; } 49 | 50 | [Column("skill_level7"), NotNull] 51 | public int SkillLevel7 { get; set; } 52 | 53 | [Column("skill_id8"), NotNull] 54 | public int SkillId8 { get; set; } 55 | 56 | [Column("skill_level8"), NotNull] 57 | public int SkillLevel8 { get; set; } 58 | 59 | [Column("skill_id9"), NotNull] 60 | public int SkillId9 { get; set; } 61 | 62 | [Column("skill_level9"), NotNull] 63 | public int SkillLevel9 { get; set; } 64 | 65 | [Column("skill_id10"), NotNull] 66 | public int SkillId10 { get; set; } 67 | 68 | [Column("skill_level10"), NotNull] 69 | public int SkillLevel10 { get; set; } 70 | } 71 | } -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/SkillEvolutionDetailsForm.cs: -------------------------------------------------------------------------------- 1 | using System.Data; 2 | using UmamusumeData.Tables; 3 | using UmamusumeExplorer.Assets; 4 | 5 | namespace UmamusumeExplorer.Controls 6 | { 7 | public partial class SkillEvolutionDetailsForm : Form 8 | { 9 | private readonly IEnumerable skillUpgradeConditions = AssetTables.SkillUpgradeConditions; 10 | private readonly int evolutionSkillId; 11 | 12 | public SkillEvolutionDetailsForm(SkillData skill, SkillData? evolutionSkill) 13 | { 14 | InitializeComponent(); 15 | 16 | toEvolveSkill.Skill = skill; 17 | evolvedSkill.Skill = evolutionSkill; 18 | 19 | evolutionSkillId = evolutionSkill?.Id ?? 0; 20 | } 21 | 22 | private void SkillEvolutionDetailsForm_Load(object sender, EventArgs e) 23 | { 24 | IEnumerable matchingConditions = skillUpgradeConditions.Where(s => s.DescriptionId == evolutionSkillId); 25 | 26 | int previousNum = matchingConditions.First().Num; 27 | List currentConditions = new(); 28 | foreach (var condition in matchingConditions) 29 | { 30 | if (condition.Num != previousNum) 31 | { 32 | AddConditions(currentConditions); 33 | currentConditions.Clear(); 34 | } 35 | 36 | currentConditions.Add(condition); 37 | previousNum = condition.Num; 38 | } 39 | 40 | AddConditions(currentConditions); 41 | } 42 | 43 | private void AddConditions(IEnumerable currentConditions) 44 | { 45 | SkillEvolutionConditionContainer conditionMultiple = new(currentConditions); 46 | conditionMultiple.Width = evolveConditionsPanel.Width - conditionMultiple.Margin.Horizontal - SystemInformation.VerticalScrollBarWidth; 47 | evolveConditionsPanel.Controls.Add(conditionMultiple); 48 | } 49 | 50 | private void CloseButton_Click(object sender, EventArgs e) 51 | { 52 | Close(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/SongsControl.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Controls 2 | { 3 | partial class SongsControl 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | songItemsPanel = new SongItemsPanel(); 32 | SuspendLayout(); 33 | // 34 | // songItemsPanel 35 | // 36 | songItemsPanel.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; 37 | songItemsPanel.Items = null; 38 | songItemsPanel.Location = new System.Drawing.Point(3, 3); 39 | songItemsPanel.Name = "songItemsPanel"; 40 | songItemsPanel.Size = new System.Drawing.Size(444, 545); 41 | songItemsPanel.TabIndex = 0; 42 | // 43 | // SongsControl 44 | // 45 | AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); 46 | AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 47 | Controls.Add(songItemsPanel); 48 | Name = "SongsControl"; 49 | Size = new System.Drawing.Size(450, 551); 50 | Load += SongsControl_Load; 51 | ResumeLayout(false); 52 | } 53 | 54 | #endregion 55 | 56 | private SongItemsPanel songItemsPanel; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Assets/EncryptedAssetStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace UmamusumeExplorer.Assets 8 | { 9 | internal class EncryptedAssetStream : FileStream 10 | { 11 | private const int headerSize = 256; 12 | private static readonly byte[] baseKeys = 13 | { 14 | 0x53, 0x2B, 0x46, 0x31, 0xE4, 0xA7, 0xB9, 0x47, 0x3E, 0x7C, 0xFB 15 | }; 16 | 17 | private readonly byte[] keys; 18 | 19 | public EncryptedAssetStream(string fileName, long key) : base(fileName, FileMode.Open, FileAccess.Read) 20 | { 21 | keys = new byte[baseKeys.Length * 8]; 22 | 23 | byte[] keyBytes = BitConverter.GetBytes(key); 24 | 25 | for (int i = 0; i < baseKeys.Length; i++) 26 | { 27 | byte baseKey = baseKeys[i]; 28 | 29 | for (int j = 0; j < 8; j++) 30 | { 31 | int index = j + (i * 8); 32 | keys[index] = (byte)(baseKey ^ keyBytes[j]); 33 | } 34 | } 35 | } 36 | 37 | public override void CopyTo(Stream destination, int bufferSize) 38 | { 39 | byte[] buffer = new byte[bufferSize]; 40 | int read; 41 | while ((read = Read(buffer, 0, buffer.Length)) != 0) 42 | { 43 | destination.Write(buffer, 0, read); 44 | } 45 | } 46 | 47 | public override int Read(byte[] buffer, int offset, int count) 48 | { 49 | int read = base.Read(buffer, offset, count); 50 | 51 | if (read > 0) 52 | { 53 | int bytesPos = (int)Position - read; 54 | if (bytesPos < headerSize) 55 | { 56 | int bytesPosOffset = offset - bytesPos; 57 | bytesPos = headerSize; 58 | offset = bytesPosOffset + headerSize; 59 | } 60 | for (int i = offset; i < read; i++) 61 | { 62 | buffer[i] = (byte)(buffer[i] ^ keys[bytesPos++ % keys.Length]); 63 | } 64 | } 65 | 66 | return read; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/SupportCardsControl.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Controls 2 | { 3 | partial class SupportCardsControl 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | supportCardItemsPanel = new UmamusumeExplorer.Controls.SupportCardItemsPanel(); 32 | SuspendLayout(); 33 | // 34 | // supportCardItemsPanel 35 | // 36 | supportCardItemsPanel.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; 37 | supportCardItemsPanel.Indeterminate = false; 38 | supportCardItemsPanel.Items = null; 39 | supportCardItemsPanel.Location = new Point(3, 3); 40 | supportCardItemsPanel.Name = "supportCardItemsPanel"; 41 | supportCardItemsPanel.Size = new Size(432, 605); 42 | supportCardItemsPanel.TabIndex = 0; 43 | // 44 | // SupportCardsControl 45 | // 46 | AutoScaleDimensions = new SizeF(7F, 15F); 47 | AutoScaleMode = AutoScaleMode.Font; 48 | Controls.Add(supportCardItemsPanel); 49 | Name = "SupportCardsControl"; 50 | Size = new Size(438, 611); 51 | Load += SupportCardsControl_Load; 52 | ResumeLayout(false); 53 | } 54 | 55 | #endregion 56 | 57 | private SupportCardItemsPanel supportCardItemsPanel; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/CharacterSelectForm.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Security; 2 | using UmamusumeData.Tables; 3 | using UmamusumeExplorer.Assets; 4 | using UmamusumeExplorer.Music.Live; 5 | using Color = System.Drawing.Color; 6 | 7 | namespace UmamusumeExplorer.Controls 8 | { 9 | partial class CharacterSelectForm : Form 10 | { 11 | private readonly IEnumerable charaDatas = AssetTables.CharaDatas; 12 | private readonly IEnumerable livePermissionData; 13 | private readonly List allowedCharas = new(); 14 | private readonly List alreadySelected = new(); 15 | 16 | public CharacterSelectForm(IEnumerable permissionData, CharacterPosition[] characters, int currentCharacterId) 17 | { 18 | InitializeComponent(); 19 | 20 | livePermissionData = permissionData; 21 | foreach (var lpd in livePermissionData) 22 | { 23 | allowedCharas.Add(lpd.CharaId); 24 | } 25 | 26 | foreach (var character in characters) 27 | { 28 | alreadySelected.Add(character.CharacterId); 29 | } 30 | 31 | characterItemsPanel.Filter = (cd) => 32 | { 33 | return allowedCharas.Contains(cd.Id); 34 | }; 35 | characterItemsPanel.LoadingFinished += (s, e) => 36 | { 37 | foreach (PictureBox charaIcon in characterItemsPanel.Controls) 38 | { 39 | if (charaIcon.Tag as CharaData is not CharaData cd) return; 40 | if (alreadySelected.Contains(cd.Id)) charaIcon.BackColor = Color.FromArgb(234, 54, 128); 41 | if (cd.Id == currentCharacterId) charaIcon.BackColor = Color.FromArgb(247, 175, 204); 42 | } 43 | }; 44 | characterItemsPanel.ItemClick += (s, e) => 45 | { 46 | if (s is not PictureBox pictureBox) return; 47 | if (pictureBox.Tag is not CharaData cd) return; 48 | SelectedCharacter = cd.Id; 49 | Close(); 50 | }; 51 | } 52 | 53 | public int SelectedCharacter { get; private set; } = 0; 54 | 55 | private void CharacterSelectForm_Load(object sender, EventArgs e) 56 | { 57 | characterItemsPanel.Items = charaDatas; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/SkillEvolutionConditionItem.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Controls 2 | { 3 | partial class SkillEvolutionConditionItem 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | conditionLabel = new System.Windows.Forms.Label(); 32 | SuspendLayout(); 33 | // 34 | // conditionLabel 35 | // 36 | conditionLabel.AutoSize = true; 37 | conditionLabel.Location = new System.Drawing.Point(15, 15); 38 | conditionLabel.Margin = new System.Windows.Forms.Padding(15, 15, 15, 0); 39 | conditionLabel.Name = "conditionLabel"; 40 | conditionLabel.Size = new System.Drawing.Size(60, 15); 41 | conditionLabel.TabIndex = 2; 42 | conditionLabel.Text = "Condition"; 43 | // 44 | // SkillEvolutionConditionSingle 45 | // 46 | AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); 47 | AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 48 | AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; 49 | BackColor = System.Drawing.SystemColors.ControlLight; 50 | Controls.Add(conditionLabel); 51 | Name = "SkillEvolutionConditionSingle"; 52 | Size = new System.Drawing.Size(352, 45); 53 | ResumeLayout(false); 54 | PerformLayout(); 55 | } 56 | 57 | #endregion 58 | 59 | private System.Windows.Forms.Label conditionLabel; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/SkillColorGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing.Drawing2D; 2 | using Color = System.Drawing.Color; 3 | using Rectangle = System.Drawing.Rectangle; 4 | 5 | namespace UmamusumeExplorer.Controls 6 | { 7 | internal static class SkillColorGenerator 8 | { 9 | public static Brush ColorFromType(SkillBackground rarity, Rectangle colorBounds, out Brush backgroundBrush) 10 | { 11 | Color[] colors = { Color.White, Color.LightSteelBlue }; 12 | float[] positions = { 0F, 1F }; 13 | 14 | backgroundBrush = new SolidBrush(Color.LightSteelBlue); 15 | 16 | bool duplicateBackground = false; 17 | 18 | switch (rarity) 19 | { 20 | case SkillBackground.Rarity2: 21 | colors = 22 | [ 23 | Color.LightYellow, 24 | Color.Gold 25 | ]; 26 | backgroundBrush = new SolidBrush(Color.Gold); 27 | break; 28 | case SkillBackground.Rarity3: 29 | case SkillBackground.Rarity4: 30 | case SkillBackground.Rarity5: 31 | colors = [ 32 | Color.LightGreen, 33 | Color.DeepSkyBlue, 34 | Color.BlueViolet, 35 | Color.DeepPink 36 | ]; 37 | positions = [0F, 1 / 3F, 2 / 3F, 1F]; 38 | duplicateBackground = true; 39 | break; 40 | case SkillBackground.Evolution: 41 | colors = 42 | [ 43 | Color.Pink, 44 | Color.HotPink 45 | ]; 46 | backgroundBrush = new SolidBrush(Color.HotPink); 47 | break; 48 | default: 49 | break; 50 | } 51 | 52 | LinearGradientBrush linearGradientBrush = new(colorBounds, Color.White, Color.White, LinearGradientMode.Horizontal); 53 | ColorBlend colorBlend = new() 54 | { 55 | Positions = positions, 56 | Colors = colors 57 | }; 58 | linearGradientBrush.InterpolationColors = colorBlend; 59 | 60 | if (duplicateBackground) 61 | backgroundBrush = linearGradientBrush; 62 | 63 | return linearGradientBrush; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Tools/LiveMusicPlayerCli/PartTrigger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LiveMusicPlayerCli 4 | { 5 | class PartTrigger 6 | { 7 | public int TimeMs { get; } 8 | 9 | public int[] MemberTracks { get; } 10 | 11 | public float[] MemberVolumes { get; } 12 | 13 | public float[] MemberPans { get; } 14 | 15 | public float VolumeRate { get; } 16 | 17 | public PartTrigger(string partCsvLine, bool hasVolumeRate) 18 | { 19 | string[] columns = partCsvLine.Split(','); 20 | 21 | int activeMembers; 22 | 23 | int ignoreColumns = hasVolumeRate ? 2 : 1; 24 | 25 | if (columns.Length - ignoreColumns > 5) 26 | activeMembers = (columns.Length - ignoreColumns) / 3; 27 | else 28 | activeMembers = columns.Length - ignoreColumns; 29 | 30 | MemberTracks = new int[activeMembers]; 31 | MemberVolumes = new float[activeMembers]; 32 | MemberPans = new float[activeMembers]; 33 | 34 | int currentIndex = 0; 35 | TimeMs = int.Parse(columns[currentIndex++]); 36 | 37 | int pivot = activeMembers / 2; 38 | 39 | for (int i = 0; i < activeMembers; i++) 40 | { 41 | MemberTracks[PositionToIndex(i, pivot)] = int.Parse(columns[currentIndex++]); 42 | } 43 | 44 | if (columns.Length - 1 > 5) 45 | { 46 | for (int i = 0; i < activeMembers; i++) 47 | { 48 | MemberVolumes[PositionToIndex(i, pivot)] = float.Parse(columns[currentIndex++]); 49 | } 50 | for (int i = 0; i < activeMembers; i++) 51 | { 52 | MemberPans[PositionToIndex(i, pivot)] = float.Parse(columns[currentIndex++]); 53 | } 54 | } 55 | else 56 | { 57 | for (int i = 0; i < activeMembers; i++) 58 | { 59 | MemberVolumes[i] = 1.0F; 60 | MemberPans[i] = 999; 61 | } 62 | } 63 | 64 | if (hasVolumeRate) 65 | VolumeRate = float.Parse(columns[currentIndex++]); 66 | } 67 | 68 | private static int PositionToIndex(int position, int pivot) 69 | { 70 | int distance = Math.Abs(pivot - position); 71 | int multiply = distance * 2; 72 | if (position < pivot) multiply -= 1; 73 | return multiply; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Assets/LoadingForm.cs: -------------------------------------------------------------------------------- 1 | using AssetsTools.NET.Extra; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Data; 6 | using System.Drawing; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Windows.Forms; 11 | 12 | namespace UmamusumeExplorer.Controls 13 | { 14 | public partial class LoadingForm : Form 15 | { 16 | private readonly AssetsManager assetsManager; 17 | private readonly IEnumerable filePaths; 18 | 19 | public LoadingForm(AssetsManager assetsManager, IEnumerable filePaths) 20 | { 21 | InitializeComponent(); 22 | 23 | this.assetsManager = assetsManager; 24 | this.filePaths = filePaths; 25 | } 26 | 27 | private void LoadingBackgroundWorker_DoWork(object sender, DoWorkEventArgs e) 28 | { 29 | if (sender is not BackgroundWorker worker) return; 30 | 31 | List<(BundleFileInstance bundle, string name)> filesAndBundles = new(); 32 | 33 | Invoke(() => statusLabel.Text = "Loading AssetBundles..."); 34 | int currentItem = 1; 35 | foreach (var file in filePaths) 36 | { 37 | BundleFileInstance bundle = assetsManager.LoadBundleFile(file); 38 | foreach (var assetsFile in bundle.file.GetAllFileNames()) 39 | { 40 | filesAndBundles.Add((bundle, assetsFile)); 41 | } 42 | worker.ReportProgress((int)((float)currentItem++ / filePaths.Count() * 100F)); 43 | } 44 | 45 | Invoke(() => statusLabel.Text = "Loading assets..."); 46 | currentItem = 1; 47 | foreach (var (bundle, name) in filesAndBundles) 48 | { 49 | assetsManager.LoadAssetsFileFromBundle(bundle, name); 50 | worker.ReportProgress((int)((float)currentItem++ / filesAndBundles.Count * 100F)); 51 | } 52 | } 53 | 54 | private void LoadingBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) 55 | { 56 | loadingProgressBar.Value = e.ProgressPercentage; 57 | } 58 | 59 | private void LoadingBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 60 | { 61 | Close(); 62 | } 63 | 64 | private void LoadingForm_Load(object sender, EventArgs e) 65 | { 66 | loadingBackgroundWorker.RunWorkerAsync(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Music/Live/PartTrigger.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Music.Live 2 | { 3 | class PartTrigger 4 | { 5 | public int TimeMs { get; } 6 | 7 | public int[] MemberTracks { get; } 8 | 9 | public float[] MemberVolumes { get; } 10 | 11 | public float[] MemberPans { get; } 12 | 13 | public float VolumeRate { get; } 14 | 15 | public PartTrigger(string partCsvLine, bool hasVolumeRate) 16 | { 17 | string[] columns = partCsvLine.Split(','); 18 | 19 | int activeMembers; 20 | 21 | int ignoreColumns = hasVolumeRate ? 2 : 1; 22 | 23 | if (columns.Length - ignoreColumns > 5) 24 | activeMembers = (columns.Length - ignoreColumns) / 3; 25 | else 26 | activeMembers = columns.Length - ignoreColumns; 27 | 28 | MemberTracks = new int[activeMembers]; 29 | MemberVolumes = new float[activeMembers]; 30 | MemberPans = new float[activeMembers]; 31 | 32 | int currentIndex = 0; 33 | TimeMs = int.Parse(columns[currentIndex++]); 34 | 35 | int pivot = activeMembers / 2; 36 | 37 | for (int i = 0; i < activeMembers; i++) 38 | { 39 | MemberTracks[PositionToIndex(i, pivot)] = int.Parse(columns[currentIndex++]); 40 | } 41 | 42 | if (columns.Length - 1 > 5) 43 | { 44 | for (int i = 0; i < activeMembers; i++) 45 | { 46 | MemberVolumes[PositionToIndex(i, pivot)] = float.Parse(columns[currentIndex++]); 47 | } 48 | for (int i = 0; i < activeMembers; i++) 49 | { 50 | MemberPans[PositionToIndex(i, pivot)] = float.Parse(columns[currentIndex++]); 51 | } 52 | } 53 | else 54 | { 55 | for (int i = 0; i < activeMembers; i++) 56 | { 57 | MemberVolumes[i] = 999F; 58 | MemberPans[i] = 999F; 59 | } 60 | } 61 | 62 | if (hasVolumeRate) 63 | VolumeRate = float.Parse(columns[currentIndex++]); 64 | else 65 | VolumeRate = 999F; 66 | } 67 | 68 | private static int PositionToIndex(int position, int pivot) 69 | { 70 | int distance = Math.Abs(pivot - position); 71 | int multiply = distance * 2; 72 | if (position < pivot) multiply -= 1; 73 | return multiply; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /UmamusumeData/Tables/CardRarityData.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | 3 | namespace UmamusumeData.Tables 4 | { 5 | [Table("card_rarity_data")] 6 | public class CardRarityData 7 | { 8 | [Column("id"), NotNull, PrimaryKey] 9 | public int Id { get; set; } 10 | 11 | [Column("card_id"), NotNull] 12 | public int CardId { get; set; } 13 | 14 | [Column("rarity"), NotNull] 15 | public int Rarity { get; set; } 16 | 17 | [Column("race_dress_id"), NotNull] 18 | public int RaceDressId { get; set; } 19 | 20 | [Column("skill_set"), NotNull] 21 | public int SkillSet { get; set; } 22 | 23 | [Column("speed"), NotNull] 24 | public int Speed { get; set; } 25 | [Column("stamina"), NotNull] 26 | public int Stamina { get; set; } 27 | [Column("pow"), NotNull] 28 | public int Pow { get; set; } 29 | [Column("guts"), NotNull] 30 | public int Guts { get; set; } 31 | [Column("wiz"), NotNull] 32 | public int Wiz { get; set; } 33 | 34 | [Column("max_speed"), NotNull] 35 | public int MaxSpeed { get; set; } 36 | [Column("max_stamina"), NotNull] 37 | public int MaxStamina { get; set; } 38 | [Column("max_pow"), NotNull] 39 | public int MaxPow { get; set; } 40 | [Column("max_guts"), NotNull] 41 | public int MaxGuts { get; set; } 42 | [Column("max_wiz"), NotNull] 43 | public int MaxWiz { get; set; } 44 | 45 | [Column("proper_distance_short"), NotNull] 46 | public int ProperDistanceShort { get; set; } 47 | [Column("proper_distance_mile"), NotNull] 48 | public int ProperDistanceMile { get; set; } 49 | [Column("proper_distance_middle"), NotNull] 50 | public int ProperDistanceMiddle { get; set; } 51 | [Column("proper_distance_long"), NotNull] 52 | public int ProperDistanceLong { get; set; } 53 | 54 | [Column("proper_running_style_nige"), NotNull] 55 | public int ProperRunningStyleNige { get; set; } 56 | [Column("proper_running_style_senko"), NotNull] 57 | public int ProperRunningStyleSenko { get; set; } 58 | [Column("proper_running_style_sashi"), NotNull] 59 | public int ProperRunningStyleSashi { get; set; } 60 | [Column("proper_running_style_oikomi"), NotNull] 61 | public int ProperRunningStyleOikomi { get; set; } 62 | 63 | [Column("proper_ground_turf"), NotNull] 64 | public int ProperGroundTurf { get; set; } 65 | [Column("proper_ground_dirt"), NotNull] 66 | public int ProperGroundDirt { get; set; } 67 | 68 | [Column("get_dress_id_1"), NotNull] 69 | public int GetDressId1 { get; set; } 70 | [Column("get_dress_id_2"), NotNull] 71 | public int GetDressId2 { get; set; } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Pages/LiveMusicPlayerControl.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Pages 2 | { 3 | partial class LiveMusicPlayerControl 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | songItemsPanel = new Controls.SongItemsPanel(); 32 | linkLabelDownload = new LinkLabel(); 33 | SuspendLayout(); 34 | // 35 | // songItemsPanel 36 | // 37 | songItemsPanel.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; 38 | songItemsPanel.Indeterminate = false; 39 | songItemsPanel.Items = null; 40 | songItemsPanel.Location = new Point(3, 18); 41 | songItemsPanel.Name = "songItemsPanel"; 42 | songItemsPanel.Size = new Size(815, 507); 43 | songItemsPanel.TabIndex = 0; 44 | // 45 | // linkLabelDownload 46 | // 47 | linkLabelDownload.AutoSize = true; 48 | linkLabelDownload.Location = new Point(3, 0); 49 | linkLabelDownload.Name = "linkLabelDownload"; 50 | linkLabelDownload.Size = new Size(143, 15); 51 | linkLabelDownload.TabIndex = 2; 52 | linkLabelDownload.TabStop = true; 53 | linkLabelDownload.Text = "Live audio data download"; 54 | linkLabelDownload.LinkClicked += LinkLabelDownload_LinkClicked; 55 | // 56 | // LiveMusicPlayerControl 57 | // 58 | AutoScaleMode = AutoScaleMode.Inherit; 59 | Controls.Add(linkLabelDownload); 60 | Controls.Add(songItemsPanel); 61 | Name = "LiveMusicPlayerControl"; 62 | Size = new Size(821, 528); 63 | Load += LiveMusicPlayerSongSelectControl_Load; 64 | ResumeLayout(false); 65 | PerformLayout(); 66 | } 67 | 68 | #endregion 69 | 70 | private Controls.SongItemsPanel songItemsPanel; 71 | private LinkLabel linkLabelDownload; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/RankedLabel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.ComponentModel.Design.Serialization; 3 | using Color = System.Drawing.Color; 4 | 5 | namespace UmamusumeExplorer.Controls 6 | { 7 | partial class RankedLabel : UserControl 8 | { 9 | private AptitudeRank rank; 10 | 11 | public RankedLabel() 12 | { 13 | InitializeComponent(); 14 | 15 | Rank = AptitudeRank.Unknown; 16 | } 17 | 18 | public AptitudeRank Rank 19 | { 20 | get 21 | { 22 | return rank; 23 | } 24 | set 25 | { 26 | rank = value; 27 | 28 | switch (rank) 29 | { 30 | case AptitudeRank.G: 31 | rankLabel.Text = "G"; 32 | rankLabel.ForeColor = Color.DarkGray; 33 | break; 34 | case AptitudeRank.F: 35 | rankLabel.Text = "F"; 36 | rankLabel.ForeColor = Color.SlateBlue; 37 | break; 38 | case AptitudeRank.E: 39 | rankLabel.Text = "E"; 40 | rankLabel.ForeColor = Color.DarkViolet; 41 | break; 42 | case AptitudeRank.D: 43 | rankLabel.Text = "D"; 44 | rankLabel.ForeColor = Color.DeepSkyBlue; 45 | break; 46 | case AptitudeRank.C: 47 | rankLabel.Text = "C"; 48 | rankLabel.ForeColor = Color.LimeGreen; 49 | break; 50 | case AptitudeRank.B: 51 | rankLabel.Text = "B"; 52 | rankLabel.ForeColor = Color.HotPink; 53 | break; 54 | case AptitudeRank.A: 55 | rankLabel.Text = "A"; 56 | rankLabel.ForeColor = Color.OrangeRed; 57 | break; 58 | case AptitudeRank.S: 59 | rankLabel.Text = "S"; 60 | rankLabel.ForeColor = Color.Goldenrod; 61 | break; 62 | default: 63 | rankLabel.Text = "?"; 64 | rankLabel.ForeColor = Color.Black; 65 | break; 66 | } 67 | 68 | Invalidate(); 69 | } 70 | } 71 | 72 | //public override string Text { get => base.Text; set => base.Text = value; } 73 | 74 | public string LabelText 75 | { 76 | get 77 | { 78 | return textLabel.Text; 79 | } 80 | set 81 | { 82 | textLabel.Text = value; 83 | Invalidate(); 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/StatusDisplayLabel.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | text/microsoft-resx 50 | 51 | 52 | 2.0 53 | 54 | 55 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 56 | 57 | 58 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 59 | 60 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/SkillLarge.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeData; 2 | using UmamusumeData.Tables; 3 | using UmamusumeExplorer.Assets; 4 | using Color = System.Drawing.Color; 5 | using Rectangle = System.Drawing.Rectangle; 6 | 7 | namespace UmamusumeExplorer.Controls 8 | { 9 | public partial class SkillLarge : UserControl 10 | { 11 | public SkillData? skill; 12 | public SkillData? evolutionSkill; 13 | public SkillBackground background; 14 | 15 | public SkillLarge() 16 | { 17 | InitializeComponent(); 18 | } 19 | 20 | public SkillData? Skill 21 | { 22 | get => skill; 23 | set 24 | { 25 | if (value is null) return; 26 | 27 | skill = value; 28 | UpdateInfo(skill); 29 | 30 | Refresh(); 31 | } 32 | } 33 | 34 | public SkillData? EvolutionSkill 35 | { 36 | get => evolutionSkill; 37 | set 38 | { 39 | if (value is null) return; 40 | 41 | evolutionSkill = value; 42 | UpdateInfo(evolutionSkill); 43 | evolutionInfoButton.Visible = true; 44 | 45 | Refresh(); 46 | } 47 | } 48 | 49 | public SkillBackground Background 50 | { 51 | get => background; 52 | set 53 | { 54 | background = value; 55 | Refresh(); 56 | } 57 | } 58 | 59 | private void EvolutionInfoButton_Click(object sender, EventArgs e) 60 | { 61 | if (skill is null || evolutionSkill is null) return; 62 | ControlHelpers.ShowFormDialogCenter(new SkillEvolutionDetailsForm(skill, evolutionSkill), this); 63 | } 64 | 65 | private void UpdateInfo(SkillData skillData) 66 | { 67 | skillNameLabel.Text = AssetTables.GetText(TextCategory.MasterSkillName, skillData.Id); 68 | skillDescriptionLabel.Text = AssetTables.GetText(TextCategory.MasterSkillExplain, skillData.Id) 69 | .Replace("\\n", "\n"); 70 | iconPictureBox.BackgroundImage = GameAssets.GetSkillIcon(skillData.IconId)?.Bitmap; 71 | } 72 | 73 | protected override void OnPaint(PaintEventArgs e) 74 | { 75 | Brush brush = SkillColorGenerator.ColorFromType(Background, ClientRectangle, out Brush backgroundBrush); 76 | 77 | Rectangle paddedRectangle = ClientRectangle; 78 | paddedRectangle.X += 2; 79 | paddedRectangle.Y += 2; 80 | paddedRectangle.Width -= 4; 81 | paddedRectangle.Height -= 4; 82 | 83 | e.Graphics.FillRectangle(backgroundBrush, ClientRectangle); 84 | e.Graphics.FillRectangle(brush, paddedRectangle); 85 | e.Graphics.FillRectangle( 86 | new SolidBrush(Color.FromArgb(127, 255, 255, 255)), 87 | paddedRectangle); 88 | 89 | base.OnPaint(e); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Tools/Extractor/Program.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | using System; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace Extractor 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | if (args.Length >= 1) 14 | { 15 | string localLow = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "AppData", "LocalLow"); 16 | string umaMusumeDirectory = Path.Combine(localLow, "Cygames", "umamusume"); 17 | 18 | SQLiteConnection connection = new(Path.Combine(umaMusumeDirectory, "meta")); 19 | string dataDirectory = Path.Combine(umaMusumeDirectory, "dat"); 20 | string outputDirectory = args[0]; 21 | 22 | var realFiles = Directory.GetFiles(dataDirectory, "*", SearchOption.AllDirectories).ToList(); 23 | var gameFiles = connection.Table().ToList(); 24 | gameFiles.Sort((gf1, gf2) => { return string.Compare(gf1.Name, gf2.Name); }); 25 | 26 | int total = gameFiles.Count; 27 | int finished = 0; 28 | 29 | object finishedLock = new(); 30 | 31 | Task[] copyTasks = new Task[total]; 32 | int index = 0; 33 | foreach (var gameFile in gameFiles) 34 | { 35 | copyTasks[index] = new Task(() => 36 | { 37 | string dataFileName = gameFile.Hash; 38 | string dataFilePath = Path.Combine(dataDirectory, dataFileName[..2], dataFileName); 39 | string realFileName = gameFile.Name.TrimStart('/'); 40 | if (gameFile.Category.StartsWith("manifest")) realFileName += ".manifest"; 41 | string realFilePath = Path.Combine(outputDirectory, realFileName); 42 | string destinationDirectory; 43 | 44 | if (!string.IsNullOrEmpty(Path.GetDirectoryName(realFileName))) 45 | destinationDirectory = Path.Combine(outputDirectory, Path.GetDirectoryName(realFileName)); 46 | else destinationDirectory = outputDirectory; 47 | 48 | Directory.CreateDirectory(destinationDirectory); 49 | 50 | if (!File.Exists(realFilePath) && File.Exists(dataFilePath)) File.Copy(dataFilePath, realFilePath); 51 | 52 | lock (finishedLock) 53 | { 54 | finished++; 55 | Console.CursorTop = 0; 56 | Console.CursorLeft = 0; 57 | Console.WriteLine($"Copying {((float)finished / total) * 100.0f:f2}% complete"); 58 | } 59 | }); 60 | copyTasks[index].Start(); 61 | 62 | index++; 63 | } 64 | 65 | Task.WaitAll(copyTasks); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /UmamusumeData/Tables/RaceBgm.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | 3 | namespace UmamusumeData.Tables 4 | { 5 | [Table("race_bgm")] 6 | public class RaceBgm 7 | { 8 | [Column("id"), NotNull, PrimaryKey] 9 | public int Id { get; set; } 10 | 11 | [Column("race_type"), NotNull] 12 | public int RaceType { get; set; } 13 | 14 | [Column("race_instance_id"), NotNull] 15 | public int RaceInstanceId { get; set; } 16 | 17 | [Column("grade"), NotNull] 18 | public int Grade { get; set; } 19 | 20 | [Column("single_mode_route_race_id"), NotNull] 21 | public int SingleModeRouteRaceId { get; set; } 22 | 23 | [Column("single_mode_program_id"), NotNull] 24 | public int SingleModeProgramId { get; set; } 25 | 26 | [Column("paddock_bgm_cue_name"), NotNull] 27 | public string PaddockBgmCueName { get; set; } = ""; 28 | 29 | [Column("paddock_bgm_cuesheet_name"), NotNull] 30 | public string PaddockBgmCuesheetName { get; set; } = ""; 31 | 32 | [Column("entrytable_bgm_cue_name"), NotNull] 33 | public string EntrytableBgmCueName { get; set; } = ""; 34 | 35 | [Column("entrytable_bgm_cuesheet_name"), NotNull] 36 | public string EntrytableBgmCuesheetName { get; set; } = ""; 37 | 38 | [Column("first_bgm_pattern"), NotNull] 39 | public int FirstBgmPattern { get; set; } 40 | 41 | [Column("second_bgm_pattern"), NotNull] 42 | public int SecondBgmPattern { get; set; } 43 | 44 | [Column("result_cutin_bgm_cue_name_1"), NotNull] 45 | public string ResultCutinBgmCueName1 { get; set; } = ""; 46 | 47 | [Column("result_cutin_bgm_cuesheet_name_1"), NotNull] 48 | public string ResultCutinBgmCuesheetName1 { get; set; } = ""; 49 | 50 | [Column("result_cutin_bgm_cue_name_2"), NotNull] 51 | public string ResultCutinBgmCueName2 { get; set; } = ""; 52 | 53 | [Column("result_cutin_bgm_cuesheet_name_2"), NotNull] 54 | public string ResultCutinBgmCuesheetName2 { get; set; } = ""; 55 | 56 | [Column("result_cutin_bgm_cue_name_3"), NotNull] 57 | public string ResultCutinBgmCueName3 { get; set; } = ""; 58 | 59 | [Column("result_cutin_bgm_cuesheet_name_3"), NotNull] 60 | public string ResultCutinBgmCuesheetName3 { get; set; } = ""; 61 | 62 | [Column("result_list_bgm_cue_name_1"), NotNull] 63 | public string ResultListBgmCueName1 { get; set; } = ""; 64 | 65 | [Column("result_list_bgm_cuesheet_name_1"), NotNull] 66 | public string ResultListBgmCuesheetName1 { get; set; } = ""; 67 | 68 | [Column("result_list_bgm_cue_name_2"), NotNull] 69 | public string ResultListBgmCueName2 { get; set; } = ""; 70 | 71 | [Column("result_list_bgm_cuesheet_name_2"), NotNull] 72 | public string ResultListBgmCuesheetName2 { get; set; } = ""; 73 | 74 | [Column("result_list_bgm_cue_name_3"), NotNull] 75 | public string ResultListBgmCueName3 { get; set; } = ""; 76 | 77 | [Column("result_list_bgm_cuesheet_name_3"), NotNull] 78 | public string ResultListBgmCuesheetName3 { get; set; } = ""; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Pages/SupportCardInfoControl.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Pages 2 | { 3 | partial class SupportCardInfoControl 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | supportCardItemsPanel = new Controls.SupportCardItemsPanel(); 32 | markLimitedCheckBox = new CheckBox(); 33 | SuspendLayout(); 34 | // 35 | // supportCardItemsPanel 36 | // 37 | supportCardItemsPanel.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; 38 | supportCardItemsPanel.Indeterminate = false; 39 | supportCardItemsPanel.Items = null; 40 | supportCardItemsPanel.Location = new Point(3, 28); 41 | supportCardItemsPanel.Name = "supportCardItemsPanel"; 42 | supportCardItemsPanel.Size = new Size(732, 306); 43 | supportCardItemsPanel.TabIndex = 0; 44 | // 45 | // markLimitedCheckBox 46 | // 47 | markLimitedCheckBox.Anchor = AnchorStyles.Top | AnchorStyles.Right; 48 | markLimitedCheckBox.AutoSize = true; 49 | markLimitedCheckBox.Location = new Point(612, 3); 50 | markLimitedCheckBox.Name = "markLimitedCheckBox"; 51 | markLimitedCheckBox.Size = new Size(123, 19); 52 | markLimitedCheckBox.TabIndex = 1; 53 | markLimitedCheckBox.Text = "Mark special cards"; 54 | markLimitedCheckBox.UseVisualStyleBackColor = true; 55 | markLimitedCheckBox.CheckedChanged += MarkLimitedCheckBox_CheckedChanged; 56 | // 57 | // SupportCardInfoControl 58 | // 59 | AutoScaleDimensions = new SizeF(7F, 15F); 60 | AutoScaleMode = AutoScaleMode.Font; 61 | Controls.Add(markLimitedCheckBox); 62 | Controls.Add(supportCardItemsPanel); 63 | Name = "SupportCardInfoControl"; 64 | Size = new Size(738, 337); 65 | Load += SupportCardInfoControl_Load; 66 | ResumeLayout(false); 67 | PerformLayout(); 68 | } 69 | 70 | #endregion 71 | 72 | private Controls.SupportCardItemsPanel supportCardItemsPanel; 73 | private CheckBox markLimitedCheckBox; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Music/Live/LiveConfigurationManager.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Music.Live 2 | { 3 | static class LiveConfigurationManager 4 | { 5 | private static readonly Dictionary songConfigurations = new(); 6 | 7 | public static void SaveConfiguration(SongConfiguration songConfiguration) 8 | { 9 | songConfigurations.Clear(); 10 | ReadConfigurationFile(); 11 | 12 | songConfigurations[songConfiguration.SongId] = songConfiguration; 13 | WriteConfigurationFile(); 14 | } 15 | 16 | public static SongConfiguration? LoadConfiguration(int songId) 17 | { 18 | songConfigurations.Clear(); 19 | ReadConfigurationFile(); 20 | 21 | if (songConfigurations.TryGetValue(songId, out SongConfiguration? value)) 22 | return value; 23 | 24 | return null; 25 | } 26 | 27 | private static FileStream OpenFile() 28 | { 29 | return new("LiveConfiguration.dat", FileMode.OpenOrCreate, FileAccess.ReadWrite); 30 | } 31 | 32 | private static void ReadConfigurationFile() 33 | { 34 | FileStream configurationFile = OpenFile(); 35 | BinaryReader reader = new(configurationFile); 36 | 37 | reader.BaseStream.Position = 0; 38 | 39 | if (reader.BaseStream.Length == 0) 40 | { 41 | configurationFile.Dispose(); 42 | return; 43 | } 44 | 45 | int entryCount = reader.ReadInt16(); 46 | for (int i = 0; i < entryCount; i++) 47 | { 48 | int songId = reader.ReadInt32(); 49 | int memberCount = reader.ReadByte(); 50 | int[] members = new int[memberCount]; 51 | for (int j = 0; j < memberCount; j++) 52 | { 53 | members[j] = reader.ReadInt32(); 54 | } 55 | bool sfx = reader.ReadBoolean(); 56 | 57 | SongConfiguration songConfiguration = new(songId, members, sfx); 58 | songConfigurations[songId] = songConfiguration; 59 | } 60 | 61 | configurationFile.Dispose(); 62 | } 63 | 64 | private static void WriteConfigurationFile() 65 | { 66 | FileStream configurationFile = OpenFile(); 67 | BinaryWriter writer = new(configurationFile); 68 | writer.BaseStream.Position = 0; 69 | 70 | writer.Write((short)songConfigurations.Count); 71 | foreach (var songConfiguration in songConfigurations) 72 | { 73 | writer.Write(songConfiguration.Value.SongId); 74 | writer.Write((byte)songConfiguration.Value.Members.Length); 75 | foreach (var member in songConfiguration.Value.Members) 76 | { 77 | writer.Write(member); 78 | } 79 | writer.Write(songConfiguration.Value.Sfx); 80 | } 81 | 82 | configurationFile.Dispose(); 83 | } 84 | } 85 | 86 | record SongConfiguration(int SongId, int[] Members, bool Sfx); 87 | } 88 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/SkillSmall.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeData; 2 | using UmamusumeData.Tables; 3 | using UmamusumeExplorer.Assets; 4 | using Color = System.Drawing.Color; 5 | using Rectangle = System.Drawing.Rectangle; 6 | 7 | namespace UmamusumeExplorer.Controls 8 | { 9 | public partial class SkillSmall : UserControl 10 | { 11 | private int skillLevel; 12 | private SkillBackground rarity; 13 | private IEnumerable? evolveSkill; 14 | 15 | public SkillSmall(SkillData skill) 16 | { 17 | InitializeComponent(); 18 | 19 | skillNameLabel.Text = AssetTables.GetText(TextCategory.MasterSkillName, skill.Id); 20 | iconPictureBox.Image = GameAssets.GetSkillIcon(skill.IconId)?.Bitmap; 21 | 22 | Skill = skill; 23 | 24 | skillLevel = 0; 25 | rarity = SkillBackground.Rarity1; 26 | evolveSkill = null; 27 | 28 | Click += OnSkillClicked; 29 | iconPictureBox.Click += OnSkillClicked; 30 | skillNameLabel.Click += OnSkillClicked; 31 | levelLabel.Click += OnSkillClicked; 32 | } 33 | 34 | public SkillData Skill { get; } 35 | 36 | public int SkillLevel 37 | { 38 | get => skillLevel; 39 | set 40 | { 41 | if (value > 0) 42 | levelLabel.Text = $"Lv{value}"; 43 | else 44 | levelLabel.Text = ""; 45 | 46 | skillLevel = value; 47 | 48 | Invalidate(); 49 | } 50 | } 51 | 52 | public SkillBackground Background 53 | { 54 | get => rarity; 55 | set 56 | { 57 | rarity = value; 58 | Invalidate(); 59 | } 60 | } 61 | 62 | public IEnumerable? EvolveSkill 63 | { 64 | get => evolveSkill; 65 | set 66 | { 67 | evolutionCircle.Visible = value is not null && value.Any(); 68 | evolveSkill = value; 69 | Invalidate(); 70 | } 71 | } 72 | 73 | public event EventHandler? SkillClick; 74 | 75 | private void OnSkillClicked(object? sender, EventArgs e) 76 | { 77 | SkillClick?.Invoke(this, e); 78 | } 79 | 80 | protected override void OnPaint(PaintEventArgs e) 81 | { 82 | Brush mainBrush = SkillColorGenerator.ColorFromType(rarity, ClientRectangle, out Brush backgroundBrush); 83 | 84 | Rectangle paddedRectangle = ClientRectangle; 85 | paddedRectangle.X += 2; 86 | paddedRectangle.Y += 2; 87 | paddedRectangle.Width -= 4; 88 | paddedRectangle.Height -= 4; 89 | 90 | e.Graphics.FillRectangle(backgroundBrush, ClientRectangle); 91 | e.Graphics.FillRectangle(mainBrush, paddedRectangle); 92 | e.Graphics.FillRectangle( 93 | new SolidBrush(Color.FromArgb(127, 255, 255, 255)), 94 | paddedRectangle); 95 | 96 | base.OnPaint(e); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Assets/LoadingForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Controls 2 | { 3 | partial class LoadingForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | loadingProgressBar = new ProgressBar(); 32 | loadingBackgroundWorker = new System.ComponentModel.BackgroundWorker(); 33 | statusLabel = new Label(); 34 | SuspendLayout(); 35 | // 36 | // loadingProgressBar 37 | // 38 | loadingProgressBar.Location = new Point(12, 60); 39 | loadingProgressBar.Name = "loadingProgressBar"; 40 | loadingProgressBar.Size = new Size(497, 25); 41 | loadingProgressBar.TabIndex = 0; 42 | // 43 | // loadingBackgroundWorker 44 | // 45 | loadingBackgroundWorker.WorkerReportsProgress = true; 46 | loadingBackgroundWorker.DoWork += LoadingBackgroundWorker_DoWork; 47 | loadingBackgroundWorker.ProgressChanged += LoadingBackgroundWorker_ProgressChanged; 48 | loadingBackgroundWorker.RunWorkerCompleted += LoadingBackgroundWorker_RunWorkerCompleted; 49 | // 50 | // statusLabel 51 | // 52 | statusLabel.AutoSize = true; 53 | statusLabel.Location = new Point(12, 9); 54 | statusLabel.Name = "statusLabel"; 55 | statusLabel.Size = new Size(132, 15); 56 | statusLabel.TabIndex = 1; 57 | statusLabel.Text = "Loading AssetBundles..."; 58 | // 59 | // LoadingForm 60 | // 61 | AutoScaleDimensions = new SizeF(7F, 15F); 62 | AutoScaleMode = AutoScaleMode.Font; 63 | ClientSize = new Size(521, 97); 64 | Controls.Add(statusLabel); 65 | Controls.Add(loadingProgressBar); 66 | FormBorderStyle = FormBorderStyle.FixedSingle; 67 | MaximizeBox = false; 68 | Name = "LoadingForm"; 69 | StartPosition = FormStartPosition.Manual; 70 | Text = "Loading..."; 71 | Load += LoadingForm_Load; 72 | ResumeLayout(false); 73 | PerformLayout(); 74 | } 75 | 76 | #endregion 77 | 78 | private ProgressBar loadingProgressBar; 79 | private System.ComponentModel.BackgroundWorker loadingBackgroundWorker; 80 | private Label statusLabel; 81 | } 82 | } -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/CharacterPositionControl.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeExplorer.Assets; 2 | using UmamusumeExplorer.Music.Live; 3 | using Image = System.Drawing.Image; 4 | 5 | namespace UmamusumeExplorer.Controls 6 | { 7 | partial class CharacterPositionControl : UserControl 8 | { 9 | private int characterPosition = 0; 10 | private int characterId = 0; 11 | private Image? characterImage; 12 | private bool disabled = false; 13 | private bool showMode = false; 14 | 15 | public CharacterPositionControl(int position, EventHandler clickEventHandler, EventHandler? modeChangedEventHandler, int width = -1) 16 | { 17 | InitializeComponent(); 18 | 19 | characterPosition = position; 20 | 21 | positionIndexLabel.Text = (characterPosition + 1).ToString(); 22 | characterPictureBox.BackgroundImage = GameAssets.GetCharaIcon(0)?.Bitmap; 23 | modeButton.Setup(typeof(TrackMode)); 24 | 25 | float ratio = (float)characterPictureBox.Height / characterPictureBox.Width; 26 | 27 | characterPictureBox.Click += clickEventHandler; 28 | modeButton.StateChanged += modeChangedEventHandler; 29 | 30 | if (width > 0) 31 | { 32 | Width = width; 33 | } 34 | 35 | characterPictureBox.Height = (int)(characterPictureBox.Width * ratio); 36 | modeButton.Top = characterPictureBox.Top + characterPictureBox.Height + 6; 37 | 38 | modeButton.Visible = showMode; 39 | } 40 | 41 | public int Position 42 | { 43 | get { return characterPosition; } 44 | set 45 | { 46 | characterPosition = value; 47 | positionIndexLabel.Text = (value + 1).ToString(); 48 | 49 | Update(); 50 | } 51 | } 52 | 53 | public int CharacterId 54 | { 55 | get { return characterId; } 56 | set 57 | { 58 | characterId = value; 59 | characterPictureBox.BackgroundImage = characterImage = GameAssets.GetCharaIcon(characterId)?.Bitmap; 60 | 61 | Update(); 62 | } 63 | } 64 | 65 | public float FontSize 66 | { 67 | get => positionIndexLabel.Font.Size; 68 | set 69 | { 70 | positionIndexLabel.Font = new Font(positionIndexLabel.Font.Name, value); 71 | } 72 | } 73 | 74 | public bool Disabled 75 | { 76 | get => disabled; 77 | set 78 | { 79 | disabled = value; 80 | characterPictureBox.BackgroundImage = disabled ? null : characterImage; 81 | } 82 | } 83 | 84 | public bool ShowEx 85 | { 86 | get => showMode; 87 | set 88 | { 89 | showMode = value; 90 | modeButton.Visible = showMode; 91 | } 92 | } 93 | 94 | public TrackMode Mode 95 | { 96 | get => modeButton.State is null ? TrackMode.Main : (TrackMode)modeButton.State; 97 | set => modeButton.State = value; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/MultiStateButton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace UmamusumeExplorer.Controls 8 | { 9 | internal class MultiStateButton : Button 10 | { 11 | private readonly List states = []; 12 | private readonly List stateTexts = []; 13 | 14 | private int stateIndex = 0; 15 | 16 | public MultiStateButton() 17 | { 18 | } 19 | 20 | public void Setup(Type statesType, List? stateTexts = null) 21 | { 22 | if (!statesType.IsEnum) 23 | throw new ArgumentException("Should be an enum.", nameof(statesType)); 24 | 25 | states.Clear(); 26 | 27 | Array values = Enum.GetValues(statesType); 28 | for (int i = 0; i < values.Length; i++) 29 | { 30 | if (values.GetValue(i) is Enum enumValue) 31 | states.Add(enumValue); 32 | } 33 | 34 | if (stateTexts is null) 35 | { 36 | this.stateTexts.Clear(); 37 | 38 | Array names = Enum.GetNames(statesType); 39 | for (int i = 0; i < values.Length; i++) 40 | { 41 | if (names.GetValue(i) is string name) 42 | this.stateTexts.Add(name); 43 | } 44 | } 45 | else 46 | { 47 | this.stateTexts.Clear(); 48 | this.stateTexts.AddRange(stateTexts); 49 | } 50 | 51 | stateIndex = 0; 52 | UpdateState(); 53 | } 54 | 55 | public Enum? State 56 | { 57 | get 58 | { 59 | if (stateIndex > states.Count) 60 | return states[stateIndex]; 61 | 62 | else return null; 63 | } 64 | set 65 | { 66 | if (value is not null) 67 | { 68 | int newStateIndex = states.IndexOf(value); 69 | if (newStateIndex >= 0) 70 | stateIndex = newStateIndex; 71 | UpdateState(); 72 | } 73 | } 74 | } 75 | 76 | protected override void OnClick(EventArgs e) 77 | { 78 | if (states.Count > 0) 79 | { 80 | stateIndex = (stateIndex + 1) % states.Count; 81 | 82 | //if (stateIndex >= states.Count) 83 | // stateIndex = 0; 84 | 85 | UpdateState(); 86 | StateChanged?.Invoke(this, new(states[stateIndex])); 87 | } 88 | } 89 | 90 | private void UpdateState() 91 | { 92 | if (states.Count < 1 || stateTexts.Count < 1) 93 | return; 94 | 95 | Text = stateTexts[stateIndex]; 96 | } 97 | 98 | public event EventHandler? StateChanged; 99 | } 100 | 101 | public class MultiStateButtonEventArgs 102 | { 103 | public MultiStateButtonEventArgs(Enum value) 104 | { 105 | Value = value; 106 | } 107 | 108 | public Enum Value { get; } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /UmamusumeExplorer/SplashForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer 2 | { 3 | partial class SplashForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | titleLabel = new Label(); 32 | loadingLabel = new Label(); 33 | loadingProgressBar = new ProgressBar(); 34 | SuspendLayout(); 35 | // 36 | // titleLabel 37 | // 38 | titleLabel.AutoSize = true; 39 | titleLabel.Font = new Font("Segoe UI", 24F, FontStyle.Bold); 40 | titleLabel.Location = new Point(115, 33); 41 | titleLabel.Name = "titleLabel"; 42 | titleLabel.Size = new Size(352, 45); 43 | titleLabel.TabIndex = 0; 44 | titleLabel.Text = "Umamusume Explorer"; 45 | // 46 | // loadingLabel 47 | // 48 | loadingLabel.Font = new Font("Segoe UI", 15.75F); 49 | loadingLabel.Location = new Point(12, 78); 50 | loadingLabel.Name = "loadingLabel"; 51 | loadingLabel.Size = new Size(559, 30); 52 | loadingLabel.TabIndex = 1; 53 | loadingLabel.Text = "Reading tables..."; 54 | loadingLabel.TextAlign = ContentAlignment.MiddleCenter; 55 | // 56 | // loadingProgressBar 57 | // 58 | loadingProgressBar.Location = new Point(115, 111); 59 | loadingProgressBar.Name = "loadingProgressBar"; 60 | loadingProgressBar.Size = new Size(352, 23); 61 | loadingProgressBar.TabIndex = 2; 62 | // 63 | // SplashForm 64 | // 65 | AutoScaleDimensions = new SizeF(7F, 15F); 66 | AutoScaleMode = AutoScaleMode.Font; 67 | ClientSize = new Size(583, 167); 68 | Controls.Add(loadingProgressBar); 69 | Controls.Add(loadingLabel); 70 | Controls.Add(titleLabel); 71 | FormBorderStyle = FormBorderStyle.None; 72 | Name = "SplashForm"; 73 | ShowInTaskbar = false; 74 | StartPosition = FormStartPosition.CenterScreen; 75 | Text = "SplashForm"; 76 | Load += SplashForm_Load; 77 | ResumeLayout(false); 78 | PerformLayout(); 79 | } 80 | 81 | #endregion 82 | 83 | private System.Windows.Forms.Label titleLabel; 84 | private System.Windows.Forms.Label loadingLabel; 85 | private System.Windows.Forms.ProgressBar loadingProgressBar; 86 | } 87 | } -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/RankedLabel.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | text/microsoft-resx 50 | 51 | 52 | 2.0 53 | 54 | 55 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 56 | 57 | 58 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 59 | 60 | 61 | True 62 | 63 | 64 | True 65 | 66 | 67 | True 68 | 69 | 70 | True 71 | 72 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/UnitSetupForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Controls 2 | { 3 | partial class UnitSetupForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(UnitSetupForm)); 32 | singersPanel = new System.Windows.Forms.FlowLayoutPanel(); 33 | tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); 34 | confirmButton = new System.Windows.Forms.Button(); 35 | sfxCheckBox = new System.Windows.Forms.CheckBox(); 36 | tableLayoutPanel.SuspendLayout(); 37 | SuspendLayout(); 38 | // 39 | // singersPanel 40 | // 41 | resources.ApplyResources(singersPanel, "singersPanel"); 42 | singersPanel.Name = "singersPanel"; 43 | // 44 | // tableLayoutPanel 45 | // 46 | resources.ApplyResources(tableLayoutPanel, "tableLayoutPanel"); 47 | tableLayoutPanel.Controls.Add(singersPanel, 0, 0); 48 | tableLayoutPanel.Controls.Add(confirmButton, 0, 1); 49 | tableLayoutPanel.Controls.Add(sfxCheckBox, 0, 2); 50 | tableLayoutPanel.GrowStyle = System.Windows.Forms.TableLayoutPanelGrowStyle.FixedSize; 51 | tableLayoutPanel.Name = "tableLayoutPanel"; 52 | // 53 | // confirmButton 54 | // 55 | resources.ApplyResources(confirmButton, "confirmButton"); 56 | confirmButton.Name = "confirmButton"; 57 | confirmButton.UseVisualStyleBackColor = true; 58 | confirmButton.Click += ConfirmButton_Click; 59 | // 60 | // sfxCheckBox 61 | // 62 | resources.ApplyResources(sfxCheckBox, "sfxCheckBox"); 63 | sfxCheckBox.Checked = true; 64 | sfxCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; 65 | sfxCheckBox.Name = "sfxCheckBox"; 66 | sfxCheckBox.UseVisualStyleBackColor = true; 67 | // 68 | // UnitSetupForm 69 | // 70 | AcceptButton = confirmButton; 71 | resources.ApplyResources(this, "$this"); 72 | AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 73 | Controls.Add(tableLayoutPanel); 74 | MaximizeBox = false; 75 | Name = "UnitSetupForm"; 76 | tableLayoutPanel.ResumeLayout(false); 77 | tableLayoutPanel.PerformLayout(); 78 | ResumeLayout(false); 79 | PerformLayout(); 80 | } 81 | 82 | #endregion 83 | 84 | private System.Windows.Forms.FlowLayoutPanel singersPanel; 85 | private System.Windows.Forms.TableLayoutPanel tableLayoutPanel; 86 | private System.Windows.Forms.Button confirmButton; 87 | private System.Windows.Forms.CheckBox sfxCheckBox; 88 | } 89 | } -------------------------------------------------------------------------------- /UmamusumeExplorer/Assets/AssetTables.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Xml.Linq; 3 | using UmamusumeData; 4 | using UmamusumeData.Tables; 5 | 6 | namespace UmamusumeExplorer.Assets 7 | { 8 | static class AssetTables 9 | { 10 | public static void Initialize() 11 | { 12 | Type assetTablesType = typeof(AssetTables); 13 | IEnumerable properties = assetTablesType.GetProperties().Where(p => p.PropertyType.Name == "List`1"); 14 | 15 | Queue customLoadAction = new(); 16 | customLoadAction.Enqueue(() => AudioAssetEntries.AddRange(UmaDataHelper.GetManifestEntries(ga => ga.Name.StartsWith("sound/")))); 17 | 18 | int completed = 0; 19 | foreach (var property in properties) 20 | { 21 | Type listType = property.PropertyType.GenericTypeArguments[0]; 22 | 23 | string name = property.Name; 24 | 25 | UpdateProgress?.Invoke((int)((float)completed / properties.Count() * 100), name); 26 | 27 | if (name == listType.Name + "s") 28 | { 29 | MethodInfo? targetMethod = (typeof(UmaDataHelper).GetMethod("GetMasterDatabaseRows")?.MakeGenericMethod(listType)) ?? 30 | throw new Exception("Could not find suitable method."); 31 | property.SetValue(null, targetMethod.Invoke(null, [null])); 32 | } 33 | else 34 | { 35 | customLoadAction.Dequeue().Invoke(); 36 | } 37 | 38 | completed++; 39 | } 40 | 41 | UpdateProgress?.Invoke(100, ""); 42 | } 43 | 44 | public delegate void LoadAction(); 45 | 46 | public delegate void ProgressUpdater(int progress, string loadedName); 47 | 48 | public static ProgressUpdater? UpdateProgress { get; set; } 49 | 50 | public static List AudioAssetEntries { get; private set; } = []; 51 | 52 | public static List AvailableSkillSets { get; private set; } = []; 53 | 54 | public static List CardDatas { get; private set; } = []; 55 | public static List CardRarityDatas { get; private set; } = []; 56 | public static List CharaDatas { get; private set; } = []; 57 | 58 | public static List CharacterSystemTexts { get; private set; } = []; 59 | 60 | public static List GachaAvailables { get; private set; } = []; 61 | 62 | public static List JukeboxMusicDatas { get; private set; } = []; 63 | 64 | public static List LiveDatas { get; private set; } = []; 65 | public static List LivePermissionDatas { get; private set; } = []; 66 | 67 | public static List RaceBgms { get; private set; } = []; 68 | public static List RaceBgmPatterns { get; private set; } = []; 69 | 70 | public static List SkillSets { get; private set; } = []; 71 | public static List SkillDatas { get; private set; } = []; 72 | 73 | public static List SingleModeSkillNeedPoints { get; private set; } = []; 74 | 75 | public static List SkillUpgradeConditions { get; private set; } = []; 76 | public static List SkillUpgradeDescriptions { get; private set; } = []; 77 | 78 | public static List SupportCardDatas { get; private set; } = []; 79 | 80 | public static List TextDatas { get; private set; } = []; 81 | 82 | public static string GetText(TextCategory category, int index) 83 | { 84 | return TextDatas.FirstOrDefault(td => td.Category == (int)category && td.Index == index)?.Text ?? ""; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/CharacterPositionControl.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Controls 2 | { 3 | partial class CharacterPositionControl 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | positionIndexLabel = new Label(); 32 | characterPictureBox = new HighQualityPictureBox(); 33 | modeButton = new MultiStateButton(); 34 | ((System.ComponentModel.ISupportInitialize)characterPictureBox).BeginInit(); 35 | SuspendLayout(); 36 | // 37 | // positionIndexLabel 38 | // 39 | positionIndexLabel.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; 40 | positionIndexLabel.Font = new Font("Segoe UI", 15.75F); 41 | positionIndexLabel.Location = new Point(3, 3); 42 | positionIndexLabel.Margin = new Padding(3); 43 | positionIndexLabel.Name = "positionIndexLabel"; 44 | positionIndexLabel.Size = new Size(109, 30); 45 | positionIndexLabel.TabIndex = 0; 46 | positionIndexLabel.Text = "0"; 47 | positionIndexLabel.TextAlign = ContentAlignment.MiddleCenter; 48 | // 49 | // characterPictureBox 50 | // 51 | characterPictureBox.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; 52 | characterPictureBox.BackgroundImageLayout = ImageLayout.Stretch; 53 | characterPictureBox.Cursor = Cursors.Hand; 54 | characterPictureBox.Location = new Point(3, 39); 55 | characterPictureBox.Name = "characterPictureBox"; 56 | characterPictureBox.Size = new Size(109, 120); 57 | characterPictureBox.TabIndex = 1; 58 | characterPictureBox.TabStop = false; 59 | // 60 | // modeButton 61 | // 62 | modeButton.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; 63 | modeButton.Location = new Point(3, 165); 64 | modeButton.Name = "modeButton"; 65 | modeButton.Size = new Size(109, 25); 66 | modeButton.State = null; 67 | modeButton.TabIndex = 2; 68 | modeButton.Text = "Mode"; 69 | modeButton.UseVisualStyleBackColor = true; 70 | // 71 | // CharacterPositionControl 72 | // 73 | AutoScaleDimensions = new SizeF(7F, 15F); 74 | AutoScaleMode = AutoScaleMode.Font; 75 | Controls.Add(modeButton); 76 | Controls.Add(characterPictureBox); 77 | Controls.Add(positionIndexLabel); 78 | Name = "CharacterPositionControl"; 79 | Size = new Size(115, 193); 80 | ((System.ComponentModel.ISupportInitialize)characterPictureBox).EndInit(); 81 | ResumeLayout(false); 82 | } 83 | 84 | #endregion 85 | 86 | private System.Windows.Forms.Label positionIndexLabel; 87 | private HighQualityPictureBox characterPictureBox; 88 | private MultiStateButton modeButton; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Pages/CharacterInfoControl.Designer.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace UmamusumeExplorer.Pages 3 | { 4 | partial class CharacterInfoControl 5 | { 6 | /// 7 | /// Required designer variable. 8 | /// 9 | private System.ComponentModel.IContainer components = null; 10 | 11 | /// 12 | /// Clean up any resources being used. 13 | /// 14 | /// true if managed resources should be disposed; otherwise, false. 15 | protected override void Dispose(bool disposing) 16 | { 17 | if (disposing && (components != null)) 18 | { 19 | components.Dispose(); 20 | } 21 | base.Dispose(disposing); 22 | } 23 | 24 | #region Component Designer generated code 25 | 26 | /// 27 | /// Required method for Designer support - do not modify 28 | /// the contents of this method with the code editor. 29 | /// 30 | private void InitializeComponent() 31 | { 32 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CharacterInfoControl)); 33 | selectLabel = new Label(); 34 | goButton = new Button(); 35 | charaListComboBox = new ComboBox(); 36 | markPlayableCheckBox = new CheckBox(); 37 | characterItemsPanel = new Controls.CharacterItemsPanel(); 38 | SuspendLayout(); 39 | // 40 | // selectLabel 41 | // 42 | resources.ApplyResources(selectLabel, "selectLabel"); 43 | selectLabel.Name = "selectLabel"; 44 | // 45 | // goButton 46 | // 47 | resources.ApplyResources(goButton, "goButton"); 48 | goButton.Name = "goButton"; 49 | goButton.UseVisualStyleBackColor = true; 50 | goButton.Click += GoButton_Click; 51 | // 52 | // charaListComboBox 53 | // 54 | charaListComboBox.AutoCompleteMode = AutoCompleteMode.SuggestAppend; 55 | charaListComboBox.AutoCompleteSource = AutoCompleteSource.ListItems; 56 | charaListComboBox.FormattingEnabled = true; 57 | resources.ApplyResources(charaListComboBox, "charaListComboBox"); 58 | charaListComboBox.Name = "charaListComboBox"; 59 | // 60 | // markPlayableCheckBox 61 | // 62 | resources.ApplyResources(markPlayableCheckBox, "markPlayableCheckBox"); 63 | markPlayableCheckBox.Name = "markPlayableCheckBox"; 64 | markPlayableCheckBox.UseVisualStyleBackColor = true; 65 | markPlayableCheckBox.CheckedChanged += ShowPlayableCheckBox_CheckedChanged; 66 | // 67 | // characterItemsPanel 68 | // 69 | resources.ApplyResources(characterItemsPanel, "characterItemsPanel"); 70 | characterItemsPanel.Items = null; 71 | characterItemsPanel.Name = "characterItemsPanel"; 72 | // 73 | // CharacterInfoControl 74 | // 75 | AutoScaleMode = AutoScaleMode.Inherit; 76 | Controls.Add(characterItemsPanel); 77 | Controls.Add(markPlayableCheckBox); 78 | Controls.Add(charaListComboBox); 79 | Controls.Add(goButton); 80 | Controls.Add(selectLabel); 81 | Name = "CharacterInfoControl"; 82 | resources.ApplyResources(this, "$this"); 83 | Load += CharacterInfoControl_Load; 84 | ResumeLayout(false); 85 | PerformLayout(); 86 | } 87 | 88 | #endregion 89 | private Label selectLabel; 90 | private Button goButton; 91 | private ComboBox charaListComboBox; 92 | private CheckBox markPlayableCheckBox; 93 | private Controls.CharacterItemsPanel characterItemsPanel; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/SkillEvolutionConditionContainer.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Controls 2 | { 3 | partial class SkillEvolutionConditionContainer 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | conditionNumberLabel = new System.Windows.Forms.Label(); 32 | conditionDescription = new System.Windows.Forms.Label(); 33 | conditionsPanel = new System.Windows.Forms.FlowLayoutPanel(); 34 | SuspendLayout(); 35 | // 36 | // conditionNumberLabel 37 | // 38 | conditionNumberLabel.BackColor = System.Drawing.SystemColors.ControlLight; 39 | conditionNumberLabel.Location = new System.Drawing.Point(15, 15); 40 | conditionNumberLabel.Margin = new System.Windows.Forms.Padding(15, 15, 15, 0); 41 | conditionNumberLabel.Name = "conditionNumberLabel"; 42 | conditionNumberLabel.Size = new System.Drawing.Size(99, 15); 43 | conditionNumberLabel.TabIndex = 0; 44 | conditionNumberLabel.Text = "Condition 0"; 45 | conditionNumberLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 46 | // 47 | // conditionDescription 48 | // 49 | conditionDescription.AutoSize = true; 50 | conditionDescription.Location = new System.Drawing.Point(144, 15); 51 | conditionDescription.Margin = new System.Windows.Forms.Padding(15, 15, 15, 0); 52 | conditionDescription.Name = "conditionDescription"; 53 | conditionDescription.Size = new System.Drawing.Size(210, 15); 54 | conditionDescription.TabIndex = 0; 55 | conditionDescription.Text = "Satisfy one of the following conditions"; 56 | // 57 | // conditionsPanel 58 | // 59 | conditionsPanel.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; 60 | conditionsPanel.Location = new System.Drawing.Point(15, 45); 61 | conditionsPanel.Margin = new System.Windows.Forms.Padding(15); 62 | conditionsPanel.Name = "conditionsPanel"; 63 | conditionsPanel.Size = new System.Drawing.Size(451, 50); 64 | conditionsPanel.TabIndex = 1; 65 | // 66 | // SkillEvolutionConditionContainer 67 | // 68 | AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); 69 | AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 70 | BackColor = System.Drawing.SystemColors.ControlLightLight; 71 | Controls.Add(conditionsPanel); 72 | Controls.Add(conditionDescription); 73 | Controls.Add(conditionNumberLabel); 74 | Name = "SkillEvolutionConditionContainer"; 75 | Size = new System.Drawing.Size(481, 110); 76 | Load += SkillEvolutionConditionMultiple_Load; 77 | ResumeLayout(false); 78 | PerformLayout(); 79 | } 80 | 81 | #endregion 82 | 83 | private System.Windows.Forms.Label conditionNumberLabel; 84 | private System.Windows.Forms.Label conditionDescription; 85 | private System.Windows.Forms.FlowLayoutPanel conditionsPanel; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/CharacterVoiceListItemControl.cs: -------------------------------------------------------------------------------- 1 | using CriWareLibrary; 2 | using NAudio.Wave; 3 | using UmamusumeAudio; 4 | using UmamusumeData; 5 | using UmamusumeData.Tables; 6 | using UmamusumeExplorer.Assets; 7 | using Timer = System.Windows.Forms.Timer; 8 | 9 | namespace UmamusumeExplorer.Controls 10 | { 11 | public partial class CharacterVoiceListItemControl : UserControl 12 | { 13 | private WaveStream? voiceWaveStream; 14 | private Timer? animationTimer; 15 | private bool processingClick = false; 16 | 17 | public CharacterVoiceListItemControl() 18 | { 19 | InitializeComponent(); 20 | 21 | DoubleBuffered = true; 22 | 23 | idLabel.Click += ClickHit; 24 | idLabel.DoubleClick += ClickHit; 25 | } 26 | 27 | public CharacterVoiceListItemControl(CharacterSystemText systemText, IWavePlayer waveOut) : this() 28 | { 29 | CharacterSystemText = systemText; 30 | 31 | idLabel.Text = systemText.VoiceId.ToString(); 32 | voiceLineTextLabel.Text = systemText.Text; 33 | playButton.Click += (s, e) => 34 | { 35 | waveOut.PlaybackStopped -= PlaybackStopped; 36 | 37 | if (waveOut.PlaybackState == PlaybackState.Playing) 38 | waveOut.Stop(); 39 | 40 | voiceWaveStream?.Dispose(); 41 | voiceWaveStream = GetWaveStream(); 42 | 43 | voiceLineTextLabel.Progress = 0; 44 | animationTimer?.Stop(); 45 | animationTimer?.Dispose(); 46 | 47 | animationTimer = new() 48 | { 49 | Enabled = true, 50 | Interval = 50 51 | }; 52 | animationTimer.Tick += (s, e) => 53 | { 54 | if (voiceWaveStream is not null) 55 | voiceLineTextLabel.Progress = (float)voiceWaveStream.Position / voiceWaveStream.Length; 56 | }; 57 | animationTimer.Start(); 58 | 59 | waveOut.PlaybackStopped += PlaybackStopped; 60 | 61 | waveOut.Init(voiceWaveStream); 62 | waveOut.Play(); 63 | }; 64 | } 65 | 66 | public CharacterSystemText? CharacterSystemText { get; } 67 | 68 | public bool Checked => checkBox.Checked; 69 | 70 | public WaveStream? GetWaveStream() 71 | { 72 | if (CharacterSystemText is null) return null; 73 | 74 | ManifestEntry? acbAsset = AssetTables.AudioAssetEntries.FirstOrDefault(a => a.BaseName == CharacterSystemText.CueSheet + ".acb"); 75 | ManifestEntry? awbAsset = AssetTables.AudioAssetEntries.FirstOrDefault(a => a.BaseName == CharacterSystemText.CueSheet + ".awb"); 76 | 77 | if (acbAsset is null && awbAsset is null) return null; 78 | 79 | string acbPath = UmaDataHelper.GetPath(acbAsset); 80 | string awbPath = UmaDataHelper.GetPath(awbAsset); 81 | 82 | if (!File.Exists(awbPath)) return null; 83 | 84 | AcbReader acb = new(File.OpenRead(acbPath)); 85 | AwbReader awb = new(File.OpenRead(awbPath)); 86 | 87 | return new UmaWaveStream(awb, acb.GetWaveIdFromCueId(CharacterSystemText.CueId)); 88 | } 89 | 90 | private void ClickHit(object? sender, EventArgs e) 91 | { 92 | if (processingClick) return; 93 | 94 | processingClick = true; 95 | 96 | try 97 | { 98 | if (e is not MouseEventArgs mouseEventArgs) return; 99 | if (mouseEventArgs.Button != MouseButtons.Left) return; 100 | 101 | checkBox.Checked = !checkBox.Checked; 102 | } 103 | finally 104 | { 105 | processingClick = false; 106 | } 107 | } 108 | 109 | private void VoiceLineTextLabel_DoubleClick(object? sender, EventArgs e) 110 | { 111 | Clipboard.SetText(voiceLineTextLabel.Text); 112 | } 113 | 114 | private void PlaybackStopped(object? sender, EventArgs e) 115 | { 116 | voiceLineTextLabel.Progress = 0; 117 | animationTimer?.Stop(); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Tools/SqlCreateStatementToClass/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | string createStatement = args.Length > 0 ? args[0] : ""; 4 | 5 | if (createStatement == "") 6 | createStatement = Console.ReadLine() ?? ""; 7 | 8 | if (createStatement != "") 9 | { 10 | createStatement = createStatement["CREATE TABLE".Length..]; 11 | 12 | string tableName = ExtractFromQuotesAndUpdate(ref createStatement); 13 | 14 | using var writer = new StreamWriter(File.Create(UnderscoredToCamelCase(tableName) + ".cs")); 15 | 16 | writer.WriteLine("using SQLite;\n"); 17 | writer.WriteLine($"[Table(\"{tableName}\")]"); 18 | writer.WriteLine($"public class {UnderscoredToCamelCase(tableName)}"); 19 | writer.WriteLine("{"); 20 | 21 | createStatement = createStatement[(createStatement.IndexOf('('))..].Trim(); 22 | 23 | Dictionary properties = new(); 24 | 25 | while (true) 26 | { 27 | if (createStatement.StartsWith(',')) 28 | createStatement = createStatement[1..]; 29 | createStatement = createStatement.Trim(); 30 | 31 | if (createStatement.StartsWith("PRIMARY KEY(")) 32 | { 33 | while (true) 34 | { 35 | string columnName = ExtractFromQuotesAndUpdate(ref createStatement); 36 | properties[columnName] += " PRIMARY KEY"; 37 | if (createStatement.StartsWith(")")) 38 | { 39 | createStatement = createStatement[1..]; 40 | break; 41 | } 42 | } 43 | } 44 | else if (createStatement.StartsWith("UNIQUE(")) 45 | { 46 | while (true) 47 | { 48 | string columnName = ExtractFromQuotesAndUpdate(ref createStatement); 49 | properties[columnName] += " UNIQUE"; 50 | if (createStatement.StartsWith(")")) 51 | { 52 | createStatement = createStatement[1..]; 53 | break; 54 | } 55 | } 56 | } 57 | else 58 | { 59 | 60 | int spaceIndex = createStatement.IndexOf(' '); 61 | if (spaceIndex < 0) break; 62 | 63 | string columnName = ExtractFromQuotesAndUpdate(ref createStatement); 64 | createStatement = createStatement.Trim(); 65 | 66 | string type = createStatement[..createStatement.IndexOf(',')]; 67 | createStatement = createStatement[(type.Length)..]; 68 | 69 | properties.Add(columnName, type); 70 | } 71 | } 72 | 73 | int index = 0; 74 | foreach (var property in properties) 75 | { 76 | string type = property.Value[..property.Value.IndexOf(' ')]; 77 | type = type switch 78 | { 79 | "INTEGER" => "int", 80 | "TEXT" => "string", 81 | _ => "object", 82 | }; 83 | 84 | writer.Write(" "); 85 | writer.WriteLine($"[Column(\"{property.Key}\")" + 86 | $"{(property.Value.Contains("NOT NULL") ? ", NotNull" : "")}" + 87 | $"{(property.Value.Contains("PRIMARY KEY") ? ", PrimaryKey" : "")}" + 88 | $"{(property.Value.Contains("UNIQUE") ? ", Unique" : "")}]"); 89 | writer.Write(" "); 90 | writer.Write($"public {type} {UnderscoredToCamelCase(property.Key)} "); 91 | 92 | if (type == "string") 93 | writer.WriteLine("{ get; set; } = \"\";"); 94 | else 95 | writer.WriteLine("{ get; set; }"); 96 | 97 | if (index < properties.Count - 1) 98 | writer.WriteLine(); 99 | 100 | index++; 101 | } 102 | 103 | writer.WriteLine("}"); 104 | } 105 | 106 | string ExtractFromQuotesAndUpdate(ref string source) 107 | { 108 | source = source[(source.IndexOf('\'') + 1)..]; 109 | string value = source[..source.IndexOf('\'')]; 110 | source = source[(value.Length + 1)..]; 111 | 112 | return value; 113 | } 114 | 115 | string UnderscoredToCamelCase(string value) 116 | { 117 | string[] words = value.Split('_'); 118 | 119 | StringBuilder newString = new(); 120 | 121 | foreach (var word in words) 122 | { 123 | newString.Append(char.ToUpper(word[0])); 124 | newString.Append(word[1..]); 125 | } 126 | 127 | return newString.ToString(); 128 | } -------------------------------------------------------------------------------- /UmamusumeExplorer/UmaApplicationContext.cs: -------------------------------------------------------------------------------- 1 | using UmamusumeData; 2 | using UmamusumeData.DataDirectories; 3 | using UmamusumeExplorer.Assets; 4 | using UmamusumeExplorer.Controls; 5 | 6 | namespace UmamusumeExplorer 7 | { 8 | class UmaApplicationContext : ApplicationContext 9 | { 10 | private const string pathTextFile = "UmamusumePath.txt"; 11 | 12 | public UmaApplicationContext() 13 | { 14 | if (File.Exists(pathTextFile)) 15 | UmaDataHelper.UmamusumeDirectory = File.ReadAllText(pathTextFile).Trim(); 16 | else 17 | { 18 | List dataDirectories = UmaDataHelper.ScanDirectories(); 19 | if (dataDirectories.Count > 0) 20 | { 21 | if (dataDirectories.Count > 1) 22 | { 23 | InstallationSelectForm installationSelectForm = new(dataDirectories); 24 | DialogResult installationSelectResult = installationSelectForm.ShowDialog(); 25 | 26 | if (installationSelectResult == DialogResult.OK) 27 | { 28 | UmaDataHelper.UmamusumeDirectory = installationSelectForm.Path; 29 | } 30 | else 31 | { 32 | Environment.Exit(1); 33 | return; 34 | } 35 | } 36 | else 37 | { 38 | UmaDataHelper.UmamusumeDirectory = dataDirectories.First().DataDirectoryPath; 39 | } 40 | } 41 | } 42 | 43 | if (!UmaDataHelper.CheckDirectory()) 44 | { 45 | DialogResult result = MessageBox.Show( 46 | "Unable to find the required files in the default known directories.\n\n" + 47 | "If you have not installed, launched, or updated the game, please do so. " + 48 | "Otherwise, please manually specify the data directory.\n\n" + 49 | "Would you like to continue?", 50 | "Missing files", 51 | MessageBoxButtons.YesNo, 52 | MessageBoxIcon.Information); 53 | if (result == DialogResult.Yes) 54 | { 55 | while (true) 56 | { 57 | FolderBrowserDialog browserDialog = new(); 58 | DialogResult browseResult = browserDialog.ShowDialog(); 59 | 60 | if (browseResult == DialogResult.OK) 61 | { 62 | UmaDataHelper.UmamusumeDirectory = browserDialog.SelectedPath; 63 | 64 | if (UmaDataHelper.CheckDirectory()) 65 | { 66 | File.WriteAllText(pathTextFile, UmaDataHelper.UmamusumeDirectory); 67 | break; 68 | } 69 | 70 | MessageBox.Show("Please select a valid data directory containing the \"dat\" folder, " + 71 | "the \"meta\" file, and the \"master\" folder with the \"master.mdb\" file.", 72 | "Missing files", 73 | MessageBoxButtons.OK, 74 | MessageBoxIcon.Information); 75 | } 76 | else 77 | { 78 | Environment.Exit(1); 79 | return; 80 | } 81 | } 82 | } 83 | else 84 | { 85 | Environment.Exit(1); 86 | return; 87 | } 88 | } 89 | 90 | SplashForm splashForm = new(); 91 | if (splashForm.LoadAndClose()) 92 | { 93 | MainForm mainForm = new(); 94 | mainForm.Show(); 95 | MainForm = mainForm; 96 | //mainForm.FormClosed += (s, e) => ExitThread(); 97 | 98 | GameAssets.MainForm = mainForm; 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /UmamusumeExplorer/Controls/IncompleteExportForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace UmamusumeExplorer.Controls 2 | { 3 | partial class IncompleteExportForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | exportFailedLabel = new Label(); 32 | failedFilesList = new ListView(); 33 | nameHeader = new ColumnHeader(); 34 | reasonHeader = new ColumnHeader(); 35 | okButton = new Button(); 36 | SuspendLayout(); 37 | // 38 | // exportFailedLabel 39 | // 40 | exportFailedLabel.AutoSize = true; 41 | exportFailedLabel.Location = new Point(12, 9); 42 | exportFailedLabel.Name = "exportFailedLabel"; 43 | exportFailedLabel.Size = new Size(198, 15); 44 | exportFailedLabel.TabIndex = 0; 45 | exportFailedLabel.Text = "Export failed for the following items:"; 46 | // 47 | // failedFilesList 48 | // 49 | failedFilesList.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; 50 | failedFilesList.Columns.AddRange(new ColumnHeader[] { nameHeader, reasonHeader }); 51 | failedFilesList.FullRowSelect = true; 52 | failedFilesList.Location = new Point(12, 27); 53 | failedFilesList.Name = "failedFilesList"; 54 | failedFilesList.Size = new Size(641, 275); 55 | failedFilesList.TabIndex = 1; 56 | failedFilesList.UseCompatibleStateImageBehavior = false; 57 | failedFilesList.View = View.Details; 58 | // 59 | // nameHeader 60 | // 61 | nameHeader.Text = "File"; 62 | nameHeader.Width = 240; 63 | // 64 | // reasonHeader 65 | // 66 | reasonHeader.Text = "Reason"; 67 | reasonHeader.Width = 397; 68 | // 69 | // okButton 70 | // 71 | okButton.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; 72 | okButton.Location = new Point(578, 308); 73 | okButton.Name = "okButton"; 74 | okButton.Size = new Size(75, 23); 75 | okButton.TabIndex = 2; 76 | okButton.Text = "OK"; 77 | okButton.UseVisualStyleBackColor = true; 78 | okButton.Click += OkButton_Click; 79 | // 80 | // IncompleteExportForm 81 | // 82 | AcceptButton = okButton; 83 | AutoScaleDimensions = new SizeF(7F, 15F); 84 | AutoScaleMode = AutoScaleMode.Font; 85 | ClientSize = new Size(665, 343); 86 | Controls.Add(okButton); 87 | Controls.Add(failedFilesList); 88 | Controls.Add(exportFailedLabel); 89 | FormBorderStyle = FormBorderStyle.FixedDialog; 90 | MaximizeBox = false; 91 | MinimizeBox = false; 92 | Name = "IncompleteExportForm"; 93 | StartPosition = FormStartPosition.Manual; 94 | Text = "Incomplete Export"; 95 | ResumeLayout(false); 96 | PerformLayout(); 97 | } 98 | 99 | #endregion 100 | 101 | private Label exportFailedLabel; 102 | private ListView failedFilesList; 103 | private ColumnHeader nameHeader; 104 | private ColumnHeader reasonHeader; 105 | private Button okButton; 106 | } 107 | } --------------------------------------------------------------------------------