1 (* Copyright (C) DooM 2D:Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 // streaming file system (virtual)
17 {$INCLUDE ../shared/a_modes.inc}
18 {$SCOPEDENUMS OFF}
19 {.$R+}
20 {.$DEFINE SFS_VOLDEBUG}
23 interface
25 uses
29 type
35 public
37 fPath: AnsiString; // ðàçäåëèòåëè êàòàëîãîâ -- "/"; êîðåíü íèêàê íå îáîçíà÷åí, åñëè íå ïóñòîå, îáÿçàíî çàâåðøàòüñÿ "/"
50 // âèðòóàëüíàÿ ôàéëîâàÿ ñèñòåìà. ÒÎËÜÊÎ ÄËß ×ÒÅÍÈß!
51 // òîì ÍÅ ÄÎËÆÅÍ óáèâàòüñÿ íèêàê èíà÷å, ÷åì ïðè ïîìîùè ôàáðèêè!
53 protected
58 // ïðèøèáèòü âñå ñòðóêòóðû.
59 // íå äîëæíà ïàäàòü, åñëè å¸ âûçûâàþò íåñêîëüêî ðàç.
62 // âûçûâàåòñÿ èç DoDirectoryRead() äëÿ çàïîëíåíèÿ ñïèñêà ôàéëîâ.
63 // ñ÷èòàåòñÿ, ÷òî âñå ìàãèêè óæå ïðîâåðåíû è ôàéë òî÷íî íàø.
64 // fFileName, fFileStream óæå óñòàíîâëåíû, fFiles ñîçäàí,
65 // â í¸ì, ñêîðåå âñåãî, íèêîãî íåò.
66 // ïîçèöèÿ ïîòîêà -- òà, ÷òî îñòàâèëà ôàáðèêà.
67 // ïðè îøèáêàõ êèäàòü èñêëþ÷åíèå, òîãäà òîì áóäåò ïðèáèò ôàáðèêîé.
68 // ðàçäåëèòåëè ïóòåé äîëæíû áûòü òîëüêî "/", êîðíåâîé "/" äîëæåí
69 // áûòü îïóùåí, ïóòè (åñëè íå ïóñòûå) äîëæíû çàâåðøàòüñÿ "/"!
70 // fName äîëæíî ñîäåðæàòü òîëüêî èìÿ, fPath -- òîëüêî ïóòü.
71 // â ïðèíöèïå, îá ýòîì ïîçàáîòèòñÿ DoDirectoryRead(), íî çà÷åì
72 // äàâàòü åìó ëèøíþþ ðàáîòó?
75 // íàéòè ôàéë, âåðíóòü åãî èíäåêñ â fFiles.
76 // ýòà ïðîöåäóðà ìîæåò ìåíÿòü fFiles!
77 // fPath -- â ïðàâèëüíîé ôîðìå, ñ "/", êîðíåâîé "/" óáèò, ôèíàëüíûé äîáàâëåí.
78 // åñëè ôàéë íå íàéäåí, âåðíóòü -1.
81 // âîçâðàùàåò êîëè÷åñòâî ôàéëîâ â fFiles
84 // âîçâðàùàåò ôàéë ñ èíäåêñîì index.
85 // ìîæåò âîçâðàùàòü NIL.
86 // íèêàêèõ ïàäåíèé íà íåïðàâèëüíûå èíäåêñû!
89 public
90 // pSt íå îáÿçàòåëüíî çàïîìèíàòü, åñëè îí íå íóæåí.
92 // fFileStream óíè÷òîæàòü íåëüçÿ, åñëè îí ðàâåí ïàðàìåòðó pSt êîíñòðóêòîðà.
95 // âûçûâàåò ReadDirectory().
96 // ýòà ïðîöåäóðà ñàìà ðàçáåð¸òñÿ ñ äóáëèêàòàìè èì¸í: ïîäîáàâëÿåò â
97 // êîíåö èì¸í-äóáëèêàòîâ ïîä÷¸ðêèâàíèå è äåñÿòè÷íûé íîìåð.
98 // òàêæå îíà íîðìàëèçóåò âèä èì¸í.
101 // ïðè îøèáêàõ êèäàòüñÿ èñêëþ÷åíèÿìè.
104 // åñëè íå ñìîãëî îòêóïîðèòü ôàéëî (èëè åù¸ ãäå îøèáëîñü), çàøâûðí¸ò èñêëþ÷åíèå.
108 // ìîæåò âîçâðàùàòü NIL.
109 // íèêàêèõ ïàäåíèé íà íåïðàâèëüíûå èíäåêñû!
113 // ôàáðèêà òîìîâ. âñå SFS ïðè ñòàðòå äîáàâëÿþò ñâîè ôàáðèêè.
114 // áëàãîäàðÿ ýòîìó ìîæíî ñîçäàâàòü ðàçíûå âñÿêèå SFS ñòàíäàðòíûì
115 // âûçîâîì ñòàíäàðòíîé ïðîöåäóðû.
116 // ôàáðèêà ÍÅ ÄÎËÆÍÀ óáèâàòüñÿ íèêàê èíà÷å, ÷åì ïðè ïîìîùè âûçîâà
117 // SFSUnregisterVolumeFactory()! ýòî ãàðàíòèðóåò, ÷òî äâèæîê
118 // ïåðåä ðàññòðåëîì îòäàñò åé âñå å¸ òîìà.
120 public
121 // åñëè äîáàâëÿåì ôàéë äàííûõ ôàéë ñ èìåíåì òèïà "zip:....", òî
122 // SFS èçâëå÷¸ò ýòî "zip" è ïåðåäàñò â ñèþ ôóíêöèþ.
123 // åæåëè ôóíêöèÿ âåðí¸ò ïðàâäó, òî SFS âûçîâåò Produce äëÿ äàííîãî
124 // ôàéëà. åñëè íè îäíà ôàáðèêà ïðåôèêñ íå ïðèçíàåò, òî ôàéë íå îòêðîþò.
125 // èñïîëüçóåòñÿ äëÿ ñêèïàíèÿ àâòîäåòåêòà.
126 // SFS ÍÅ Ñ×ÈÒÀÅÒ ÏÐÅÔÈÊÑÎÌ ÑÒÐÎÊÓ ÊÎÐÎ×Å ÒÐ¨Õ ÑÈÌÂÎËÎÂ!
128 // ïðîâåðÿåò, ìîæåò ëè ôàáðèêà ñäåëàòü òîì äëÿ äàííîãî ôàéëà.
129 // st -- îòêðûòûé äëÿ ÷òåíèÿ ôàéëîâé ïîòîê. óêàçàòåëü ÷òåíèÿ ñòîèò â íà÷àëå.
130 // ýòîò ïîòîê íåëüçÿ çàêðûâàòü!
131 // prefix: òî, ÷òî áûëî ïåðåäàíî â IsMyVolumePrefix() èëè ''.
132 // èñêëþ÷åíèå ñ÷èòàåòñÿ îøèáêîé, âîçâðàò NIL ñ÷èòàåòñÿ îøèáêîé.
133 function Produce (const prefix, fileName: AnsiString; st: TStream): TSFSVolume; virtual; abstract;
134 // êîãäà òîì áîëüøå íå íóæåí, îí áóäåò îòäàí ôàáðèêå íà ïåðåðàáîòêó.
135 // äàëåå äâèæîê íå áóäåò þçàòü ñåé òîì.
139 // "èòåðàòîð", âîçâðàùàåìûé SFSFileList()
141 protected
147 public
153 // ïðè íåïðàâèëüíîì èíäåêñå ìîë÷à âåðí¸ò NIL.
154 // ïðè ïðàâèëüíîì òîæå ìîæåò âåðíóòü NIL!
155 // î÷åíü íå ñîâåòóþ ìåíÿòü ñîäåðæèìîå ïîëó÷åííîãî êëàññà.
156 // êîíå÷íî, ÿ ìîã áû âîçâðàùàòü íîâóþ ñòðóêòóðó èëè íå÷òî ïîõîæåå,
157 // íî áëèí, åñëè òû èäèîò è íå óìååøü äàæå êîììåíòû ÷èòàòü, òî
158 // êàêîãî òû âîîáùå â ïðîãðàììèíã ïîëåç?
164 // ýòà ôóíêöèÿ àâòîìàòè÷åñêè ïðèáü¸ò factory.
167 // äîáàâèòü ñáîðíèê â ïîñòîÿííûé ñïèñîê.
168 // åñëè ñáîðíèê ñ òàêèì èìåíåì óæå îòêðûò, òî íå îòêðûâàåò åãî ïîâòîðíî.
169 // íèêîãäà íå êèäàåò èñêëþ÷åíèé.
170 // top: äîáàâèòü â íà÷àëî ñïèñêà ïîèñêà.
171 // âåðí¸ò ëîæü ïðè îøèáêå.
172 // ñïîñîáíî îòêðûâàòü ñáîðíèêè â ñáîðíèêàõ ïðè ïîìîùè êðóòûõ èì¸í a-la:
173 // "zip:pack0::pack:pack1::wad2:pack2".
174 // â äàëüíåéøåì ñëåäóåò îáðàùàòüñÿ ê ñáîðíèêó êàê "pack2::xxx".
175 // èëè ìîæíî íàïèñàòü:
176 // "zip:pack0::pack:pack1::wad2:pack2|datafile".
177 // è îáðàùàòüñÿ êàê "datafile::xxx".
178 // "||" ïðåîáðàçóþòñÿ â ïðîñòîé "|" è ðàçäåëèòåëåì íå ñ÷èòàþòñÿ.
179 // ïðèíèìàåòñÿ âî âíèìàíèå òîëüêî ïîñëåäíÿÿ òðóáà.
182 // äîáàâèòü ñáîðíèê âðåìåííî
185 // äîáàâèòü â ïîñòîÿííûé ñïèñîê ñáîðíèê èç ïîòîêà ds.
186 // åñëè âîçâðàùàåò èñòèíó, òî SFS ñòàíîâèòñÿ âëÿäåëüöåì ïîòîêà ds è ñàìà
187 // óãðîáèò ñåé ïîòîê ïî íåîáõîäèìîñòè.
188 // virtualName ñòàíîâèòñÿ èìåíåì ñáîðíèêà äëÿ îïåðàöèè îòêðûòèÿ ôàéëà òèïà
189 // "packfile:file.ext".
190 // åñëè êàêîé-íèáóäü ñáîðíèê ñ èìåíåì virtualName óæå îòêðûò, âåðí¸ò false.
191 // íèêîãäà íå êèäàåò èñêëþ÷åíèé.
192 // top: äîáàâèòü â íà÷àëî ñïèñêà ïîèñêà.
193 // âåðí¸ò ëîæü ïðè îøèáêå.
194 // îòêðûâàåò ñáîðíèê èç ïîòîêà. dataFileName -- ÂÈÐÒÓÀËÜÍÎÅ èìÿ.
195 // ò.å. íà ñàìîì äåëå òàêîãî ôàéëà ìîæåò è íå áûòü íà äèñêå.
196 function SFSAddSubDataFile (const virtualName: AnsiString; ds: TStream; top: Boolean=false): Boolean;
198 // øâûðÿåòñÿ èñêëþ÷åíèÿìè.
199 // åñëè fName íå èìååò óêàçàíèÿ íà ôàéë äàííûõ (ýòî òî, ÷òî îòäåëåíî îò
200 // îñòàëüíîãî èìåíè äâîåòî÷èåì), òî èùåì ñíà÷àëà ïî âñåì çàðåãèñòðèðîâàííûì
201 // ôàéëàì äàííûõ, ïîòîì â òåêóùåì êàòàëîãå, ïîòîì â êàòàëîãå, îòêóäà ñòàðòîâàëè.
202 // åñëè íè÷åãî íå íàøëè, êèäàåì èñêëþ÷åíèå.
205 // ïðè îøèáêå -- NIL, è íèêàêèõ èñêëþ÷åíèé.
208 // âîçâðàùàåò NIL ïðè îøèáêå.
209 // ïîñëå èñïîëüçîâàíèÿ, íàòóðàëüíî, èòåðàòîð íàäî ãðîõíóòü %-)
212 // çàïðåòèòü îñâîáîæäåíèå âðåìåííûõ òîìîâ (ìîæíî âûçûâàòü ðåêóðñèâíî)
215 // ðàçðåøèòü îñâîáîæäåíèå âðåìåííûõ òîìîâ (ìîæíî âûçûâàòü ðåêóðñèâíî)
218 // for completeness sake
223 // ðàçîáðàòü òîëñòîå èìÿ ôàéëà, âåðíóòü âèðòóàëüíîå èìÿ ïîñëåäíåãî ñïèñêà
224 // èëè ïóñòóþ ñòîðîêó, åñëè ñïèñêîâ íå áûëî.
227 // Wildcard matching
228 // this code is meant to allow wildcard pattern matches. tt is VERY useful
229 // for matching filename wildcard patterns. tt allows unix grep-like pattern
230 // comparisons, for instance:
231 //
232 // ? Matches any single characer
233 // + Matches any single characer or nothing
234 // * Matches any number of contiguous characters
235 // [abc] Matches a or b or c at that position
236 // [!abc] Matches anything but a or b or c at that position
237 // [a-e] Matches a through e at that position
238 //
239 // 'ma?ch.*' -Would match match.exe, mavch.dat, march.on, etc
240 // 'this [e-n]s a [!zy]est' -Would match 'this is a test', but would
241 // not match 'this as a yest'
242 //
248 var
249 // ïðàâäà: ðàçðåøåíî èñêàòü ôàéëî íå òîëüêî â ôàéëàõ äàííûõ, íî è íà äèñêå.
251 // ïðàâäà: åñëè ôàéë íå ïðåôèêñîâàí, òî ñíà÷àëà èùåì ôàéëî íà äèñêå,
252 // ïîòîì â ôàéëàõ äàííûõ.
254 // ïðàâäà: äàæå äëÿ ïðåôèêñîâàíûõ ôàéëîâ ñíà÷àëà ïðîñìîòðèì äèñê
255 // (åñëè óñòàíîâëåí ôëàæîê sfsDiskFirst è sfsDiskEnabled).
257 // ñïèñîê äèñêîâûõ êàòàëîãîâ äëÿ ïîèñêà ôàéëà. åñëè ïóñò -- èùåì òîëüêî â
258 // òåêóùåì. êàòàëîãè ðàçäåëÿþòñÿ òðóáîé ("|").
259 // <currentdir> çàìåíÿåòñÿ íà òåêóùèé êàòàëîã (ñ çàâåðøàþùèì "/"),
260 // <exedir> çàìåíÿåòñÿ íà êàòàëîã, ãäå ñèäèò .EXE (ñ çàâåðøàþùèì "/").
264 implementation
266 uses
270 const
271 // character defines
283 begin
284 result :=
292 function MatchMask (const pattern: AnsiString; p, pend: Integer; const text: AnsiString; t, tend: Integer): Boolean;
293 var
297 begin
298 // sanity checks
304 begin
306 begin
307 // no more text. check if there's no more chars in pattern (except "*" & "+")
312 exit;
315 WILD_CHAR_SINGLE: ;
316 WILD_CHAR_ESCAPE:
317 begin
322 WILD_CHAR_RANGE_OPEN:
323 begin
331 repeat
337 begin
341 begin
343 end
345 end
350 // skip the rest or the range
354 WILD_CHAR_SINGLE_OR_NONE:
355 begin
359 exit;
361 WILD_CHAR_MULTI:
362 begin
366 begin
370 exit;
381 begin
388 var
390 begin
396 begin
398 begin
400 begin
407 begin
417 type
419 public
424 fPermanent: Boolean; // èñòèíà -- íå áóäåò óãðîáëåíà, åñëè íå îñòàíåòñÿ íè îäíîãî îòêðûòîãî òîìà
425 // èñòèíà -- ýòîò òîì áûë ñîçäàí èç ïîòîêà è íå èìååò äèñêîâîãî ôàéëà, ïîòîìó ôàáðèêå áóäåò ïåðåäàíî íå èìÿ ñáîðíèêà, à ïóñòàÿ ñòðîêà
433 protected
436 public
442 var
449 var
453 begin
454 // collect garbage
457 begin
461 begin
462 // this volume probably can be removed
466 begin
468 begin
476 begin
477 {$IFDEF SFS_VOLDEBUG}writeln('000: destroying volume "', TVolumeInfo(volumes[f]).fPackName, '"');{$ENDIF}
481 continue;
489 begin
494 begin
497 begin
504 // ðàçáèòü èìÿ ôàéëà íà ÷àñòè: ïðåôèêñ ôàéëîâîé ñèñòåìû, èìÿ ôàéëà äàííûõ,
505 // ñîáñòâåííî èìÿ ôàéëà
506 // èìÿ âûãëÿäèò êàê:
507 // (("sfspfx:")?"datafile::")*"filename"
509 var
511 begin
514 begin
519 else
520 begin
526 // ñàéäýôôåêò: âûðåçàåò âèðòóàëüíîå èìÿ èç dataFile.
528 var
530 begin
533 begin
536 begin
538 else
539 begin
542 break;
549 // ðàçáèòü èìÿ ñáîðíèêà íà ÷àñòè: ïðåôèêñ ôàéëîâîé ñèñòåìû, èìÿ ôàéëà äàííûõ,
550 // âèðòóàëüíîå èìÿ. åñëè âèðòóàëüíîãî èìåíè íå äàíî, îíî áóäåò ðàâíî dataFile.
551 // èìÿ âûãëÿäèò êàê:
552 // [sfspfx:]datafile[|virtname]
553 // åñëè ïåðåä äâîåòî÷èåì ìåíüøå òð¸õ áóêâ, òî ýòî ñ÷èòàåòñÿ íå ïðåôèêñîì,
554 // à èìåíåì äèñêà.
556 var
558 begin
561 else
562 begin
569 // íàéòè ïðîèçâîäèòåëÿ äëÿ ýòîãî ôàéëà (åñëè ôàéë óæå îòêðûò).
570 // onlyPerm: òîëüêî "ïîñòîÿííûå" ïðîèçâîäèòåëè.
572 var
575 begin
578 begin
580 begin
583 begin
585 begin
587 exit;
596 // íàéòè èíôó äëÿ ýòîãî òîìà.
597 // õîðîøåå èìÿ, ïðàâäà? %-)
599 begin
602 begin
604 begin
612 // adds '/' too
614 var
616 begin
620 begin
622 begin
624 continue;
627 begin
629 end
630 else
631 begin
640 var
642 begin
645 begin
647 begin
648 // avoid unnecessary string changes
655 var
658 begin
660 repeat
668 { TVolumeInfo }
670 var
673 begin
680 // òèïà ìóñîðîñáîðíèê: åñëè íàø ïîòîê áîëåå íèêåì íå þçàåòñÿ, òî óãðîáèòü åãî íàôèã
682 begin
686 begin
688 begin
691 begin
704 { TOwnedPartialStream }
707 begin
714 var
716 begin
719 begin
722 begin
725 begin
726 {$IFDEF SFS_VOLDEBUG}writeln('001: destroying volume "', TVolumeInfo(volumes[f]).fPackName, '"');{$ENDIF}
734 { TSFSFileInfo }
736 begin
747 begin
753 { TSFSVolume }
755 begin
763 var
767 begin
774 begin
776 // normalize name & path
783 begin
784 // split path and name
796 begin
803 begin
808 begin
810 else
811 begin
814 begin
817 begin
827 begin
832 begin
834 else
835 begin
842 var
845 begin
847 // normalize name, find split position
851 begin
864 { TSFSFileList }
866 var
868 begin
878 var
880 begin
884 // óáü¸ì çàïèñü, åñëè îíà âðåìåííàÿ, è â íåé íåò áîëüøå íè÷åãî îòêðûòîãî
885 if (gcdisabled = 0) and not TVolumeInfo(volumes[f]).fPermanent and (TVolumeInfo(volumes[f]).fOpenedFilesCount < 1) then
886 begin
887 {$IFDEF SFS_VOLDEBUG}writeln('002: destroying volume "', TVolumeInfo(volumes[f]).fPackName, '"');{$ENDIF}
894 begin
899 begin
906 var
908 begin
917 var
920 begin
925 begin
933 function SFSAddDataFileEx (dataFileName: AnsiString; ds: TStream; top, permanent: Integer): Integer;
934 // dataFileName ìîæåò èìåòü ïðåôèêñ òèïà "zip:" (ñì. âûøå: IsMyPrefix).
935 // ìîæåò âûêèíóòü èñêëþ÷åíèå!
936 // top:
937 // <0: äîáàâèòü â íà÷àëî ñïèñêà ïîèñêà.
938 // =0: íå ìåíÿòü.
939 // >0: äîáàâèòü â êîíåö ñïèñêà ïîèñêà.
940 // permanent:
941 // <0: ñîçäàòü "âðåìåííûé" òîì.
942 // =0: íå ìåíÿòü ôëàæîê ïîñòîÿíñòâà.
943 // >0: ñîçäàòü "ïîñòîÿííûé" òîì.
944 // åñëè ds <> nil, òî ñîçäà¸ò ñáîðíèê èç ïîòîêà. åñëè ñáîðíèê ñ èìåíåì
945 // dataFileName óæå çàðåãèñòðèðîâàí, òî ïàäàåò íàôèã.
946 // âîçâðàùàåò èíäåêñ â volumes.
947 // óìååò äåëàòü ðåêóðñèþ.
948 var
956 begin
959 begin
960 // ðåêóðñèâíîå îòêðûòèå.
961 // ðàçîáü¸ì dataFileName íà èìÿ ñáîðíèêà è îñòàòîê.
962 // pfx áóäåò èìåíåì ñáîðíèêà, dataFileName -- îñòàòêîì.
964 // ñíà÷àëà îòêðîåì ïåðâûé ñïèñîê...
966 // ...òåïåðü ïðîäîëæèì ñ îñòàòêîì.
967 // óçíàåì, êàêîå ôàéëî îòêðûâàòü.
968 // âûêîâûðÿåì ïåðâûé "::" ïðåôèêñ (ýòî áóäåò èìÿ ôàéëà).
971 // dataFileName õðàíèò îñòàòîê.
972 // èçâëå÷¸ì èìÿ ôàéëà:
974 // îòêðîåì ýòîò ôàéë
976 try
979 except
981 // óäàëèì íåèñïîëüçóåìûé âðåìåííûé òîì.
982 if (gcdisabled = 0) and not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[result] := nil;
985 // óðà. îòêðûëè ôàéë. êèäàåì â âîçäóõ ÷åï÷èêè, ïðîäîëæàåì ðàçâëå÷åíèå.
987 try
990 except
994 exit;
997 // îáûêíîâåííîå íåðåêóðñèâíîå îòêðûòèå.
1002 begin
1009 exit;
1019 try
1021 begin
1026 try
1029 except
1035 except
1041 try
1043 begin
1046 end
1048 except
1064 function SFSAddSubDataFile (const virtualName: AnsiString; ds: TStream; top: Boolean=false): Boolean;
1065 var
1067 begin
1069 try
1073 except
1079 var
1081 begin
1082 try
1086 except
1092 var
1094 begin
1095 try
1099 except
1107 var
1110 begin
1113 begin
1124 begin
1129 end
1135 var
1143 // ïðîâåðèì, åñòü ëè ôàëî fn ãäå-òî íà äèñêàõ.
1144 var
1147 begin
1154 begin
1160 try
1162 exit;
1163 except
1168 begin
1175 begin
1176 // ïðåôèêñîâàíûé ôàéë
1178 begin
1186 try
1189 except
1191 if (gcdisabled = 0) and not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil;
1194 exit;
1196 //Inc(vi.fOpenedFilesCount);
1198 exit;
1201 // íåïðåôèêñîâàíûé ôàéë
1203 begin
1207 // èùåì ïî âñåì ïåðìàíåíòíûì ïðåôèêñàì
1210 begin
1213 begin
1215 begin
1218 begin
1219 try
1222 //Inc(vi.fOpenedFilesCount);
1223 except
1237 begin
1238 try
1240 except
1246 var
1249 begin
1253 try
1255 except
1256 exit;
1260 try
1262 except
1263 if (gcdisabled = 0) and not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil;
1268 initialization
1271 //finalization
1272 //volumes.Free(); // it fails for some reason... Runtime 217 (^C hit). wtf?!
1273 //factories.Free(); // not need to be done actually...