function CopyTable(o)
	local new = {}
	for k, v in pairs(o) do
		if type(v) == "table" then
			new[k] = CopyTable(v)
		elseif type(v) == "function" then
			new[k] = loadstring(string.dump(v))
		else
			new[k] = v
		end
	end
	return new
end

function ByteMax(count)
	return (256^count) - 1
end

function ReadBytes(s)
	local b = {}
	for i = 1, string.len(s) do
		table.insert(b, string.byte(s,i))
	end

	local n = 0
	for i = 1, #b do
		n = n + b[i] * (256 ^ (i-1))
	end
	return n
end

function WriteBytes(n,cnt)
	local i = 1
	local b = {n%256}
	local p = {n}

	while b[i] and p[i] ~= 0 do
		i = i + 1
		p[i] = (p[i-1] - b[i-1])/256
		b[i] = p[i]%256
	end

	local str = ""
	for i = 1, cnt do
		if i <= #b - 1 then
			str = str .. string.char(b[i])
		else
			str = str .. string.char(0)
		end
	end
	return str
end

TStream = {}
TStream.data = ""
TStream.pos = 1

function TStream:Pos()
	return self.pos
end

function TStream:Size()
	return string.len(self.data) - self.pos + 1
end

function TStream:Seek(pos)
	self.pos = pos
end

function ReadStream(dir)
	local f = io.open(dir, "rb")
	if f then
		local stream = CreateStream()
		stream.data = f:read("*a")

		function stream:Read(n)
			local data = string.sub(stream.data, stream.pos, stream.pos + n - 1)
			stream:Seek(stream.pos + n)
			return data
		end

		function stream:Close()
			return f:close()
		end
		return stream
	end
end

function WriteStream(dir)
	local f = io.open(dir, "wb")
	if f then
		local stream = CreateStream()

		function stream:Write(data)
			return f:write(data)
		end

		function stream:Close()
			return f:close()
		end
		return stream
	end
end

function TStream:ReadBytes(count)
	local s = self:Read(count)
	if s and string.len(s) <= count then return ReadBytes(s) end
end

function TStream:WriteBytes(n,count)
	if n > ByteMax(count) then
		return false
	end
	return self:Write(WriteBytes(n, count))
end

function TStream:SkipBytes(count)
	self:Read(count)
end

function ReadByte(stream)
	return stream:ReadBytes(1)
end

function WriteByte(stream,n)
	return stream:WriteBytes(n, 1)
end

function ReadShort(stream)
	return stream:ReadBytes(2)
end

function WriteShort(stream,n)
	return stream:WriteBytes(n, 2)
end

function ReadInt(stream)
	return stream:ReadBytes(4)
end

function WriteInt(stream,n)
	return stream:WriteBytes(n, 4)
end

function ReadLong(stream)
	return stream:ReadBytes(8)
end

function WriteLong(stream,n)
	return stream:WriteBytes(n, 8)
end

function ReadFloat(stream)
	return stream:ReadBytes(4)
end

function WriteFloat(stream,n)
	return stream:WriteBytes(n, 4)
end

function ReadDouble(stream)
	return stream:ReadBytes(8)
end

function WriteDouble(stream,n)
	return stream:WriteBytes(n, 8)
end

function ReadLine(stream)
	local str = ""
	while true do
		local n = string.byte(stream:Read(1))
		if not n or n == 10 then
			break
		end
		if n ~= 13 then
			str = str..string.char(n)
		end
	end
	return str
end

function WriteLine(stream,str)
	return stream:Write(str..string.char(13)..string.char(10))
end

function ReadString(stream,length)
	return stream:Read(length)
end

function WriteString(stream,str)
	return stream:Write(str)
end

function Eof(stream)
	return stream:Pos() > stream:Size()
end

function StreamPos(stream)
	return stream:Pos()
end

function StreamSize(stream)
	return stream:Size()
end

function SeekStream(stream,pos)
	return stream:Seek(pos)
end

function CloseStream(stream)
	if stream then
		return stream:Close()
	end
end

function FileExists(dir)
	local f = io.open(dir)
	if f then
		f:close()
		return true
	end
end

function CopyBytes(fromStream,toStream,count)
	local data = fromStream:Read(count)
	toStream:Write(data)
end

function CopyStream(streamA,streamB)
	if streamA and streamB then
		CopyBytes(streamA, streamB, StreamSize(streamA))
	end
end

function CreateStream()
	return CopyTable(TStream)
end

function CreateBankStream()
	local stream = CreateStream()
	stream.data = ""

	function stream:Read(n)
		local data = string.sub(stream.data, stream.pos, stream.pos + n - 1)
		stream:Seek(stream.pos + n)
		return data
	end

	function stream:Write(data)
		stream.data = stream.data .. data
	end
	return stream
end
