У меня эти tbt и tcht в ходе анализа и преобразований используются тысячи раз, и что, мне эти тысячи раз гонять процедуры преобразования типов? Да ну, ерунда какая-то. Чем мне в этой задаче поможет Вирт? По моему, в данном случае вариантная запись - самый эффективный способ обработки содержимого бинарного файла скриптов.
А то есть как union
в C++
? Ну, во всём .Net нету его, но есть костыльная замена через атрибуты:
uses System.Runtime.InteropServices;
type
[StructLayout(LayoutKind.&Explicit)]
r1=record
public [FieldOffset(0)] int:integer;
public [FieldOffset(0)] float:single;
end;
begin
var a:r1;
a.int := 5;
writeln(a.float);
end.
В делфи это должно работать, наверное, а фри паскаль, PascalABC (не путать с PascalABC.Net, на форуме которого вы находитесь) уже давно сдохли, вряд ли у них что то такое продвинутое есть.
Найти в книжке ещё сложнее чем в интернете)))
Там есть такой подраздел ))) Ну и книжка в Интернете есть сама.
А что за игра, если не секрет? Если хотите - можете ещё выложить в проекты на PascalABC.Net если переведете на этот язык.
Не переведет, если не мыслит себе жизни без вариантных записей. Блин, как я почти полвека активного программирования без них прожил?
Кстати, в PascalABC.Net
тип char
занимает 2 байта, из за того что кодировка у него Unicode. Поэтому вторая переменная должна иметь тип word
а не byte
. Но, в .Net этот перевод вам вряд ли понадобится, потому что можно делать так:
begin
var ch := 'g';
var f := System.IO.File.Create('my file.txt');
var bw := new System.IO.BinaryWriter(f);
var tw := new System.IO.StreamWriter(f,System.Text.Encoding.UTF8);//допустим вы всё же хотите сохранять по 1 байту на символ, значит UTF8
bw.Write(new byte[](//Можно записывать массив байтов (или по 1 байту) через BinaryWriter
$01,
$23,
$45,
$67,
$89
));
bw.Write(byte(5));//А вот так по 1 байту (это, конечно, медлинее если у вас уже готовый массив)
bw.Write(5);//И числа тоже можно. На integer выделяется 4 байта, поэтому эта строчка добавит 4 байта в поток
tw.Write(ch);//Можно записывать текст (или по 1 символу) через StreamWriter. При этом используется указаная раньше кодировка.
//А стандартная кодировка, в котой сохранён символ в переменной ch это System.Text.Encoding.Unicode
tw.Write(' test');
f.Close;
end.
Точно, именно union, подзабыл термин.
Жаль! И даже странно. Спасибо, посмотрю эти костыли.
В дельфи, краем глаза нагуглил, есть тип Variant, буду дальше изучать проблему. Просто для моих консольных задач хотелось что-нибудь попроще этого монстра Дельфи
Извините, я про “неэффективную” реализацию.
Ну так я и прошу совета, как легко перейти от них, чтоб в каждой строчке кода не гонять типы туда-сюда
Нет, ну такое и в .Net есть:
begin
var o:object;
writeln(o);//nil, то есть ничего
o := 5;
writeln(o);//5
o := 'g';
writeln(o);//g
end.
Но вы не сможете так запечатать в o
char
, а получить назад word
. И я подозреваю что в делфи тоже. И про делфи я имел в виду что тот же костыль будет работать (наверное) потому что в делфи тоже есть атрибуты.
Ну не зря же в .NET Framework от таких типов отказались.
Их не оказалось не из за неэффективности, а из за безопасности. Типа это не безопасно читать значение типа single
из переменной типа integer
.
Можно сделать безопасно, но реализация такой системы как раз и порождает большие накладные расходы, т.е. в конечном счете делает код неэффективным. А надобность в этой экзотике очень спорна. Я нахлебался когда-то досыта этих чудес с фортрановским EQUIVALENCE.
Спасибо! Но это всё равно означает, что придётся полностью переделывать весь имеющийся код. Гигантская работа, и как говорится: “овчинка выделки не стоит”. Придётся видимо под ДОСом оставаться
Старую собаку не научишь новым штукам.
Может, проще это пристрелить, чтобы не мучиться? ))
А разве union так часто нужен?
В моих проектах переменные этого типа почти в каждой строчке кода (выше писал).
Нет, это я услышал, но зачем? Мне за всё время сколько я программировал - такое меньше 10 раз понадобилось…
Компилятор/декомпилятор. В исходном бинарном файле всё вперемешку же. Примерно так: Из этого
Z*level5.mazД°party has gained 800 points of experience.
ю ю8·ш
ю юs·їыю юА·їёїЁёїЁёїЁёЎaуЎ
у ўу
получть это
[Part 1]
{1A2A} ;Смещение, с которого начинается Part 5 (разумеется за вычетом -0Ah)
"level5.maz", 00, 00 ;Maze name (12 байт, после первого $00 байты игнорируются)
"blue", 00, "5.maz", 00, 00 ;VCN/VMP name (12 байт, после первого $00 байты игнорируются)
"blue", 00, "5.maz", 00, 00 ;Palette name (12 байт, после первого $00 байты игнорируются)
02, 00, FF, 03 ;Nb of doors and doorknobs (???)
02, 00, 00, 32, 00 ;Max monster / tdiff etc (???)
01 ;Number of procedure for extraction of monster picture (???)
"dwarf", 00, ".maz", 00, 00 ;Name Of Monster pic CPS file (12 байт, после первого $00 байты игнорируются)
02 ;Number of procedure for extraction of monster picture (???)
"spider", 00, "maz", 00, 00 ;Name Of Monster pic CPS file (12 байт, после первого $00 байты игнорируются)
00, 19 ;Timer0 | Timer 1| Timerdiff (in Ticks)
01, 0F ;Timer0 | Timer 1| Timerdiff
FF ;End of Timer
А потом, после редактирования, скомпилировать назад в бинарный файл.
Ну, вот участок декомпилятора (переменные ubd и uchd):
with UDest do begin
{Алгоритм bytes2string
b1 - текущая позиция в строке (от 0);
b2 - 0: предыдущий символ был не буквой, 1: предыдущий символ был буквой}
b1:=0;for j:=1 to 12 do begin
read(fn1b,ubd);
case ubd of
0..31,127,176..223, 242..255: begin
if b1=0 then write(fn2,dec1hex(ubd))
else
if b2=0 then write(fn2,', '+dec1hex(ubd))
else write(fn2,'", '+dec1hex(ubd));
b2:=0 end
else begin
if b1=0 then begin write(fn2,'"'+uchd);if uchd='"' then write(fn2,'"') end
else
if b2=0 then begin write(fn2,', "'+uchd);if uchd='"' then write(fn2,'"') end
else begin write(fn2,uchd); if uchd='"' then write(fn2,'"') end;
b2:=1 end
end;{case}
inc(b1);
end;{for j}