Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a070399f9b | |||
|
|
d4bde012d8 | ||
|
|
e1847dc108 | ||
|
|
0bcd814cc6 | ||
|
|
b6a15f5a77 | ||
|
|
ef4eab02d6 | ||
|
|
57b40f4082 | ||
|
|
556695535f | ||
|
|
16abd571a7 | ||
|
|
df082ab3cc |
@@ -0,0 +1,3 @@
|
||||
This repo is hosted at https://gitea.tangentfox.com/tangent/RGB and force-pushed to GitHub when commits are made.
|
||||
|
||||
Feel free to submit pull requests or whatever, but know that I would have to manage them over there.
|
||||
@@ -0,0 +1,248 @@
|
||||
love.math.setRandomSeed(os.time())
|
||||
|
||||
local input = require "util.input"
|
||||
|
||||
-- Gamestates
|
||||
--local won = require "gamestates.won" --TODO MAKE THIS
|
||||
local lost = require "gamestates.lost"
|
||||
local paused = require "gamestates.paused"
|
||||
|
||||
-- This Gamestate
|
||||
local game = {}
|
||||
|
||||
-- Locals
|
||||
local boxes = {}
|
||||
local score, totalScore = 0, 0
|
||||
local level, time, startingTime = 0, 0, 0
|
||||
local previousState, gameSettings, controls
|
||||
local pingTimer, session = 0, false
|
||||
--these are defined on each entry to this gamestate
|
||||
local screenWidth, screenHeight --defines where things are rendered
|
||||
local boxColumns, boxRows --defines how many boxes
|
||||
--these are loaded from values passed on entry to this gamestate
|
||||
local boxSize, colorStep, timeLimit = 20, 80, 60 --default values just in case
|
||||
|
||||
local function nextLevel()
|
||||
log("Creating the next level!")
|
||||
|
||||
totalScore = totalScore + score
|
||||
score = 0
|
||||
level = level + 1
|
||||
time = time + timeLimit --your remaining time is added on to the next level
|
||||
startingTime = time --save where you started on this level for scoring
|
||||
|
||||
-- (re)create black boxes
|
||||
boxes = {}
|
||||
for i=0,boxColumns do
|
||||
boxes[i] = {}
|
||||
for j=0,boxRows do
|
||||
boxes[i][j] = {0, 0, 0}
|
||||
end
|
||||
end
|
||||
|
||||
-- assign a random set of boxes random colors
|
||||
for i=1,math.floor(math.pow(level, 1.07) * 1.03 + 2) do --(level * 1.5 + 2)
|
||||
local x, y = love.math.random(0, #boxes), love.math.random(0, #boxes[1])
|
||||
boxes[x][y] = {love.math.random(0, 255), love.math.random(0, 255), love.math.random(0, 255)}
|
||||
boxes[x][y][1] = boxes[x][y][1] - boxes[x][y][1] % colorStep --is this right?
|
||||
boxes[x][y][2] = boxes[x][y][2] - boxes[x][y][2] % colorStep
|
||||
boxes[x][y][3] = boxes[x][y][3] - boxes[x][y][3] % colorStep
|
||||
end
|
||||
|
||||
log("Done!")
|
||||
end
|
||||
|
||||
local function colorsEqual(A, B)
|
||||
if A[1] == B[1] and A[2] == B[2] and A[3] == B[3] then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function copyColor(A)
|
||||
return {A[1], A[2], A[3]}
|
||||
end
|
||||
|
||||
function game:enter(previous, settings, gameControls, gamejoltSession)
|
||||
log("Entering game...")
|
||||
-- save the state we came from
|
||||
previousState = previous
|
||||
-- set locals based on screen size
|
||||
screenWidth = love.graphics.getWidth()
|
||||
screenHeight = love.graphics.getHeight()
|
||||
boxColumns = math.floor(screenWidth / boxSize) - 1
|
||||
boxRows = math.floor(screenHeight / boxSize) - 5
|
||||
-- save the settings for later use
|
||||
if settings then
|
||||
gameSettings = settings
|
||||
end
|
||||
--gameSettings = settings or gameSettings
|
||||
controls = gameControls or controls
|
||||
session = gamejoltSession
|
||||
-- ping our active state immediately
|
||||
if session then
|
||||
local pingSuccess = Gamejolt.pingSession(true)
|
||||
if not pingSuccess then
|
||||
log("Couldn't ping Game Jolt session. Session may close.")
|
||||
end
|
||||
end
|
||||
-- set how to play the game based on settings
|
||||
boxSize = gameSettings.boxSize
|
||||
colorStep = gameSettings.colorStep
|
||||
timeLimit = gameSettings.timeLimit
|
||||
-- set the font we're going to use
|
||||
love.graphics.setNewFont(28)
|
||||
-- this is nextLevel shit
|
||||
nextLevel()
|
||||
end
|
||||
|
||||
function game:resume(previous, action)
|
||||
if action == "LOST" then
|
||||
log("Game restarted.")
|
||||
game:enter(previousState) --we want to keep the old values
|
||||
totalScore = 0 --this should have happened in game:leave() but does not for an unknown reason
|
||||
end
|
||||
if action == "UNPAUSED" then
|
||||
log("Game resumed.")
|
||||
love.graphics.setNewFont(28) -- fix our font!
|
||||
end
|
||||
end
|
||||
|
||||
function game:update(dt)
|
||||
-- ping every 30 seconds if we are in a session
|
||||
pingTimer = pingTimer + dt
|
||||
if pingTimer >= 30 then
|
||||
if session then
|
||||
local pingSuccess = Gamejolt.pingSession(true)
|
||||
if not pingSuccess then
|
||||
log("Couldn't ping Game Jolt session. Session may close.") --this is lazy but I don't care
|
||||
end
|
||||
end
|
||||
pingTimer = pingTimer - 30
|
||||
end
|
||||
|
||||
-- check if level complete
|
||||
local coloredBoxes = {}
|
||||
for i=0,#boxes do
|
||||
for j=0,#boxes[i] do
|
||||
if not colorsEqual(boxes[i][j], {0, 0, 0}) then
|
||||
table.insert(coloredBoxes, boxes[i][j])
|
||||
end
|
||||
end
|
||||
end
|
||||
local won, color
|
||||
if #coloredBoxes >= 2 then
|
||||
won = true
|
||||
color = copyColor(coloredBoxes[1])
|
||||
for i=2,#coloredBoxes do
|
||||
if not colorsEqual(color, coloredBoxes[i]) then
|
||||
won = false
|
||||
end
|
||||
end
|
||||
end
|
||||
if won then
|
||||
log("Level beat!")
|
||||
-- TODO we need a brief push/pop of gamestate to display a winning message
|
||||
nextLevel()
|
||||
end
|
||||
|
||||
-- else decrement time, and check if out of time
|
||||
time = time - dt
|
||||
if time <= 0 then
|
||||
-- TODO we need to pass an image of the screen and data about time of losing
|
||||
Gamestate.push(lost, love.graphics.newScreenshot(), totalScore + score, controls, session)
|
||||
-- call leave to clean up the gamestate
|
||||
game:leave()
|
||||
end
|
||||
|
||||
-- update the current score
|
||||
score = #coloredBoxes / math.pow(startingTime - time, 0.02) * colorStep
|
||||
end
|
||||
|
||||
function game:draw()
|
||||
--boxes
|
||||
for i=0,#boxes do
|
||||
for j=0,#boxes[i] do
|
||||
love.graphics.setColor(boxes[i][j])
|
||||
love.graphics.rectangle("fill", i * boxSize, j * boxSize + boxSize * 2, boxSize, boxSize)
|
||||
end
|
||||
end
|
||||
|
||||
--lines
|
||||
-- vertical
|
||||
love.graphics.setColor(255, 255, 255)
|
||||
for i=0,boxColumns+1 do
|
||||
love.graphics.line(i * boxSize, 0 + boxSize * 2, i * boxSize, screenHeight - boxSize * 2)
|
||||
|
||||
end
|
||||
-- horizontal
|
||||
for j=0,boxRows+1 do
|
||||
love.graphics.line(0, j * boxSize + boxSize * 2, screenWidth, j * boxSize + boxSize * 2)
|
||||
end
|
||||
|
||||
-- Info Overlays
|
||||
--love.graphics.setNewFont(28) --purposely stays same no matter screen res
|
||||
love.graphics.setColor(255, 255, 255)
|
||||
-- top of screen stuff
|
||||
love.graphics.printf(string.format("Total Score: %.1f", totalScore), 0, 3, screenWidth / 2, "center")
|
||||
--love.graphics.printf(string.format("Best Score: %.1f", bestScore), screenWidth / 2, 3, screenWidth / 2, "center")
|
||||
|
||||
-- bottom of screen stuff
|
||||
love.graphics.printf(string.format("Time: %.1f", time), 0, screenWidth / 2 + 25, screenWidth / 2, "center")
|
||||
love.graphics.printf("Level: "..level, 0, screenWidth / 2 + 25, screenWidth, "center")
|
||||
love.graphics.printf(string.format("Current Score: %.1f", score), screenWidth / 2, screenWidth / 2 + 25, screenWidth / 2, "center")
|
||||
end
|
||||
|
||||
function game:mousepressed(x, y, button)
|
||||
-- new x/y adjusted for where boxes actually are
|
||||
local nx = math.floor(x / boxSize)
|
||||
local ny = math.floor((y - boxSize * 2) / boxSize)
|
||||
-- check if we are actually over a box first
|
||||
if boxes[nx][ny] then
|
||||
-- left, red
|
||||
if input(button, controls.red) then
|
||||
boxes[nx][ny][1] = boxes[nx][ny][1] + colorStep
|
||||
if boxes[nx][ny][1] > 255 then
|
||||
boxes[nx][ny][1] = 0
|
||||
end
|
||||
-- middle, green
|
||||
elseif input(button, controls.green) then
|
||||
boxes[nx][ny][2] = boxes[nx][ny][2] + colorStep
|
||||
if boxes[nx][ny][2] > 255 then
|
||||
boxes[nx][ny][2] = 0
|
||||
end
|
||||
-- right, blue
|
||||
elseif input(button, controls.blue) then
|
||||
boxes[nx][ny][3] = boxes[nx][ny][3] + colorStep
|
||||
if boxes[nx][ny][3] > 255 then
|
||||
boxes[nx][ny][3] = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function game:keypressed(key, unicode)
|
||||
if input(key, controls.back) then
|
||||
log("Leaving game...")
|
||||
Gamestate.switch(previousState)
|
||||
elseif input(key, controls.pause) then
|
||||
Gamestate.push(paused, love.graphics.newScreenshot(), controls, session)
|
||||
end
|
||||
end
|
||||
|
||||
function game:focus(isFocused)
|
||||
if not isFocused then
|
||||
Gamestate.push(paused, love.graphics.newScreenshot(), controls, session)
|
||||
end
|
||||
end
|
||||
|
||||
function game:leave()
|
||||
-- clear the game upon any exit (except pause)
|
||||
level = 0
|
||||
score = 0
|
||||
totalScore = 0
|
||||
time = 0
|
||||
startingTime = 0
|
||||
end
|
||||
|
||||
return game
|
||||
@@ -0,0 +1,137 @@
|
||||
local input = require "util.input"
|
||||
local inifile = require "lib.inifile"
|
||||
local ser = require "lib.ser"
|
||||
|
||||
local Gamestate = require "lib.gamestate"
|
||||
local game = require "gamestates.game"
|
||||
|
||||
local menu = {}
|
||||
|
||||
local screenWidth, screenHeight
|
||||
local settings, controls
|
||||
local pingTimer, session = 0, false
|
||||
|
||||
function menu:init()
|
||||
log("Initializing menu...")
|
||||
-- load or create settings
|
||||
if love.filesystem.isFile("settings.ini") then
|
||||
log("Loading settings (again)...")
|
||||
settings = inifile.parse("settings.ini")
|
||||
else
|
||||
log("Creating settings...")
|
||||
settings = {
|
||||
display = {
|
||||
width = love.graphics.getWidth(),
|
||||
height = love.graphics.getHeight(),
|
||||
fullscreen = false,
|
||||
borderless = true,
|
||||
},
|
||||
gamejolt = {
|
||||
username = false,
|
||||
usertoken = false
|
||||
},
|
||||
sound = {
|
||||
music = 0,
|
||||
sfx = 0
|
||||
},
|
||||
difficulty = {
|
||||
preset = "normal",
|
||||
timeLimit = 60,
|
||||
colorStep = 80,
|
||||
boxSize = 20
|
||||
}
|
||||
}
|
||||
-- TODO WRITE TO FILE
|
||||
end
|
||||
-- load or create controls
|
||||
if love.filesystem.isFile("controls.lua") then
|
||||
log("Loading controls...")
|
||||
controls = require "controls"
|
||||
else
|
||||
log("Creating controls...")
|
||||
controls = {
|
||||
select = {
|
||||
clicks = {"l"},
|
||||
buttons = {}
|
||||
},
|
||||
back = {
|
||||
clicks = {},
|
||||
buttons = {"escape"}
|
||||
},
|
||||
pause = {
|
||||
clicks = {},
|
||||
buttons = {" ", "escape"}
|
||||
},
|
||||
red = {
|
||||
clicks = {"l"},
|
||||
buttons = {}
|
||||
},
|
||||
green = {
|
||||
clicks = {"wd", "wu", "m"},
|
||||
buttons = {}
|
||||
},
|
||||
blue = {
|
||||
clicks = {"r"},
|
||||
buttons = {}
|
||||
}
|
||||
}
|
||||
-- TODO WRITE THE CONTROLS TO FILE
|
||||
end
|
||||
end
|
||||
|
||||
function menu:enter(previous, gamejoltSession)
|
||||
log("Entering menu...")
|
||||
session = gamejoltSession
|
||||
-- ping our idle state immediately
|
||||
if session then
|
||||
local idleSuccess = Gamejolt.pingSession(false)
|
||||
if not idleSuccess then
|
||||
log("Couldn't ping Game Jolt session. Session may close.")
|
||||
end
|
||||
end
|
||||
screenWidth = love.graphics.getWidth()
|
||||
screenHeight = love.graphics.getHeight()
|
||||
end
|
||||
|
||||
function menu:update(dt)
|
||||
-- we want to ping every 30 seconds if connected
|
||||
pingTimer = pingTimer + dt
|
||||
if pingTimer >= 30 then
|
||||
if session then
|
||||
local idleSuccess = Gamejolt.pingSession(false)
|
||||
if not idleSuccess then
|
||||
log("Couldn't ping Game Jolt session. Session may close.") --this is lazy but I don't care
|
||||
end
|
||||
end
|
||||
pingTimer = pingTimer - 30
|
||||
end
|
||||
end
|
||||
|
||||
function menu:draw()
|
||||
love.graphics.setNewFont(30 * screenWidth / 800)
|
||||
love.graphics.printf("RGB - The Color Chooser", 0, screenHeight / 7, screenWidth, "center")
|
||||
love.graphics.setNewFont(16 * screenWidth / 800)
|
||||
love.graphics.printf("1. Left click to cycle red.\n2. Middle click or scroll to cycle green.\n3. Right click to cycle blue.", 0, screenHeight / 3, screenWidth, "center")
|
||||
love.graphics.printf("Your goal is to get every panel that is not black to be the same color.", 0, screenHeight / 1.75, screenWidth, "center")
|
||||
love.graphics.printf("Click to begin.", 0, screenHeight / 1.3, screenWidth, "center")
|
||||
love.graphics.setNewFont(12 * screenWidth / 800)
|
||||
love.graphics.printf("(Esc exits the game.)", 0, screenHeight - 20 * screenHeight / 400, screenWidth, "center")
|
||||
end
|
||||
|
||||
function menu:mousepressed(x, y, button)
|
||||
if input(button, controls.select) then
|
||||
Gamestate.switch(game, difficulty, controls, session)
|
||||
end
|
||||
end
|
||||
|
||||
function menu:keypressed(key, unicode)
|
||||
if input(key, controls.back) then
|
||||
log("Quitting.")
|
||||
if session then
|
||||
Gamejolt.closeSession()
|
||||
end
|
||||
love.event.quit()
|
||||
end
|
||||
end
|
||||
|
||||
return menu
|
||||
@@ -0,0 +1,224 @@
|
||||
local folder = (...):gsub('%.init$', '')
|
||||
local md5 = require(folder .. ".md5" )
|
||||
local http = require("socket.http")
|
||||
|
||||
local GJ = {
|
||||
gameID, gameKey,
|
||||
isLoggedIn = false,
|
||||
username, userToken
|
||||
}
|
||||
|
||||
local BASE_URL = "http://gamejolt.com/api/game/v1/"
|
||||
|
||||
local function req(s, f, pu, pt)
|
||||
local url = BASE_URL .. s .. "&game_id=" .. tostring(GJ.gameID) .. "&format=" .. f
|
||||
if pu then url = url .. "&username=" .. GJ.username end
|
||||
if pt then url = url .. "&user_token=" .. GJ.userToken end
|
||||
|
||||
local b = md5.sumhexa(url .. GJ.gameKey)
|
||||
url = url .. "&signature=" .. b
|
||||
|
||||
local r, e = http.request(url)
|
||||
return r
|
||||
end
|
||||
|
||||
local function parseKeypair(s, on)
|
||||
local c, len = 0, string.len(s)
|
||||
local b, k, v
|
||||
|
||||
while c < len do
|
||||
b = string.find(s, ":", c)
|
||||
if b == nil then break end
|
||||
k = string.sub(s, c, b - 1)
|
||||
c = b + 2
|
||||
b = string.find(s, '"', c)
|
||||
v = string.sub(s, c, b - 1)
|
||||
c = b + 3
|
||||
on(k, v)
|
||||
end
|
||||
end
|
||||
|
||||
local function handleTrophies(str)
|
||||
local d = req("trophies/?" .. str, "keypair", true, true)
|
||||
local t, f = {}
|
||||
|
||||
parseKeypair(d, function(k, v)
|
||||
if k ~= "success" then
|
||||
if k == "id" then
|
||||
f = {}
|
||||
table.insert(t, f)
|
||||
end
|
||||
f[k] = v
|
||||
end
|
||||
end)
|
||||
return t
|
||||
end
|
||||
|
||||
function GJ.init(id, key)
|
||||
GJ.gameID = id
|
||||
GJ.gameKey = key
|
||||
end
|
||||
|
||||
-- users
|
||||
function GJ.authUser(name, token)
|
||||
GJ.username = name
|
||||
GJ.userToken = token
|
||||
|
||||
local s = string.find(req("users/auth/?", "dump", true, true), "SUCCESS") ~= nil
|
||||
GJ.isLoggedIn = s
|
||||
return s
|
||||
end
|
||||
|
||||
function GJ.fetchUserByName(name)
|
||||
local r = req("users/?username=" .. name, "keypair", false, false)
|
||||
|
||||
local t = {}
|
||||
parseKeypair(r, function(k, v)
|
||||
t[k] = v
|
||||
end)
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
function GJ.fetchUserByID(id)
|
||||
local r = req("users/?user_id=" .. id, "keypair", false, false)
|
||||
|
||||
local t = {}
|
||||
parseKeypair(r, function(k, v)
|
||||
t[k] = v
|
||||
end)
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
-- sessions
|
||||
function GJ.openSession()
|
||||
return string.find(req("sessions/open/?", "dump", true, true), "SUCCESS") ~= nil
|
||||
end
|
||||
|
||||
function GJ.pingSession(active)
|
||||
local status = "idle"
|
||||
if active then status = "active" end
|
||||
|
||||
return string.find(req("sessions/open/?status=" .. status, "dump", true, true), "SUCCESS") ~= nil
|
||||
end
|
||||
|
||||
function GJ.closeSession()
|
||||
return string.find(req("sessions/close/?", "dump", true, true), "SUCCESS") ~= nil
|
||||
end
|
||||
|
||||
-- data store
|
||||
function GJ.fetchData(key, isGlobal)
|
||||
local pu, pt = true, true
|
||||
if isGlobal then pu, pt = false, false end
|
||||
|
||||
local d = req("data-store/?key=" .. key, "dump", pu, pt)
|
||||
return string.sub(d, string.find(d, "\n"), string.len(d))
|
||||
end
|
||||
|
||||
function GJ.setData(key, data, isGlobal)
|
||||
local pu, pt = true, true
|
||||
if isGlobal then pu, pt = false, false end
|
||||
|
||||
return string.find(req("data-store/set/?key=" .. key .. "&data=" .. tostring(data), "dump", pu, pt), "SUCCESS") ~= nil
|
||||
end
|
||||
|
||||
function GJ.updateData(key, value, operation, isGlobal)
|
||||
local pu, pt = true, true
|
||||
if isGlobal then pu, pt = false, false end
|
||||
|
||||
local d = req("data-store/update/?key=" .. key .. "&operation=" .. operation .. "&value=" .. tostring(value), "dump", pu, pt)
|
||||
return string.sub(d, string.find(d, "\n"), string.len(d))
|
||||
end
|
||||
|
||||
function GJ.removeData(key, isGlobal)
|
||||
local pu, pt = true, true
|
||||
if isGlobal then pu, pt = false, false end
|
||||
|
||||
return string.find(req("data-store/remove/?key=" .. key, "dump", pu, pt), "SUCCESS") ~= nil
|
||||
end
|
||||
|
||||
function GJ.fetchStorageKeys(isGlobal)
|
||||
local pu, pt = true, true
|
||||
if isGlobal then pu, pt = false, false end
|
||||
|
||||
local d = req("data-store/get-keys/?", "keypair", pu, pt)
|
||||
|
||||
local t = {}
|
||||
parseKeypair(d, function(k, v)
|
||||
if k ~= "success" then table.insert(t, v) end
|
||||
end)
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
-- trophies
|
||||
function GJ.giveTrophy(id)
|
||||
return string.find(req("trophies/add-achieved/?trophy_id=" .. tostring(id), "dump", true, true), "SUCCESS") ~= nil
|
||||
end
|
||||
|
||||
function GJ.fetchTrophy(id)
|
||||
local d = req("trophies/?trophy_id=" .. tostring(id), "keypair", true, true)
|
||||
|
||||
local t = {}
|
||||
parseKeypair(d, function(k, v)
|
||||
if k ~= "success" then t[k] = v end
|
||||
end)
|
||||
return t
|
||||
end
|
||||
|
||||
function GJ.fetchTrophiesByStatus(achieved)
|
||||
return handleTrophies("achieved=" .. tostring(achieved))
|
||||
end
|
||||
|
||||
function GJ.fetchAllTrophies()
|
||||
return handleTrophies("")
|
||||
end
|
||||
|
||||
-- scores
|
||||
function GJ.addScore(score, desc, tableID, guestName, extraData)
|
||||
local pu, pt, s = true, true, ""
|
||||
if guestName then pu, pt = false, false, s .. "&guest=" .. guestName end
|
||||
|
||||
if extraData then s = s .. "&extra_data=" .. tostring(extraData) end
|
||||
if tableID then s = s .. "&table_id=" .. tostring(tableID) end
|
||||
|
||||
return string.find(req("scores/add/?score=" .. tostring(desc) .. "&sort=" .. score .. s, "dump", pu, pt), "SUCCESS") ~= nil
|
||||
end
|
||||
|
||||
function GJ.fetchScores(limit, tableID)
|
||||
local pu, pt, s = true, true, ""
|
||||
if tableID then pu, pt, s = false, false, "&table_id=" .. tostring(tableID) end
|
||||
|
||||
local d = req("scores/?limit=" .. tostring(limit) .. s, "keypair", pu, pt)
|
||||
local t, f = {}
|
||||
|
||||
parseKeypair(d, function(k, v)
|
||||
if k ~= "success" then
|
||||
if k == "score" then
|
||||
f = {}
|
||||
table.insert(t, f)
|
||||
end
|
||||
f[k] = v
|
||||
end
|
||||
end)
|
||||
return t
|
||||
end
|
||||
|
||||
function GJ.fetchTables()
|
||||
local d = req("scores/tables/?", "keypair", false, false)
|
||||
local t, f = {}
|
||||
|
||||
parseKeypair(d, function(k, v)
|
||||
if k ~= "success" then
|
||||
if k == "id" then
|
||||
f = {}
|
||||
table.insert(t, f)
|
||||
end
|
||||
f[k] = v
|
||||
end
|
||||
end)
|
||||
return t
|
||||
end
|
||||
|
||||
return GJ
|
||||
@@ -0,0 +1,384 @@
|
||||
local md5 = {
|
||||
_VERSION = "md5.lua 0.5.0",
|
||||
_DESCRIPTION = "MD5 computation in Lua (5.1)",
|
||||
_URL = "https://github.com/kikito/md5.lua",
|
||||
_LICENSE = [[
|
||||
MIT LICENSE
|
||||
|
||||
Copyright (c) 2013 Enrique García Cota + Adam Baldwin + hanzao + Equi 4 Software
|
||||
|
||||
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:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
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.
|
||||
]]
|
||||
}
|
||||
|
||||
-- bit lib implementions
|
||||
|
||||
local floor, abs, max = math.floor, math.abs, math.max
|
||||
local char, byte, format, rep, sub =
|
||||
string.char, string.byte, string.format, string.rep, string.sub
|
||||
|
||||
local function check_int(n)
|
||||
-- checking not float
|
||||
if(n - floor(n) > 0) then
|
||||
error("trying to use bitwise operation on non-integer!")
|
||||
end
|
||||
end
|
||||
|
||||
local function tbl2number(tbl)
|
||||
local n = #tbl
|
||||
|
||||
local rslt = 0
|
||||
local power = 1
|
||||
for i = 1, n do
|
||||
rslt = rslt + tbl[i]*power
|
||||
power = power*2
|
||||
end
|
||||
|
||||
return rslt
|
||||
end
|
||||
|
||||
local function expand(tbl_m, tbl_n)
|
||||
local big = {}
|
||||
local small = {}
|
||||
if(#tbl_m > #tbl_n) then
|
||||
big = tbl_m
|
||||
small = tbl_n
|
||||
else
|
||||
big = tbl_n
|
||||
small = tbl_m
|
||||
end
|
||||
-- expand small
|
||||
for i = #small + 1, #big do
|
||||
small[i] = 0
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local to_bits -- needs to be declared before bit_not
|
||||
|
||||
local function bit_not(n)
|
||||
local tbl = to_bits(n)
|
||||
local size = max(#tbl, 32)
|
||||
for i = 1, size do
|
||||
if(tbl[i] == 1) then
|
||||
tbl[i] = 0
|
||||
else
|
||||
tbl[i] = 1
|
||||
end
|
||||
end
|
||||
return tbl2number(tbl)
|
||||
end
|
||||
|
||||
-- defined as local above
|
||||
to_bits = function (n)
|
||||
check_int(n)
|
||||
if(n < 0) then
|
||||
-- negative
|
||||
return to_bits(bit_not(abs(n)) + 1)
|
||||
end
|
||||
-- to bits table
|
||||
local tbl = {}
|
||||
local cnt = 1
|
||||
while (n > 0) do
|
||||
local last = math.mod(n,2)
|
||||
if(last == 1) then
|
||||
tbl[cnt] = 1
|
||||
else
|
||||
tbl[cnt] = 0
|
||||
end
|
||||
n = (n-last)/2
|
||||
cnt = cnt + 1
|
||||
end
|
||||
|
||||
return tbl
|
||||
end
|
||||
|
||||
local function bit_or(m, n)
|
||||
local tbl_m = to_bits(m)
|
||||
local tbl_n = to_bits(n)
|
||||
expand(tbl_m, tbl_n)
|
||||
|
||||
local tbl = {}
|
||||
local rslt = max(#tbl_m, #tbl_n)
|
||||
for i = 1, rslt do
|
||||
if(tbl_m[i]== 0 and tbl_n[i] == 0) then
|
||||
tbl[i] = 0
|
||||
else
|
||||
tbl[i] = 1
|
||||
end
|
||||
end
|
||||
|
||||
return tbl2number(tbl)
|
||||
end
|
||||
|
||||
local function bit_and(m, n)
|
||||
local tbl_m = to_bits(m)
|
||||
local tbl_n = to_bits(n)
|
||||
expand(tbl_m, tbl_n)
|
||||
|
||||
local tbl = {}
|
||||
local rslt = max(#tbl_m, #tbl_n)
|
||||
for i = 1, rslt do
|
||||
if(tbl_m[i]== 0 or tbl_n[i] == 0) then
|
||||
tbl[i] = 0
|
||||
else
|
||||
tbl[i] = 1
|
||||
end
|
||||
end
|
||||
|
||||
return tbl2number(tbl)
|
||||
end
|
||||
|
||||
local function bit_xor(m, n)
|
||||
local tbl_m = to_bits(m)
|
||||
local tbl_n = to_bits(n)
|
||||
expand(tbl_m, tbl_n)
|
||||
|
||||
local tbl = {}
|
||||
local rslt = max(#tbl_m, #tbl_n)
|
||||
for i = 1, rslt do
|
||||
if(tbl_m[i] ~= tbl_n[i]) then
|
||||
tbl[i] = 1
|
||||
else
|
||||
tbl[i] = 0
|
||||
end
|
||||
end
|
||||
|
||||
return tbl2number(tbl)
|
||||
end
|
||||
|
||||
local function bit_rshift(n, bits)
|
||||
check_int(n)
|
||||
|
||||
local high_bit = 0
|
||||
if(n < 0) then
|
||||
-- negative
|
||||
n = bit_not(abs(n)) + 1
|
||||
high_bit = 2147483648 -- 0x80000000
|
||||
end
|
||||
|
||||
for i=1, bits do
|
||||
n = n/2
|
||||
n = bit_or(floor(n), high_bit)
|
||||
end
|
||||
return floor(n)
|
||||
end
|
||||
|
||||
local function bit_lshift(n, bits)
|
||||
check_int(n)
|
||||
|
||||
if(n < 0) then
|
||||
-- negative
|
||||
n = bit_not(abs(n)) + 1
|
||||
end
|
||||
|
||||
for i=1, bits do
|
||||
n = n*2
|
||||
end
|
||||
return bit_and(n, 4294967295) -- 0xFFFFFFFF
|
||||
end
|
||||
|
||||
-- convert little-endian 32-bit int to a 4-char string
|
||||
local function lei2str(i)
|
||||
local f=function (s) return char( bit_and( bit_rshift(i, s), 255)) end
|
||||
return f(0)..f(8)..f(16)..f(24)
|
||||
end
|
||||
|
||||
-- convert raw string to big-endian int
|
||||
local function str2bei(s)
|
||||
local v=0
|
||||
for i=1, #s do
|
||||
v = v * 256 + byte(s, i)
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
-- convert raw string to little-endian int
|
||||
local function str2lei(s)
|
||||
local v=0
|
||||
for i = #s,1,-1 do
|
||||
v = v*256 + byte(s, i)
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
-- cut up a string in little-endian ints of given size
|
||||
local function cut_le_str(s,...)
|
||||
local o, r = 1, {}
|
||||
local args = {...}
|
||||
for i=1, #args do
|
||||
table.insert(r, str2lei(sub(s, o, o + args[i] - 1)))
|
||||
o = o + args[i]
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
local swap = function (w) return str2bei(lei2str(w)) end
|
||||
|
||||
local function hex2binaryaux(hexval)
|
||||
return char(tonumber(hexval, 16))
|
||||
end
|
||||
|
||||
local function hex2binary(hex)
|
||||
local result, _ = hex:gsub('..', hex2binaryaux)
|
||||
return result
|
||||
end
|
||||
|
||||
-- An MD5 mplementation in Lua, requires bitlib (hacked to use LuaBit from above, ugh)
|
||||
-- 10/02/2001 jcw@equi4.com
|
||||
|
||||
local FF = 0xffffffff
|
||||
local CONSTS = {
|
||||
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
|
||||
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
||||
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
|
||||
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
|
||||
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
|
||||
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
||||
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
|
||||
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
|
||||
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
|
||||
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
||||
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
|
||||
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
||||
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
|
||||
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
||||
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
|
||||
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
|
||||
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476
|
||||
}
|
||||
|
||||
local f=function (x,y,z) return bit_or(bit_and(x,y),bit_and(-x-1,z)) end
|
||||
local g=function (x,y,z) return bit_or(bit_and(x,z),bit_and(y,-z-1)) end
|
||||
local h=function (x,y,z) return bit_xor(x,bit_xor(y,z)) end
|
||||
local i=function (x,y,z) return bit_xor(y,bit_or(x,-z-1)) end
|
||||
local z=function (f,a,b,c,d,x,s,ac)
|
||||
a=bit_and(a+f(b,c,d)+x+ac,FF)
|
||||
-- be *very* careful that left shift does not cause rounding!
|
||||
return bit_or(bit_lshift(bit_and(a,bit_rshift(FF,s)),s),bit_rshift(a,32-s))+b
|
||||
end
|
||||
|
||||
local function transform(A,B,C,D,X)
|
||||
local a,b,c,d=A,B,C,D
|
||||
local t=CONSTS
|
||||
|
||||
a=z(f,a,b,c,d,X[ 0], 7,t[ 1])
|
||||
d=z(f,d,a,b,c,X[ 1],12,t[ 2])
|
||||
c=z(f,c,d,a,b,X[ 2],17,t[ 3])
|
||||
b=z(f,b,c,d,a,X[ 3],22,t[ 4])
|
||||
a=z(f,a,b,c,d,X[ 4], 7,t[ 5])
|
||||
d=z(f,d,a,b,c,X[ 5],12,t[ 6])
|
||||
c=z(f,c,d,a,b,X[ 6],17,t[ 7])
|
||||
b=z(f,b,c,d,a,X[ 7],22,t[ 8])
|
||||
a=z(f,a,b,c,d,X[ 8], 7,t[ 9])
|
||||
d=z(f,d,a,b,c,X[ 9],12,t[10])
|
||||
c=z(f,c,d,a,b,X[10],17,t[11])
|
||||
b=z(f,b,c,d,a,X[11],22,t[12])
|
||||
a=z(f,a,b,c,d,X[12], 7,t[13])
|
||||
d=z(f,d,a,b,c,X[13],12,t[14])
|
||||
c=z(f,c,d,a,b,X[14],17,t[15])
|
||||
b=z(f,b,c,d,a,X[15],22,t[16])
|
||||
|
||||
a=z(g,a,b,c,d,X[ 1], 5,t[17])
|
||||
d=z(g,d,a,b,c,X[ 6], 9,t[18])
|
||||
c=z(g,c,d,a,b,X[11],14,t[19])
|
||||
b=z(g,b,c,d,a,X[ 0],20,t[20])
|
||||
a=z(g,a,b,c,d,X[ 5], 5,t[21])
|
||||
d=z(g,d,a,b,c,X[10], 9,t[22])
|
||||
c=z(g,c,d,a,b,X[15],14,t[23])
|
||||
b=z(g,b,c,d,a,X[ 4],20,t[24])
|
||||
a=z(g,a,b,c,d,X[ 9], 5,t[25])
|
||||
d=z(g,d,a,b,c,X[14], 9,t[26])
|
||||
c=z(g,c,d,a,b,X[ 3],14,t[27])
|
||||
b=z(g,b,c,d,a,X[ 8],20,t[28])
|
||||
a=z(g,a,b,c,d,X[13], 5,t[29])
|
||||
d=z(g,d,a,b,c,X[ 2], 9,t[30])
|
||||
c=z(g,c,d,a,b,X[ 7],14,t[31])
|
||||
b=z(g,b,c,d,a,X[12],20,t[32])
|
||||
|
||||
a=z(h,a,b,c,d,X[ 5], 4,t[33])
|
||||
d=z(h,d,a,b,c,X[ 8],11,t[34])
|
||||
c=z(h,c,d,a,b,X[11],16,t[35])
|
||||
b=z(h,b,c,d,a,X[14],23,t[36])
|
||||
a=z(h,a,b,c,d,X[ 1], 4,t[37])
|
||||
d=z(h,d,a,b,c,X[ 4],11,t[38])
|
||||
c=z(h,c,d,a,b,X[ 7],16,t[39])
|
||||
b=z(h,b,c,d,a,X[10],23,t[40])
|
||||
a=z(h,a,b,c,d,X[13], 4,t[41])
|
||||
d=z(h,d,a,b,c,X[ 0],11,t[42])
|
||||
c=z(h,c,d,a,b,X[ 3],16,t[43])
|
||||
b=z(h,b,c,d,a,X[ 6],23,t[44])
|
||||
a=z(h,a,b,c,d,X[ 9], 4,t[45])
|
||||
d=z(h,d,a,b,c,X[12],11,t[46])
|
||||
c=z(h,c,d,a,b,X[15],16,t[47])
|
||||
b=z(h,b,c,d,a,X[ 2],23,t[48])
|
||||
|
||||
a=z(i,a,b,c,d,X[ 0], 6,t[49])
|
||||
d=z(i,d,a,b,c,X[ 7],10,t[50])
|
||||
c=z(i,c,d,a,b,X[14],15,t[51])
|
||||
b=z(i,b,c,d,a,X[ 5],21,t[52])
|
||||
a=z(i,a,b,c,d,X[12], 6,t[53])
|
||||
d=z(i,d,a,b,c,X[ 3],10,t[54])
|
||||
c=z(i,c,d,a,b,X[10],15,t[55])
|
||||
b=z(i,b,c,d,a,X[ 1],21,t[56])
|
||||
a=z(i,a,b,c,d,X[ 8], 6,t[57])
|
||||
d=z(i,d,a,b,c,X[15],10,t[58])
|
||||
c=z(i,c,d,a,b,X[ 6],15,t[59])
|
||||
b=z(i,b,c,d,a,X[13],21,t[60])
|
||||
a=z(i,a,b,c,d,X[ 4], 6,t[61])
|
||||
d=z(i,d,a,b,c,X[11],10,t[62])
|
||||
c=z(i,c,d,a,b,X[ 2],15,t[63])
|
||||
b=z(i,b,c,d,a,X[ 9],21,t[64])
|
||||
|
||||
return A+a,B+b,C+c,D+d
|
||||
end
|
||||
|
||||
----------------------------------------------------------------
|
||||
|
||||
function md5.sumhexa(s)
|
||||
local msgLen = #s
|
||||
local padLen = 56 - msgLen % 64
|
||||
|
||||
if msgLen % 64 > 56 then padLen = padLen + 64 end
|
||||
|
||||
if padLen == 0 then padLen = 64 end
|
||||
|
||||
s = s .. char(128) .. rep(char(0),padLen-1) .. lei2str(8*msgLen) .. lei2str(0)
|
||||
|
||||
assert(#s % 64 == 0)
|
||||
|
||||
local t = CONSTS
|
||||
local a,b,c,d = t[65],t[66],t[67],t[68]
|
||||
|
||||
for i=1,#s,64 do
|
||||
local X = cut_le_str(sub(s,i,i+63),4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4)
|
||||
assert(#X == 16)
|
||||
X[0] = table.remove(X,1) -- zero based!
|
||||
a,b,c,d = transform(a,b,c,d,X)
|
||||
end
|
||||
|
||||
return format("%08x%08x%08x%08x",swap(a),swap(b),swap(c),swap(d))
|
||||
end
|
||||
|
||||
function md5.sum(s)
|
||||
return hex2binary(md5.sumhexa(s))
|
||||
end
|
||||
|
||||
return md5
|
||||
+1
-1
@@ -1 +1 @@
|
||||
build=6
|
||||
build=7
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 724 B |
Binary file not shown.
|
After Width: | Height: | Size: 164 KiB |
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" standalone="no" ?>
|
||||
<!DOCTYPE project PUBLIC "-//audacityproject-1.3.0//DTD//EN" "http://audacity.sourceforge.net/xml/audacityproject-1.3.0.dtd" >
|
||||
<project xmlns="http://audacity.sourceforge.net/xml/" projname="music_data" version="1.3.0" audacityversion="2.0.6" sel0="0.0000000000" sel1="0.0000000000" vpos="0" h="0.0000000000" zoom="43.0664062500" rate="44100.0" snapto="off" selectionformat="hh:mm:ss + milliseconds">
|
||||
<tags/>
|
||||
<wavetrack name="Audio Track" channel="0" linked="1" mute="0" solo="0" height="150" minimized="0" isSelected="1" rate="44100" gain="1.0" pan="0.0">
|
||||
<waveclip offset="0.00000000">
|
||||
<sequence maxsamples="262144" sampleformat="262159" numsamples="1319648">
|
||||
<waveblock start="0">
|
||||
<simpleblockfile filename="e00007da.au" len="232288" min="-0.999886" max="0.999427" rms="0.0752"/>
|
||||
</waveblock>
|
||||
<waveblock start="232288">
|
||||
<simpleblockfile filename="e0000b57.au" len="262144" min="-0.762005" max="0.895501" rms="0.071501"/>
|
||||
</waveblock>
|
||||
<waveblock start="494432">
|
||||
<simpleblockfile filename="e0000c99.au" len="262144" min="-0.833859" max="0.99766" rms="0.083378"/>
|
||||
</waveblock>
|
||||
<waveblock start="756576">
|
||||
<simpleblockfile filename="e0000c77.au" len="262144" min="-0.892351" max="0.950365" rms="0.07782"/>
|
||||
</waveblock>
|
||||
<waveblock start="1018720">
|
||||
<simpleblockfile filename="e0000104.au" len="152832" min="-0.91912" max="0.794532" rms="0.060174"/>
|
||||
</waveblock>
|
||||
<waveblock start="1171552">
|
||||
<simpleblockfile filename="e00003c7.au" len="148096" min="-0.996522" max="1.003055" rms="0.077017"/>
|
||||
</waveblock>
|
||||
</sequence>
|
||||
<envelope numpoints="0"/>
|
||||
</waveclip>
|
||||
</wavetrack>
|
||||
<wavetrack name="Audio Track" channel="1" linked="0" mute="0" solo="0" height="150" minimized="0" isSelected="1" rate="44100" gain="1.0" pan="0.0">
|
||||
<waveclip offset="0.00000000">
|
||||
<sequence maxsamples="262144" sampleformat="262159" numsamples="1319648">
|
||||
<waveblock start="0">
|
||||
<simpleblockfile filename="e0000f67.au" len="232288" min="-0.999839" max="0.999528" rms="0.073794"/>
|
||||
</waveblock>
|
||||
<waveblock start="232288">
|
||||
<simpleblockfile filename="e000050a.au" len="262144" min="-0.747472" max="0.878466" rms="0.070136"/>
|
||||
</waveblock>
|
||||
<waveblock start="494432">
|
||||
<simpleblockfile filename="e00006aa.au" len="262144" min="-0.817923" max="0.978549" rms="0.081787"/>
|
||||
</waveblock>
|
||||
<waveblock start="756576">
|
||||
<simpleblockfile filename="e0000481.au" len="262144" min="-0.875147" max="0.932644" rms="0.076333"/>
|
||||
</waveblock>
|
||||
<waveblock start="1018720">
|
||||
<simpleblockfile filename="e0000f00.au" len="152832" min="-0.901614" max="0.779474" rms="0.059024"/>
|
||||
</waveblock>
|
||||
<waveblock start="1171552">
|
||||
<simpleblockfile filename="e00002f5.au" len="148096" min="-0.996578" max="1.003052" rms="0.075558"/>
|
||||
</waveblock>
|
||||
</sequence>
|
||||
<envelope numpoints="0"/>
|
||||
</waveclip>
|
||||
</wavetrack>
|
||||
</project>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
-1
@@ -2,7 +2,7 @@ local debug = true
|
||||
|
||||
function love.conf(t)
|
||||
t.identity = "rgb"
|
||||
t.version = "0.9.1"
|
||||
--t.version = "0.9.1"
|
||||
--t.author = "Guard13007"
|
||||
if debug then t.console = true end
|
||||
|
||||
|
||||
@@ -73,10 +73,7 @@ function game:enter(previous, settings, gameControls, gamejoltSession)
|
||||
boxColumns = math.floor(screenWidth / boxSize) - 1
|
||||
boxRows = math.floor(screenHeight / boxSize) - 5
|
||||
-- save the settings for later use
|
||||
if settings then
|
||||
gameSettings = settings
|
||||
end
|
||||
--gameSettings = settings or gameSettings
|
||||
gameSettings = settings or gameSettings
|
||||
controls = gameControls or controls
|
||||
session = gamejoltSession
|
||||
-- ping our active state immediately
|
||||
|
||||
@@ -120,7 +120,7 @@ end
|
||||
|
||||
function menu:mousepressed(x, y, button)
|
||||
if input(button, controls.select) then
|
||||
Gamestate.switch(game, difficulty, controls, session)
|
||||
Gamestate.switch(game, {boxSize = settings.difficulty.boxSize, colorStep = settings.difficulty.colorStep, timeLimit = settings.difficulty.timeLimit}, controls)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- HTTP/1.1 client support for the Lua language.
|
||||
-- LuaSocket toolkit.
|
||||
-- Author: Diego Nehab
|
||||
-- RCS ID: $Id: http.lua,v 1.71 2007/10/13 23:55:20 diego Exp $
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Declare module and import dependencies
|
||||
-------------------------------------------------------------------------------
|
||||
local socket = require("socket")
|
||||
local url = require("socket.url")
|
||||
local ltn12 = require("ltn12")
|
||||
local mime = require("mime")
|
||||
local string = require("string")
|
||||
local base = _G
|
||||
local table = require("table")
|
||||
module("socket.http")
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Program constants
|
||||
-----------------------------------------------------------------------------
|
||||
-- connection timeout in seconds
|
||||
TIMEOUT = 60
|
||||
-- default port for document retrieval
|
||||
PORT = 80
|
||||
-- user agent field sent in request
|
||||
USERAGENT = socket._VERSION
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Reads MIME headers from a connection, unfolding where needed
|
||||
-----------------------------------------------------------------------------
|
||||
local function receiveheaders(sock, headers)
|
||||
local line, name, value, err
|
||||
headers = headers or {}
|
||||
-- get first line
|
||||
line, err = sock:receive()
|
||||
if err then return nil, err end
|
||||
-- headers go until a blank line is found
|
||||
while line ~= "" do
|
||||
-- get field-name and value
|
||||
name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)"))
|
||||
if not (name and value) then return nil, "malformed reponse headers" end
|
||||
name = string.lower(name)
|
||||
-- get next line (value might be folded)
|
||||
line, err = sock:receive()
|
||||
if err then return nil, err end
|
||||
-- unfold any folded values
|
||||
while string.find(line, "^%s") do
|
||||
value = value .. line
|
||||
line = sock:receive()
|
||||
if err then return nil, err end
|
||||
end
|
||||
-- save pair in table
|
||||
if headers[name] then headers[name] = headers[name] .. ", " .. value
|
||||
else headers[name] = value end
|
||||
end
|
||||
return headers
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Extra sources and sinks
|
||||
-----------------------------------------------------------------------------
|
||||
socket.sourcet["http-chunked"] = function(sock, headers)
|
||||
return base.setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = function()
|
||||
-- get chunk size, skip extention
|
||||
local line, err = sock:receive()
|
||||
if err then return nil, err end
|
||||
local size = base.tonumber(string.gsub(line, ";.*", ""), 16)
|
||||
if not size then return nil, "invalid chunk size" end
|
||||
-- was it the last chunk?
|
||||
if size > 0 then
|
||||
-- if not, get chunk and skip terminating CRLF
|
||||
local chunk, err, part = sock:receive(size)
|
||||
if chunk then sock:receive() end
|
||||
return chunk, err
|
||||
else
|
||||
-- if it was, read trailers into headers table
|
||||
headers, err = receiveheaders(sock, headers)
|
||||
if not headers then return nil, err end
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
socket.sinkt["http-chunked"] = function(sock)
|
||||
return base.setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = function(self, chunk, err)
|
||||
if not chunk then return sock:send("0\r\n\r\n") end
|
||||
local size = string.format("%X\r\n", string.len(chunk))
|
||||
return sock:send(size .. chunk .. "\r\n")
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Low level HTTP API
|
||||
-----------------------------------------------------------------------------
|
||||
local metat = { __index = {} }
|
||||
|
||||
function open(host, port, create)
|
||||
-- create socket with user connect function, or with default
|
||||
local c = socket.try((create or socket.tcp)())
|
||||
local h = base.setmetatable({ c = c }, metat)
|
||||
-- create finalized try
|
||||
h.try = socket.newtry(function() h:close() end)
|
||||
-- set timeout before connecting
|
||||
h.try(c:settimeout(TIMEOUT))
|
||||
h.try(c:connect(host, port or PORT))
|
||||
-- here everything worked
|
||||
return h
|
||||
end
|
||||
|
||||
function metat.__index:sendrequestline(method, uri)
|
||||
local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri)
|
||||
return self.try(self.c:send(reqline))
|
||||
end
|
||||
|
||||
function metat.__index:sendheaders(headers)
|
||||
local h = "\r\n"
|
||||
for i, v in base.pairs(headers) do
|
||||
h = i .. ": " .. v .. "\r\n" .. h
|
||||
end
|
||||
self.try(self.c:send(h))
|
||||
return 1
|
||||
end
|
||||
|
||||
function metat.__index:sendbody(headers, source, step)
|
||||
source = source or ltn12.source.empty()
|
||||
step = step or ltn12.pump.step
|
||||
-- if we don't know the size in advance, send chunked and hope for the best
|
||||
local mode = "http-chunked"
|
||||
if headers["content-length"] then mode = "keep-open" end
|
||||
return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step))
|
||||
end
|
||||
|
||||
function metat.__index:receivestatusline()
|
||||
local status = self.try(self.c:receive(5))
|
||||
-- identify HTTP/0.9 responses, which do not contain a status line
|
||||
-- this is just a heuristic, but is what the RFC recommends
|
||||
if status ~= "HTTP/" then return nil, status end
|
||||
-- otherwise proceed reading a status line
|
||||
status = self.try(self.c:receive("*l", status))
|
||||
local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)"))
|
||||
return self.try(base.tonumber(code), status)
|
||||
end
|
||||
|
||||
function metat.__index:receiveheaders()
|
||||
return self.try(receiveheaders(self.c))
|
||||
end
|
||||
|
||||
function metat.__index:receivebody(headers, sink, step)
|
||||
sink = sink or ltn12.sink.null()
|
||||
step = step or ltn12.pump.step
|
||||
local length = base.tonumber(headers["content-length"])
|
||||
local t = headers["transfer-encoding"] -- shortcut
|
||||
local mode = "default" -- connection close
|
||||
if t and t ~= "identity" then mode = "http-chunked"
|
||||
elseif base.tonumber(headers["content-length"]) then mode = "by-length" end
|
||||
return self.try(ltn12.pump.all(socket.source(mode, self.c, length),
|
||||
sink, step))
|
||||
end
|
||||
|
||||
function metat.__index:receive09body(status, sink, step)
|
||||
local source = ltn12.source.rewind(socket.source("until-closed", self.c))
|
||||
source(status)
|
||||
return self.try(ltn12.pump.all(source, sink, step))
|
||||
end
|
||||
|
||||
function metat.__index:close()
|
||||
return self.c:close()
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- High level HTTP API
|
||||
-----------------------------------------------------------------------------
|
||||
local function adjusturi(reqt)
|
||||
local u = reqt
|
||||
-- if there is a proxy, we need the full url. otherwise, just a part.
|
||||
if not reqt.proxy and not PROXY then
|
||||
u = {
|
||||
path = socket.try(reqt.path, "invalid path 'nil'"),
|
||||
params = reqt.params,
|
||||
query = reqt.query,
|
||||
fragment = reqt.fragment
|
||||
}
|
||||
end
|
||||
return url.build(u)
|
||||
end
|
||||
|
||||
local function adjustproxy(reqt)
|
||||
local proxy = reqt.proxy or PROXY
|
||||
if proxy then
|
||||
proxy = url.parse(proxy)
|
||||
return proxy.host, proxy.port or 3128
|
||||
else
|
||||
return reqt.host, reqt.port
|
||||
end
|
||||
end
|
||||
|
||||
local function adjustheaders(reqt)
|
||||
-- default headers
|
||||
local lower = {
|
||||
["user-agent"] = USERAGENT,
|
||||
["host"] = reqt.host,
|
||||
["connection"] = "close, TE",
|
||||
["te"] = "trailers"
|
||||
}
|
||||
-- if we have authentication information, pass it along
|
||||
if reqt.user and reqt.password then
|
||||
lower["authorization"] =
|
||||
"Basic " .. (mime.b64(reqt.user .. ":" .. reqt.password))
|
||||
end
|
||||
-- override with user headers
|
||||
for i,v in base.pairs(reqt.headers or lower) do
|
||||
lower[string.lower(i)] = v
|
||||
end
|
||||
return lower
|
||||
end
|
||||
|
||||
-- default url parts
|
||||
local default = {
|
||||
host = "",
|
||||
port = PORT,
|
||||
path ="/",
|
||||
scheme = "http"
|
||||
}
|
||||
|
||||
local function adjustrequest(reqt)
|
||||
-- parse url if provided
|
||||
local nreqt = reqt.url and url.parse(reqt.url, default) or {}
|
||||
-- explicit components override url
|
||||
for i,v in base.pairs(reqt) do nreqt[i] = v end
|
||||
if nreqt.port == "" then nreqt.port = 80 end
|
||||
socket.try(nreqt.host and nreqt.host ~= "",
|
||||
"invalid host '" .. base.tostring(nreqt.host) .. "'")
|
||||
-- compute uri if user hasn't overriden
|
||||
nreqt.uri = reqt.uri or adjusturi(nreqt)
|
||||
-- ajust host and port if there is a proxy
|
||||
nreqt.host, nreqt.port = adjustproxy(nreqt)
|
||||
-- adjust headers in request
|
||||
nreqt.headers = adjustheaders(nreqt)
|
||||
return nreqt
|
||||
end
|
||||
|
||||
local function shouldredirect(reqt, code, headers)
|
||||
return headers.location and
|
||||
string.gsub(headers.location, "%s", "") ~= "" and
|
||||
(reqt.redirect ~= false) and
|
||||
(code == 301 or code == 302) and
|
||||
(not reqt.method or reqt.method == "GET" or reqt.method == "HEAD")
|
||||
and (not reqt.nredirects or reqt.nredirects < 5)
|
||||
end
|
||||
|
||||
local function shouldreceivebody(reqt, code)
|
||||
if reqt.method == "HEAD" then return nil end
|
||||
if code == 204 or code == 304 then return nil end
|
||||
if code >= 100 and code < 200 then return nil end
|
||||
return 1
|
||||
end
|
||||
|
||||
-- forward declarations
|
||||
local trequest, tredirect
|
||||
|
||||
function tredirect(reqt, location)
|
||||
local result, code, headers, status = trequest {
|
||||
-- the RFC says the redirect URL has to be absolute, but some
|
||||
-- servers do not respect that
|
||||
url = url.absolute(reqt.url, location),
|
||||
source = reqt.source,
|
||||
sink = reqt.sink,
|
||||
headers = reqt.headers,
|
||||
proxy = reqt.proxy,
|
||||
nredirects = (reqt.nredirects or 0) + 1,
|
||||
create = reqt.create
|
||||
}
|
||||
-- pass location header back as a hint we redirected
|
||||
headers = headers or {}
|
||||
headers.location = headers.location or location
|
||||
return result, code, headers, status
|
||||
end
|
||||
|
||||
function trequest(reqt)
|
||||
-- we loop until we get what we want, or
|
||||
-- until we are sure there is no way to get it
|
||||
local nreqt = adjustrequest(reqt)
|
||||
local h = open(nreqt.host, nreqt.port, nreqt.create)
|
||||
-- send request line and headers
|
||||
h:sendrequestline(nreqt.method, nreqt.uri)
|
||||
h:sendheaders(nreqt.headers)
|
||||
-- if there is a body, send it
|
||||
if nreqt.source then
|
||||
h:sendbody(nreqt.headers, nreqt.source, nreqt.step)
|
||||
end
|
||||
local code, status = h:receivestatusline()
|
||||
-- if it is an HTTP/0.9 server, simply get the body and we are done
|
||||
if not code then
|
||||
h:receive09body(status, nreqt.sink, nreqt.step)
|
||||
return 1, 200
|
||||
end
|
||||
local headers
|
||||
-- ignore any 100-continue messages
|
||||
while code == 100 do
|
||||
headers = h:receiveheaders()
|
||||
code, status = h:receivestatusline()
|
||||
end
|
||||
headers = h:receiveheaders()
|
||||
-- at this point we should have a honest reply from the server
|
||||
-- we can't redirect if we already used the source, so we report the error
|
||||
if shouldredirect(nreqt, code, headers) and not nreqt.source then
|
||||
h:close()
|
||||
return tredirect(reqt, headers.location)
|
||||
end
|
||||
-- here we are finally done
|
||||
if shouldreceivebody(nreqt, code) then
|
||||
h:receivebody(headers, nreqt.sink, nreqt.step)
|
||||
end
|
||||
h:close()
|
||||
return 1, code, headers, status
|
||||
end
|
||||
|
||||
local function srequest(u, b)
|
||||
local t = {}
|
||||
local reqt = {
|
||||
url = u,
|
||||
sink = ltn12.sink.table(t)
|
||||
}
|
||||
if b then
|
||||
reqt.source = ltn12.source.string(b)
|
||||
reqt.headers = {
|
||||
["content-length"] = string.len(b),
|
||||
["content-type"] = "application/x-www-form-urlencoded"
|
||||
}
|
||||
reqt.method = "POST"
|
||||
end
|
||||
local code, headers, status = socket.skip(1, trequest(reqt))
|
||||
return table.concat(t), code, headers, status
|
||||
end
|
||||
|
||||
request = socket.protect(function(reqt, body)
|
||||
if base.type(reqt) == "string" then return srequest(reqt, body)
|
||||
else return trequest(reqt) end
|
||||
end)
|
||||
@@ -0,0 +1,276 @@
|
||||
local folder = ({...})[1]:gsub('%.init$', '')
|
||||
local md5 = require(folder .. ".md5" )
|
||||
local http = require("socket.http")
|
||||
|
||||
local GJ = {
|
||||
gameID, gameKey,
|
||||
isLoggedIn = false,
|
||||
username, userToken,
|
||||
trophies = {}
|
||||
}
|
||||
|
||||
local BASE_URL = "http://gamejolt.com/api/game/v1/"
|
||||
|
||||
local escape = function (a)
|
||||
return tostring(a):gsub("([^%w%-%.%_])",function (a)
|
||||
return string.format("%%%02X",string.byte(a))
|
||||
end)
|
||||
end
|
||||
|
||||
local function req(s, f, pu, pt, data)
|
||||
local url = BASE_URL .. s .. "&game_id=" .. GJ.gameID .. "&format=" .. f
|
||||
if pu then url = url .. "&username=" .. GJ.username end
|
||||
if pt then url = url .. "&user_token=" .. GJ.userToken end
|
||||
|
||||
local b = md5.sumhexa(url .. GJ.gameKey)
|
||||
url = url .. "&signature=" .. b
|
||||
|
||||
local r, e = http.request(url, data) --POST request if needed
|
||||
return r
|
||||
end
|
||||
|
||||
local function parseKeypair(s, on)
|
||||
local c, len = 0, string.len(s)
|
||||
local b, k, v
|
||||
|
||||
while c < len do
|
||||
b = string.find(s, ":", c)
|
||||
if b == nil then break end
|
||||
k = string.sub(s, c, b - 1)
|
||||
c = b + 2
|
||||
b = string.find(s, '"', c)
|
||||
v = string.sub(s, c, b - 1)
|
||||
c = b + 3
|
||||
on(k, v)
|
||||
end
|
||||
end
|
||||
|
||||
local function handleTrophies(str)
|
||||
local d = req("trophies/?" .. str, "keypair", true, true)
|
||||
local t, f = {}
|
||||
|
||||
parseKeypair(d, function(k, v)
|
||||
if k ~= "success" then
|
||||
if k == "id" then
|
||||
f = {}
|
||||
table.insert(t, f)
|
||||
end
|
||||
f[k] = v
|
||||
end
|
||||
end)
|
||||
return t
|
||||
end
|
||||
|
||||
function GJ.init(id, key, args)
|
||||
GJ.gameID = id
|
||||
GJ.gameKey = key
|
||||
|
||||
if args and type(args)=="table" then
|
||||
for k,v in pairs(args) do
|
||||
local a = v:match("^gjapi_(.*)")
|
||||
|
||||
if a then
|
||||
key, value = a:match("^(.-)=(.-)$")
|
||||
|
||||
if key == "username" then
|
||||
GJ.username = value
|
||||
elseif key == "token" then
|
||||
GJ.userToken = value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function GJ.getCredentials()
|
||||
local a = love.system.getOS() == "Windows" and "\\" or "/"
|
||||
local f = io.open(love.filesystem.getWorkingDirectory()..a.."gjapi-credentials.txt")
|
||||
|
||||
if f then
|
||||
GJ.username = f:read()
|
||||
GJ.userToken = f:read()
|
||||
end
|
||||
|
||||
if GJ.username and GJ.userToken then
|
||||
return true, GJ.username, GJ.userToken
|
||||
else
|
||||
return false, "Couldn't find, open or read the \"gjapi-credentials.txt\" file"
|
||||
end
|
||||
end
|
||||
|
||||
-- users
|
||||
function GJ.authUser(name, token)
|
||||
GJ.username = name or GJ.username or "" --Here we could put LÖVE default ones
|
||||
GJ.userToken = token or GJ.userToken or ""
|
||||
|
||||
local s = string.find(req("users/auth/?", "dump", true, true), "SUCCESS") ~= nil
|
||||
GJ.isLoggedIn = s
|
||||
return s
|
||||
end
|
||||
|
||||
function GJ.fetchUserByName(name)
|
||||
local r = req("users/?username=" .. name, "keypair", false, false)
|
||||
|
||||
local t = {}
|
||||
parseKeypair(r, function(k, v)
|
||||
t[k] = v
|
||||
end)
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
function GJ.fetchUserByID(id)
|
||||
local r = req("users/?user_id=" .. id, "keypair", false, false)
|
||||
|
||||
local t = {}
|
||||
parseKeypair(r, function(k, v)
|
||||
t[k] = v
|
||||
end)
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
-- sessions
|
||||
function GJ.openSession()
|
||||
return string.find(req("sessions/open/?", "dump", true, true), "SUCCESS") ~= nil
|
||||
end
|
||||
|
||||
function GJ.pingSession(active)
|
||||
local status = "idle"
|
||||
if active then status = "active" end
|
||||
|
||||
return string.find(req("sessions/open/?status=" .. status, "dump", true, true), "SUCCESS") ~= nil
|
||||
end
|
||||
|
||||
function GJ.closeSession()
|
||||
return string.find(req("sessions/close/?", "dump", true, true), "SUCCESS") ~= nil
|
||||
end
|
||||
|
||||
-- data store
|
||||
function GJ.fetchData(key, isGlobal)
|
||||
local pu, pt = true, true
|
||||
if isGlobal then pu, pt = false, false end
|
||||
|
||||
local d = req("data-store/?key=" .. escape(key), "dump", pu, pt)
|
||||
|
||||
return string.sub(d, string.find(d, "\n"), string.len(d))
|
||||
end
|
||||
|
||||
function GJ.setData(key, data, isGlobal)
|
||||
local pu, pt = true, true
|
||||
if isGlobal then pu, pt = false, false end
|
||||
|
||||
return string.find(req("data-store/set/?key=" .. escape(key) .. '&data=' .. escape(data), "dump", pu, pt), "SUCCESS") ~= nil
|
||||
end
|
||||
|
||||
function GJ.setBigData(key, data, isGlobal)
|
||||
local pu, pt = true, true
|
||||
if isGlobal then pu, pt = false, false end
|
||||
|
||||
return string.find(req("data-store/set/?key=" .. escape(key), "dump", pu, pt, "data="..escape(data)), "SUCCESS") ~= nil
|
||||
end
|
||||
|
||||
function GJ.updateData(key, value, operation, isGlobal)
|
||||
local pu, pt = true, true
|
||||
if isGlobal then pu, pt = false, false end
|
||||
|
||||
local d = req("data-store/update/?key=" .. escape(key) .. "&operation=" .. operation .. "&value=" .. escape(value), "dump", pu, pt)
|
||||
return string.sub(d, string.find(d, "\n"), string.len(d))
|
||||
end
|
||||
|
||||
function GJ.removeData(key, isGlobal)
|
||||
local pu, pt = true, true
|
||||
if isGlobal then pu, pt = false, false end
|
||||
|
||||
return string.find(req("data-store/remove/?key=" .. escape(key), "dump", pu, pt), "SUCCESS") ~= nil
|
||||
end
|
||||
|
||||
function GJ.fetchStorageKeys(isGlobal)
|
||||
local pu, pt = true, true
|
||||
if isGlobal then pu, pt = false, false end
|
||||
|
||||
local d = req("data-store/get-keys/?", "keypair", pu, pt)
|
||||
|
||||
local t = {}
|
||||
parseKeypair(d, function(k, v)
|
||||
if k ~= "success" then table.insert(t, v) end
|
||||
end)
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
-- trophies
|
||||
function GJ.giveTrophy(id)
|
||||
local s = string.find(req("trophies/add-achieved/?trophy_id=" .. id, "dump", true, true), "SUCCESS") ~= nil
|
||||
GJ.fetchAllTrophies(true)
|
||||
return
|
||||
end
|
||||
|
||||
function GJ.fetchTrophy(id)
|
||||
local d = req("trophies/?trophy_id=" .. id, "keypair", true, true)
|
||||
|
||||
local t = {}
|
||||
parseKeypair(d, function(k, v)
|
||||
if k ~= "success" then t[k] = v end
|
||||
end)
|
||||
return t
|
||||
end
|
||||
|
||||
function GJ.fetchTrophiesByStatus(achieved)
|
||||
return handleTrophies("achieved=" .. (achieved and "true" or "false"))
|
||||
end
|
||||
|
||||
function GJ.fetchAllTrophies(f)
|
||||
if f then
|
||||
GJ.trophies = handleTrophies("")
|
||||
end
|
||||
return GJ.trophies
|
||||
end
|
||||
|
||||
-- scores
|
||||
function GJ.addScore(score, desc, tableID, guestName, extraData)
|
||||
local pu, pt, s = true, true, ""
|
||||
if guestName then pu, pt = false, false, s .. "&guest=" .. escape(guestName) end
|
||||
|
||||
if extraData then s = s .. "&extra_data=" .. escape(extraData) end
|
||||
if tableID then s = s .. "&table_id=" .. escape(tableID) end
|
||||
|
||||
return string.find(req("scores/add/?score=" .. escape(desc) .. "&sort=" .. score .. s, "dump", pu, pt), "SUCCESS") ~= nil
|
||||
end
|
||||
|
||||
function GJ.fetchScores(limit, tableID)
|
||||
local pu, pt, s = true, true, ""
|
||||
if tableID then pu, pt, s = false, false, "&table_id=" .. escape(tableID) end
|
||||
|
||||
local d = req("scores/?limit=" .. limit .. s, "keypair", pu, pt)
|
||||
local t, f = {}
|
||||
|
||||
parseKeypair(d, function(k, v)
|
||||
if k ~= "success" then
|
||||
if k == "score" then
|
||||
f = {}
|
||||
table.insert(t, f)
|
||||
end
|
||||
f[k] = v
|
||||
end
|
||||
end)
|
||||
return t
|
||||
end
|
||||
|
||||
function GJ.fetchTables()
|
||||
local d = req("scores/tables/?", "keypair", false, false)
|
||||
local t, f = {}
|
||||
|
||||
parseKeypair(d, function(k, v)
|
||||
if k ~= "success" then
|
||||
if k == "id" then
|
||||
f = {}
|
||||
table.insert(t, f)
|
||||
end
|
||||
f[k] = v
|
||||
end
|
||||
end)
|
||||
return t
|
||||
end
|
||||
|
||||
return GJ
|
||||
@@ -0,0 +1,393 @@
|
||||
local md5 = {
|
||||
_VERSION = "md5.lua 1.0.0",
|
||||
_DESCRIPTION = "MD5 computation in Lua (5.1-3, LuaJIT)",
|
||||
_URL = "https://github.com/kikito/md5.lua",
|
||||
_LICENSE = [[
|
||||
MIT LICENSE
|
||||
|
||||
Copyright (c) 2013 Enrique García Cota + Adam Baldwin + hanzao + Equi 4 Software
|
||||
|
||||
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:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
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.
|
||||
]]
|
||||
}
|
||||
|
||||
-- bit lib implementions
|
||||
|
||||
local floor, abs, max = math.floor, math.abs, math.max
|
||||
local char, byte, format, rep, sub =
|
||||
string.char, string.byte, string.format, string.rep, string.sub
|
||||
local bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift
|
||||
|
||||
local ok, bit = pcall(require, 'bit')
|
||||
if not ok then ok, bit = pcall(require, 'bit32') end
|
||||
|
||||
if ok then
|
||||
bit_or, bit_and, bit_not, bit_xor = bit.bor, bit.band, bit.bnot, bit.bxor
|
||||
bit_rshift, bit_lshift = bit.rshift, bit.lshift
|
||||
else
|
||||
local function check_int(n)
|
||||
-- checking not float
|
||||
if(n - floor(n) > 0) then
|
||||
error("trying to use bitwise operation on non-integer!")
|
||||
end
|
||||
end
|
||||
|
||||
local function tbl2number(tbl)
|
||||
local n = #tbl
|
||||
|
||||
local rslt = 0
|
||||
local power = 1
|
||||
for i = 1, n do
|
||||
rslt = rslt + tbl[i]*power
|
||||
power = power*2
|
||||
end
|
||||
|
||||
return rslt
|
||||
end
|
||||
|
||||
local function expand(tbl_m, tbl_n)
|
||||
local big = {}
|
||||
local small = {}
|
||||
if(#tbl_m > #tbl_n) then
|
||||
big = tbl_m
|
||||
small = tbl_n
|
||||
else
|
||||
big = tbl_n
|
||||
small = tbl_m
|
||||
end
|
||||
-- expand small
|
||||
for i = #small + 1, #big do
|
||||
small[i] = 0
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local to_bits -- needs to be declared before bit_not
|
||||
|
||||
function bit_not(n)
|
||||
local tbl = to_bits(n)
|
||||
local size = max(#tbl, 32)
|
||||
for i = 1, size do
|
||||
if(tbl[i] == 1) then
|
||||
tbl[i] = 0
|
||||
else
|
||||
tbl[i] = 1
|
||||
end
|
||||
end
|
||||
return tbl2number(tbl)
|
||||
end
|
||||
|
||||
-- defined as local above
|
||||
to_bits = function (n)
|
||||
check_int(n)
|
||||
if(n < 0) then
|
||||
-- negative
|
||||
return to_bits(bit_not(abs(n)) + 1)
|
||||
end
|
||||
-- to bits table
|
||||
local tbl = {}
|
||||
local cnt = 1
|
||||
while (n > 0) do
|
||||
local last = math.mod(n,2)
|
||||
if(last == 1) then
|
||||
tbl[cnt] = 1
|
||||
else
|
||||
tbl[cnt] = 0
|
||||
end
|
||||
n = (n-last)/2
|
||||
cnt = cnt + 1
|
||||
end
|
||||
|
||||
return tbl
|
||||
end
|
||||
|
||||
function bit_or(m, n)
|
||||
local tbl_m = to_bits(m)
|
||||
local tbl_n = to_bits(n)
|
||||
expand(tbl_m, tbl_n)
|
||||
|
||||
local tbl = {}
|
||||
local rslt = max(#tbl_m, #tbl_n)
|
||||
for i = 1, rslt do
|
||||
if(tbl_m[i]== 0 and tbl_n[i] == 0) then
|
||||
tbl[i] = 0
|
||||
else
|
||||
tbl[i] = 1
|
||||
end
|
||||
end
|
||||
|
||||
return tbl2number(tbl)
|
||||
end
|
||||
|
||||
function bit_and(m, n)
|
||||
local tbl_m = to_bits(m)
|
||||
local tbl_n = to_bits(n)
|
||||
expand(tbl_m, tbl_n)
|
||||
|
||||
local tbl = {}
|
||||
local rslt = max(#tbl_m, #tbl_n)
|
||||
for i = 1, rslt do
|
||||
if(tbl_m[i]== 0 or tbl_n[i] == 0) then
|
||||
tbl[i] = 0
|
||||
else
|
||||
tbl[i] = 1
|
||||
end
|
||||
end
|
||||
|
||||
return tbl2number(tbl)
|
||||
end
|
||||
|
||||
function bit_xor(m, n)
|
||||
local tbl_m = to_bits(m)
|
||||
local tbl_n = to_bits(n)
|
||||
expand(tbl_m, tbl_n)
|
||||
|
||||
local tbl = {}
|
||||
local rslt = max(#tbl_m, #tbl_n)
|
||||
for i = 1, rslt do
|
||||
if(tbl_m[i] ~= tbl_n[i]) then
|
||||
tbl[i] = 1
|
||||
else
|
||||
tbl[i] = 0
|
||||
end
|
||||
end
|
||||
|
||||
return tbl2number(tbl)
|
||||
end
|
||||
|
||||
function bit_rshift(n, bits)
|
||||
check_int(n)
|
||||
|
||||
local high_bit = 0
|
||||
if(n < 0) then
|
||||
-- negative
|
||||
n = bit_not(abs(n)) + 1
|
||||
high_bit = 2147483648 -- 0x80000000
|
||||
end
|
||||
|
||||
for i=1, bits do
|
||||
n = n/2
|
||||
n = bit_or(floor(n), high_bit)
|
||||
end
|
||||
return floor(n)
|
||||
end
|
||||
|
||||
function bit_lshift(n, bits)
|
||||
check_int(n)
|
||||
|
||||
if(n < 0) then
|
||||
-- negative
|
||||
n = bit_not(abs(n)) + 1
|
||||
end
|
||||
|
||||
for i=1, bits do
|
||||
n = n*2
|
||||
end
|
||||
return bit_and(n, 4294967295) -- 0xFFFFFFFF
|
||||
end
|
||||
end
|
||||
|
||||
-- convert little-endian 32-bit int to a 4-char string
|
||||
local function lei2str(i)
|
||||
local f=function (s) return char( bit_and( bit_rshift(i, s), 255)) end
|
||||
return f(0)..f(8)..f(16)..f(24)
|
||||
end
|
||||
|
||||
-- convert raw string to big-endian int
|
||||
local function str2bei(s)
|
||||
local v=0
|
||||
for i=1, #s do
|
||||
v = v * 256 + byte(s, i)
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
-- convert raw string to little-endian int
|
||||
local function str2lei(s)
|
||||
local v=0
|
||||
for i = #s,1,-1 do
|
||||
v = v*256 + byte(s, i)
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
-- cut up a string in little-endian ints of given size
|
||||
local function cut_le_str(s,...)
|
||||
local o, r = 1, {}
|
||||
local args = {...}
|
||||
for i=1, #args do
|
||||
table.insert(r, str2lei(sub(s, o, o + args[i] - 1)))
|
||||
o = o + args[i]
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
local swap = function (w) return str2bei(lei2str(w)) end
|
||||
|
||||
local function hex2binaryaux(hexval)
|
||||
return char(tonumber(hexval, 16))
|
||||
end
|
||||
|
||||
local function hex2binary(hex)
|
||||
local result, _ = hex:gsub('..', hex2binaryaux)
|
||||
return result
|
||||
end
|
||||
|
||||
-- An MD5 mplementation in Lua, requires bitlib (hacked to use LuaBit from above, ugh)
|
||||
-- 10/02/2001 jcw@equi4.com
|
||||
|
||||
local FF = 0xffffffff
|
||||
local CONSTS = {
|
||||
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
|
||||
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
||||
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
|
||||
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
|
||||
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
|
||||
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
||||
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
|
||||
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
|
||||
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
|
||||
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
||||
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
|
||||
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
||||
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
|
||||
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
||||
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
|
||||
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
|
||||
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476
|
||||
}
|
||||
|
||||
local f=function (x,y,z) return bit_or(bit_and(x,y),bit_and(-x-1,z)) end
|
||||
local g=function (x,y,z) return bit_or(bit_and(x,z),bit_and(y,-z-1)) end
|
||||
local h=function (x,y,z) return bit_xor(x,bit_xor(y,z)) end
|
||||
local i=function (x,y,z) return bit_xor(y,bit_or(x,-z-1)) end
|
||||
local z=function (f,a,b,c,d,x,s,ac)
|
||||
a=bit_and(a+f(b,c,d)+x+ac,FF)
|
||||
-- be *very* careful that left shift does not cause rounding!
|
||||
return bit_or(bit_lshift(bit_and(a,bit_rshift(FF,s)),s),bit_rshift(a,32-s))+b
|
||||
end
|
||||
|
||||
local function transform(A,B,C,D,X)
|
||||
local a,b,c,d=A,B,C,D
|
||||
local t=CONSTS
|
||||
|
||||
a=z(f,a,b,c,d,X[ 0], 7,t[ 1])
|
||||
d=z(f,d,a,b,c,X[ 1],12,t[ 2])
|
||||
c=z(f,c,d,a,b,X[ 2],17,t[ 3])
|
||||
b=z(f,b,c,d,a,X[ 3],22,t[ 4])
|
||||
a=z(f,a,b,c,d,X[ 4], 7,t[ 5])
|
||||
d=z(f,d,a,b,c,X[ 5],12,t[ 6])
|
||||
c=z(f,c,d,a,b,X[ 6],17,t[ 7])
|
||||
b=z(f,b,c,d,a,X[ 7],22,t[ 8])
|
||||
a=z(f,a,b,c,d,X[ 8], 7,t[ 9])
|
||||
d=z(f,d,a,b,c,X[ 9],12,t[10])
|
||||
c=z(f,c,d,a,b,X[10],17,t[11])
|
||||
b=z(f,b,c,d,a,X[11],22,t[12])
|
||||
a=z(f,a,b,c,d,X[12], 7,t[13])
|
||||
d=z(f,d,a,b,c,X[13],12,t[14])
|
||||
c=z(f,c,d,a,b,X[14],17,t[15])
|
||||
b=z(f,b,c,d,a,X[15],22,t[16])
|
||||
|
||||
a=z(g,a,b,c,d,X[ 1], 5,t[17])
|
||||
d=z(g,d,a,b,c,X[ 6], 9,t[18])
|
||||
c=z(g,c,d,a,b,X[11],14,t[19])
|
||||
b=z(g,b,c,d,a,X[ 0],20,t[20])
|
||||
a=z(g,a,b,c,d,X[ 5], 5,t[21])
|
||||
d=z(g,d,a,b,c,X[10], 9,t[22])
|
||||
c=z(g,c,d,a,b,X[15],14,t[23])
|
||||
b=z(g,b,c,d,a,X[ 4],20,t[24])
|
||||
a=z(g,a,b,c,d,X[ 9], 5,t[25])
|
||||
d=z(g,d,a,b,c,X[14], 9,t[26])
|
||||
c=z(g,c,d,a,b,X[ 3],14,t[27])
|
||||
b=z(g,b,c,d,a,X[ 8],20,t[28])
|
||||
a=z(g,a,b,c,d,X[13], 5,t[29])
|
||||
d=z(g,d,a,b,c,X[ 2], 9,t[30])
|
||||
c=z(g,c,d,a,b,X[ 7],14,t[31])
|
||||
b=z(g,b,c,d,a,X[12],20,t[32])
|
||||
|
||||
a=z(h,a,b,c,d,X[ 5], 4,t[33])
|
||||
d=z(h,d,a,b,c,X[ 8],11,t[34])
|
||||
c=z(h,c,d,a,b,X[11],16,t[35])
|
||||
b=z(h,b,c,d,a,X[14],23,t[36])
|
||||
a=z(h,a,b,c,d,X[ 1], 4,t[37])
|
||||
d=z(h,d,a,b,c,X[ 4],11,t[38])
|
||||
c=z(h,c,d,a,b,X[ 7],16,t[39])
|
||||
b=z(h,b,c,d,a,X[10],23,t[40])
|
||||
a=z(h,a,b,c,d,X[13], 4,t[41])
|
||||
d=z(h,d,a,b,c,X[ 0],11,t[42])
|
||||
c=z(h,c,d,a,b,X[ 3],16,t[43])
|
||||
b=z(h,b,c,d,a,X[ 6],23,t[44])
|
||||
a=z(h,a,b,c,d,X[ 9], 4,t[45])
|
||||
d=z(h,d,a,b,c,X[12],11,t[46])
|
||||
c=z(h,c,d,a,b,X[15],16,t[47])
|
||||
b=z(h,b,c,d,a,X[ 2],23,t[48])
|
||||
|
||||
a=z(i,a,b,c,d,X[ 0], 6,t[49])
|
||||
d=z(i,d,a,b,c,X[ 7],10,t[50])
|
||||
c=z(i,c,d,a,b,X[14],15,t[51])
|
||||
b=z(i,b,c,d,a,X[ 5],21,t[52])
|
||||
a=z(i,a,b,c,d,X[12], 6,t[53])
|
||||
d=z(i,d,a,b,c,X[ 3],10,t[54])
|
||||
c=z(i,c,d,a,b,X[10],15,t[55])
|
||||
b=z(i,b,c,d,a,X[ 1],21,t[56])
|
||||
a=z(i,a,b,c,d,X[ 8], 6,t[57])
|
||||
d=z(i,d,a,b,c,X[15],10,t[58])
|
||||
c=z(i,c,d,a,b,X[ 6],15,t[59])
|
||||
b=z(i,b,c,d,a,X[13],21,t[60])
|
||||
a=z(i,a,b,c,d,X[ 4], 6,t[61])
|
||||
d=z(i,d,a,b,c,X[11],10,t[62])
|
||||
c=z(i,c,d,a,b,X[ 2],15,t[63])
|
||||
b=z(i,b,c,d,a,X[ 9],21,t[64])
|
||||
|
||||
return A+a,B+b,C+c,D+d
|
||||
end
|
||||
|
||||
----------------------------------------------------------------
|
||||
|
||||
function md5.sumhexa(s)
|
||||
local msgLen = #s
|
||||
local padLen = 56 - msgLen % 64
|
||||
|
||||
if msgLen % 64 > 56 then padLen = padLen + 64 end
|
||||
|
||||
if padLen == 0 then padLen = 64 end
|
||||
|
||||
s = s .. char(128) .. rep(char(0),padLen-1) .. lei2str(8*msgLen) .. lei2str(0)
|
||||
|
||||
assert(#s % 64 == 0)
|
||||
|
||||
local t = CONSTS
|
||||
local a,b,c,d = t[65],t[66],t[67],t[68]
|
||||
|
||||
for i=1,#s,64 do
|
||||
local X = cut_le_str(sub(s,i,i+63),4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4)
|
||||
assert(#X == 16)
|
||||
X[0] = table.remove(X,1) -- zero based!
|
||||
a,b,c,d = transform(a,b,c,d,X)
|
||||
end
|
||||
|
||||
return format("%08x%08x%08x%08x",swap(a),swap(b),swap(c),swap(d))
|
||||
end
|
||||
|
||||
function md5.sum(s)
|
||||
return hex2binary(md5.sumhexa(s))
|
||||
end
|
||||
|
||||
return md5
|
||||
+4
-7
@@ -42,11 +42,8 @@ function love.load()
|
||||
log("Window icon set.")
|
||||
|
||||
-- initialize Game Jolt
|
||||
if GameJolt.init(48728, "b8e4a0eae1509d3edef3d8451bae1842") then
|
||||
log("Game Jolt initialized.")
|
||||
else
|
||||
log("Err: Game Jolt NOT initialized.")
|
||||
end
|
||||
GameJolt.init(48728, "b8e4a0eae1509d3edef3d8451bae1842")
|
||||
log("Game Jolt initialized.")
|
||||
|
||||
-- load settings and change if needed
|
||||
local gamejoltSessionOpen = false --set true if we get a session open
|
||||
@@ -68,12 +65,12 @@ function love.load()
|
||||
inifile.save("settings.ini", settings)
|
||||
error("You have been banned from Game Jolt. Your login data has been deleted, re-open RGB to continue playing without Game Jolt account integration.")
|
||||
end
|
||||
gamejoltSessionOpen = GameJolt.openSession() -- tell Game Jolt the user is playing
|
||||
if sessionSuccess then
|
||||
if GameJolt.openSession() then -- tell Game Jolt the user is playing
|
||||
-- we don't ping immediately, also the menu DOES ping immediately
|
||||
gamejoltSessionOpen = true
|
||||
log("Game Jolt session opened.")
|
||||
else
|
||||
gamejoltSessionOpen = false
|
||||
-- TODO make this known to user
|
||||
log("Couldn't open a session with Game Jolt.")
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user