├── 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 | 
10 | 2. Set the `Language` property to the language you want to localize the form in
11 |
12 | 
13 | 3. Change the text properties of the controls to your localized text
14 |
15 | 
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 | [](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 | 
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