Ł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.zip
2nd Jul 20021.03
los.zip
31st Aug 20032.01
Los2.zip
28th Sep 20032.01
Los_murek.zip
11th 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.py
script) - Pillow (tested with Pillow 9.2.0) (optional) (needed for
*.bar
↔*.png
conversion)
In order to install Łacel run the following command (Git is required):
pip install git+https://github.com/Mikulus6/lacel.git
Note: 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
*.dat
or*.zpl
archive 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
*.bar
image file under given bar_file_name file path, converts its content to a*.bmp
image file data with colors paletteR5 G6 B5
and saves it as a*.bmp
file under given bmp_file_name file path.
lacel.bar2img(bar_file_name, image_file_name, set_black_to_alpha=False
)
Reads content of a
*.bar
image file under given bar_file_name file path, converts its content to a Pillow Image Object with color palleteR8 G8 B8
orR8 G8 B8 A8
and saves it as a*.bmp
or*.png
file 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
*.bmp
image file with colors paletteR5 G6 B5
under given bmp_file_name file path, converts its content to*.bar
image data and saves it as a*.bar
file under given bar_file_name file path.
lacel.cfg2json(cfg_file_name, json_file_name)
Reads content of a
*.cfg
file under given cfg_file_name file path, converts its content to a human-readable list and saves it as a*.json
file 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
*.dat
or*.zpl
archive 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
*.bmp
or*.png
file image file under given image_file_name file path, converts its content to a Pillow Image Object with color palleteR5 G6 B5
and saves it as a*.bar
file under given bar_file_name file path.
lacel.json2cfg(json_file_name, cfg_file_name)
Reads content of a specific
*.json
file under given json_file_name file path, converts its content to*.cfg
file data and saves it as a*.cfg
file under given cfg_file_name file path.
lacel.json2pln(json_file_name, pln_file_name)
Reads content of a specific
*.json
file under given json_file_name file path, converts its content to a*.pln
file data and saves it as a*.pln
file 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
*.dat
or*.zpl
archive 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
*.lpl
file under given lpl_file_name file path and copies it to*.txt
file under given txt_file_name file path without any data conversion.
lacel.pln2json(pln_file_name, json_file_name)
Reads content of a
*.pln
file under given pln_file_name file path, converts its content to a human-readable list and saves it as a*.json
file under given json_file_name file path.
lacel.txt2lpl(txt_file_name, lpl_file_name)
Reads content of a
*.txt
file under given txt_file_name file path and copies it to*.lpl
file under given lpl_file_name file path with potential encoding fixes fromANSI
,ASCII
orUTF-8
toWindows-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-1250
displayed 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
*.cfg
file creation time (formatted asyyyy.mm.dd HH:MM:SS.SSSSSSS
in 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+1
th 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-1250
with 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/bary
Convert 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 true
Pack 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.