DEADSOFTWARE

sfs system now works!
[d2df-sdl.git] / src / sfs / sfs.pas
1 // streaming file system (virtual)
2 {$MODE DELPHI}
3 {.$R-}
4 unit sfs;
6 interface
8 uses
9 SysUtils, Classes, Contnrs;
12 type
13 ESFSError = class(Exception);
15 TSFSChar = AnsiChar;
16 TSFSString = AnsiString;
18 TSFSVolume = class;
20 TSFSFileInfo = class
21 public
22 fOwner: TSFSVolume; // òàê, íà âñÿêèé ñëó÷àé
23 fPath: TSFSString; // ðàçäåëèòåëè êàòàëîãîâ -- "/"; êîðåíü íèêàê íå îáîçíà÷åí, åñëè íå ïóñòîå, îáÿçàíî çàâåðøàåòñÿ "/"
24 fName: TSFSString; // òîëüêî èìÿ
25 fSize: Int64; // unpacked
26 fOfs: Int64; // in VFS (many of 'em need this %-)
28 constructor Create (pOwner: TSFSVolume);
29 destructor Destroy (); override;
31 property path: TSFSString read fPath;
32 property name: TSFSString read fName;
33 property size: Int64 read fSize;
34 end;
36 // âèðòóàëüíàÿ ôàéëîâàÿ ñèñòåìà. ÒÎËÜÊÎ ÄËß ×ÒÅÍÈß!
37 // òîì ÍÅ ÄÎËÆÅÍ óáèâàòüñÿ íèêàê èíà÷å, ÷åì ïðè ïîìîùè ôàáðèêè!
38 TSFSVolume = class
39 protected
40 fRC: Integer; // refcounter for other objects
41 fFileName: TSFSString;// îáû÷íî èìÿ îðèãèíàëüíîãî ôàéëà
42 fFileStream: TStream; // îáû÷íî ïîòîê äëÿ ÷òåíèÿ îðèãèíàëüíîãî ôàéëà
43 fFiles: TObjectList; // TSFSFileInfo èëè íàñëåäíèêè
45 // ïðèøèáèòü âñå ñòðóêòóðû.
46 // íå äîëæíà ïàäàòü, åñëè å¸ âûçûâàþò íåñêîëüêî ðàç.
47 procedure Clear (); virtual;
49 // âûçûâàåòñÿ èç DoDirectoryRead() äëÿ çàïîëíåíèÿ ñïèñêà ôàéëîâ.
50 // ñ÷èòàåòñÿ, ÷òî âñå ìàãèêè óæå ïðîâåðåíû è ôàéë òî÷íî íàø.
51 // fFileName, fFileStream óæå óñòàíîâëåíû, fFiles ñîçäàí,
52 // â í¸ì, ñêîðåå âñåãî, íèêîãî íåò.
53 // ïîçèöèÿ ïîòîêà -- òà, ÷òî îñòàâèëà ôàáðèêà.
54 // ïðè îøèáêàõ êèäàòü èñêëþ÷åíèå, òîãäà òîì áóäåò ïðèáèò ôàáðèêîé.
55 // ðàçäåëèòåëè ïóòåé äîëæíû áûòü òîëüêî "/", êîðíåâîé "/" äîëæåí
56 // áûòü îïóùåí, ïóòè (åñëè íå ïóñòûå) äîëæíû çàâåðøàòüñÿ "/"!
57 // fName äîëæíî ñîäåðæàòü òîëüêî èìÿ, fPath -- òîëüêî ïóòü.
58 // â ïðèíöèïå, îá ýòîì ïîçàáîòèòñÿ DoDirectoryRead(), íî çà÷åì
59 // äàâàòü åìó ëèøíþþ ðàáîòó?
60 procedure ReadDirectory (); virtual; abstract;
62 // íàéòè ôàéë, âåðíóòü åãî èíäåêñ â fFiles.
63 // ýòà ïðîöåäóðà ìîæåò ìåíÿòü fFiles!
64 // fPath -- â ïðàâèëüíîé ôîðìå, ñ "/", êîðíåâîé "/" óáèò, ôèíàëüíûé äîáàâëåí.
65 // åñëè ôàéë íå íàéäåí, âåðíóòü -1.
66 function FindFile (const fPath, fName: TSFSString): Integer; virtual;
68 // âîçâðàùàåò êîëè÷åñòâî ôàéëîâ â fFiles
69 function GetFileCount (): Integer; virtual;
71 // âîçâðàùàåò ôàéë ñ èíäåêñîì index.
72 // ìîæåò âîçâðàùàòü NIL.
73 // íèêàêèõ ïàäåíèé íà íåïðàâèëüíûå èíäåêñû!
74 function GetFiles (index: Integer): TSFSFileInfo; virtual;
76 public
77 // pSt íå îáÿçàòåëüíî çàïîìèíàòü, åñëè îí íå íóæåí.
78 constructor Create (const pFileName: TSFSString; pSt: TStream); virtual;
79 // fFileStream óíè÷òîæàòü íåëüçÿ, åñëè îí ðàâåí ïàðàìåòðó pSt êîíñòðóêòîðà.
80 destructor Destroy (); override;
82 // âûçûâàåò ReadDirectory().
83 // ýòà ïðîöåäóðà ñàìà ðàçáåð¸òñÿ ñ äóáëèêàòàìè èì¸í: ïîäîáàâëÿåò â
84 // êîíåö èì¸í-äóáëèêàòîâ ïîä÷¸ðêèâàíèå è äåñÿòè÷íûé íîìåð.
85 // òàêæå îíà íîðìàëèçóåò âèä èì¸í.
86 procedure DoDirectoryRead ();
88 // ïðè îøèáêàõ êèäàòüñÿ èñêëþ÷åíèÿìè.
89 function OpenFileByIndex (const index: Integer): TStream; virtual; abstract;
91 // åñëè íå ñìîãëî îòêóïîðèòü ôàéëî (èëè åù¸ ãäå îøèáëîñü), çàøâûðí¸ò èñêëþ÷åíèå.
92 function OpenFileEx (const fName: TSFSString): TStream; virtual;
94 property FileCount: Integer read GetFileCount; // ìîæåò âåðíóòü íîëü
95 // ìîæåò âîçâðàùàòü NIL.
96 // íèêàêèõ ïàäåíèé íà íåïðàâèëüíûå èíäåêñû!
97 property Files [index: Integer]: TSFSFileInfo read GetFiles;
98 end;
100 // ôàáðèêà òîìîâ. âñå SFS ïðè ñòàðòå äîáàâëÿþò ñâîè ôàáðèêè.
101 // áëàãîäàðÿ ýòîìó ìîæíî ñîçäàâàòü ðàçíûå âñÿêèå SFS ñòàíäàðòíûì
102 // âûçîâîì ñòàíäàðòíîé ïðîöåäóðû.
103 // ôàáðèêà ÍÅ ÄÎËÆÍÀ óáèâàòüñÿ íèêàê èíà÷å, ÷åì ïðè ïîìîùè âûçîâà
104 // SFSUnregisterVolumeFactory()! ýòî ãàðàíòèðóåò, ÷òî äâèæîê
105 // ïåðåä ðàññòðåëîì îòäàñò åé âñå å¸ òîìà.
106 TSFSVolumeFactory = class
107 public
108 // åñëè äîáàâëÿåì ôàéë äàííûõ ôàéë ñ èìåíåì òèïà "zip:....", òî
109 // SFS èçâëå÷¸ò ýòî "zip" è ïåðåäàñò â ñèþ ôóíêöèþ.
110 // åæåëè ôóíêöèÿ âåðí¸ò ïðàâäó, òî SFS âûçîâåò Produce äëÿ äàííîãî
111 // ôàéëà. åñëè íè îäíà ôàáðèêà ïðåôèêñ íå ïðèçíàåò, òî ôàéë íå îòêðîþò.
112 // èñïîëüçóåòñÿ äëÿ ñêèïàíèÿ àâòîäåòåêòà.
113 // SFS ÍÅ Ñ×ÈÒÀÅÒ ÏÐÅÔÈÊÑÎÌ ÑÒÐÎÊÓ ÊÎÐÎ×Å ÒÐ¨Õ ÑÈÌÂÎËÎÂ!
114 function IsMyVolumePrefix (const prefix: TSFSString): Boolean; virtual; abstract;
115 // ïðîâåðÿåò, ìîæåò ëè ôàáðèêà ñäåëàòü òîì äëÿ äàííîãî ôàéëà.
116 // st -- îòêðûòûé äëÿ ÷òåíèÿ ôàéëîâé ïîòîê. óêàçàòåëü ÷òåíèÿ ñòîèò â íà÷àëå.
117 // ýòîò ïîòîê íåëüçÿ çàêðûâàòü!
118 // prefix: òî, ÷òî áûëî ïåðåäàíî â IsMyVolumePrefix() èëè ''.
119 // èñêëþ÷åíèå ñ÷èòàåòñÿ îøèáêîé, âîçâðàò NIL ñ÷èòàåòñÿ îøèáêîé.
120 function Produce (const prefix, fileName: TSFSString; st: TStream): TSFSVolume; virtual; abstract;
121 // êîãäà òîì áîëüøå íå íóæåí, îí áóäåò îòäàí ôàáðèêå íà ïåðåðàáîòêó.
122 // äàëåå äâèæîê íå áóäåò þçàòü ñåé òîì.
123 procedure Recycle (vol: TSFSVolume); virtual; abstract;
124 end;
126 // "èòåðàòîð", âîçâðàùàåìûé SFSFileList()
127 TSFSFileList = class
128 protected
129 fVolume: TSFSVolume;
131 function GetCount (): Integer;
132 function GetFiles (index: Integer): TSFSFileInfo;
134 public
135 constructor Create (const pVolume: TSFSVolume);
136 destructor Destroy (); override;
138 property Volume: TSFSVolume read fVolume;
139 property Count: Integer read GetCount;
140 // ïðè íåïðàâèëüíîì èíäåêñå ìîë÷à âåðí¸ò NIL.
141 // ïðè ïðàâèëüíîì òîæå ìîæåò âåðíóòü NIL!
142 // î÷åíü íå ñîâåòóþ ìåíÿòü ñîäåðæèìîå ïîëó÷åííîãî êëàññà.
143 // êîíå÷íî, ÿ ìîã áû âîçâðàùàòü íîâóþ ñòðóêòóðó èëè íå÷òî ïîõîæåå,
144 // íî áëèí, åñëè òû èäèîò è íå óìååøü äàæå êîììåíòû ÷èòàòü, òî
145 // êàêîãî òû âîîáùå â ïðîãðàììèíã ïîëåç?
146 property Files [index: Integer]: TSFSFileInfo read GetFiles; default;
147 end;
150 procedure SFSRegisterVolumeFactory (factory: TSFSVolumeFactory);
151 // ýòà ôóíêöèÿ àâòîìàòè÷åñêè ïðèáü¸ò factory.
152 procedure SFSUnregisterVolumeFactory (factory: TSFSVolumeFactory);
154 // äîáàâèòü ñáîðíèê â ïîñòîÿííûé ñïèñîê.
155 // åñëè ñáîðíèê ñ òàêèì èìåíåì óæå îòêðûò, òî íå îòêðûâàåò åãî ïîâòîðíî.
156 // íèêîãäà íå êèäàåò èñêëþ÷åíèé.
157 // top: äîáàâèòü â íà÷àëî ñïèñêà ïîèñêà.
158 // âåðí¸ò ëîæü ïðè îøèáêå.
159 // ñïîñîáíî îòêðûâàòü ñáîðíèêè â ñáîðíèêàõ ïðè ïîìîùè êðóòûõ èì¸í a-la:
160 // "zip:pack0::pack:pack1::wad2:pack2".
161 // â äàëüíåéøåì ñëåäóåò îáðàùàòüñÿ ê ñáîðíèêó êàê "pack2::xxx".
162 // èëè ìîæíî íàïèñàòü:
163 // "zip:pack0::pack:pack1::wad2:pack2|datafile".
164 // è îáðàùàòüñÿ êàê "datafile::xxx".
165 // "||" ïðåîáðàçóþòñÿ â ïðîñòîé "|" è ðàçäåëèòåëåì íå ñ÷èòàþòñÿ.
166 // ïðèíèìàåòñÿ âî âíèìàíèå òîëüêî ïîñëåäíÿÿ òðóáà.
167 function SFSAddDataFile (const dataFileName: TSFSString; top: Boolean=false): Boolean;
169 // äîáàâèòü â ïîñòîÿííûé ñïèñîê ñáîðíèê èç ïîòîêà ds.
170 // åñëè âîçâðàùàåò èñòèíó, òî SFS ñòàíîâèòñÿ âëÿäåëüöåì ïîòîêà ds è ñàìà
171 // óãðîáèò ñåé ïîòîê ïî íåîáõîäèìîñòè.
172 // virtualName ñòàíîâèòñÿ èìåíåì ñáîðíèêà äëÿ îïåðàöèè îòêðûòèÿ ôàéëà òèïà
173 // "packfile:file.ext".
174 // åñëè êàêîé-íèáóäü ñáîðíèê ñ èìåíåì virtualName óæå îòêðûò, âåðí¸ò false.
175 // íèêîãäà íå êèäàåò èñêëþ÷åíèé.
176 // top: äîáàâèòü â íà÷àëî ñïèñêà ïîèñêà.
177 // âåðí¸ò ëîæü ïðè îøèáêå.
178 // îòêðûâàåò ñáîðíèê èç ïîòîêà. dataFileName -- ÂÈÐÒÓÀËÜÍÎÅ èìÿ.
179 // ò.å. íà ñàìîì äåëå òàêîãî ôàéëà ìîæåò è íå áûòü íà äèñêå.
180 function SFSAddSubDataFile (const virtualName: TSFSString; ds: TStream; top: Boolean=false): Boolean;
182 // øâûðÿåòñÿ èñêëþ÷åíèÿìè.
183 // åñëè fName íå èìååò óêàçàíèÿ íà ôàéë äàííûõ (ýòî òî, ÷òî îòäåëåíî îò
184 // îñòàëüíîãî èìåíè äâîåòî÷èåì), òî èùåì ñíà÷àëà ïî âñåì çàðåãèñòðèðîâàííûì
185 // ôàéëàì äàííûõ, ïîòîì â òåêóùåì êàòàëîãå, ïîòîì â êàòàëîãå, îòêóäà ñòàðòîâàëè.
186 // åñëè íè÷åãî íå íàøëè, êèäàåì èñêëþ÷åíèå.
187 function SFSFileOpenEx (const fName: TSFSString): TStream;
189 // ïðè îøèáêå -- NIL, è íèêàêèõ èñêëþ÷åíèé.
190 function SFSFileOpen (const fName: TSFSString): TStream;
192 // âîçâðàùàåò NIL ïðè îøèáêå.
193 // ïîñëå èñïîëüçîâàíèÿ, íàòóðàëüíî, èòåðàòîð íàäî ãðîõíóòü %-)
194 function SFSFileList (const dataFileName: TSFSString): TSFSFileList;
196 function SFSReplacePathDelims (const s: TSFSString; newDelim: TSFSChar): TSFSString;
197 // èãíîðèðóåò ðåãèñòð ñèìâîëîâ
198 // <0: s0 < s1
199 // =0: s0 = s1
200 // >0: s0 > s1
201 function SFSStrComp (const s0, s1: TSFSString): Integer;
203 // ðàçîáðàòü òîëñòîå èìÿ ôàéëà, âåðíóòü âèðòóàëüíîå èìÿ ïîñëåäíåãî ñïèñêà
204 // èëè ïóñòóþ ñòîðîêó, åñëè ñïèñêîâ íå áûëî.
205 function SFSGetLastVirtualName (const fn: TSFSString): string;
207 // ïðåîáðàçîâàòü ÷èñëî â ñòðîêó, êðàñèâî ðàçáàâëÿÿ çàïÿòûìè
208 function Int64ToStrComma (i: Int64): string;
210 // Wildcard matching
211 // this code is meant to allow wildcard pattern matches. tt is VERY useful
212 // for matching filename wildcard patterns. tt allows unix grep-like pattern
213 // comparisons, for instance:
214 //
215 // ? Matches any single characer
216 // + Matches any single characer or nothing
217 // * Matches any number of contiguous characters
218 // [abc] Matches a or b or c at that position
219 // [!abc] Matches anything but a or b or c at that position
220 // [a-e] Matches a through e at that position
221 //
222 // 'ma?ch.*' -Would match match.exe, mavch.dat, march.on, etc
223 // 'this [e-n]s a [!zy]est' -Would match 'this is a test', but would
224 // not match 'this as a yest'
225 //
226 function WildMatch (pattern, text: TSFSString): Boolean;
227 function WildListMatch (wildList, text: TSFSString; delimChar: AnsiChar=':'): Integer;
228 function HasWildcards (const pattern: TSFSString): Boolean;
231 var
232 // ïðàâäà: ðàçðåøåíî èñêàòü ôàéëî íå òîëüêî â ôàéëàõ äàííûõ, íî è íà äèñêå.
233 sfsDiskEnabled: Boolean = true;
234 // ïðàâäà: åñëè ôàéë íå ïðåôèêñîâàí, òî ñíà÷àëà èùåì ôàéëî íà äèñêå,
235 // ïîòîì â ôàéëàõ äàííûõ.
236 sfsDiskFirst: Boolean = true;
237 // ïðàâäà: äàæå äëÿ ïðåôèêñîâàíûõ ôàéëîâ ñíà÷àëà ïðîñìîòðèì äèñê
238 // (åñëè óñòàíîâëåí ôëàæîê sfsDiskFirst è sfsDiskEnabled).
239 sfsForceDiskForPrefixed: Boolean = false;
240 // ñïèñîê äèñêîâûõ êàòàëîãîâ äëÿ ïîèñêà ôàéëà. åñëè ïóñò -- èùåì òîëüêî â
241 // òåêóùåì. êàòàëîãè ðàçäåëÿþòñÿ òðóáîé ("|").
242 // <currentdir> çàìåíÿåòñÿ íà òåêóùèé êàòàëîã (ñ çàâåðøàþùèì "/"),
243 // <exedir> çàìåíÿåòñÿ íà êàòàëîã, ãäå ñèäèò .EXE (ñ çàâåðøàþùèì "/").
244 sfsDiskDirs: TSFSString = '<currentdir>|<exedir>';
247 implementation
249 uses
250 xstreams;
253 function Int64ToStrComma (i: Int64): string;
254 var
255 f: Integer;
256 begin
257 Str(i, result);
258 f := Length(result)+1;
259 while f > 4 do
260 begin
261 Dec(f, 3); Insert(',', result, f);
262 end;
263 end;
266 const
267 // character defines
268 WILD_CHAR_ESCAPE = '\';
269 WILD_CHAR_SINGLE = '?';
270 WILD_CHAR_SINGLE_OR_NONE = '+';
271 WILD_CHAR_MULTI = '*';
272 WILD_CHAR_RANGE_OPEN = '[';
273 WILD_CHAR_RANGE = '-';
274 WILD_CHAR_RANGE_CLOSE = ']';
275 WILD_CHAR_RANGE_NOT = '!';
278 function HasWildcards (const pattern: TSFSString): Boolean;
279 begin
280 result :=
281 (Pos(WILD_CHAR_ESCAPE, pattern) <> 0) or
282 (Pos(WILD_CHAR_SINGLE, pattern) <> 0) or
283 (Pos(WILD_CHAR_SINGLE_OR_NONE, pattern) <> 0) or
284 (Pos(WILD_CHAR_MULTI, pattern) <> 0) or
285 (Pos(WILD_CHAR_RANGE_OPEN, pattern) <> 0);
286 end;
288 function MatchMask (const pattern: TSFSString; p, pend: Integer; const text: TSFSString; t, tend: Integer): Boolean;
289 var
290 rangeStart, rangeEnd: AnsiChar;
291 rangeNot, rangeMatched: Boolean;
292 ch: AnsiChar;
293 begin
294 // sanity checks
295 if (pend < 0) or (pend > Length(pattern)) then pend := Length(pattern);
296 if (tend < 0) or (tend > Length(text)) then tend := Length(text);
297 if t < 1 then t := 1;
298 if p < 1 then p := 1;
299 while p <= pend do
300 begin
301 if t > tend then
302 begin
303 // no more text. check if there's no more chars in pattern (except "*" & "+")
304 while (p <= pend) and
305 ((pattern[p] = WILD_CHAR_MULTI) or
306 (pattern[p] = WILD_CHAR_SINGLE_OR_NONE)) do Inc(p);
307 result := (p > pend);
308 exit;
309 end;
310 case pattern[p] of
311 WILD_CHAR_SINGLE: ;
312 WILD_CHAR_ESCAPE:
313 begin
314 Inc(p);
315 if p > pend then result := false else result := (pattern[p] = text[t]);
316 if not result then exit;
317 end;
318 WILD_CHAR_RANGE_OPEN:
319 begin
320 result := false;
321 Inc(p); if p > pend then exit; // sanity check
322 rangeNot := (pattern[p] = WILD_CHAR_RANGE_NOT);
323 if rangeNot then begin Inc(p); if p > pend then exit; {sanity check} end;
324 if pattern[p] = WILD_CHAR_RANGE_CLOSE then exit; // sanity check
325 ch := text[t]; // speed reasons
326 rangeMatched := false;
327 repeat
328 if p > pend then exit; // sanity check
329 rangeStart := pattern[p];
330 if rangeStart = WILD_CHAR_RANGE_CLOSE then break;
331 Inc(p); if p > pend then exit; // sanity check
332 if pattern[p] = WILD_CHAR_RANGE then
333 begin
334 Inc(p); if p > pend then exit; // sanity check
335 rangeEnd := pattern[p]; Inc(p);
336 if rangeStart < rangeEnd then
337 begin
338 rangeMatched := (ch >= rangeStart) and (ch <= rangeEnd);
339 end
340 else rangeMatched := (ch >= rangeEnd) and (ch <= rangeStart);
341 end
342 else rangeMatched := (ch = rangeStart);
343 until rangeMatched;
344 if rangeNot = rangeMatched then exit;
346 // skip the rest or the range
347 while (p <= pend) and (pattern[p] <> WILD_CHAR_RANGE_CLOSE) do Inc(p);
348 if p > pend then exit; // sanity check
349 end;
350 WILD_CHAR_SINGLE_OR_NONE:
351 begin
352 Inc(p);
353 result := MatchMask(pattern, p, pend, text, t, tend);
354 if not result then result := MatchMask(pattern, p, pend, text, t+1, tend);
355 exit;
356 end;
357 WILD_CHAR_MULTI:
358 begin
359 while (p <= pend) and (pattern[p] = WILD_CHAR_MULTI) do Inc(p);
360 result := (p > pend); if result then exit;
361 while not result and (t <= tend) do
362 begin
363 result := MatchMask(pattern, p, pend, text, t, tend);
364 Inc(t);
365 end;
366 exit;
367 end;
368 else result := (pattern[p] = text[t]); if not result then exit;
369 end;
370 Inc(p); Inc(t);
371 end;
372 result := (t > tend);
373 end;
376 function WildMatch (pattern, text: TSFSString): Boolean;
377 begin
378 if pattern <> '' then pattern := AnsiLowerCase(pattern);
379 if text <> '' then text := AnsiLowerCase(text);
380 result := MatchMask(pattern, 1, -1, text, 1, -1);
381 end;
383 function WildListMatch (wildList, text: TSFSString; delimChar: AnsiChar=':'): Integer;
384 var
385 s, e: Integer;
386 begin
387 if wildList <> '' then wildList := AnsiLowerCase(wildList);
388 if text <> '' then text := AnsiLowerCase(text);
389 result := 0;
390 s := 1;
391 while s <= Length(wildList) do
392 begin
393 e := s; while e <= Length(wildList) do
394 begin
395 if wildList[e] = WILD_CHAR_RANGE_OPEN then
396 begin
397 while (e <= Length(wildList)) and (wildList[e] <> WILD_CHAR_RANGE_CLOSE) do Inc(e);
398 end;
399 if wildList[e] = delimChar then break;
400 Inc(e);
401 end;
402 if s < e then
403 begin
404 if MatchMask(wildList, s, e-1, text, 1, -1) then exit;
405 end;
406 Inc(result);
407 s := e+1;
408 end;
409 result := -1;
410 end;
413 type
414 TVolumeInfo = class
415 fFactory: TSFSVolumeFactory;
416 fVolume: TSFSVolume;
417 fPackName: TSFSString; // äëÿ îäíîãî è òîãî æå ôàéëà áóäåò òîëüêî îäèí òîì!
418 fStream: TStream; // ôàéëîâûé ïîòîê äëÿ ñáîðíèêà
419 fPermanent: Boolean; // èñòèíà -- íå áóäåò óãðîáëåíà, åñëè íå îñòàíåòñÿ íè îäíîãî îòêðûòîãî òîìà
420 // èñòèíà -- ýòîò òîì áûë ñîçäàí èç ïîòîêà è íå èìååò äèñêîâîãî ôàéëà, ïîòîìó ôàáðèêå áóäåò ïåðåäàíî íå èìÿ ñáîðíèêà, à ïóñòàÿ ñòðîêà
421 fNoDiskFile: Boolean;
422 fOpenedFilesCount: Integer;
424 destructor Destroy (); override;
425 end;
427 TOwnedPartialStream = class (TSFSPartialStream)
428 protected
429 fOwner: TVolumeInfo;
431 public
432 constructor Create (pOwner: TVolumeInfo; pSrc: TStream; pPos, pSize: Int64; pKillSrc: Boolean);
433 destructor Destroy (); override;
434 end;
437 var
438 factories: TObjectList; // TSFSVolumeFactory
439 volumes: TObjectList; // TVolumeInfo
442 // ðàçáèòü èìÿ ôàéëà íà ÷àñòè: ïðåôèêñ ôàéëîâîé ñèñòåìû, èìÿ ôàéëà äàííûõ,
443 // ñîáñòâåííî èìÿ ôàéëà
444 // èìÿ âûãëÿäèò êàê:
445 // (("sfspfx:")?"datafile::")*"filename"
446 procedure SplitFName (const fn: string; out dataFile, fileName: string);
447 var
448 f: Integer;
449 begin
450 f := Length(fn)-1;
451 while f >= 1 do
452 begin
453 if (fn[f] = ':') and (fn[f+1] = ':') then break;
454 Dec(f);
455 end;
456 if f < 1 then begin dataFile := ''; fileName := fn; end
457 else
458 begin
459 dataFile := Copy(fn, 1, f-1);
460 fileName := Copy(fn, f+2, maxInt-10000);
461 end;
462 end;
464 // ñàéäýôôåêò: âûðåçàåò âèðòóàëüíîå èìÿ èç dataFile.
465 function ExtractVirtName (var dataFile: string): string;
466 var
467 f: Integer;
468 begin
469 f := Length(dataFile); result := dataFile;
470 while f > 1 do
471 begin
472 if dataFile[f] = ':' then break;
473 if dataFile[f] = '|' then
474 begin
475 if dataFile[f-1] = '|' then begin Dec(f); Delete(dataFile, f, 1); end
476 else
477 begin
478 result := Copy(dataFile, f+1, Length(dataFile));
479 Delete(dataFile, f, Length(dataFile));
480 break;
481 end;
482 end;
483 Dec(f);
484 end;
485 end;
487 // ðàçáèòü èìÿ ñáîðíèêà íà ÷àñòè: ïðåôèêñ ôàéëîâîé ñèñòåìû, èìÿ ôàéëà äàííûõ,
488 // âèðòóàëüíîå èìÿ. åñëè âèðòóàëüíîãî èìåíè íå äàíî, îíî áóäåò ðàâíî dataFile.
489 // èìÿ âûãëÿäèò êàê:
490 // [sfspfx:]datafile[|virtname]
491 // åñëè ïåðåä äâîåòî÷èåì ìåíüøå òð¸õ áóêâ, òî ýòî ñ÷èòàåòñÿ íå ïðåôèêñîì,
492 // à èìåíåì äèñêà.
493 procedure SplitDataName (const fn: string; out pfx, dataFile, virtName: string);
494 var
495 f: Integer;
496 begin
497 f := Pos(':', fn);
498 if f <= 3 then begin pfx := ''; dataFile := fn; end
499 else
500 begin
501 pfx := Copy(fn, 1, f-1);
502 dataFile := Copy(fn, f+1, maxInt-10000);
503 end;
504 virtName := ExtractVirtName(dataFile);
505 end;
507 // íàéòè ïðîèçâîäèòåëÿ äëÿ ýòîãî ôàéëà (åñëè ôàéë óæå îòêðûò).
508 // onlyPerm: òîëüêî "ïîñòîÿííûå" ïðîèçâîäèòåëè.
509 function FindVolumeInfo (const dataFileName: TSFSString; onlyPerm: Boolean=false): Integer;
510 var
511 f: Integer;
512 vi: TVolumeInfo;
513 begin
514 f := 0;
515 while f < volumes.Count do
516 begin
517 if volumes[f] <> nil then
518 begin
519 vi := TVolumeInfo(volumes[f]);
520 if not onlyPerm or vi.fPermanent then
521 begin
522 if SFSStrComp(vi.fPackName, dataFileName) = 0 then
523 begin
524 result := f;
525 exit;
526 end;
527 end;
528 end;
529 Inc(f);
530 end;
531 result := -1;
532 end;
534 // íàéòè èíôó äëÿ ýòîãî òîìà.
535 // õîðîøåå èìÿ, ïðàâäà? %-)
536 function FindVolumeInfoByVolumeInstance (vol: TSFSVolume): Integer;
537 begin
538 result := volumes.Count-1;
539 while result >= 0 do
540 begin
541 if volumes[result] <> nil then
542 begin
543 if TVolumeInfo(volumes[result]).fVolume = vol then exit;
544 end;
545 Dec(result);
546 end;
547 end;
549 // <0: s0 < s1
550 // =0: s0 = s1
551 // >0: s0 > s1
552 function SFSStrComp (const s0, s1: TSFSString): Integer;
553 begin
554 result := AnsiCompareText(s0, s1);
555 end;
557 function SFSReplacePathDelims (const s: TSFSString; newDelim: TSFSChar): TSFSString;
558 var
559 f: Integer;
560 begin
561 result := s;
562 for f := 1 to Length(result) do
563 begin
564 if (result[f] = '/') or (result[f] = '\') then
565 begin
566 // avoid unnecessary string changes
567 if result[f] <> newDelim then result[f] := newDelim;
568 end;
569 end;
570 end;
572 function SFSGetLastVirtualName (const fn: TSFSString): string;
573 var
574 rest, tmp: string;
575 f: Integer;
576 begin
577 rest := fn;
578 repeat
579 f := Pos('::', rest); if f = 0 then f := Length(rest)+1;
580 tmp := Copy(rest, 1, f-1); Delete(rest, 1, f+1);
581 result := ExtractVirtName(tmp);
582 until rest = '';
583 end;
586 { TVolumeInfo }
587 destructor TVolumeInfo.Destroy ();
588 var
589 f, me: Integer;
590 used: Boolean; // ôëàæîê çàþçàíîñòè ïîòîêà êåì-òî åù¸
591 begin
592 if fFactory <> nil then fFactory.Recycle(fVolume);
593 if fVolume <> nil then used := (fVolume.fRC <> 0) else used := false;
594 fVolume := nil;
595 fFactory := nil;
596 fPackName := '';
598 // òèïà ìóñîðîñáîðíèê: åñëè íàø ïîòîê áîëåå íèêåì íå þçàåòñÿ, òî óãðîáèòü åãî íàôèã
599 if not used then
600 begin
601 me := volumes.IndexOf(self);
602 f := volumes.Count-1;
603 while not used and (f >= 0) do
604 begin
605 if (f <> me) and (volumes[f] <> nil) then
606 begin
607 used := (TVolumeInfo(volumes[f]).fStream = fStream);
608 if not used then
609 begin
610 used := (TVolumeInfo(volumes[f]).fVolume.fFileStream = fStream);
611 end;
612 if used then break;
613 end;
614 Dec(f);
615 end;
616 end;
617 if not used then FreeAndNil(fStream); // åñëè áîëüøå íèêåì íå þçàíî, ïðèøèá¸ì
618 inherited Destroy();
619 end;
622 { TOwnedPartialStream }
623 constructor TOwnedPartialStream.Create (pOwner: TVolumeInfo; pSrc: TStream;
624 pPos, pSize: Int64; pKillSrc: Boolean);
625 begin
626 inherited Create(pSrc, pPos, pSize, pKillSrc);
627 fOwner := pOwner;
628 if pOwner <> nil then Inc(pOwner.fOpenedFilesCount);
629 end;
631 destructor TOwnedPartialStream.Destroy ();
632 var
633 f: Integer;
634 begin
635 inherited Destroy();
636 if fOwner <> nil then
637 begin
638 Dec(fOwner.fOpenedFilesCount);
639 if not fOwner.fPermanent and (fOwner.fOpenedFilesCount < 1) then
640 begin
641 f := volumes.IndexOf(fOwner);
642 if f <> -1 then volumes[f] := nil; // this will destroy the volume
643 end;
644 end;
645 end;
648 { TSFSFileInfo }
649 constructor TSFSFileInfo.Create (pOwner: TSFSVolume);
650 begin
651 inherited Create();
652 fOwner := pOwner;
653 fPath := '';
654 fName := '';
655 fSize := 0;
656 fOfs := 0;
657 if pOwner <> nil then pOwner.fFiles.Add(self);
658 end;
660 destructor TSFSFileInfo.Destroy ();
661 begin
662 if fOwner <> nil then fOwner.fFiles.Extract(self);
663 inherited Destroy();
664 end;
667 { TSFSVolume }
668 constructor TSFSVolume.Create (const pFileName: TSFSString; pSt: TStream);
669 begin
670 inherited Create();
671 fRC := 0;
672 fFileStream := pSt;
673 fFileName := pFileName;
674 fFiles := TObjectList.Create(true);
675 end;
677 procedure TSFSVolume.DoDirectoryRead ();
678 var
679 fl: TStringList; //!!!FIXME! change to list of wide TSFSStrings or so!
680 f, c, n: Integer;
681 sfi: TSFSFileInfo;
682 tmp, fn, ext: TSFSString;
683 begin
684 fl := nil;
685 fFileName := ExpandFileName(SFSReplacePathDelims(fFileName, '/'));
686 try
687 ReadDirectory();
688 fFiles.Pack();
690 // check for duplicate file names
691 fl := TStringList.Create(); fl.Sorted := true;
692 for f := 0 to fFiles.Count-1 do
693 begin
694 sfi := TSFSFileInfo(fFiles[f]);
696 // normalize name & path
697 sfi.fPath := SFSReplacePathDelims(sfi.fPath, '/');
698 if (sfi.fPath <> '') and (sfi.fPath[1] = '/') then Delete(sfi.fPath, 1, 1);
699 if (sfi.fPath <> '') and (sfi.fPath[Length(sfi.fPath)] <> '/') then sfi.fPath := sfi.fPath+'/';
700 tmp := SFSReplacePathDelims(sfi.fName, '/');
701 c := Length(tmp); while (c > 0) and (tmp[c] <> '/') do Dec(c);
702 if c > 0 then
703 begin
704 // split path and name
705 Delete(sfi.fName, 1, c); // cut name
706 tmp := Copy(tmp, 1, c); // get path
707 if tmp = '/' then tmp := ''; // just delimiter; ignore it
708 sfi.fPath := sfi.fPath+tmp;
709 end;
711 // check for duplicates
712 if fl.Find(sfi.fPath+sfi.fName, c) then
713 begin
714 n := 0; tmp := sfi.fName;
715 c := Length(tmp); while (c > 0) and (tmp[c] <> '.') do Dec(c);
716 if c < 1 then c := Length(tmp)+1;
717 fn := Copy(tmp, 1, c-1); ext := Copy(tmp, c, Length(tmp));
718 repeat
719 tmp := fn+'_'+IntToStr(n)+ext;
720 if not fl.Find(sfi.fPath+tmp, c) then break;
721 Inc(n);
722 until false;
723 sfi.fName := tmp;
724 end;
725 fl.Add(sfi.fName);
726 end;
727 fl.Free();
728 except
729 fl.Free();
730 raise;
731 end;
732 end;
734 destructor TSFSVolume.Destroy ();
735 begin
736 Clear();
737 FreeAndNil(fFiles);
738 inherited Destroy();
739 end;
741 procedure TSFSVolume.Clear ();
742 begin
743 fRC := 0; //FIXME
744 fFiles.Clear();
745 end;
747 function TSFSVolume.FindFile (const fPath, fName: TSFSString): Integer;
748 begin
749 if fFiles = nil then result := -1
750 else
751 begin
752 result := fFiles.Count;
753 while result > 0 do
754 begin
755 Dec(result);
756 if fFiles[result] <> nil then
757 begin
758 if (SFSStrComp(fPath, TSFSFileInfo(fFiles[result]).fPath) = 0) and
759 (SFSStrComp(fName, TSFSFileInfo(fFiles[result]).fName) = 0) then exit;
760 end;
761 end;
762 result := -1;
763 end;
764 end;
766 function TSFSVolume.GetFileCount (): Integer;
767 begin
768 if fFiles = nil then result := 0 else result := fFiles.Count;
769 end;
771 function TSFSVolume.GetFiles (index: Integer): TSFSFileInfo;
772 begin
773 if fFiles = nil then result := nil
774 else
775 begin
776 if (index < 0) or (index >= fFiles.Count) then result := nil
777 else result := TSFSFileInfo(fFiles[index]);
778 end;
779 end;
781 function TSFSVolume.OpenFileEx (const fName: TSFSString): TStream;
782 var
783 fp, fn: TSFSString;
784 f, ls: Integer;
785 begin
786 fp := fName;
787 // normalize name, find split position
788 if (fp <> '') and ((fp[1] = '/') or (fp[1] = '\')) then Delete(fp, 1, 1);
789 ls := 0;
790 for f := 1 to Length(fp) do
791 begin
792 if fp[f] = '\' then fp[f] := '/';
793 if fp[f] = '/' then ls := f;
794 end;
795 fn := Copy(fp, ls+1, Length(fp));
796 fp := Copy(fp, 1, ls);
797 f := FindFile(fp, fn);
798 if f = -1 then raise ESFSError.Create('file not found: "'+fName+'"');
799 result := OpenFileByIndex(f);
800 if result = nil then raise ESFSError.Create('file not found: "'+fName+'"');
801 end;
804 { TSFSFileList }
805 constructor TSFSFileList.Create (const pVolume: TSFSVolume);
806 var
807 f: Integer;
808 begin
809 inherited Create();
810 ASSERT(pVolume <> nil);
811 f := FindVolumeInfoByVolumeInstance(pVolume);
812 ASSERT(f <> -1);
813 fVolume := pVolume;
814 Inc(TVolumeInfo(volumes[f]).fOpenedFilesCount); // íå ïîçâîëèì óáèòü çàïèñü!
815 end;
817 destructor TSFSFileList.Destroy ();
818 var
819 f: Integer;
820 begin
821 f := FindVolumeInfoByVolumeInstance(fVolume);
822 ASSERT(f <> -1);
823 if fVolume <> nil then Dec(fVolume.fRC);
824 Dec(TVolumeInfo(volumes[f]).fOpenedFilesCount);
825 // óáü¸ì çàïèñü, åñëè îíà âðåìåííàÿ, è â íåé íåò áîëüøå íè÷åãî îòêðûòîãî
826 if not TVolumeInfo(volumes[f]).fPermanent and
827 (TVolumeInfo(volumes[f]).fOpenedFilesCount < 1) then volumes[f] := nil;
828 inherited Destroy();
829 end;
831 function TSFSFileList.GetCount (): Integer;
832 begin
833 result := fVolume.fFiles.Count;
834 end;
836 function TSFSFileList.GetFiles (index: Integer): TSFSFileInfo;
837 begin
838 if (index < 0) or (index >= fVolume.fFiles.Count) then result := nil
839 else result := TSFSFileInfo(fVolume.fFiles[index]);
840 end;
843 procedure SFSRegisterVolumeFactory (factory: TSFSVolumeFactory);
844 var
845 f: Integer;
846 begin
847 if factory = nil then exit;
848 if factories.IndexOf(factory) <> -1 then
849 raise ESFSError.Create('duplicate factories are not allowed');
850 f := factories.IndexOf(nil);
851 if f = -1 then factories.Add(factory) else factories[f] := factory;
852 end;
854 procedure SFSUnregisterVolumeFactory (factory: TSFSVolumeFactory);
855 var
856 f: Integer;
857 c: Integer;
858 begin
859 if factory = nil then exit;
860 f := factories.IndexOf(factory);
861 if f = -1 then raise ESFSError.Create('can''t unregister nonexisting factory');
862 c := 0; while c < volumes.Count do
863 begin
864 if (volumes[c] <> nil) and (TVolumeInfo(volumes[c]).fFactory = factory) then volumes[c] := nil;
865 Inc(c);
866 end;
867 factories[f] := nil;
868 end;
871 function SFSAddDataFileEx (dataFileName: TSFSString; ds: TStream; top, permanent: Integer): Integer;
872 // dataFileName ìîæåò èìåòü ïðåôèêñ òèïà "zip:" (ñì. âûøå: IsMyPrefix).
873 // ìîæåò âûêèíóòü èñêëþ÷åíèå!
874 // top:
875 // <0: äîáàâèòü â íà÷àëî ñïèñêà ïîèñêà.
876 // =0: íå ìåíÿòü.
877 // >0: äîáàâèòü â êîíåö ñïèñêà ïîèñêà.
878 // permanent:
879 // <0: ñîçäàòü "âðåìåííûé" òîì.
880 // =0: íå ìåíÿòü ôëàæîê ïîñòîÿíñòâà.
881 // >0: ñîçäàòü "ïîñòîÿííûé" òîì.
882 // åñëè ds <> nil, òî ñîçäà¸ò ñáîðíèê èç ïîòîêà. åñëè ñáîðíèê ñ èìåíåì
883 // dataFileName óæå çàðåãèñòðèðîâàí, òî ïàäàåò íàôèã.
884 // âîçâðàùàåò èíäåêñ â volumes.
885 // óìååò äåëàòü ðåêóðñèþ.
886 var
887 fac: TSFSVolumeFactory;
888 vol: TSFSVolume;
889 vi: TVolumeInfo;
890 f: Integer;
891 st, st1: TStream;
892 pfx: TSFSString;
893 fn, vfn, tmp: TSFSString;
894 begin
895 f := Pos('::', dataFileName);
896 if f <> 0 then
897 begin
898 // ðåêóðñèâíîå îòêðûòèå.
899 // ðàçîáü¸ì dataFileName íà èìÿ ñáîðíèêà è îñòàòîê.
900 // pfx áóäåò èìåíåì ñáîðíèêà, dataFileName -- îñòàòêîì.
901 pfx := Copy(dataFileName, 1, f-1); Delete(dataFileName, 1, f+1);
902 // ñíà÷àëà îòêðîåì ïåðâûé ñïèñîê...
903 result := SFSAddDataFileEx(pfx, ds, 0, 0);
904 // ...òåïåðü ïðîäîëæèì ñ îñòàòêîì.
905 // óçíàåì, êàêîå ôàéëî îòêðûâàòü.
906 // âûêîâûðÿåì ïåðâûé "::" ïðåôèêñ (ýòî áóäåò èìÿ ôàéëà).
907 f := Pos('::', dataFileName); if f = 0 then f := Length(dataFileName)+1;
908 fn := Copy(dataFileName, 1, f-1); Delete(dataFileName, 1, f-1);
909 // dataFileName õðàíèò îñòàòîê.
910 // èçâëå÷¸ì èìÿ ôàéëà:
911 SplitDataName(fn, pfx, tmp, vfn);
912 // îòêðîåì ýòîò ôàéë
913 vi := TVolumeInfo(volumes[result]); st := nil;
914 try
915 st := vi.fVolume.OpenFileEx(tmp);
916 st1 := TOwnedPartialStream.Create(vi, st, 0, st.Size, true);
917 except
918 FreeAndNil(st);
919 // óäàëèì íåèñïîëüçóåìûé âðåìåííûé òîì.
920 if not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[result] := nil;
921 raise;
922 end;
923 // óðà. îòêðûëè ôàéë. êèäàåì â âîçäóõ ÷åï÷èêè, ïðîäîëæàåì ðàçâëå÷åíèå.
924 fn := fn+dataFileName;
925 try
926 st1.Position := 0;
927 result := SFSAddDataFileEx(fn, st1, top, permanent);
928 except
929 st1.Free(); // à âîò íå çàëàäèëîñü. çàêðûëè îòêðûòîå ôàéëî, âûëåòåëè.
930 raise;
931 end;
932 exit;
933 end;
935 // îáûêíîâåííîå íåðåêóðñèâíîå îòêðûòèå.
936 SplitDataName(dataFileName, pfx, fn, vfn);
938 f := FindVolumeInfo(vfn);
939 if f <> -1 then
940 begin
941 if ds <> nil then raise ESFSError.Create('subdata name conflict');
942 if permanent <> 0 then TVolumeInfo(volumes[f]).fPermanent := (permanent > 0);
943 if top = 0 then result := f
944 else if top < 0 then result := 0
945 else result := volumes.Count-1;
946 if result <> f then volumes.Move(f, result);
947 exit;
948 end;
950 if ds <> nil then st := ds
951 else st := TFileStream.Create(fn, fmOpenRead or fmShareDenyWrite);
952 st.Position := 0;
954 volumes.Pack();
956 fac := nil; vol := nil;
957 try
958 for f := 0 to factories.Count-1 do
959 begin
960 fac := TSFSVolumeFactory(factories[f]);
961 if fac = nil then continue;
962 if (pfx <> '') and not fac.IsMyVolumePrefix(pfx) then continue;
963 st.Position := 0;
964 try
965 if ds <> nil then vol := fac.Produce(pfx, '', st)
966 else vol := fac.Produce(pfx, fn, st);
967 except
968 vol := nil;
969 end;
970 if vol <> nil then break;
971 end;
972 if vol = nil then raise ESFSError.Create('no factory for "'+dataFileName+'"');
973 except
974 if st <> ds then st.Free();
975 raise;
976 end;
978 vi := TVolumeInfo.Create();
979 try
980 if top < 0 then
981 begin
982 result := 0;
983 volumes.Insert(0, vi);
984 end
985 else result := volumes.Add(vi);
986 except
987 vol.Free();
988 if st <> ds then st.Free();
989 vi.Free();
990 raise;
991 end;
993 vi.fFactory := fac;
994 vi.fVolume := vol;
995 vi.fPackName := vfn;
996 vi.fStream := st;
997 vi.fPermanent := (permanent > 0);
998 vi.fNoDiskFile := (ds <> nil);
999 vi.fOpenedFilesCount := 0;
1000 end;
1002 function SFSAddSubDataFile (const virtualName: TSFSString; ds: TStream;
1003 top: Boolean = false): Boolean;
1004 var
1005 tv: Integer;
1006 begin
1007 ASSERT(ds <> nil);
1008 try
1009 if top then tv := -1 else tv := 1;
1010 SFSAddDataFileEx(virtualName, ds, tv, 0);
1011 result := true;
1012 except
1013 result := false;
1014 end;
1015 end;
1017 function SFSAddDataFile (const dataFileName: TSFSString; top: Boolean = false): Boolean;
1018 var
1019 tv: Integer;
1020 begin
1021 try
1022 if top then tv := -1 else tv := 1;
1023 SFSAddDataFileEx(dataFileName, nil, tv, 1);
1024 result := true;
1025 except
1026 result := false;
1027 end;
1028 end;
1031 function SFSExpandDirName (const s: TSFSString): TSFSString;
1032 var
1033 f, e: Integer;
1034 es: TSFSString;
1035 begin
1036 f := 1; result := s;
1037 while f < Length(result) do
1038 begin
1039 while (f < Length(result)) and (result[f] <> '<') do Inc(f);
1040 if f >= Length(result) then exit;
1041 e := f; while (e < Length(result)) and (result[e] <> '>') do Inc(e);
1042 es := Copy(result, f, e+1-f);
1044 if es = '<currentdir>' then es := GetCurrentDir
1045 else if es = '<exedir>' then es := ExtractFilePath(ParamStr(0))
1046 else es := '';
1048 if es <> '' then
1049 begin
1050 if (es[Length(es)] <> '/') and (es[Length(es)] <> '\') then es := es+'/';
1051 Delete(result, f, e+1-f);
1052 Insert(es, result, f);
1053 Inc(f, Length(es));
1054 end
1055 else f := e+1;
1056 end;
1057 end;
1059 function SFSFileOpenEx (const fName: TSFSString): TStream;
1060 var
1061 dataFileName, fn: TSFSString;
1062 f: Integer;
1063 vi: TVolumeInfo;
1064 diskChecked: Boolean;
1065 ps: TStream;
1067 function CheckDisk (): TStream;
1068 // ïðîâåðèì, åñòü ëè ôàëî fn ãäå-òî íà äèñêàõ.
1069 var
1070 dfn, dirs, cdir: TSFSString;
1071 f: Integer;
1072 begin
1073 result := nil;
1074 if diskChecked or not sfsDiskEnabled then exit;
1075 diskChecked := true;
1076 dfn := SFSReplacePathDelims(fn, '/');
1077 dirs := sfsDiskDirs; if dirs = '' then dirs := '<currentdir>';
1078 while dirs <> '' do
1079 begin
1080 f := 1; while (f <= Length(dirs)) and (dirs[f] <> '|') do Inc(f);
1081 cdir := Copy(dirs, 1, f-1); Delete(dirs, 1, f);
1082 if cdir = '' then continue;
1083 cdir := SFSReplacePathDelims(SFSExpandDirName(cdir), '/');
1084 if cdir[Length(cdir)] <> '/' then cdir := cdir+'/';
1085 try
1086 result := TFileStream.Create(cdir+dfn, fmOpenRead or fmShareDenyWrite);
1087 exit;
1088 except
1089 end;
1090 end;
1091 end;
1093 begin
1094 SplitFName(fName, dataFileName, fn);
1095 if fn = '' then raise ESFSError.Create('invalid file name: "'+fName+'"');
1097 diskChecked := false;
1099 if dataFileName <> '' then
1100 begin
1101 // ïðåôèêñîâàíûé ôàéë
1102 if sfsForceDiskForPrefixed then
1103 begin
1104 result := CheckDisk();
1105 if result <> nil then exit;
1106 end;
1108 f := SFSAddDataFileEx(dataFileName, nil, 0, 0);
1109 vi := TVolumeInfo(volumes[f]);
1111 try
1112 result := vi.fVolume.OpenFileEx(fn);
1113 ps := TOwnedPartialStream.Create(vi, result, 0, result.Size, true);
1114 except
1115 result.Free();
1116 if not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil;
1117 result := CheckDisk(); // îáëîì ñ datafile, ïðîâåðèì äèñê
1118 if result = nil then raise ESFSError.Create('file not found: "'+fName+'"');
1119 exit;
1120 end;
1121 //Inc(vi.fOpenedFilesCount);
1122 result := ps;
1123 exit;
1124 end;
1126 // íåïðåôèêñîâàíûé ôàéë
1127 if sfsDiskFirst then
1128 begin
1129 result := CheckDisk();
1130 if result <> nil then exit;
1131 end;
1132 // èùåì ïî âñåì ïåðìàíåíòíûì ïðåôèêñàì
1133 f := 0;
1134 while f < volumes.Count do
1135 begin
1136 vi := TVolumeInfo(volumes[f]);
1137 if (vi <> nil) and vi.fPermanent then
1138 begin
1139 if vi.fVolume <> nil then
1140 begin
1141 result := vi.fVolume.OpenFileEx(fn);
1142 if result <> nil then
1143 begin
1144 try
1145 ps := TOwnedPartialStream.Create(vi, result, 0, result.Size, true);
1146 result := ps;
1147 //Inc(vi.fOpenedFilesCount);
1148 except
1149 FreeAndNil(result);
1150 end;
1151 end;
1152 if result <> nil then exit;
1153 end;
1154 end;
1155 Inc(f);
1156 end;
1157 result := CheckDisk();
1158 if result = nil then raise ESFSError.Create('file not found: "'+fName+'"');
1159 end;
1161 function SFSFileOpen (const fName: TSFSString): TStream;
1162 begin
1163 try
1164 result := SFSFileOpenEx(fName);
1165 except
1166 result := nil;
1167 end;
1168 end;
1170 function SFSFileList (const dataFileName: TSFSString): TSFSFileList;
1171 var
1172 f: Integer;
1173 vi: TVolumeInfo;
1174 begin
1175 result := nil;
1176 if dataFileName = '' then exit;
1178 try
1179 f := SFSAddDataFileEx(dataFileName, nil, 0, 0);
1180 except
1181 exit;
1182 end;
1183 vi := TVolumeInfo(volumes[f]);
1185 try
1186 result := TSFSFileList.Create(vi.fVolume);
1187 Inc(vi.fVolume.fRC);
1188 except
1189 if not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil;
1190 end;
1191 end;
1194 initialization
1195 factories := TObjectList.Create(true);
1196 volumes := TObjectList.Create(true);
1197 finalization
1198 //volumes.Free(); // it fails for some reason... Runtime 217 (^C hit). wtf?!
1199 //factories.Free(); // not need to be done actually...
1200 end.