Łoś assets conversions external library (shortly "Łacel") is a library (package) written in Python 3 for assets and files conversions from the video game "Po prostu Łoś".
The library supports following versions of the game:
(Original archive name) | (Executable file compilation date) | (Version number)
LosV1_03.zip2nd Jul 20021.03los.zip31st Aug 20032.01Los2.zip28th Sep 20032.01Los_murek.zip11th May 20062.01
The library supports following conversions:
*.bar↔*.bmp*.bar↔*.png(requires Pillow package)*.cfg↔*.json*.dat↔ folder/files*.lpl↔*.txt*.pln↔*.json*.zpl↔ folder/files
Following programs and packages must be installed in order to use Łacel:
- python 3 (tested with python 3.10)
- python 3 built-in libraries:
- setuptools (necessary only for
setup.pyscript) - Pillow (tested with Pillow 9.2.0) (optional) (needed for
*.bar↔*.pngconversion)
In order to install Łacel run the following command (Git is required):
pip install git+https://github.com/Mikulus6/lacel.gitNote: Lacel installation may not always install Pillow automatically. Please install it manually if you want to be able to use conversion *.bar ↔ *.png.
Alternatively you can manually install all dependencies and paste the directory lacel into the directory Lib\site-packages.
lacel.arch2dir(archive_name, directory_name)
Reads content of a
*.dator*.zplarchive under given archive_name file path, extracts its content and saves all files and subfolders included in that archive under given directory_name file path.
lacel.bar2bmp(bar_file_name, bmp_file_name)
Reads content of a
*.barimage file under given bar_file_name file path, converts its content to a*.bmpimage file data with colors paletteR5 G6 B5and saves it as a*.bmpfile under given bmp_file_name file path.
lacel.bar2img(bar_file_name, image_file_name, set_black_to_alpha=False)
Reads content of a
*.barimage file under given bar_file_name file path, converts its content to a Pillow Image Object with color palleteR8 G8 B8orR8 G8 B8 A8and saves it as a*.bmpor*.pngfile under given image_file_name file path.If set_black_to_alpha is set to
True, all black pixels will be replaced with transparent pixels.
lacel.bmp2bar(bmp_file_name, bar_file_name)
Reads content of a
*.bmpimage file with colors paletteR5 G6 B5under given bmp_file_name file path, converts its content to*.barimage data and saves it as a*.barfile under given bar_file_name file path.
lacel.cfg2json(cfg_file_name, json_file_name)
Reads content of a
*.cfgfile under given cfg_file_name file path, converts its content to a human-readable list and saves it as a*.jsonfile under given json_file_name file path.
lacel.dir2arch(directory_name, archive_name)
Reads content of a directory under given directory_name file path and archives its content to a
*.dator*.zplarchive under given archive_name file path.Headers of archived files will not contain folder name in their names.
Empty directories will not be archived.
lacel.img2bar(image_file_name, bar_file_name)
Reads content of a
*.bmpor*.pngfile image file under given image_file_name file path, converts its content to a Pillow Image Object with color palleteR5 G6 B5and saves it as a*.barfile under given bar_file_name file path.
lacel.json2cfg(json_file_name, cfg_file_name)
Reads content of a specific
*.jsonfile under given json_file_name file path, converts its content to*.cfgfile data and saves it as a*.cfgfile under given cfg_file_name file path.
lacel.json2pln(json_file_name, pln_file_name)
Reads content of a specific
*.jsonfile under given json_file_name file path, converts its content to a*.plnfile data and saves it as a*.plnfile under given pln_file_name file path.
lacel.list2arch(files_list, archive_name, remove_base_dirs_from_names=False, remove_all_dirs_from_names=False)
Reads content of each file path included in list under given files_list object and archives theirs content to a
*.dator*.zplarchive under given archive_name file path.If remove_base_dirs_from_names is set to
True, headers of archived files will not contain the highest folder names from given file paths.If remove_all_dirs_from_names is set to
True, headers of archived files will contain only the name of given archived files. This option consequently removes any potential directories inside the archive.Empty directories will not be archived.
lacel.lpl2txt(lpl_file_name, txt_file_name)
Reads content of an
*.lplfile under given lpl_file_name file path and copies it to*.txtfile under given txt_file_name file path without any data conversion.
lacel.pln2json(pln_file_name, json_file_name)
Reads content of a
*.plnfile under given pln_file_name file path, converts its content to a human-readable list and saves it as a*.jsonfile under given json_file_name file path.
lacel.txt2lpl(txt_file_name, lpl_file_name)
Reads content of a
*.txtfile under given txt_file_name file path and copies it to*.lplfile under given lpl_file_name file path with potential encoding fixes fromANSI,ASCIIorUTF-8toWindows-1250.Encoding correction may not always work.
Each point/subpoint in following section describes one element of list/sublist included in a specific *.json file.
- Key value "W LEWO" (left) (each key value can be an integer or a specific string in encoding
Windows-1250displayed in game settings. All string values for keys are in filelacel/data/keys.json) - Key value "W PRAWO" (right)
- Key value "W GÓRĘ" (up)
- Key value "W DÓŁ" (down)
- Key value "PURCHAWA" (puffball / bomb)
- Highscores
- First record data
- 1st record numeric value
- names list (current 1st record holder name and all potential corrupted names)
- Second record data
- 2nd record numeric value
- names list (current 2nd record holder name and all potential corrupted names)
- [...]
- Tenth record data
- 10th record numeric value
- names list (current 10th record holder name and all potential corrupted names)
- First record data
- Last played level numeric value
- Sound volume numeric value
- String with garbage data represented by hexadecimal digits
*.cfgfile creation time (formatted asyyyy.mm.dd HH:MM:SS.SSSSSSSin Gregorian Calendar and Coordinated Universal Time)- Key value "PAUZA" (pause)
- Key value "HARAKIRI" (suicide)
- (Optional) Last played package data
- Last played package name
- Two empty bytes
- String of garbage data represented by hexadecimal digits
- (Optional) Music volume numeric value
- (Optional) Key value "ZMIANA MUZYCZKI" (music change)
Config files can contain more than one name per record data.
Whenever a new name with n characters is saved to the config file, first n bytes are overwritten with certain characters and n+1th byte is set to null value. All remaining bytes hold their previous values which may contain older and longer names corrupted by newer and shorter names.
All corrupted bytes are saved as \u0000 in *.json files.
- Stage width numeric value
- Stage height numeric value
- Stage content
- First tile data
- 1st tile numeric value (without including water bit)
- 1st tile water bit
- Second tile data
- 2nd tile numeric value (without including water bit)
- 2nd tile water bit
- [...]
- Last tile data
- Last tile numeric value (without including water bit)
- Last tile water bit
- First tile data
- Required cones numeric value
- Camera blockades data
- First blockade data
- 1st blockade numeric value (without including horizontal bit)
- 1st blockade horizontal bit
- Second blockade data
- 2nd blockade numeric value (without including horizontal bit)
- 2nd blockade horizontal bit
- [...]
- Last blockade data (Maximum number of camera blockades is 10)
- Last blockade numeric value (without including horizontal bit)
- Last blockade horizontal bit
- First blockade data
- Camera blockades breakpoint (should always be equal to 0)
- String encoded in encoding
Windows-1250with stage name (Maximal number of characters is 20)
When reading and writing tile data or blockade data in *.pln files, water bit and horizontal bit values are based on 9th bit of numeric value.
If 9th bit is set to 1, number 2^15 will be subtracted from numeric value and separated bool value will be set to true in *.json file. Otherwise, number 2^15 will not be subtracted and separated bool value will be set to false in *.json file.
Therefore while editing *.json file content, numeric value describing tile or blockade cannot have 9th bit set to 1 while separated bool value is set to false.
While using bar2img() and img2bar() functions, primary colors are converted between R5 G6 B5 and R8 G8 B8 in a way described below by given formulas:
R5 G6 B5 → R8 G8 B8:
red := round(red * 255/31)green := round(green * 255/63)blue := round(blue * 255/31)
R8 G8 B8 → R5 G6 B5:
red := round(red * 31/255)green := round(green * 63/255)blue := round(blue * 31/255)
In order to avoid incorrect text conversion while using json2cfg(), json2pln() and txt2lpl() all *.txt and *.json files should be saved in encoding Windows-1250.
It is recommended to use Notepad++ or similar application while reading or editing *.json files.
Unpack all textures from an archive bary/bary.dat and save them as *.png files.
import lacel
import os
lacel.arch2dir("bary/bary.dat", "bary_new")
for filename in os.listdir("bary_new"):
file_path = os.path.join("bary_new", filename)
if os.path.isfile(file_path) and filename[-4:] == ".bar":
new_filename = filename.replace(".bar", ".png")
new_file_path = os.path.join("images", new_filename)
lacel.bar2img(file_path, new_file_path)Print in-game timer initial values from stages from an archive plansze/los1.dat.
import json
import lacel
import os
lacel.arch2dir("plansze/los1.dat", "plansze/los1_stages")
for filename in os.listdir("plansze/los1_stages"):
file_path = os.path.join("plansze/los1_stages", filename)
if os.path.isfile(file_path) and filename[-4:] == ".pln":
new_filename = filename.replace(".pln", ".json")
new_file_path = os.path.join("plansze/los1_stages/jsons", new_filename)
lacel.pln2json(file_path, new_file_path)
file = open(new_file_path)
json_data = json.load(file)
in_game_time = json_data[4]
file.close()
print(filename + " time = "+str(in_game_time)+"s")
input()Reset highscores in a file los.cfg.
import json
import lacel
lacel.cfg2json("los.cfg", "los.json")
file = open("los.json", 'r')
json_data = json.load(file)
file.close()
highscores_data = json_data[5]
for index_counter in range(len(highscores_data)):
json_data[5][index_counter] = [0, ["---"]]
file = open("los.json", 'w')
file.write(json.dumps(json_data))
file.close()
lacel.json2cfg("los.json", "los.cfg")Instead of using Python itself, it is possible to compile lacel into an executable file and simply run it in the command line.
Parent directory of lacel should contain two given files: compile.cmd and lac.py. By running the first one, an attempt will be made to compiled the second one to lac.exe. (pip must be added to PATH in order to run successfully compile.cmd script.)
It is also possible to download pre-compiled executable file from lacel releases section on GitHub.
Open command line in the directory where lac.exe file is located.
Usage syntax is the following:
lac.exe <command name> <input file> <output file> <*optional arguments>All arguments are interpreted as strings as long as they cannot be interpreted as *.json file content.
Otherwise they are interpreted as *.json file content.
The order of arguments is the same as in the Documentation section.
Run lac.exe without any arguments in order to display a list of available commands.
Extract bary/bary.dat archive to bary/bary folder.
lac.exe arch2dir bary/bary.dat bary/baryConvert bary/bary/alfabet.bar file to bary/bary/alfabet.png image with black pixels set to transparent pixels.
lac.exe bar2img bary/bary/alfabet.bar bary/bary/alfabet.png truePack plansze/plansza_000.pln and plansze/plansza_001.pln into plansze/zestaw.zpl package.
lac.exe list2arch [\"plansze/plansza_000.pln\",\"plansze/plansza_001.pln\"] plansze/zestaw.zplŁacel was created by Mikołaj Walc aka. "Mikulus" (GitHub profile).
This library is a fan-made tool. It is not affiliated with the official legacy of the video game "Po prostu Łoś".
For an archived version of the official "Po prostu Łoś" website, visit baroslaw.republika.pl via web.archive.org.