diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs index 5f5d423c..620753a3 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/PublishController.cs @@ -8,7 +8,6 @@ using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.PlayerData; using LBPUnion.ProjectLighthouse.PlayerData.Profiles; using LBPUnion.ProjectLighthouse.Serialization; -using LBPUnion.ProjectLighthouse.Types; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -47,6 +46,12 @@ public class PublishController : ControllerBase if (string.IsNullOrEmpty(slot.ResourceCollection)) slot.ResourceCollection = slot.RootLevel; + LbpFile? rootLevel = LbpFile.FromHash(slot.RootLevel); + if (rootLevel == null) return this.BadRequest(); + + GameVersion slotVersion = FileHelper.ParseLevelVersion(rootLevel); + if (slotVersion == GameVersion.Unknown) slotVersion = gameToken.GameVersion; + // Republish logic if (slot.SlotId != 0) { @@ -54,7 +59,7 @@ public class PublishController : ControllerBase if (oldSlot == null) return this.NotFound(); if (oldSlot.CreatorId != user.UserId) return this.BadRequest(); } - else if (user.GetUsedSlotsForGame(gameToken.GameVersion) > ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots) + else if (user.GetUsedSlotsForGame(slotVersion) > ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots) { return this.StatusCode(403, ""); } @@ -127,6 +132,11 @@ public class PublishController : ControllerBase return this.BadRequest(); } + GameVersion slotVersion = FileHelper.ParseLevelVersion(rootLevel); + + slot.GameVersion = slotVersion; + if (slotVersion == GameVersion.Unknown) slot.GameVersion = gameToken.GameVersion; + // Republish logic if (slot.SlotId != 0) { @@ -177,16 +187,6 @@ public class PublishController : ControllerBase slot.TeamPick = oldSlot.TeamPick; - // Only update a slot's gameVersion if the level was actually change - if (oldSlot.RootLevel != slot.RootLevel) - { - slot.GameVersion = gameToken.GameVersion; - } - else - { - slot.GameVersion = oldSlot.GameVersion; - } - if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0) { slot.MinimumPlayers = 1; @@ -198,7 +198,7 @@ public class PublishController : ControllerBase return this.Ok(oldSlot.Serialize(gameToken.GameVersion)); } - if (user.GetUsedSlotsForGame(gameToken.GameVersion) > ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots) + if (user.GetUsedSlotsForGame(slotVersion) > ServerConfiguration.Instance.UserGeneratedContentLimits.EntitledSlots) { Logger.Warn("Rejecting level upload, too many published slots", LogArea.Publish); return this.BadRequest(); @@ -216,7 +216,6 @@ public class PublishController : ControllerBase slot.CreatorId = user.UserId; slot.FirstUploaded = TimeHelper.UnixTimeMilliseconds(); slot.LastUpdated = TimeHelper.UnixTimeMilliseconds(); - slot.GameVersion = gameToken.GameVersion; if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0) { diff --git a/ProjectLighthouse.Tests/ExampleFiles/TestScript.ff b/ProjectLighthouse.Tests/ExampleFiles/TestScript.ff index ba1ce0e4..dfc876f9 100644 --- a/ProjectLighthouse.Tests/ExampleFiles/TestScript.ff +++ b/ProjectLighthouse.Tests/ExampleFiles/TestScript.ff @@ -1,3 +1,3 @@ -FSH +FSHb this is not my stuff to upload so its just gonna be a file like this for now :/ \ No newline at end of file diff --git a/ProjectLighthouse/Administration/Maintenance/MigrationTasks/CleanupSlotVersionMismatchMigration.cs b/ProjectLighthouse/Administration/Maintenance/MigrationTasks/CleanupSlotVersionMismatchMigration.cs new file mode 100644 index 00000000..0b48441d --- /dev/null +++ b/ProjectLighthouse/Administration/Maintenance/MigrationTasks/CleanupSlotVersionMismatchMigration.cs @@ -0,0 +1,27 @@ +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Files; +using LBPUnion.ProjectLighthouse.Levels; +using LBPUnion.ProjectLighthouse.PlayerData; + +namespace LBPUnion.ProjectLighthouse.Administration.Maintenance.MigrationTasks; + +public class CleanupSlotVersionMismatchMigration : IMigrationTask +{ + public string Name() => "Cleanup slot versions"; + + async Task IMigrationTask.Run(Database database) + { + foreach (Slot slot in database.Slots) + { + LbpFile rootLevel = LbpFile.FromHash(slot.RootLevel); + if (rootLevel == null) continue; + + GameVersion slotVersion = FileHelper.ParseLevelVersion(rootLevel); + + if (slotVersion != GameVersion.Unknown) slot.GameVersion = slotVersion; + } + + await database.SaveChangesAsync(); + return true; + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Files/FileHelper.cs b/ProjectLighthouse/Files/FileHelper.cs index 18a542fc..710af8b9 100644 --- a/ProjectLighthouse/Files/FileHelper.cs +++ b/ProjectLighthouse/Files/FileHelper.cs @@ -11,6 +11,7 @@ using ICSharpCode.SharpZipLib.Zip.Compression; using LBPUnion.ProjectLighthouse.Configuration; using LBPUnion.ProjectLighthouse.Extensions; using LBPUnion.ProjectLighthouse.Logging; +using LBPUnion.ProjectLighthouse.PlayerData; using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; @@ -50,6 +51,51 @@ public static class FileHelper }; } + public static GameVersion ParseLevelVersion(LbpFile file) + { + if (file.FileType != LbpFileType.Level || file.Data.Length < 16 || file.Data[3] != 'b') return GameVersion.Unknown; + + // Revision numbers borrowed from https://github.com/ennuo/toolkit/blob/main/src/main/java/ennuo/craftworld/resources/structs/Revision.java + + const ushort lbp2Latest = 0x3F8; + const ushort lbp1Latest = 0x272; + const ushort lbpVitaLatest = 0x3E2; + const ushort lbpVitaDescriptor = 0x4431; + // There are like 1600 revisions so this doesn't cover everything + uint revision = 0; + + // construct a 32 bit number from 4 individual bytes + for (int i = 4; i <= 7; i++) + { + revision <<= 8; + revision |= file.Data[i]; + } + + if (revision >= 0x271) + { + // construct a 16 bit number from 2 individual bytes + ushort branchDescriptor = (ushort) (file.Data[12] << 8 | file.Data[13]); + if (revision == lbpVitaLatest && branchDescriptor == lbpVitaDescriptor) return GameVersion.LittleBigPlanetVita; + } + + + GameVersion version = GameVersion.Unknown; + if (revision <= lbp1Latest) + { + version = GameVersion.LittleBigPlanet1; + } + else if (revision >> 0x10 != 0) + { + version = GameVersion.LittleBigPlanet3; + } + else if(revision <= lbp2Latest) + { + version = GameVersion.LittleBigPlanet2; + } + + return version; + } + public static LbpFileType DetermineFileType(byte[] data) { if (data.Length == 0) return LbpFileType.Unknown; // Can't be anything if theres no data. @@ -63,18 +109,18 @@ public static class FileHelper string footer = Encoding.ASCII.GetString(readLastBytes(reader, 4)); if (footer == "FARC") return LbpFileType.FileArchive; - byte[] header = reader.ReadBytes(3); + byte[] header = reader.ReadBytes(4); return Encoding.ASCII.GetString(header) switch { - "REC" => LbpFileType.MotionRecording, - "PRF" => LbpFileType.CrossLevel, - "PTG" => LbpFileType.Painting, - "TEX" => LbpFileType.Texture, - "FSH" => LbpFileType.Script, - "VOP" => LbpFileType.Voice, - "LVL" => LbpFileType.Level, - "PLN" => LbpFileType.Plan, + "RECb" => LbpFileType.MotionRecording, + "PRFb" => LbpFileType.CrossLevel, + "PTGb" => LbpFileType.Painting, + "TEX " => LbpFileType.Texture, + "FSHb" => LbpFileType.Script, + "VOPb" => LbpFileType.Voice, + "LVLb" => LbpFileType.Level, + "PLNb" => LbpFileType.Plan, _ => readAlternateHeader(reader), }; }