Skip to content

Commit

Permalink
resdir: cache contents on creation, serve case-insensitive, fixes #104
Browse files Browse the repository at this point in the history
  • Loading branch information
niv committed Dec 17, 2023
1 parent 7f50682 commit 053a14c
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 17 deletions.
50 changes: 33 additions & 17 deletions neverwinter/resdir.nim
Original file line number Diff line number Diff line change
@@ -1,31 +1,55 @@
## A Resdir is the contents of a single directory mapped into resman. The contents
## are available live (meaning that adding files makes them available immediately).
## This may change in the future.
## are cached on creation and additions to the local filesystem are not reflected.

import streams, os
import std/[streams, os, tables, logging]

import resman

type
ResDir* = ref object of ResContainer
directory: string
contentMap: Table[ResRef, string]
contentSet: OrderedSet[ResRef]

proc rebuild(self: ResDir) =
assert(self.directory != "")
self.contentMap.clear()
self.contentSet.clear()
for pc in walkDir(self.directory, relative = true):
if pc.kind in {pcFile, pcLinkToFile}:
let rr = tryNewResolvedResRef(pc.path)
if rr.isSome:
self.contentMap[rr.unsafeGet] = pc.path
self.contentSet.incl(rr.unsafeGet)

proc newResDir*(directory: string): ResDir =
new(result)
result.directory = directory
result.rebuild()

proc directory*(self: ResDir): string =
self.directory

proc resRefToFullPath(self: ResDir, rr: ResolvedResRef): string =
self.directory & DirSep & rr.toFile
proc checkExists(self: ResDir, rr: ResRef): bool =
if not self.contentSet.contains(rr):
return false

if not fileExists(self.directory / self.contentMap[rr]):
debug self, ": ", rr, " disappeared since ResDir creation"
self.contentMap.del(rr)
self.contentSet.excl(rr)
return false

return true

method contains*(self: ResDir, rr: ResRef): bool =
let r = rr.resolve()
result = r.isSome and fileExists(self.resRefToFullPath(r.get()))
self.checkExists(rr)

method demand*(self: ResDir, rr: ResRef): Res =
let fp = self.resRefToFullPath(rr.resolve().get())
if not self.checkExists(rr):
return nil

let fp = self.directory / self.contentMap[rr]
let mtime = getLastModificationTime(fp)
let sz = getFileSize(fp).int

Expand All @@ -38,15 +62,7 @@ method count*(self: ResDir): int =
self.contents.card

method contents*(self: ResDir): OrderedSet[ResRef] =
result = initOrderedSet[ResRef]()

for pc in walkDir(self.directory, true):
if (pc.kind == pcFile or pc.kind == pcLinkToFile):
let rr = tryNewResolvedResRef(pc.path)
if rr.isSome:
# if pc.path != pc.path.toLowerAscii:
# warn self, ": '" & pc.path & "' is not lowercase. This will break on Linux."
result.incl(rr.get())
self.contentSet

method `$`*(self: ResDir): string =
"ResDir:" & self.directory
1 change: 1 addition & 0 deletions tests/resman/corpus_uc/SAMPLE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
OLLEH
18 changes: 18 additions & 0 deletions tests/resman/tcase.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import std/[os, strutils]
import neverwinter/[resman, resdir]

let rr = newResolvedResRef("SAMPLE.txt")
var rm = newResMan(0)

const sourceDir = currentSourcePath().splitFile().dir

rm.add newResDir(sourceDir / "corpus")
rm.add newResDir(sourceDir / "corpus_uc")

let rd = rm.demand(rr, useCache=false)

# The uppercase file must override the lowercase variant further up the stack
# (the uppercase variant contains OLLEH, the lowercase variant HELLO)
# This test will only be effective on case-sensitive file sytems.
let check = rd.readAll().strip()
doAssert check == "OLLEH", "got: " & check

0 comments on commit 053a14c

Please sign in to comment.