├── .gitattributes ├── preview.gif ├── KarlsonLevelImporterBanner.png ├── KarlsonLevelImporter ├── Properties │ └── launchSettings.json ├── Core │ ├── Components │ │ ├── CanPickup.cs │ │ ├── ComponentBase.cs │ │ └── PlayerStart.cs │ ├── LZMA │ │ ├── Compress │ │ │ ├── LZ │ │ │ │ ├── IMatchFinder.cs │ │ │ │ ├── LzOutWindow.cs │ │ │ │ ├── LzInWindow.cs │ │ │ │ └── LzBinTree.cs │ │ │ ├── LZMA │ │ │ │ ├── LzmaBase.cs │ │ │ │ ├── LzmaDecoder.cs │ │ │ │ └── LzmaEncoder.cs │ │ │ └── RangeCoder │ │ │ │ ├── RangeCoderBit.cs │ │ │ │ ├── RangeCoderBitTree.cs │ │ │ │ └── RangeCoder.cs │ │ ├── Common │ │ │ ├── OutBuffer.cs │ │ │ ├── CRC.cs │ │ │ ├── InBuffer.cs │ │ │ └── CommandLineParser.cs │ │ ├── SevenZipHelper.cs │ │ └── ICoder.cs │ ├── LevelMetadata.cs │ ├── LevelScripts.cs │ ├── Patches │ │ └── GamePatch.cs │ ├── Menu │ │ ├── LevelButtons.cs │ │ └── LoadingError.cs │ ├── LevelTimes.cs │ └── LevelLoader.cs ├── PluginInfo.cs ├── KarlsonLevelImporter.csproj └── Base.cs ├── LICENSE ├── THIRDPARTY.md ├── README.md ├── KarlsonLevelImporter.sln └── .gitignore /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jor02/KarlsonLevelImporter/HEAD/preview.gif -------------------------------------------------------------------------------- /KarlsonLevelImporterBanner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jor02/KarlsonLevelImporter/HEAD/KarlsonLevelImporterBanner.png -------------------------------------------------------------------------------- /KarlsonLevelImporter/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "KarlsonLevelImporter": { 4 | "commandName": "Executable", 5 | "executablePath": "%UserProfile%\\Documents\\programs\\Karlson\\Karlson.exe" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /KarlsonLevelImporter/Core/Components/CanPickup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using UnityEngine; 5 | 6 | namespace KarlsonLevelImporter.Core.Components 7 | { 8 | class CanPickup : ComponentBase 9 | { 10 | protected override void StartComp() 11 | { 12 | gameObject.tag = "Gun"; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /KarlsonLevelImporter/Core/Components/ComponentBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using UnityEngine; 5 | 6 | namespace KarlsonLevelImporter.Core.Components 7 | { 8 | abstract class ComponentBase : MonoBehaviour 9 | { 10 | internal bool canStart { get; private set; } = false; 11 | 12 | public void StartComponent() 13 | { 14 | StartComp(); 15 | canStart = true; 16 | } 17 | 18 | protected abstract void StartComp(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /KarlsonLevelImporter/Core/LZMA/Compress/LZ/IMatchFinder.cs: -------------------------------------------------------------------------------- 1 | // IMatchFinder.cs 2 | 3 | using System; 4 | 5 | namespace SevenZip.Compression.LZ 6 | { 7 | interface IInWindowStream 8 | { 9 | void SetStream(System.IO.Stream inStream); 10 | void Init(); 11 | void ReleaseStream(); 12 | Byte GetIndexByte(Int32 index); 13 | UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit); 14 | UInt32 GetNumAvailableBytes(); 15 | } 16 | 17 | interface IMatchFinder : IInWindowStream 18 | { 19 | void Create(UInt32 historySize, UInt32 keepAddBufferBefore, 20 | UInt32 matchMaxLen, UInt32 keepAddBufferAfter); 21 | UInt32 GetMatches(UInt32[] distances); 22 | void Skip(UInt32 num); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /KarlsonLevelImporter/Core/Components/PlayerStart.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using UnityEngine; 5 | 6 | namespace KarlsonLevelImporter.Core.Components 7 | { 8 | class PlayerStart : ComponentBase 9 | { 10 | protected override void StartComp() 11 | { 12 | UnityEngine.Debug.Log("Test"); 13 | 14 | Transform player = GameObject.Find("/Player").transform; 15 | player.position = transform.position + (Vector3.up * 1.4953f); 16 | player.GetComponent().playerCam.rotation = Quaternion.Euler(0, transform.eulerAngles.y, 0); 17 | 18 | Destroy(gameObject); 19 | } 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /KarlsonLevelImporter/PluginInfo.cs: -------------------------------------------------------------------------------- 1 | using KarlsonLevelImporter; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | using System.Text; 6 | 7 | [assembly: AssemblyVersion(PluginInfo.VERSION)] 8 | [assembly: AssemblyTitle(PluginInfo.NAME + " (" + PluginInfo.GUID + ")")] 9 | [assembly: AssemblyProduct(PluginInfo.NAME)] 10 | 11 | namespace KarlsonLevelImporter 12 | { 13 | /// 14 | /// Plugin infomation 15 | /// Based on https://github.com/BepInEx/BepInEx.PluginTemplate/blob/main/src/PluginInfo.cs 16 | /// 17 | internal static class PluginInfo 18 | { 19 | public const string GUID = "com.jor02.karlsonlevelimporter"; 20 | public const string NAME = "Karlson Level Importer"; 21 | public const string VERSION = "1.1"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /KarlsonLevelImporter/Core/LevelMetadata.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace KarlsonLevelImporter.Core 5 | { 6 | [Serializable] 7 | public struct LevelMetadata 8 | { 9 | public string LevelName { get; } 10 | private byte[] Thumbnail; 11 | private byte type; 12 | 13 | public LevelMetadata(string levelName, byte[] thumbnail, byte thumbnailFormat) 14 | { 15 | LevelName = levelName; 16 | Thumbnail = thumbnail; 17 | type = thumbnailFormat; 18 | } 19 | 20 | public Texture2D GetThumbnail() 21 | { 22 | Texture2D thumbnail = new Texture2D(960, 540, (TextureFormat)type, false); 23 | ImageConversion.LoadImage(thumbnail, Thumbnail); 24 | return thumbnail; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /KarlsonLevelImporter/Core/LevelScripts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using UnityEngine; 6 | 7 | namespace KarlsonLevelImporter.Core 8 | { 9 | /// 10 | /// Used for adding modded components to all GameObjects 11 | /// 12 | [Serializable] 13 | public class LevelScripts 14 | { 15 | public List objects = new List(); 16 | 17 | [Serializable] 18 | public class ScriptedObject 19 | { 20 | public string path; 21 | public List components = new List(); 22 | } 23 | 24 | [Serializable] 25 | public class ObjectComponent 26 | { 27 | public string typeName; 28 | public List members = new List(); 29 | } 30 | 31 | [Serializable] 32 | public struct Member 33 | { 34 | public string name; 35 | public string value; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Jor02 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /KarlsonLevelImporter/Core/LZMA/Common/OutBuffer.cs: -------------------------------------------------------------------------------- 1 | // OutBuffer.cs 2 | 3 | namespace SevenZip.Buffer 4 | { 5 | public class OutBuffer 6 | { 7 | byte[] m_Buffer; 8 | uint m_Pos; 9 | uint m_BufferSize; 10 | System.IO.Stream m_Stream; 11 | ulong m_ProcessedSize; 12 | 13 | public OutBuffer(uint bufferSize) 14 | { 15 | m_Buffer = new byte[bufferSize]; 16 | m_BufferSize = bufferSize; 17 | } 18 | 19 | public void SetStream(System.IO.Stream stream) { m_Stream = stream; } 20 | public void FlushStream() { m_Stream.Flush(); } 21 | public void CloseStream() { m_Stream.Close(); } 22 | public void ReleaseStream() { m_Stream = null; } 23 | 24 | public void Init() 25 | { 26 | m_ProcessedSize = 0; 27 | m_Pos = 0; 28 | } 29 | 30 | public void WriteByte(byte b) 31 | { 32 | m_Buffer[m_Pos++] = b; 33 | if (m_Pos >= m_BufferSize) 34 | FlushData(); 35 | } 36 | 37 | public void FlushData() 38 | { 39 | if (m_Pos == 0) 40 | return; 41 | m_Stream.Write(m_Buffer, 0, (int)m_Pos); 42 | m_Pos = 0; 43 | } 44 | 45 | public ulong GetProcessedSize() { return m_ProcessedSize + m_Pos; } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /THIRDPARTY.md: -------------------------------------------------------------------------------- 1 | ## Third party licenses 2 | > **Newtonsoft.JSON** 3 | ``` 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2007 James Newton-King 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![image](./KarlsonLevelImporterBanner.png) 2 | 3 | # Karlson Level Importer 4 | ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/Jor02/KarlsonLevelImporter?style=flat-square&color=brightgreen) 5 | ![Lines of code](https://img.shields.io/tokei/lines/github/Jor02/KarlsonLevelImporter?style=flat-square) 6 | > _A mod for [KARLSON](https://danidev.itch.io/karlson) to import custom levels_ 7 | 8 | ![image](./preview.gif) 9 | 10 | ## Installation 11 | - [Get latest release here](https://github.com/Jor02/KarlsonLevelImporter/releases/latest) 12 | - [Get BepInEx here here](https://github.com/BepInEx/BepInEx/releases/latest) 13 | 14 | 1. Copy the contents of BepInEx_xxx_x.x.x.x.zip into the directory that contains Karlson.exe (`.../Karlson/`) 15 | 2. Copy the contents of CustomLeverImporterMod.zip into the `.../Karlson/BepInEx/plugins/` directory 16 | 17 | ## Creating your own level 18 | - **[Get started here](https://github.com/Jor02/KarlsonLevelImporter/wiki)** 19 | - If you instead want a more detailed guide you can check out **[this (WIP) guide](https://github.com/whyllay/karlson-IL-Guide)** made by whyllay! 20 | 21 | ## Third party licenses 22 | > Please check [Third party licenses](./THIRDPARTY.md) 23 | -------------------------------------------------------------------------------- /KarlsonLevelImporter.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31424.327 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KarlsonLevelImporter", "KarlsonLevelImporter\KarlsonLevelImporter.csproj", "{7A6C2170-CCEB-4750-9E06-EB7BA7AB2673}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {7A6C2170-CCEB-4750-9E06-EB7BA7AB2673}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {7A6C2170-CCEB-4750-9E06-EB7BA7AB2673}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {7A6C2170-CCEB-4750-9E06-EB7BA7AB2673}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {7A6C2170-CCEB-4750-9E06-EB7BA7AB2673}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {EE5F143C-75B5-469E-AFEB-F1CE63158158} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /KarlsonLevelImporter/Core/LZMA/Common/CRC.cs: -------------------------------------------------------------------------------- 1 | // Common/CRC.cs 2 | 3 | namespace SevenZip 4 | { 5 | class CRC 6 | { 7 | public static readonly uint[] Table; 8 | 9 | static CRC() 10 | { 11 | Table = new uint[256]; 12 | const uint kPoly = 0xEDB88320; 13 | for (uint i = 0; i < 256; i++) 14 | { 15 | uint r = i; 16 | for (int j = 0; j < 8; j++) 17 | if ((r & 1) != 0) 18 | r = (r >> 1) ^ kPoly; 19 | else 20 | r >>= 1; 21 | Table[i] = r; 22 | } 23 | } 24 | 25 | uint _value = 0xFFFFFFFF; 26 | 27 | public void Init() { _value = 0xFFFFFFFF; } 28 | 29 | public void UpdateByte(byte b) 30 | { 31 | _value = Table[(((byte)(_value)) ^ b)] ^ (_value >> 8); 32 | } 33 | 34 | public void Update(byte[] data, uint offset, uint size) 35 | { 36 | for (uint i = 0; i < size; i++) 37 | _value = Table[(((byte)(_value)) ^ data[offset + i])] ^ (_value >> 8); 38 | } 39 | 40 | public uint GetDigest() { return _value ^ 0xFFFFFFFF; } 41 | 42 | static uint CalculateDigest(byte[] data, uint offset, uint size) 43 | { 44 | CRC crc = new CRC(); 45 | // crc.Init(); 46 | crc.Update(data, offset, size); 47 | return crc.GetDigest(); 48 | } 49 | 50 | static bool VerifyDigest(uint digest, byte[] data, uint offset, uint size) 51 | { 52 | return (CalculateDigest(data, offset, size) == digest); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /KarlsonLevelImporter/Core/LZMA/Common/InBuffer.cs: -------------------------------------------------------------------------------- 1 | // InBuffer.cs 2 | 3 | namespace SevenZip.Buffer 4 | { 5 | public class InBuffer 6 | { 7 | byte[] m_Buffer; 8 | uint m_Pos; 9 | uint m_Limit; 10 | uint m_BufferSize; 11 | System.IO.Stream m_Stream; 12 | bool m_StreamWasExhausted; 13 | ulong m_ProcessedSize; 14 | 15 | public InBuffer(uint bufferSize) 16 | { 17 | m_Buffer = new byte[bufferSize]; 18 | m_BufferSize = bufferSize; 19 | } 20 | 21 | public void Init(System.IO.Stream stream) 22 | { 23 | m_Stream = stream; 24 | m_ProcessedSize = 0; 25 | m_Limit = 0; 26 | m_Pos = 0; 27 | m_StreamWasExhausted = false; 28 | } 29 | 30 | public bool ReadBlock() 31 | { 32 | if (m_StreamWasExhausted) 33 | return false; 34 | m_ProcessedSize += m_Pos; 35 | int aNumProcessedBytes = m_Stream.Read(m_Buffer, 0, (int)m_BufferSize); 36 | m_Pos = 0; 37 | m_Limit = (uint)aNumProcessedBytes; 38 | m_StreamWasExhausted = (aNumProcessedBytes == 0); 39 | return (!m_StreamWasExhausted); 40 | } 41 | 42 | 43 | public void ReleaseStream() 44 | { 45 | // m_Stream.Close(); 46 | m_Stream = null; 47 | } 48 | 49 | public bool ReadByte(byte b) // check it 50 | { 51 | if (m_Pos >= m_Limit) 52 | if (!ReadBlock()) 53 | return false; 54 | b = m_Buffer[m_Pos++]; 55 | return true; 56 | } 57 | 58 | public byte ReadByte() 59 | { 60 | // return (byte)m_Stream.ReadByte(); 61 | if (m_Pos >= m_Limit) 62 | if (!ReadBlock()) 63 | return 0xFF; 64 | return m_Buffer[m_Pos++]; 65 | } 66 | 67 | public ulong GetProcessedSize() 68 | { 69 | return m_ProcessedSize + m_Pos; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /KarlsonLevelImporter/Core/Patches/GamePatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using HarmonyLib; 5 | using UnityEngine; 6 | using UnityEngine.SceneManagement; 7 | 8 | namespace KarlsonLevelImporter.Core.Patches 9 | { 10 | [HarmonyPatch(typeof(Game), "Win")] 11 | class GamePatch 12 | { 13 | static void Prefix(ref bool ___playing, ref bool ___done) 14 | { 15 | ___playing = false; 16 | Timer.Instance.Stop(); 17 | Time.timeScale = 0.05f; 18 | Cursor.lockState = CursorLockMode.None; 19 | Cursor.visible = true; 20 | UIManger.Instance.WinUI(true); 21 | 22 | float timer = Timer.Instance.GetTimer(); 23 | 24 | if (!LevelLoader.Playing) 25 | { 26 | int sceneIndex = int.Parse(SceneManager.GetActiveScene().name[0].ToString() ?? ""); 27 | int subIndex; 28 | if (int.TryParse(SceneManager.GetActiveScene().name.Substring(0, 2) ?? "", out subIndex)) 29 | { 30 | sceneIndex = subIndex; 31 | } 32 | float lastTime = SaveManager.Instance.state.times[sceneIndex]; 33 | if (timer < lastTime || lastTime == 0f) 34 | { 35 | SaveManager.Instance.state.times[sceneIndex] = timer; 36 | SaveManager.Instance.Save(); 37 | } 38 | } else 39 | { 40 | LevelTimes levelTimes = new LevelTimes(); 41 | 42 | float lastTime = levelTimes.Get(LevelLoader.currentBundlePath); 43 | if (timer < lastTime || lastTime == 0f) 44 | { 45 | levelTimes.Set(LevelLoader.currentBundlePath, timer); 46 | levelTimes.Save(); 47 | } 48 | } 49 | UnityEngine.Debug.Log("time has been saved as: " + Timer.Instance.GetFormattedTime(timer)); 50 | ___done = true; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /KarlsonLevelImporter/Core/Menu/LevelButtons.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using UnityEngine; 5 | using TMPro; 6 | using UnityEngine.UI; 7 | 8 | namespace KarlsonLevelImporter.Core.Menu 9 | { 10 | class LevelButtons : MonoBehaviour 11 | { 12 | private GameObject template; 13 | private Transform menu; 14 | 15 | public void Init(Transform Template, RectTransform alignment) 16 | { 17 | Template.name = "Template"; 18 | 19 | template = Template.gameObject; 20 | template.SetActive(false); 21 | 22 | template.transform.Find("Text (TMP)").GetComponent().enableAutoSizing = true; 23 | 24 | menu = alignment; 25 | } 26 | 27 | public void AddButton(string name, string time, Texture2D thumb, string path, long HeaderSize) 28 | { 29 | GameObject tmpButton = Instantiate(template); 30 | 31 | tmpButton.GetComponent().sprite = Sprite.Create(thumb, new Rect(0.0f, 0.0f, thumb.width, thumb.height), new Vector2(0.5f, 0.5f)); 32 | 33 | tmpButton.transform.Find("Text (TMP)").GetComponent().text = name; 34 | tmpButton.transform.Find("Text (TMP) (1)").GetComponent().text = time; 35 | 36 | Button btn = tmpButton.GetComponent