-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6aeb988
commit 6de6247
Showing
25 changed files
with
2,286 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/subprojects/blueprint-compiler |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
-- Internal Modules | ||
local gameConfigFolder = require("extra_modules.programMetadata").folders.gamesConfig | ||
local fsUtils = require("general_modules.fsUtils") | ||
local logSystem = require("general_modules.logSystem") | ||
|
||
-- External Modules | ||
local json = require("dkjson") | ||
|
||
local configManager = {} | ||
|
||
--[[ | ||
Name : function configManager.createGameConfig(gameId) | ||
Description : Creates a config file for a game. | ||
Arg 1 : gameId (string) : The game's Steam ID. | ||
Return : The game's config. | ||
]] | ||
function configManager.createGameConfig(gameId) | ||
-- Grab the default template | ||
local gameConfig = require("config_modules.defaultConfigTemplate") | ||
gameConfig.steamGameId = gameId | ||
|
||
-- Write the new table to the file | ||
local path = string.format("%s/%s.json", gameConfigFolder, gameId) | ||
local file = assert(io.open(path, "w"), "Couldn't open " .. path .. " !") | ||
file:write(json.encode(gameConfig, { indent = false })) | ||
file:close() | ||
|
||
return gameConfig | ||
end | ||
|
||
--[[ | ||
Name : configManager.getGameConfig(gameId) | ||
Description : Retrieves a game's config. | ||
Arg 1 : gameId (string) : The game's Steam ID. | ||
]] | ||
function configManager.getGameConfig(gameId) | ||
-- Check if the config exists | ||
local path = string.format("%s/%s.json", gameConfigFolder, gameId) | ||
if not fsUtils.exists(path) then | ||
local warningMsg = "Config for game " .. gameId .. " at " .. path .. " doesn't exist. Creating it..." | ||
logSystem.log("warning", warningMsg) | ||
return configManager.createGameConfig(gameId) | ||
end | ||
|
||
-- Read the file | ||
local file = assert(io.open(path, "r"), "Couldn't open " .. path .. " !") | ||
local fileContent = file:read("*a") | ||
file:close() | ||
|
||
return json.decode(fileContent) | ||
end | ||
|
||
--[[ | ||
Name : function configManager.modifyGameConfig(gameId, dataToChange) | ||
Description : Modifies a game's config. | ||
Arg 1 : gameId (string) : The game's Steam ID. | ||
Arg 2 : dataToChange (string) : The data to change. Example : "steamGameId" | ||
Arg 3 : value (any) : The value to set. | ||
Return : The game's config. | ||
]] | ||
function configManager.modifyGameConfig(gameId, dataToChange, value) | ||
-- Grab the original config data | ||
local gameConfig = configManager.getGameConfig(gameId) | ||
|
||
-- Parse dataToChange | ||
local keys = {} | ||
for substring in dataToChange:gmatch("[^.]+") do | ||
keys[#keys + 1] = substring | ||
end | ||
|
||
-- Modify the table | ||
local pointer = gameConfig | ||
for i = 1, #keys - 1 do | ||
pointer = pointer[keys[i]] or {} | ||
end | ||
pointer[keys[#keys]] = value | ||
|
||
-- Write the new table | ||
local path = string.format("%s/%s.json", gameConfigFolder, gameId) | ||
local file = assert(io.open(path, "w"), "Couldn't open " .. path .. " !") | ||
|
||
file:write(json.encode(gameConfig)) | ||
file:close() | ||
|
||
return gameConfig | ||
end | ||
|
||
return configManager |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
return | ||
{ | ||
["config_version"] = 1; | ||
["steamGameId"] = nil; | ||
["utilities"] = { | ||
["gamemode"] = { | ||
["enabled"] = false; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
For *"fun"*, I have compiled a list of every single problem I consider shouldn't have happened that I've encountered anyways, going through Steam's configuration files. | ||
|
||
I put them in order, from ***most annoying*** to *least annoying*. | ||
|
||
This is meant to warn people to not create code that may cross these issues. | ||
|
||
___ | ||
|
||
# Problems | ||
|
||
## Linux games don't get removed from *compat.vdf* when going back from their Windows version. | ||
|
||
### Conclusion | ||
|
||
We have no way from Steam's configuration files to know if a game that ever had a Windows version installed now has its Linux version installed. | ||
|
||
I looked everywhere I could for an alternative configuration file (or a file that could be useful through another method), but came empty-handed. | ||
|
||
### Workaround | ||
|
||
We check for Linux executables or shell scripts in every Windows game's root directory. | ||
|
||
We previously used in a loop | ||
|
||
```lua | ||
os.execute("file "..location.."/"..file.." | grep -e 'Linux' -e 'shell' &> /dev/null") | ||
``` | ||
|
||
to detect those. | ||
|
||
However, ironically, this method led us into another problem later on, which required us to scrap the os.execute approach. | ||
|
||
## *libraryfolders.vdf* doesn't get immediately updated upon game installation/uninstallation. | ||
|
||
### Conclusion | ||
|
||
This problem effectively makes it completely unreliable (and thus useless) to gather the game IDs stored inside it. | ||
|
||
### Workaround | ||
|
||
As a workaround, we look in the library folders for *appmanifest_ID.acf* files, and use them to guess the installed game IDs and gather the data. | ||
|
||
## Steam can get stuck trying to start a game when using *os.execute* many times. | ||
|
||
### Workaround | ||
|
||
Use os.execute as little as possible if launching a game. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
return { | ||
name="SimpleSteamTinker", | ||
executable="sst", | ||
version="indev", | ||
developer="JordanViknar", | ||
url = "https://github.com/JordanViknar/SimpleSteamTinker", | ||
installdir = os.getenv("SST_SCRIPT_PATH"), | ||
folders = { | ||
config = os.getenv("HOME").."/.config/SimpleSteamTinker", | ||
gamesConfig = os.getenv("HOME").."/.config/SimpleSteamTinker/games", | ||
|
||
storage = os.getenv("HOME").."/.local/share/SimpleSteamTinker", | ||
cache = os.getenv("HOME").."/.cache/SimpleSteamTinker" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
-- External Modules | ||
local lfs = require("lfs") | ||
|
||
-- Internal Modules | ||
local logSystem = require("general_modules.logSystem") | ||
|
||
-- Module | ||
local fsUtils = {} | ||
|
||
--[[ | ||
Name : function fsUtils.exists(path) | ||
Description : Checks if a file or directory exists. | ||
Arg 1 : path (string) : The path to check. | ||
Return : true if the file or directory exists, false otherwise. | ||
]] | ||
function fsUtils.exists(path) | ||
local attributes, err = lfs.attributes(path) | ||
if attributes then | ||
return true | ||
else | ||
return false, err | ||
end | ||
end | ||
|
||
--[[ | ||
Name : function fsUtils.createDirectory(path) | ||
Description : Creates a directory. | ||
Arg 1 : path (string) : The path to create. | ||
Return : true if the directory was created, false otherwise. | ||
]] | ||
function fsUtils.createDirectory(path) | ||
local success, err = lfs.mkdir(path) | ||
if success then | ||
return true | ||
else | ||
return false, err | ||
end | ||
end | ||
|
||
--[[ | ||
Name : function fsUtils.createOrUseDirectory(path) | ||
Description : Creates a directory if it doesn't exist, or uses it if it does. | ||
Arg 1 : path (string) : The path to create or use. | ||
Return : true if the directory was created or used, false otherwise. | ||
]] | ||
function fsUtils.createOrUseDirectory(path) | ||
if not fsUtils.exists(path) then | ||
logSystem.log("debug", "Directory "..path.." not found. Creating it...") | ||
return fsUtils.createDirectory(path) | ||
else | ||
return true | ||
end | ||
end | ||
|
||
--[[ | ||
Name : function fsUtils.getSize(path) | ||
Description : Gets the size of a file or directory. | ||
Arg 1 : path (string) : The path to get the size of. | ||
Return : The size of the file or directory. | ||
]] | ||
function fsUtils.getSize(path) | ||
local totalSize = 0 | ||
local stack = { path } -- Initialize a stack with the initial path | ||
|
||
while #stack > 0 do | ||
local currentPath = table.remove(stack) -- Get the last path from the stack | ||
|
||
local attributes = lfs.attributes(currentPath) | ||
if attributes then | ||
if attributes.mode == "file" then | ||
totalSize = totalSize + attributes.size -- Add file size to total size | ||
elseif attributes.mode == "directory" then | ||
for file in lfs.dir(currentPath) do | ||
if file ~= "." and file ~= ".." then | ||
local filePath = currentPath .. "/" .. file | ||
table.insert(stack, filePath) -- Add subdirectories to the stack | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
return totalSize | ||
end | ||
|
||
--[[ | ||
Name : function fsUtils.sizeToUnit(size) | ||
Description : Converts a size in bytes to a human-readable size. | ||
Arg 1 : size (number) : The size to convert. | ||
Return : The human-readable size. | ||
]] | ||
function fsUtils.sizeToUnit(size) | ||
local unit = "B" | ||
if size > 1024 then | ||
size = size / 1024 | ||
unit = "KB" | ||
end | ||
if size > 1024 then | ||
size = size / 1024 | ||
unit = "MB" | ||
end | ||
if size > 1024 then | ||
size = size / 1024 | ||
unit = "GB" | ||
end | ||
if size > 1024 then | ||
size = size / 1024 | ||
unit = "TB" | ||
end | ||
return string.format("%.2f", size).." "..unit | ||
end | ||
|
||
--[[ | ||
Name : function fsUtils.directoryContainsLinuxData(location) | ||
Description : Checks if a directory contains Linux data. | ||
Arg 1 : location (string) : The directory to check. | ||
Return : true if the directory contains Linux data, false otherwise. | ||
Note : Used to for game platform detection to work around Steam not cleaning up the compat.vdf file when going back from the Windows version of a game to the Linux version. | ||
Note 2 : The reason we don't use os.execute(file) is because Steam can get stuck while starting a game for whatever reason. | ||
]] | ||
-- Alternative to using file command | ||
local function checkFileForKeywords(location, file) | ||
local filepath = location .. "/" .. file | ||
|
||
local fileHandle = io.open(filepath, "r") | ||
if not fileHandle then | ||
return false | ||
end | ||
|
||
local foundKeywords = false | ||
|
||
for line in fileHandle:lines() do | ||
if line:find("Linux") or line:find("shell") then | ||
foundKeywords = true | ||
break | ||
end | ||
end | ||
|
||
fileHandle:close() | ||
|
||
return foundKeywords | ||
end | ||
|
||
function fsUtils.directoryContainsLinuxData(location) | ||
for file in lfs.dir(location) do | ||
if (file:find(".sh") or not file:find(".")) and (file ~= "." or "..") then | ||
local containsKeywords = checkFileForKeywords(location, file) | ||
if containsKeywords then | ||
return true | ||
end | ||
end | ||
end | ||
|
||
return false | ||
end | ||
|
||
--[[ | ||
Name : function fsUtils.getFilenamePatternInDirectory(directory, pattern) | ||
Description : Returns a table containing every filename matching a pattern in a directory. | ||
Arg 1 : directory (string) : The directory to search in. | ||
Arg 2 : pattern (string) : The pattern to search for. | ||
Return : A table containing every filename matching the pattern. | ||
Note : Used to for game detection to work around Steam not updating the libraryfolders.vdf file immediately. | ||
]] | ||
function fsUtils.getFilenamePatternInDirectory(directory, pattern) | ||
local result = {} | ||
|
||
for file in lfs.dir(directory) do | ||
if file:match(pattern) then | ||
table.insert(result, file) | ||
end | ||
end | ||
|
||
return result | ||
end | ||
|
||
return fsUtils |
Oops, something went wrong.