[feat-607] add support for map info v4

This commit is contained in:
MathieuG-P
2024-10-15 20:26:05 +02:00
parent 0d93c984be
commit 7468554ea0
15 changed files with 272 additions and 86 deletions
@@ -1,13 +1,14 @@
import { getLocalTimeZone, parseAbsolute, toCalendarDateTime } from "@internationalized/date";
import { MapItemComponentProps } from "renderer/components/maps-playlists-panel/maps/map-item.component";
import { BsvMapDetail, RawMapInfoData, SongDetailDiffCharactertistic, SongDetails, SongDiffName } from "shared/models/maps";
import { BsvMapDetail, SongDetailDiffCharactertistic, SongDetails, SongDiffName } from "shared/models/maps";
import { BsmLocalMap } from "shared/models/maps/bsm-local-map.interface";
import { MapInfo } from "shared/models/maps/info/map-info.model";
export type ParsedMapDiff = { name: SongDiffName; libelle: string; stars: number, nps: number, njs: number };
export abstract class MapItemComponentPropsMapper {
public static extractMapDiffs({rawMapInfo, songDetails, bsvMap}: {rawMapInfo?: RawMapInfoData, songDetails?: SongDetails, bsvMap?: BsvMapDetail}): Map<SongDetailDiffCharactertistic, ParsedMapDiff[]> {
public static extractMapDiffs({mapInfo, songDetails, bsvMap}: {mapInfo?: MapInfo, songDetails?: SongDetails, bsvMap?: BsvMapDetail}): Map<SongDetailDiffCharactertistic, ParsedMapDiff[]> {
const res = new Map<SongDetailDiffCharactertistic, ParsedMapDiff[]>();
if (bsvMap?.versions?.at(0)?.diffs) {
@@ -22,20 +23,18 @@ export abstract class MapItemComponentPropsMapper {
if (songDetails?.difficulties) {
songDetails?.difficulties.forEach(diff => {
const arr = res.get(diff.characteristic) || [];
const diffName = rawMapInfo?._difficultyBeatmapSets?.find(set => set._beatmapCharacteristicName === diff.characteristic)._difficultyBeatmaps.find(rawDiff => rawDiff._difficulty === diff.difficulty)?._customData?._difficultyLabel || diff.difficulty;
const diffName = mapInfo?.difficulties?.find(mapDiff => mapDiff.characteristic === diff.characteristic && mapDiff.difficulty === diff.difficulty)?.difficultyLabel || diff.difficulty;
arr.push({ libelle: diffName, name: diff.difficulty, stars: diff.stars, nps: diff.nps, njs: diff.njs });
res.set(diff.characteristic, arr);
});
return res;
}
if(rawMapInfo?._difficultyBeatmapSets){
rawMapInfo._difficultyBeatmapSets.forEach(set => {
set._difficultyBeatmaps.forEach(diff => {
const arr = res.get(set._beatmapCharacteristicName) || [];
arr.push({ libelle: diff._customData?._difficultyLabel || diff._difficulty, name: diff._difficulty, stars: null, nps: null, njs: diff._noteJumpMovementSpeed });
res.set(set._beatmapCharacteristicName, arr);
});
if(mapInfo?.difficulties){
mapInfo.difficulties.forEach(diff => {
const arr = res.get(diff.characteristic) || [];
arr.push({ libelle: diff.difficultyLabel || diff.difficulty, name: diff.difficulty, stars: null, nps: null, njs: diff.noteJumpMovementSpeed });
res.set(diff.characteristic, arr);
});
}
@@ -45,14 +44,14 @@ export abstract class MapItemComponentPropsMapper {
public static fromBsmLocalMap(map: BsmLocalMap): MapItemComponentProps<BsmLocalMap> {
return {
hash: map.hash,
title: map.rawInfo._songName,
title: map.mapInfo.songName,
coverUrl: map.coverUrl,
songUrl: map.songUrl,
autor: map.rawInfo._levelAuthorName,
songAutor: map.rawInfo._songAuthorName,
bpm: map.rawInfo._beatsPerMinute,
autor: map.mapInfo.levelMappers.at(0),
songAutor: map.mapInfo.songAuthorName,
bpm: map.mapInfo.beatsPerMinute,
duration: map.songDetails?.duration,
diffs: MapItemComponentPropsMapper.extractMapDiffs({ rawMapInfo: map.rawInfo, songDetails: map.songDetails }),
diffs: MapItemComponentPropsMapper.extractMapDiffs({ mapInfo: map.mapInfo, songDetails: map.songDetails }),
mapId: map.songDetails?.id,
ranked: map.songDetails?.ranked,
blRanked: map.songDetails?.blRanked,
@@ -102,7 +101,7 @@ export abstract class MapItemComponentPropsMapper {
}
public static from(mapDetails: BsmLocalMap|BsvMapDetail|SongDetails): MapItemComponentProps<BsmLocalMap|BsvMapDetail|SongDetails> {
if ((mapDetails as BsmLocalMap).rawInfo) {
if ((mapDetails as BsmLocalMap).mapInfo) {
return MapItemComponentPropsMapper.fromBsmLocalMap(mapDetails as BsmLocalMap) as MapItemComponentProps<BsmLocalMap|BsvMapDetail|SongDetails>;
}
if ((mapDetails as BsvMapDetail).metadata) {
@@ -1,11 +1,11 @@
import { RawMapInfoData } from "./raw-map.model";
import { MapInfo } from "./info/map-info.model";
import { SongDetails } from "./song-details-cache/song-details-cache.model";
export interface BsmLocalMap {
hash: string;
coverUrl: string;
songUrl: string;
rawInfo: RawMapInfoData;
mapInfo: MapInfo;
songDetails?: SongDetails;
path: string;
}
-1
View File
@@ -1,3 +1,2 @@
export { RawMapInfoData, RawMapDifficulty, RawDifficultySet } from "./raw-map.model";
export { BsvInstant, BsvMapDetail, BsvMapDetailMetadata, BsvMapDifficulty, BsvMapParitySummary, BsvMapStats, BsvMapTestplay, BsvMapVersion, BsvUserDetail } from "./beat-saver.model";
export { SongDetails, SongDifficulty, SongUploader, SongDetailDiffCharactertistic, SongDiffName } from "./song-details-cache/song-details-cache.model";
@@ -0,0 +1,42 @@
import { SongDetailDiffCharactertistic, SongDiffName } from "../song-details-cache/song-details-cache.model";
import { RawMapInfoDataV2 } from "./raw-map-info-v2.model";
import { RawMapInfoDataV4 } from "./raw-map-info-v4.model";
export type AnyRawMapInfo = RawMapInfoDataV2 | RawMapInfoDataV4;
// interfaces/mapInfo.ts
export interface MapInfo {
version: string;
songName: string;
songSubName?: string;
songAuthorName: string;
levelMappers: string[];
levelLighters: string[];
beatsPerMinute: number;
shuffle?: number;
shufflePeriod?: number;
previewStartTime: number;
previewDuration: number;
songFilename: string;
songPreviewFilename: string;
coverImageFilename: string;
environmentNames: string[];
difficulties: MapDifficulty[];
}
export interface MapDifficulty {
characteristic: SongDetailDiffCharactertistic;
difficulty: SongDiffName;
difficultyLabel?: string;
beatmapFilename: string;
noteJumpMovementSpeed: number;
noteJumpStartBeatOffset: number;
beatmapColorSchemeIdx?: number;
environmentNameIdx?: number;
// Additional fields from version 4.0.0
beatmapAuthors?: {
mappers: string[];
lighters: string[];
};
lightshowDataFilename?: string;
}
@@ -1,6 +1,6 @@
import { SongDetailDiffCharactertistic, SongDiffName } from "./song-details-cache/song-details-cache.model";
import { SongDetailDiffCharactertistic, SongDiffName } from "../song-details-cache/song-details-cache.model";
export interface RawMapInfoData<T = unknown> {
export interface RawMapInfoDataV2 {
_version: string;
_songName: string;
_songSubName: string;
@@ -16,24 +16,24 @@ export interface RawMapInfoData<T = unknown> {
_environmentName: string;
_allDirectionsEnvironmentName: string;
_songTimeOffset: number;
_customData: T;
_difficultyBeatmapSets: RawDifficultySet[];
_difficultyBeatmapSets: RawDifficultySetV2[];
// Additional fields for 2.1.0
_environmentNames?: string[];
_colorSchemes?: unknown[];
}
export interface RawDifficultySet {
interface RawDifficultySetV2 {
_beatmapCharacteristicName: SongDetailDiffCharactertistic;
_difficultyBeatmaps: RawMapDifficulty[];
_difficultyBeatmaps: RawMapDifficultyV2[];
}
export interface RawMapDifficulty {
interface RawMapDifficultyV2 {
_difficulty: SongDiffName;
_difficultyRank: string;
_difficultyRank: number;
_beatmapFilename: string;
_noteJumpMovementSpeed: number;
_noteJumpStartBeatOffset: number;
_customData?: RawMapDifficultyCustomData;
}
export interface RawMapDifficultyCustomData {
_difficultyLabel?: string;
_beatmapColorSchemeIdx?: number;
_environmentNameIdx?: number;
_customData?: { _difficultyLabel?: string; };
}
@@ -0,0 +1,40 @@
import { SongDetailDiffCharactertistic, SongDiffName } from "../song-details-cache/song-details-cache.model";
// interfaces/version4.ts
export interface RawMapInfoDataV4 {
version: string;
song: {
title: string;
subTitle: string;
author: string;
};
audio: {
songFilename: string;
songDuration: number;
audioDataFilename: string;
bpm: number;
lufs: number;
previewStartTime: number;
previewDuration: number;
};
songPreviewFilename: string;
coverImageFilename: string;
environmentNames: string[];
colorSchemes: unknown[];
difficultyBeatmaps: RawMapDifficultyV4[];
}
interface RawMapDifficultyV4 {
characteristic: SongDetailDiffCharactertistic;
difficulty: SongDiffName;
beatmapAuthors: {
mappers: string[];
lighters: string[];
};
environmentNameIdx: number;
beatmapColorSchemeIdx: number;
noteJumpMovementSpeed: number;
noteJumpStartBeatOffset: number;
beatmapDataFilename: string;
lightshowDataFilename: string;
}
@@ -0,0 +1,82 @@
import { MapDifficulty, MapInfo, AnyRawMapInfo } from "shared/models/maps/info/map-info.model";
import { RawMapInfoDataV2 } from "shared/models/maps/info/raw-map-info-v2.model";
import { RawMapInfoDataV4 } from "shared/models/maps/info/raw-map-info-v4.model";
function parseVersion2(data: RawMapInfoDataV2): MapInfo {
return {
version: data._version,
songName: data._songName,
songSubName: data._songSubName,
songAuthorName: data._songAuthorName,
levelMappers: [data._levelAuthorName],
levelLighters: [],
beatsPerMinute: data._beatsPerMinute,
shuffle: data._shuffle,
shufflePeriod: data._shufflePeriod,
previewStartTime: data._previewStartTime,
previewDuration: data._previewDuration,
songFilename: data._songFilename,
songPreviewFilename: data._songFilename,
coverImageFilename: data._coverImageFilename,
environmentNames: data._environmentNames || [data._environmentName],
difficulties: data._difficultyBeatmapSets.flatMap(set =>
set._difficultyBeatmaps.map<MapDifficulty>((diff) => ({
characteristic: set._beatmapCharacteristicName,
difficulty: diff._difficulty,
difficultyLabel: diff._customData?._difficultyLabel,
beatmapFilename: diff._beatmapFilename,
noteJumpMovementSpeed: diff._noteJumpMovementSpeed,
noteJumpStartBeatOffset: diff._noteJumpStartBeatOffset,
beatmapColorSchemeIdx: diff._beatmapColorSchemeIdx,
environmentNameIdx: diff._environmentNameIdx,
}))
),
};
}
function parseVersion4(data: RawMapInfoDataV4): MapInfo {
return {
version: data.version,
songName: data.song.title,
songSubName: data.song.subTitle,
songAuthorName: data.song.author,
levelMappers: data.difficultyBeatmaps.flatMap(diff => diff.beatmapAuthors.mappers),
levelLighters: data.difficultyBeatmaps.flatMap(diff => diff.beatmapAuthors.lighters),
beatsPerMinute: data.audio.bpm,
previewStartTime: data.audio.previewStartTime,
previewDuration: data.audio.previewDuration,
songFilename: data.audio.songFilename,
songPreviewFilename: data.songPreviewFilename,
coverImageFilename: data.coverImageFilename,
environmentNames: data.environmentNames,
difficulties: data.difficultyBeatmaps.map<MapDifficulty>(diff => ({
characteristic: diff.characteristic,
difficulty: diff.difficulty,
beatmapFilename: diff.beatmapDataFilename,
noteJumpMovementSpeed: diff.noteJumpMovementSpeed,
noteJumpStartBeatOffset: diff.noteJumpStartBeatOffset,
beatmapColorSchemeIdx: diff.beatmapColorSchemeIdx,
environmentNameIdx: diff.environmentNameIdx,
beatmapAuthors: diff.beatmapAuthors,
lightshowDataFilename: diff.lightshowDataFilename,
})),
};
}
export function parseMapInfoDat(info: AnyRawMapInfo): MapInfo | never {
const version = (info as RawMapInfoDataV2)?._version || (info as RawMapInfoDataV4)?.version;
if(!version) {
throw new Error('Cannot determine info.dat version');
}
if (version.startsWith('2.')) {
return parseVersion2(info as RawMapInfoDataV2);
}
if (version.startsWith('4.')) {
return parseVersion4(info as RawMapInfoDataV4);
}
throw new Error(`Unsupported info.dat version: ${version}`);
}