📄 bffio.m
字号:
function [varargout] = bffio(varargin)
% bffio - read/write binary file with BFF spec (and content)
%
% FORMAT: bffcont = bffio(filename, bffspec [, options])
% OR bffio(wfilename, bffspec, bffcont [, options])
%
% Input fields:
%
% filename filename of binary file to read
% bffspec either filename or content or spec struct of BFF
% options optional arguments, one of
% 'verbose', 'v' or '-v' (for verbose reading/writing)
% 'force', 'f', '-f' (for returning partial content)
%
% wfilename filename of binary file to write
% bffcont file content (struct)
%
% Output fields:
%
% bffcont file contents struct (depending on BFF)
%
% See also bffdocu, bffparse
% Version: v0.7b
% Build: 7083014
% Date: Aug-30 2007, 2:50 PM CEST
% Author: Jochen Weber, Brain Innovation, B.V., Maastricht, NL
% URL/Info: http://wiki.brainvoyager.com/BVQXtools
bffversion = 'v0.7b';
% argument check
if nargin < 2 || ...
~ischar(varargin{1}) || ...
isempty(varargin{1}) || ...
(~isstruct(varargin{2}) && ...
~ischar(varargin{2})) || ...
isempty(varargin{2})
error( ...
'BVQXtools:BadArgument', ...
'Bad or missing argument for bffio.' ...
);
end
filename = varargin{1}(:)';
bffspec = varargin{2}(:)';
bffcont = [];
% default options
forcemode = false;
verbosemode = false;
writemode = false;
% bff content structure given
if nargin > 2 && ...
isstruct(varargin{3}) && ...
~isempty(varargin{3})
bffcont = varargin{3};
writemode = true;
end
% check additional arguments
if nargin > 2
for argc = 3:nargin
% what is given
argv = varargin{argc};
% character argument
if ischar(argv)
% known argument
switch lower(argv)
% force mode
case {'f', 'force', '-f'}
forcemode = true;
% verbose mode
case {'v', 'verbose', '-v'}
verbosemode = true;
end
% discard other arguments/options
end
end
end
% BFF is a valid SPEC struct
if isstruct(bffspec) && ...
~isempty(bffspec) && ...
isfield(bffspec, 'AfterReadCode') && ...
ischar(bffspec.AfterReadCode) && ...
isfield(bffspec, 'BeforeWriteCode') && ...
ischar(bffspec.BeforeWriteCode) && ...
isfield(bffspec, 'EncodingSyntax') && ...
ischar(bffspec.EncodingSyntax) && ...
~isempty(bffspec.EncodingSyntax) && ...
isfield(bffspec, 'Extensions') && ...
iscell(bffspec.Extensions) && ...
isfield(bffspec, 'FilenameMatch') && ...
iscell(bffspec.FilenameMatch) && ...
isfield(bffspec, 'ListOfFields') && ...
isstruct(bffspec.ListOfFields) && ...
isfield(bffspec, 'Loops') && ...
isstruct(bffspec.Loops) && ...
isfield(bffspec, 'Magic') && ...
isstruct(bffspec.Magic) && ...
isfield(bffspec, 'TransIOSize') && ...
isa(bffspec.TransIOSize, 'double') && ...
length(bffspec.TransIOSize) == 1 && ...
isfield(bffspec, 'Variables') && ...
isstruct(bffspec.Variables)
% any other struct is rejected
elseif isstruct(bffspec)
error( ...
'BVQXtools:BadArgument', ...
'Invalid struct BFF specification.' ...
);
% for character arrays
elseif ischar(bffspec)
% valid file contents (0x0a/0x0d delimited)
if ...
any(bffspec == char(10) | bffspec == char(13)) || ...
exist(bffspec, 'file') == 2
% parse content
try
if ~writemode
varargout{1} = bffio( ...
filename, ...
bffparse(bffspec), ...
varargin{:});
else
[varargout{:}] = bffio( ...
filename, ...
bffparse(bffspec), ...
varargin{:});
end
return;
catch
rethrow(lasterror);
end
end
% otherwise
error( ...
'BVQXtools:BadArgument', ...
'Invalid bffspec argument given.' ...
);
end
% initialize rules variables
rule = bffspec.ListOfFields;
rules = length(rule);
% get critical transio size
tiosz = bffspec.TransIOSize;
tiole = bffspec.EncodingSyntax;
% get loops shortcut and prepare loopi struct, loopx
loops = bffspec.Loops;
loopf = fieldnames(loops);
if ~isempty(loopf)
loopf = fieldnames(loops.(loopf{1}));
loopi = cell2struct(cell(0, 0, length(loopf)), loopf, 3);
else
loopi = struct;
end
loopx = {};
% get file extension
[fnamex{1:3}] = fileparts(filename);
fnamex = fnamex{3};
fnamex(fnamex == '.') = [];
% initialize output
if isempty(bffcont)
bffcont = struct;
end
% initialize internal variables
namevars = struct;
namevars.BFFVERSION = bffversion;
namevars.BFFPATH = fileparts(mfilename('fullpath'));
namevars.BFFREAD = ~writemode;
namevars.BFFWRITE = writemode;
namevars.FILENAME = filename;
namevars.EXTENSION = fnamex;
% reading BFF content
if ~writemode
% create transio object template if size suggests it
if ~isinf(tiosz)
try
tioobjt = transio(filename, tiole, 'uint32', 0, [1, 1]);
catch
warning( ...
'BVQXtools:TransIOError', ...
'Error using transio for file ''%s''.', ...
filename ...
);
tiosz = Inf;
end
end
% check file (opening in little endian syntax by default)
fid = fopen(filename, 'r', tiole);
if fid < 1
error( ...
'BVQXtools:FileNotReadable', ...
'File not readable: ''%s''.', ...
filename ...
);
end
% get file size
fseek(fid, 0, 1);
flen = ftell(fid);
fseek(fid, 0, -1);
namevars.FILESIZE = flen;
% writing BFF content
else
% BeforeWriteCode ?
if ~isempty(bffspec.BeforeWriteCode)
try
eval(bff_parsecode( ...
bffspec.BeforeWriteCode(:)', 'namevars', 'bffcont'));
catch
error( ...
'BVQXtools:EvaluationError', ...
'Error raised by BeforeWriteCode: ''%s''.', ...
lasterr ...
);
end
end
% check open writable file
wfilename = [filename '.tmp'];
fid = fopen(wfilename, 'w', tiole);
if fid < 1
error( ...
'BVQXtools:FileNotWritable', ...
'File not writable: ''%s''.', ...
wfilename ...
);
end
% for verbose mode, we also need read access
if verbosemode
% close file (it has been truncated now)
fclose(fid);
% open again with write AND read access
fid = fopen(wfilename, 'w+', tiole);
if fid < 1
warning( ...
'BVQXtools:FileNotReadWritable', ...
'File cannot be opened in ''w+'' mode: ''%s''.', ...
wfilename ...
);
fid = fopen(wfilename, 'w', tiole);
verbosemode = false;
end
% check file id once more, just to make sure
if fid < 1
error( ...
'BVQXtools:FileNotWritable', ...
'File not writable: ''%s''.', ...
wfilename ...
);
end
end
end
% grand loop
rulec = 1; % pointer to next rule to process
loopc = 0; % counter of loops (in which loop are we)
% try used for forcemode (on indentation !!!)
try % forcemode
while rulec <= rules
% get shortcut to current rule to process (and subfields)
prule = rule(rulec);
rtype = lower(prule.type);
rdisk = lower(prule.disktype);
rdsks = prule.disksize;
rdata = lower(prule.datatype);
rcond = bff_parsecode(prule.cond, 'namevars', 'bffcont');
rdim = bff_parsecode(prule.dim, 'namevars', 'bffcont');
% rdefv = bff_parsecode(prule.default, 'namevars', 'bffcont');
rexpr = bff_parsecode(prule.varname, 'namevars', 'bffcont');
% try to resolve dim
if isempty(rdim)
rdim = [1, 1];
elseif ischar(rdim)
try
eval(['rdim=double([' rdim ']);']);
if isempty(rdim)
error('EMPTYDIM');
elseif size(rdim, 2) < 2
rdim = [1 rdim];
end
catch
error( ...
'BVQXtools:BadExpression', ...
'Couldn''t evaluate DIM expression: ''%s''.', ...
prule.dim ...
);
end
end
% what to do
switch (rtype)
% begin of a loop
case {'bloop'}
% check loop var syntax (and extract dim, if needed)
[ldimmatcht{1:3}] = regexpi( ...
rexpr, '^([a-z][a-z_0-9]*)(\(\d+\))?');
ldimmatcht = ldimmatcht{3};
if isempty(ldimmatcht)
error( ...
'BVQXtools:BadBFFSpec', ...
'Invalid LOOP variable name given: ''%s''.', ...
rexpr ...
);
end
lvname = rexpr(ldimmatcht{1}(1, 1):ldimmatcht{1}(1, 2));
if size(ldimmatcht{1}, 1) > 1 && ...
ldimmatcht{1}(2, 2) > ldimmatcht{1}(2, 1)
ldim = str2double(rexpr(ldimmatcht{1}(2, 1):ldimmatcht{1}(2, 2)));
else
ldim = 1;
end
% check whether appropriate entry in Loops.(...) exists
if ~isfield(loops, lvname) || ...
length(loops.(lvname)) < ldim
error( ...
'BVQXtools:BadBFFSpec', ...
'Invalid LOOP variable given (not found: ''%s''.', ...
rexpr ...
);
end
% get specific loop info
loopinfo = loops.(lvname)(ldim);
% check whether the loop should be entered at all
enterloop = true;
if ~isempty(rcond)
enterloop = false;
try
eval(['if ' rcond ',enterloop = true;end']);
catch
error( ...
'BVQXtools:BadExpression', ...
'Couldn''t evaluate COND expression: ''%s''.', ...
rcond ...
);
end
end
% if we're entering the loop
if enterloop
% try to resolve loop dim
try
loopinfo.dim = eval( ...
bff_parsecode(loopinfo.dim, 'namevars', 'bffcont'));
catch
error( ...
'BVQXtools:BadExpression', ...
'Couldn''t evaluate LOOP.DIM expression: ''%s''.', ...
loopinfo.dim ...
);
end
% initialize loop counter
try
eval(['namevars.' rexpr '=1;']);
catch
error( ...
'BVQXtools:BadExpression', ...
'Error initializing LOOP counter ''%s'' to 1.', ...
rexpr ...
);
end
% only truly enter loop if dim is > 0
if loopinfo.dim > 0
% increase loop counter and keep track of loop variable name
loopc = loopc + 1;
loopi(loopc) = loopinfo;
loopx{loopc} = rexpr;
% otherwise
else
% skip loop
rulec = loopinfo.lastrule;
end
% we're not entering the loop
else
% set current rule pointer to end of loop
rulec = loopinfo.lastrule;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -