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
460 begin
461 // this volume probably can be removed
465 begin
467 begin
475 begin
476 {$IFDEF SFS_VOLDEBUG}writeln('000: destroying volume "', TVolumeInfo(volumes[f]).fPackName, '"');{$ENDIF}
480 continue;
488 begin
493 begin
496 begin
503 // ðàçáèòü èìÿ ôàéëà íà ÷àñòè: ïðåôèêñ ôàéëîâîé ñèñòåìû, èìÿ ôàéëà äàííûõ,
504 // ñîáñòâåííî èìÿ ôàéëà
505 // èìÿ âûãëÿäèò êàê:
506 // (("sfspfx:")?"datafile::")*"filename"
508 var
510 begin
513 begin
518 else
519 begin
525 // ñàéäýôôåêò: âûðåçàåò âèðòóàëüíîå èìÿ èç dataFile.
527 var
529 begin
532 begin
535 begin
537 else
538 begin
541 break;
548 // ðàçáèòü èìÿ ñáîðíèêà íà ÷àñòè: ïðåôèêñ ôàéëîâîé ñèñòåìû, èìÿ ôàéëà äàííûõ,
549 // âèðòóàëüíîå èìÿ. åñëè âèðòóàëüíîãî èìåíè íå äàíî, îíî áóäåò ðàâíî dataFile.
550 // èìÿ âûãëÿäèò êàê:
551 // [sfspfx:]datafile[|virtname]
552 // åñëè ïåðåä äâîåòî÷èåì ìåíüøå òð¸õ áóêâ, òî ýòî ñ÷èòàåòñÿ íå ïðåôèêñîì,
553 // à èìåíåì äèñêà.
555 var
557 begin
560 else
561 begin
568 // íàéòè ïðîèçâîäèòåëÿ äëÿ ýòîãî ôàéëà (åñëè ôàéë óæå îòêðûò).
569 // onlyPerm: òîëüêî "ïîñòîÿííûå" ïðîèçâîäèòåëè.
571 var
574 begin
577 begin
579 begin
582 begin
584 begin
586 exit;
595 // íàéòè èíôó äëÿ ýòîãî òîìà.
596 // õîðîøåå èìÿ, ïðàâäà? %-)
598 begin
601 begin
603 begin
611 // adds '/' too
613 var
615 begin
619 begin
621 begin
623 continue;
626 begin
628 end
629 else
630 begin
639 var
641 begin
644 begin
646 begin
647 // avoid unnecessary string changes
654 var
657 begin
659 repeat
667 { TVolumeInfo }
669 var
672 begin
679 // òèïà ìóñîðîñáîðíèê: åñëè íàø ïîòîê áîëåå íèêåì íå þçàåòñÿ, òî óãðîáèòü åãî íàôèã
681 begin
685 begin
687 begin
690 begin
703 { TOwnedPartialStream }
706 begin
713 var
715 begin
718 begin
721 begin
724 begin
725 {$IFDEF SFS_VOLDEBUG}writeln('001: destroying volume "', TVolumeInfo(volumes[f]).fPackName, '"');{$ENDIF}
733 { TSFSFileInfo }
735 begin
746 begin
752 { TSFSVolume }
754 begin
762 var
766 begin
773 begin
775 // normalize name & path
782 begin
783 // split path and name
795 begin
802 begin
807 begin
809 else
810 begin
813 begin
816 begin
826 begin
831 begin
833 else
834 begin
841 var
844 begin
846 // normalize name, find split position
850 begin
863 { TSFSFileList }
865 var
867 begin
877 var
879 begin
883 // óáü¸ì çàïèñü, åñëè îíà âðåìåííàÿ, è â íåé íåò áîëüøå íè÷åãî îòêðûòîãî
884 if (gcdisabled = 0) and not TVolumeInfo(volumes[f]).fPermanent and (TVolumeInfo(volumes[f]).fOpenedFilesCount < 1) then
885 begin
886 {$IFDEF SFS_VOLDEBUG}writeln('002: destroying volume "', TVolumeInfo(volumes[f]).fPackName, '"');{$ENDIF}
893 begin
898 begin
905 var
907 begin
916 var
919 begin
924 begin
932 function SFSAddDataFileEx (dataFileName: AnsiString; ds: TStream; top, permanent: Integer): Integer;
933 // dataFileName ìîæåò èìåòü ïðåôèêñ òèïà "zip:" (ñì. âûøå: IsMyPrefix).
934 // ìîæåò âûêèíóòü èñêëþ÷åíèå!
935 // top:
936 // <0: äîáàâèòü â íà÷àëî ñïèñêà ïîèñêà.
937 // =0: íå ìåíÿòü.
938 // >0: äîáàâèòü â êîíåö ñïèñêà ïîèñêà.
939 // permanent:
940 // <0: ñîçäàòü "âðåìåííûé" òîì.
941 // =0: íå ìåíÿòü ôëàæîê ïîñòîÿíñòâà.
942 // >0: ñîçäàòü "ïîñòîÿííûé" òîì.
943 // åñëè ds <> nil, òî ñîçäà¸ò ñáîðíèê èç ïîòîêà. åñëè ñáîðíèê ñ èìåíåì
944 // dataFileName óæå çàðåãèñòðèðîâàí, òî ïàäàåò íàôèã.
945 // âîçâðàùàåò èíäåêñ â volumes.
946 // óìååò äåëàòü ðåêóðñèþ.
947 var
955 begin
958 begin
959 // ðåêóðñèâíîå îòêðûòèå.
960 // ðàçîáü¸ì dataFileName íà èìÿ ñáîðíèêà è îñòàòîê.
961 // pfx áóäåò èìåíåì ñáîðíèêà, dataFileName -- îñòàòêîì.
963 // ñíà÷àëà îòêðîåì ïåðâûé ñïèñîê...
965 // ...òåïåðü ïðîäîëæèì ñ îñòàòêîì.
966 // óçíàåì, êàêîå ôàéëî îòêðûâàòü.
967 // âûêîâûðÿåì ïåðâûé "::" ïðåôèêñ (ýòî áóäåò èìÿ ôàéëà).
970 // dataFileName õðàíèò îñòàòîê.
971 // èçâëå÷¸ì èìÿ ôàéëà:
973 // îòêðîåì ýòîò ôàéë
975 try
978 except
980 // óäàëèì íåèñïîëüçóåìûé âðåìåííûé òîì.
981 if (gcdisabled = 0) and not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[result] := nil;
984 // óðà. îòêðûëè ôàéë. êèäàåì â âîçäóõ ÷åï÷èêè, ïðîäîëæàåì ðàçâëå÷åíèå.
986 try
989 except
993 exit;
996 // îáûêíîâåííîå íåðåêóðñèâíîå îòêðûòèå.
1001 begin
1008 exit;
1018 try
1020 begin
1025 try
1028 except
1034 except
1040 try
1042 begin
1045 end
1047 except
1063 function SFSAddSubDataFile (const virtualName: AnsiString; ds: TStream; top: Boolean=false): Boolean;
1064 var
1066 begin
1068 try
1072 except
1078 var
1080 begin
1081 try
1085 except
1091 var
1093 begin
1094 try
1098 except
1106 var
1109 begin
1112 begin
1123 begin
1128 end
1134 var
1142 // ïðîâåðèì, åñòü ëè ôàëî fn ãäå-òî íà äèñêàõ.
1143 var
1146 begin
1153 begin
1159 try
1161 exit;
1162 except
1167 begin
1174 begin
1175 // ïðåôèêñîâàíûé ôàéë
1177 begin
1185 try
1188 except
1190 if (gcdisabled = 0) and not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil;
1193 exit;
1195 //Inc(vi.fOpenedFilesCount);
1197 exit;
1200 // íåïðåôèêñîâàíûé ôàéë
1202 begin
1206 // èùåì ïî âñåì ïåðìàíåíòíûì ïðåôèêñàì
1209 begin
1212 begin
1214 begin
1217 begin
1218 try
1221 //Inc(vi.fOpenedFilesCount);
1222 except
1236 begin
1237 try
1239 except
1245 var
1248 begin
1252 try
1254 except
1255 exit;
1259 try
1261 except
1262 if (gcdisabled = 0) and not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil;
1267 initialization
1270 //finalization
1271 //volumes.Free(); // it fails for some reason... Runtime 217 (^C hit). wtf?!
1272 //factories.Free(); // not need to be done actually...