From d7cfa257e9f2da87df57fe7dfc24f68304f7e1e3 Mon Sep 17 00:00:00 2001 From: igor Date: Tue, 9 Jan 2018 21:12:03 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BF=D0=BE=20=D0=BB=D0=B8?= =?UTF-8?q?=D0=B1=D0=B5=20AdvancedLua?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Applications.cfg | 8 +- Applications/MultiScreen/MultiScreen.lua | 2 +- Applications/Weather/Weather.lua | 2 +- Documentation/advancedLua.md | 377 +++++++++++++++++++++++ lib/FormatModules/OCIF.lua | 6 +- lib/GUI.lua | 4 +- lib/advancedLua.lua | 50 +-- 7 files changed, 413 insertions(+), 36 deletions(-) create mode 100644 Documentation/advancedLua.md diff --git a/Applications.cfg b/Applications.cfg index 334ca958..01b7e6d2 100644 --- a/Applications.cfg +++ b/Applications.cfg @@ -253,7 +253,7 @@ url="https://raw.githubusercontent.com/IgorTimofeev/OpenComputers/master/lib/advancedLua.lua", type="Library", preloadFile=true, - version=1.31, + version=1.32, }, { path="/lib/web.lua", @@ -286,7 +286,7 @@ url="https://raw.githubusercontent.com/IgorTimofeev/OpenComputers/master/lib/FormatModules/OCIF.lua", type="Library", preloadFile=true, - version=1.03, + version=1.04, }, { path="/lib/FormatModules/OCAF.lua", @@ -319,7 +319,7 @@ url="https://raw.githubusercontent.com/IgorTimofeev/OpenComputers/master/lib/GUI.lua", type="Library", preloadFile=true, - version=2.09, + version=2.10, }, { path="/lib/rayEngine.lua", @@ -605,7 +605,7 @@ type="Application", icon="https://raw.githubusercontent.com/IgorTimofeev/OpenComputers/master/Applications/Weather/Icon.pic", createShortcut=true, - version=1.19, + version=1.20, resources={ { path="/Cloudy.pic", diff --git a/Applications/MultiScreen/MultiScreen.lua b/Applications/MultiScreen/MultiScreen.lua index 9bed5878..f50c75ce 100644 --- a/Applications/MultiScreen/MultiScreen.lua +++ b/Applications/MultiScreen/MultiScreen.lua @@ -264,7 +264,7 @@ local function drawBigImageFromOCIFRawFile(x, y, path) local background = color.to24Bit(string.byte(file:read(1))) local foreground = color.to24Bit(string.byte(file:read(1))) file:read(1) - local symbol = string.readUnicodeChar(file) + local symbol = fs.readUnicodeChar(file) multiScreenSet(x + i - 1, y + j - 1, background, foreground, symbol) end diff --git a/Applications/Weather/Weather.lua b/Applications/Weather/Weather.lua index ff44e123..3cc39a86 100755 --- a/Applications/Weather/Weather.lua +++ b/Applications/Weather/Weather.lua @@ -101,7 +101,7 @@ local function updateForecast(city) object.draw = function() bigLetters.drawText(object.x, object.y, 0xFFFFFF, math.round((currentDay.temp.max + currentDay.temp.min) / 2) .. "°") buffer.text(object.x, object.y + 6, 0xFFFFFF, result.city.name .. ", " .. result.city.country) - buffer.text(object.x, object.y + 7, 0xFFFFFF, "Population: " .. math.shortenNumber(result.city.population, 2)) + buffer.text(object.x, object.y + 7, 0xFFFFFF, "Population: " .. math.shorten(result.city.population, 2)) end y = y + object.height + 1 diff --git a/Documentation/advancedLua.md b/Documentation/advancedLua.md new file mode 100644 index 00000000..13765c7f --- /dev/null +++ b/Documentation/advancedLua.md @@ -0,0 +1,377 @@ +| Содержание | +| ----- | +| [О библиотеке](#О-библиотеке) | +| [Установка](#Установка) | +| [Глобальные функции](#Глобальные-функции) | +| [Дополнения библиотеки table](#Дополнения-библиотеки-table) | +| [Дополнения библиотеки string](#Дополнения-библиотеки-string) | +| [Дополнения библиотеки math](#Дополнения-библиотеки-math) | +| [Дополнения библиотеки filesystem (OpenOS)](#Дополнения-библиотеки-filesystem-openos) | + +О библиотеке +====== +AdvancedLua - библиотека, дополняющая стандартные библиотеки Lua и OpenOS отсутствующими, однако крайне необходимыми в быту функциями: быстрой сериализацией таблиц, определением текущего исполняемого скрипта, переносом строк, округлением чисел, получением сортированных файловых списков, различными методами обработки бинарных данных и т.п. + +Установка +====== + +Исходный код доступен по ссылке: https://github.com/IgorTimofeev/OpenComputers/blob/master/lib/advancedLua.lua + +Для загрузки на компьютер вы можете воспользоваться стандартной утилитой **wget**: + + wget https://raw.githubusercontent.com/IgorTimofeev/OpenComputers/master/lib/advancedLua.lua -f + +Глобальные функции +====== + +**getCurrentScript**( ): *string* path +----------------------------------------------------------- + +Функция возвращает путь к текущему исполняемому скрипту. К примеру, если запустить файл /Test/Main.lua с содержимым + +```lua +print("Путь к текущему скрипту: " .. getCurrentScript()) +``` +То в результате на экране будет отображена следующая строка: + +```lua +Путь к текущему скрипту: /Test/Main.lua +``` + +**enum**( ... ): *table* associativeResult +----------------------------------------------------------- + +Функция принимает строковые аргументы и возвращает таблицу с этими аргументами в виде ключей, а также с их порядковым номером в виде значений. Данная функция создана для удобства, когда нет желания вручную изменять значения полей на нужные: + +```lua +local alignment = enum( + "left", + "center", + "right" +) +``` + +В результате таблица alignment будет иметь следующий вид: + +```lua +{ + left = 1, + center = 2, + right = 3 +} +``` + +Дополнения библиотеки table +====== + +table.**serialize**( tableToSerialize, [ pretty, indentationWidth, indentUsingTabs, recursionStackLimit ] ): *string* result +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *table* | tableToSerialize | Таблица, которую необходимо сериализовать | +| *boolean* | pretty | Опциональный аргумент, сериализующий таблицу для наилучшего визуального восприятия человеком. По умолчанию имеет значение false | +| *int* | indentationWidth | Опциональный аргумент, отвечающий за ширину отступа в символах при сериализации в режиме **pretty** | +| *boolean* | indentUsingTabs | Опциональный аргумент, отвечающий за выбор символа отступа при сериализации в режиме **pretty**. По умолчанию имеет значение false | +| *int* | recursionStackLimit | Опциональный аргумент, отвечающий за ограничение стека рекурсии при сериализации таблиц большого размера | + +Метод изначально создан в качестве быстрой альтернативы /lib/serialization.lua, поставляемой OpenOS. Он преобразует содержимое таблицы в строку и крайне удобен для сохранения конфигов различного ПО в понятном для человека виде с сохранением исходной структуры таблицы. Для примера рассмотрим следующий код: + +```lua +local myTable = { + "Hello", + "world", + abc = 123, + def = "456", + ghi = { + jkl = true, + } +} + +print("Обычная сериализация: " .. table.serialize(myTable)) +print(" ") +print("Красивая сериализация: " .. table.serialize(myTable, true)) +``` + +В результате выполнения скрипта на экране отобразится следующее: + +```lua +Обычная сериализация: {[1]="Hello",[2]="world",["abc"]=123,["def"]="456",["ghi"]={["jkl"]=true}} + +Красивая сериализация: { + [1] = "Hello", + [2] = "world", + abc = 123, + def = "456", + ghi = { + jkl = true, + } +} +``` + +Обращаю ваше внимание, что аргумент **pretty** выполняет несколько дополнительных проверок на тип ключей и значений таблицы, а также генерирует символы переноса строки после каждого значения. Поэтому используйте его только в том случае, когда читабельность результата стоит в приоритете над производительностью. + +table.**unserialize**( text ): *table or nil* result, *string* reason +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *string* | text | Строка, созданная методом table.**serialize**() | + +Метод пытается десериализовать строковое представление lua-таблицы и вернуть результат. Если это невозможно, то возвращается nil и строка с причиной синтаксической ошибки. Для примера выполним следующий код: + +```lua +local result = table.unserialize("{ abc = 123 }") +``` + +В результате таблица result будет иметь следующее содержимое: + +```lua +{ + abc = 123 +} +``` + +table.**toFile**( path, ... ) +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *string* | path | Путь к файлу, в который необходимо записать сериализованный результат | +| - | ... | Множество аргументов, принимаемых функцией table.**serialize**(...) | + +Метод аналогичен table.**serialize**(...), однако вместо возвращения строкового результата записывает его в файл. Он крайне удобен для быстрого сохранения конфига ПО без излишних заморочек. + +table.**fromFile**( path ): *string* result +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *string* | path | Путь к файлу, содержимое которого необходимо десериализовать | + +Метод аналогичен table.**unserialize**(...), однако строковое содержимое он читает напрямую из существующего файла, возвращая десериализованный результат. Опять же, по большей части он применяется для удобного сохранения конфигов ПО. + +table.**size**( t ): *int* result +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *table* | t | Таблица, число ключей которой необходимо вычислить | + +Метод возвращает число ключей переданной таблицы. Отличается от варианта #t тем, что подсчитывает также ненумерические индексы + +table.**contains**( t, object ): *boolean* result +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *table* | t | Таблица, в которой будет осуществлен поиск объекта | +| *var* | object | Объект, наличие которого в таблице необходимо проверить | + +Метод определяет, присутствует ли объект в таблице и возвращает результат + +table.**indexOf**( t, object ): *var* result +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *table* | t | Таблица, в которой будет осуществлен поиск объекта | +| *var* | object | Объект, индекс которого необходимо определить | + +Метод возвращает индекс (ключ) переданного объекта. Тип индекса может быть различным в зависимости от структуры таблицы: к примеру, в таблице {abc = 123} число 123 имеет строковый индекс abc + +table.**copy**( t ): *table* result +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *table* | t | Таблица, которую необходимо сдублирвать | + +Метод рекурсивно копирует содержимое таблицы t в новую и возвращает результат. Обращаю внимание на то, что таблицы, ссылающиеся сами на себя, не поддерживаются (ограничение рекурсии по аналогии с table.**serialize**() пилить было оч оч лень, прости <3) + +Дополнения библиотеки string +====== + +string.**limit**( s, limit, mode, noDots ): *string* result +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *string* | s | Строка, длину которой необходимо ограничить | +| *int* | limit | Максимальная длину строки | +| *string* | mode | Режим ограничения для вставки символа многоточия. Может принимать значения **left**, **center** или **right** | +| *boolean* | noDots | Режим ограничения строки классической обрезкой без использования символа многоточия | + +Метод ограничивает строку, вставляя символ многоточия в необходимом месте и возвращая результат. Для примера запустим код: + +```lua +print("Ограничение слева: " .. string.limit("HelloBeautifulWorld", 10, "left")) +print("Ограничение по центру: " .. string.limit("HelloBeautifulWorld", 10, "center")) +print("Ограничение справа: " .. string.limit("HelloBeautifulWorld", 10, "right")) +``` + +В результате на экране будет отображено следующее: + +```lua +Ограничение слева: …ifulWorld +Ограничение по центру: Hello…orld +Ограничение справа: HelloBeau… +``` + +string.**wrap**( s, wrapWidth ): *table* result +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *string/string[]* | s | Строка либо массив строк, которые необходимо перенести по указанной ширине | +| *int* | wrapWidth | Максимальная ширина строки, на которую следует ориентироваться при переносе | + +Метод осуществляет перенос строк по указанной ширине, возвращая таблицу с результатом. Если размер отдельно взятого слова превышает указанную ширину, то слово будет "разрезано" на составляющие части + +string.**unicodeFind**( ... ): ... +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| - | ... | Множество аргументов, аналогичных таковым для функции string.**find**(...) | + +Метод аналогичен string.**find*(...), однако позволяет работать с юникодом. Незаменимая штука для русскоговорящей аудитории! + +Дополнения библиотеки math +====== + +math.**round**( number ): *float* result +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *float* | number | Число, которое необходимо округлить | + +Метод округляет число до ближайшего целого и возвращает результат + +math.**roundToDecimalPlaces**( number, decimalPlaces ): *float* result +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *float* | number | Число, которое необходимо округлить | +| *int* | decimalPlaces | Число знаков после запятой у округляемого числа | + +Метод округляет число до ближайшего целого, лимитируя результат до указаннонного числа знаков после запятой и возвращает результат + +math.**shorten**( number, decimalPlaces ): *string* result +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *int* | number | Число, которое необходимо визуально сократить | +| *int* | decimalPlaces | Число знаков после запятой у результата | + +Метод преобразует входное число в строку с приставками "**K**", "**M**", "**B**" и "**T**" в завимости от размера числа. Для примера выполним код: + +```lua +print("Сокращенный результат: " .. math.shortenNumber(13484381, 2)) +``` + +В результате на экране отобразится следующее: + +```lua +Сокращенный результат: 13.48M +``` + +math.**getDigitCount**( number ): *int* digitCount +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *int* | number | Число, количество десятичных знаков которого необходимо вычислить | + +Метод возвращает количество десятичных знаков в числе. К примеру, в числе **512** их **3**, в числе **1888** их **4** + +Дополнения библиотеки bit32 +====== + +bit32.**merge**( number1, number2 ): *int* result +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *int* | number1 | Первое число для склейки | +| *int* | number2 | Второе число для склейки | + +Метод "склеивает" два числа и возвращает результат. К примеру, вызов метода с аргументами **0xAA** и **0xBB** вернет число **0xAABB** + +bit32.**numberToByteArray**( number ) : *table* byteArray +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *int* | number | Число, которое необходимо преобразовать в байт-массив | + +Метод извлекает составляющие байты из числа и возвращает таблицу с ними. К примеру, вызов метода с аргументом **0xAABBCC** вернет таблицу **{0xAA, 0xBB, 0xCC}** + +bit32.**numberToFixedSizeByteArray**( number, arraySize ) : *table* byteArray +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *int* | number | Число, которое необходимо преобразовать в байт-массив | +| *int* | arraySize | Фиксированный размер выходного массива | + +Метод аналогичен bit32.**numberToByteArray**(), однако размер выходного массива указывается вручную. Если количество байт в числе меньше указанного размера, то выходной массив будет дополнен отсутствующими нулями, в противном случае массив заполнится лишь частью байт числа. К примеру, вызов метода с аргументами **0xAABBCC** и **5** вернет таблицу **{0x00, 0x00, 0xAA, 0xBB, 0xCC}** + +bit32.**byteArrayToNumber**( byteArray ) : *int* number +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *int* | number | Байт-массив, который необходимо преобразовать в число | + +Метод преобразует байты из переданного массива в целое число. К примеру, вызов метода с аргументом **{0xAA, 0xBB, 0xCC}** вернет число **0xAABBCC** + +bit32.**byteArrayToNumber**( byteArray ) : *int* number +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *int* | number | Байт-массив, который необходимо преобразовать в число | + +Метод преобразует байты из переданного массива в целое число. К примеру, вызов метода с аргументом **{0xAA, 0xBB, 0xCC}** вернет число **0xAABBCC** + + +Дополнения библиотеки filesystem (OpenOS) +====== + +filesystem.**extension**( path ): *string* result +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *string* | path | Путь к файлу, расширение которого необходимо получить | + +Метод возвращает строковое расширение файла по указанному пути. К примеру, для файла **/Test/HelloWorld.lua** будет возвращена строка **.lua** + +filesystem.**hideExtension**( path ): *string* result +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *string* | path | Путь к файлу, расширение которого необходимо скрыть | + +Метод скрывает расширение файла по указанному пути (если оно имеется) и возвращает строковый результат. К примеру, для файла **/Test/HelloWorld.lua** будет возвращена строка **/Test/HelloWorld** + +filesystem.**isFileHidden**( path ): *boolean* result +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *string* | path | Путь к файлу, скрытость которого необходимо проверить | + +Метод проверяет, является ли файл скрытым (т.е. начинается ли его название с символа "**.**") и возвращает результат. К примеру, для файла **/Test/.Hello.lua** будет возвращено **true** + +filesystem.**sortedList**(path, sortingMethod, [ showHiddenFiles, filenameMatcher, filenameMatcherCaseSensitive ] ): *table* fileList +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *string* | path | Путь к директории, список файлов которой необходимо получить | +| *string* | sortingMethod | Метод сортировки файлов. Может принимать значения **name**, **type** и **date** | +| *boolean* | showHiddenFiles | Опциональный аргумент, отвечающий за включение в список скрытых файлов. По умолчанию имеет значение false | +| *string* | filenameMatcher | Опциональный аргумент, представляющий из себя регулярное выражение, по которому будут отсекаться файлы из списка. К примеру, выражение "**%d+%.lua**" будет включать в список только файлы с расширением **.lua** и имеющие в название исключительно цифры | +| *string* | filenameMatcherCaseSensitive | Опциональный аргумент, позволяющий игнорировать регистр символов при использовании аргумента **filenameMatcher** | + +Метод получает список файлов из указанной директории в соответствии с переданными условиями, сортируя их определенным методом. Возвращаемый результат представляет собой классическую нумерически индексированную таблицу: + +```lua +{ + "bin/", + "lib/", + "home/", + "Main.lua" +} +``` + + +filesystem.**readUnicodeChar**( fileHandle ): *string* char +----------------------------------------------------------- +| Тип | Аргумент | Описание | +| ------ | ------ | ------ | +| *handle* | fileHandle | Открытый файловый дескриптор в бинарном режиме (**rb**) | + +Метод читает из файлового дескриптора юникод-символ, используя бинарные операции. Поскольку "чистый" Lua не позволяет работать с юникод-символами при чтении файлов в текстовом режиме, то этот метод крайне полезен при написании собственных форматов данных. Отмечу, что для успешного чтения вы должны быть уверены, что читаемые байт-последовательности из дескриптора **гарантированно** соответствует какому-либо юникод-символу \ No newline at end of file diff --git a/lib/FormatModules/OCIF.lua b/lib/FormatModules/OCIF.lua index af32bcb4..ac26c88b 100755 --- a/lib/FormatModules/OCIF.lua +++ b/lib/FormatModules/OCIF.lua @@ -5,7 +5,7 @@ local image = args[1] ---------------------------------------- Libraries ---------------------------------------- local bit32 = require("bit32") -local advancedLua = require("advancedLua") +require("advancedLua") local unicode = require("unicode") local fs = require("filesystem") local color = require("color") @@ -55,7 +55,7 @@ encodingMethods.load[5] = function(file, picture) table.insert(picture, color.to24Bit(string.byte(file:read(1)))) table.insert(picture, color.to24Bit(string.byte(file:read(1)))) table.insert(picture, string.byte(file:read(1)) / 255) - table.insert(picture, string.readUnicodeChar(file)) + table.insert(picture, fs.readUnicodeChar(file)) end end @@ -121,7 +121,7 @@ encodingMethods.load[6] = function(file, picture) symbolSize = readNumberFromFile(file, 2) for symbol = 1, symbolSize do - currentSymbol = string.readUnicodeChar(file) + currentSymbol = fs.readUnicodeChar(file) backgroundSize = string.byte(file:read(1)) for background = 1, backgroundSize do diff --git a/lib/GUI.lua b/lib/GUI.lua index 8bb449de..20d17b0e 100755 --- a/lib/GUI.lua +++ b/lib/GUI.lua @@ -380,7 +380,7 @@ local function containerStartEventHandling(container, eventHandlingDelay) local eventData, animationIndex, animation, animationOnFinishMethods repeat - eventData = {event.pull(container.animations and 0 or container.eventHandlingDelay)} + eventData = { event.pull(container.animations and 0 or container.eventHandlingDelay) } containerHandler( ( @@ -1094,7 +1094,7 @@ local function getAxisValue(number, postfix, roundValues) local integer, fractional = math.modf(number) local firstPart, secondPart = "", "" if math.abs(integer) >= 1000 then - return math.shortenNumber(integer, 2) .. postfix + return math.shorten(integer, 2) .. postfix else if math.abs(fractional) > 0 then return string.format("%.2f", number) .. postfix diff --git a/lib/advancedLua.lua b/lib/advancedLua.lua index 2d082e95..46529b6d 100755 --- a/lib/advancedLua.lua +++ b/lib/advancedLua.lua @@ -126,7 +126,7 @@ function math.doubleToString(num, digitCount) return string.format("%." .. (digitCount or 1) .. "f", num) end -function math.shortenNumber(number, digitCount) +function math.shorten(number, digitCount) local shortcuts = { "K", "M", @@ -146,13 +146,13 @@ end ---------------------------------------------- Filesystem extensions ------------------------------------------------------------------------ --- function filesystem.path(path) --- return path:match("^(.+%/).") or "" --- end +function filesystem.path(path) + return path:match("^(.+%/).") or "" +end --- function filesystem.name(path) --- return path:match("%/?([^%/]+)%/?$") --- end +function filesystem.name(path) + return path:match("%/?([^%/]+)%/?$") +end function filesystem.extension(path, lower) local extension = path:match("[^%/]+(%.[^%/]+)%/?$") @@ -258,6 +258,24 @@ function filesystem.directorySize(path) return size end +function filesystem.readUnicodeChar(file) + local byteArray = {string.byte(file:read(1))} + + local nullBitPosition = 0 + for i = 1, 7 do + if bit32.band(bit32.rshift(byteArray[1], 8 - i), 0x1) == 0x0 then + nullBitPosition = i + break + end + end + + for i = 1, nullBitPosition - 2 do + table.insert(byteArray, string.byte(file:read(1))) + end + + return string.char(table.unpack(byteArray)) +end + -------------------------------------------------- Table extensions -------------------------------------------------- local function doSerialize(array, prettyLook, indentationSymbol, indentationSymbolAdder, equalsSymbol, currentRecusrionStack, recursionStackLimit) @@ -476,24 +494,6 @@ function string.brailleChar(a, b, c, d, e, f, g, h) return unicode.char(10240 + 128*h + 64*g + 32*f + 16*d + 8*b + 4*e + 2*c + a) end -function string.readUnicodeChar(file) - local byteArray = {string.byte(file:read(1))} - - local nullBitPosition = 0 - for i = 1, 7 do - if bit32.band(bit32.rshift(byteArray[1], 8 - i), 0x1) == 0x0 then - nullBitPosition = i - break - end - end - - for i = 1, nullBitPosition - 2 do - table.insert(byteArray, string.byte(file:read(1))) - end - - return string.char(table.unpack(byteArray)) -end - function string.canonicalPath(str) return string.gsub("/" .. str, "%/+", "/") end