dobrograd-13-06-2022/garrysmod/addons/feature-wire/lua/entities/gmod_wire_hdd.lua
Jonny_Bro (Nikita) e4d5311906 first commit
2023-11-16 15:01:19 +05:00

328 lines
8.5 KiB
Lua

AddCSLuaFile()
DEFINE_BASECLASS( "base_wire_entity" )
ENT.PrintName = "Wire Flash EEPROM"
ENT.WireDebugName = "WireHDD"
if CLIENT then return end -- No more client
function ENT:OnRemove()
for k,v in pairs(self.CacheUpdated) do
file.Write(self:GetStructName(k),self:MakeFloatTable(self.Cache[k]))
end
end
function ENT:Initialize()
self:PhysicsInit(SOLID_VPHYSICS)
self:SetMoveType(MOVETYPE_VPHYSICS)
self:SetSolid(SOLID_VPHYSICS)
self:SetUseType(SIMPLE_USE)
self.Outputs = Wire_CreateOutputs(self, { "Data", "Capacity", "DriveID" })
self.Inputs = Wire_CreateInputs(self, { "Clk", "AddrRead", "AddrWrite", "Data" })
self.Clk = 0
self.AWrite = 0
self.ARead = 0
self.Data = 0
self.Out = 0
-- Flash type
-- 0: compatibility 16 values per block mode
-- 1: 128 values per block mode
self.FlashType = 0
self.BlockSize = 16
-- Hard drive id/folder id:
self.DriveID = 0
self.PrevDriveID = nil
-- Hard drive capicacity (loaded from hdd)
self.DriveCap = 0
self.MaxAddress = 0
-- Current cache (array of blocks)
self.Cache = {}
self.CacheUpdated = {}
-- Owners STEAMID
self.Owner_SteamID = "SINGLEPLAYER"
self:NextThink(CurTime()+1.0)
end
function ENT:Setup(DriveID, DriveCap)
self.DriveID = DriveID
self.DriveCap = DriveCap
self:UpdateCap()
self:SetOverlayText(self.DriveCap.."kb".."\nWriteAddr:"..self.AWrite.." Data:"..self.Data.." Clock:"..self.Clk.."\nReadAddr:"..self.ARead.." = ".. self.Out)
Wire_TriggerOutput(self, "DriveID", self.DriveID)
end
function ENT:GetStructName(name)
return "WireFlash/"..(self.Owner_SteamID or "UNKNOWN").."/HDD"..self.DriveID.."/"..name..".txt"
end
function ENT:GetCap()
-- If hard drive exists
if file.Exists(self:GetStructName("drive"),"DATA") then
-- Read format data
local formatData = file.Read(self:GetStructName("drive"),"DATA")
if tonumber(formatData) then
self.DriveCap = tonumber(formatData)
self.FlashType = 0
self.BlockSize = 16
self.MaxAddress = self.DriveCap * 1024
else
local formatInfo = string.Explode("\n",formatData)
if (formatInfo[1] == "FLASH1") then
self.DriveCap = tonumber(formatInfo[2]) or 0
self.MaxAddress = tonumber(formatInfo[3]) or (self.DriveCap * 1024)
self.FlashType = 1
self.BlockSize = 32
else
file.Write(self:GetStructName("drive"),"FLASH1\n"..self.DriveCap.."\n"..self.MaxAddress)
end
end
else
file.Write(self:GetStructName("drive"),"FLASH1\n"..self.DriveCap.."\n"..self.MaxAddress)
self.FlashType = 1
self.BlockSize = 32
self.MaxAddress = 0
end
--Can't have cap bigger than 256 in MP
if (not game.SinglePlayer()) and (self.DriveCap > 256) then
self.DriveCap = 256
end
Wire_TriggerOutput(self, "Capacity", self.DriveCap)
end
function ENT:UpdateCap()
--Can't have cap bigger than 256 in MP
if (not game.SinglePlayer()) and (self.DriveCap > 256) then
self.DriveCap = 256
end
if self.FlashType == 0 then
file.Write(self:GetStructName("drive"),self.DriveCap)
else
file.Write(self:GetStructName("drive"),"FLASH1\n"..self.DriveCap.."\n"..self.MaxAddress)
end
self:GetCap()
end
function ENT:GetFloatTable(Text)
local text = Text
local tbl = {}
local ptr = 0
while (string.len(text) > 0) do
local value = string.sub(text, 1, 24)
text = string.sub(text, 25)
tbl[ptr] = tonumber(value)
ptr = ptr + 1
end
return tbl
end
function ENT:MakeFloatTable(Table)
local text = ""
for i=0,#Table-1 do
--Clamp size to 24 chars
local floatstr = string.sub(tostring(Table[i]),1,24)
--Make a string, and append missing spaces
floatstr = floatstr .. string.rep(" ",24-string.len(floatstr))
text = text..floatstr
end
return text
end
function ENT:ReadCell(Address)
Address = math.floor(Address)
--DriveID should be > 0, and less than 4 in MP
if ((self.DriveID < 0) || (!game.SinglePlayer() && (self.DriveID >= 4))) then
return nil
end
local player = self:GetPlayer()
if player:IsValid() then
local steamid = player:SteamID()
steamid = string.gsub(steamid, ":", "_")
if (steamid ~= "UNKNOWN") then
self.Owner_SteamID = steamid
else
self.Owner_SteamID = "SINGLEPLAYER"
end
-- If drive has changed, change cap
if self.DriveID ~= self.PrevDriveID then
self:GetCap()
self.PrevDriveID = self.DriveID
end
-- Check if address is valid
if (Address < self.DriveCap * 1024) and (Address >= 0) then
-- Compute address
local block = math.floor(Address / self.BlockSize)
local blockaddress = math.floor(Address) % self.BlockSize
-- Check if this address is cached for read
if self.Cache[block] then
return self.Cache[block][blockaddress] or 0
end
-- If sector isn't created yet, return 0
if not file.Exists(self:GetStructName(block),"DATA") then
self.Cache[block] = {}
self.CacheUpdated[block] = true
for i=0,self.BlockSize-1 do
self.Cache[block][i] = 0
end
return 0
end
-- Read the block
local blockdata = self:GetFloatTable(file.Read(self:GetStructName(block)))
self.Cache[block] = {}
for i=0,self.BlockSize-1 do
self.Cache[block][i] = blockdata[i] or 0
end
return self.Cache[block][blockaddress]
else
return nil
end
else
return nil
end
end
function ENT:WriteCell(Address, value)
Address = math.floor(Address)
--DriveID should be > 0, and less than 4 in MP
if ((self.DriveID < 0) || (!game.SinglePlayer() && (self.DriveID >= 4))) then
return false
end
local player = self:GetPlayer()
if (player:IsValid()) then
local steamid = player:SteamID()
steamid = string.gsub(steamid, ":", "_")
if (steamid ~= "UNKNOWN") then
self.Owner_SteamID = steamid
else
self.Owner_SteamID = "SINGLEPLAYER"
end
-- If drive has changed, change cap
if self.DriveID ~= self.PrevDriveID then
self:GetCap()
self.PrevDriveID = self.DriveID
end
-- Check if address is valid
if (Address < self.DriveCap * 1024) and (Address >= 0) then
-- Compute address
local block = math.floor(Address / self.BlockSize)
local blockaddress = math.floor(Address) % self.BlockSize
-- Check if this address is cached
if self.Cache[block] then
self.CacheUpdated[block] = true
self.Cache[block][blockaddress] = value
if Address > self.MaxAddress then
self.MaxAddress = Address
end
return true
end
-- If sector isn't created yet, cache it
if not file.Exists(self:GetStructName(block),"DATA") then
self.Cache[block] = {}
self.CacheUpdated[block] = true
for i=0,self.BlockSize-1 do
self.Cache[block][i] = 0
end
self.Cache[block][blockaddress] = value
if Address > self.MaxAddress then
self.MaxAddress = Address
end
return true
end
-- Read the block
local blockdata = self:GetFloatTable(file.Read(self:GetStructName(block)))
self.Cache[block] = {}
for i=0,self.BlockSize-1 do
self.Cache[block][i] = blockdata[i] or 0
end
self.CacheUpdated[block] = true
self.Cache[block][blockaddress] = value
if Address > self.MaxAddress then
self.MaxAddress = Address
end
return true
else
return false
end
else
return false
end
end
function ENT:Think()
local cachedBlockIndex = next(self.CacheUpdated)
if cachedBlockIndex then
self.CacheUpdated[cachedBlockIndex] = nil
file.CreateDir(string.GetPathFromFilename(self:GetStructName(cachedBlockIndex)))
file.Write(self:GetStructName(cachedBlockIndex),self:MakeFloatTable(self.Cache[cachedBlockIndex]))
self:UpdateCap()
end
self:NextThink(CurTime()+0.25)
return true
end
function ENT:TriggerInput(iname, value)
if (iname == "Clk") then
self.Clk = value
if (self.Clk >= 1) then
self:WriteCell(self.AWrite, self.Data)
if (self.ARead == self.AWrite) then
local val = self:ReadCell(self.ARead)
if (val) then
Wire_TriggerOutput(self, "Data", val)
self.Out = val
end
end
end
elseif (iname == "AddrRead") then
self.ARead = value
local val = self:ReadCell(value)
if (val) then
Wire_TriggerOutput(self, "Data", val)
self.Out = val
end
elseif (iname == "AddrWrite") then
self.AWrite = value
if (self.Clk >= 1) then
self:WriteCell(self.AWrite, self.Data)
end
elseif (iname == "Data") then
self.Data = value
if (self.Clk >= 1) then
self:WriteCell(self.AWrite, self.Data)
if (self.ARead == self.AWrite) then
local val = self:ReadCell(self.ARead)
if (val) then
Wire_TriggerOutput(self, "Data", val)
self.Out = val
end
end
end
end
self:SetOverlayText(self.DriveCap.."kb".."\nWriteAddr:"..self.AWrite.." Data:"..self.Data.." Clock:"..self.Clk.."\nReadAddr:"..self.ARead.." = ".. self.Out)
end
duplicator.RegisterEntityClass("gmod_wire_hdd", WireLib.MakeWireEnt, "Data", "DriveID", "DriveCap")