noeska's right. Get your header/chunk code sorted and after that directories and sub-directories are easy. Just specify the virtual path and filename when adding the file.

Eg: File c:\example.txt can be stored as df0:\dir1\subdir1\example.txt in the file system(with df0: being the virtual root drive)

All you need to do is store that filename and the directories can be worked out from that, not affecting how you save the chunk data. Of course, this doesn't take into account empty directories because i can't see the point of storing them

Here's some code. It's from a file packer i started to write a little while back but didn't finish(the packing worked, i couldn't get my head round compressing the data).

edit: found this Compression Library which was exactly what i was looking for. I'm sure you guys know all about it I think i'll have another go at finishing my project now.
[pascal]
const
rootdir = 'df0:';

{add a trailing character C to a string S}
function TrailC(S: string;C: Char): string;
begin
if (Length(s)>0) and (s[Length(s)]<>c) then S:=S+C;
Result:=S;
end;

{split a filename into separate parts. Return the path or the file depending on dir(true for directory). default returns path}
function split(f0: string; dir: boolean): string;
var
k: integer;
begin
// no path specified
if (pos('\', f0)=0) then begin
if dir then begin
result:=trailc(rootdir, '\');
end
else if not dir then result:=f0;
exit;
end;

// find last path separator
k:=length(f0);
while (f0[k]<>'\') and (k>0) do dec(k);
if dir then begin
result:=copy(f0, 1, k);
if length(result)=1 then result:=trailc(rootdir, '\'); // convert '\' to 'df0:\'
if (result[1]='\') then insert(rootdir, result, 1) // convert '\directory1' to 'df0:\directory1'
else if (pos(rootdir,result)=0) then insert(trailc(rootdir, '\'), result, 1); // force rootdir into filename
end
else result:=copy(f0, k+1, length(f0));
end;

{string satisfy function. allows widcard matches of a string S using Mask like *.txt or file??.txt. Returns True on success}
function StrSatisfy(S,Mask: PChar) : Boolean;
label
next_char;
begin
next_char:
Result:=True;
if (S^=#0) and (Mask^=#0) then exit;
if (Mask^='*') and (Mask[1]=#0) then exit;
if S^=#0 then begin
while Mask^ = '*' do Inc(Mask);
Result:=Mask^=#0;
exit;
end;

Result:=False;
if Mask^=#0 then exit;
if Mask^='?' then begin
Inc(S);Inc(Mask);goto next_char;
end;
if Mask^='*' then begin
Inc( Mask );
while S^<>#0 do begin
Result:=StrSatisfy(S,Mask);
if Result then exit;
Inc(S);
end;
exit; // (Result = False)
end;
Result:=S^=Mask^;
Inc(S);Inc(Mask);
if Result then goto next_char;
end;

{enumerate all files using mask. findexes was the array for my file index}
function enumfiles(mask: string): tstrings;
var
f, p: string;
i: tindex;
begin
result:=nil;
if (trim(mask)='') or (length(findexes)=0) then exit;
result:=tstringlist.create;
p:=split(mask);
f:=split(mask, false);
for i in findexes do
if (split(i.filename)=p) and (strsatisfy(pchar(split(i.filename,false)),pchar(f ))) then result.add(i.filename);
end;

{enumerate all folders using Mask}
function enumfolders(mask: string): tstrings;
var
k: integer;
p: string;
i: tindex;
begin
result:=nil;
if (trim(mask)='') or (length(findexes)=0) then exit;
result:=tstringlist.create;
for i in findexes do begin
p:=split(i.filename, true);
p:=copy(p,1,length(p)-1); // remove the last '\' from filepath
k:=result.indexof(p); // only add new directories
if (strsatisfy(pchar(p), pchar(mask))) and (k=-1) then result.add(p);
end;
end;
[/pascal]

So given 5 files(with df0:\ being the root directory):
df0:\a.txt
df0:\directory1\b.txt
df0:\directory1\c.txt
df0:\directory1\directory1\d.txt <- same named sub-dirs are allowed
df0:\directory2\e.txt

enumfiles('*') = df0:\a.txt (if you wanted all files, just read the index - it would be easier and quicker)
enumfiles('\directory1\*') = df0:\directory1\b.txt & df0:\directory1\c.txt
enumfolders('directory1\*') = directory1