Einmal Market „mit alles“ für’s Adam

Der Market von Android hat (neben einigem Carrier-Nerv) die an sich tolle Funktion, Software, die bestimmte Hardware als erforderlich markiert zu filtern, falls diese auf dem aktuellen Gerät nicht verügbar ist. Der Market (genau genommen der Server) prüft dann, ob diese existiert und entscheidet, ob ein Eintrag angezeigt wird oder nicht.

Ich sage „an sich toll“, weil das auch schiefgehen kann: nämlich dann, wenn das Gerät Hardware hat, die Market-App das aber nicht weiß. So ist es auf dem Notion Ink Adam mit Stock-ROM mit dem GPS – das führt dazu, dass jede Anwendung die GPS obligatorisch anfordert nicht sichtbar ist, obwohl die Hardware eigentlich verfügbar ist. Populäre Beispiele: GPS Info-Tools, Osmand, Dolphin HD und andere.

Continue reading „Einmal Market „mit alles“ für’s Adam“

Minecraft in spielbar

Seit heute gibt’s das inoffizielle 1.9-Prerelease. Und wieder mal ist Minecraft 20% langsamer geworden, jetzt bin ich bei 7fps. Nicht spielbar also.

Wie gut also, dass es Leute wie sp614x gibt, die sich mal die Mühe gemacht haben und notch’s Gefrickel etwas aufräumen. Ergebnis des ganzen ist OptiFine, ein Mod, der bei mir Wunder bewirkt hat. Noch ist der leider nicht auf 1.8 (und erst recht nicht auf 1.9) portiert, aber die Ergebnisse bei 1.7.3 sprechen für sich.

acraft-stadt
Als „Testbild“ hab ich die Aussicht von meinem Dachausgang über die Hauptstadt auf der Map von ACraft genommen. Normalerweise bekomme ich hier nur auf „Short“ eine spielbare Framerate von um die 20fps. Mit OptiFine kann ich problemlos „Far“ verwenden, denn hier bekomme ich statt 10fps glatte 25fps!

Sollte ein ähnlicher Effekt auch messbar sein, wenn der Mod auf 1.9 portiert wurde, könnte man fast sagen: Minecraft wird spielbar 🙂

Jetzt heißt es nur noch: warten auf eine neue Version. Keine Ahnung, wie schnell der Programmierer ist, aber selbst Bukkit haben ja nun 2 Wochen gebraucht. Könnte also noch eine Weile dauern…

Trust in Me!

Nehmen wir mal an, ein (Delphi-)object, welches eine Liste gleicher Objekte verwalten soll. Man könnte das einen Baum nennen.

  TValueList = array of TValue;
  TValue = object
    FList: TValueList;
    SomeOtherStuff: integer;
  end;

(Für später geborene: das ist die alte Syntax für „record mit Methoden“.)

Leider nimmt uns der Compiler das so nicht ab. Warum? Weil TValue natürlich vorher noch nicht bekannt ist. Und im Gegensatz zu Klassen kann man Records nicht vorwärtsdeklarieren. Wie also könnten wir dieses Problem lösen? Man könnte Pointer verwenden, und bei Verwendung entsprechend erzeugen. Aber freigeben? Es gibt ja keinen Destructor, also keine Option. Man könnte das gesamte Objekt in eine Klassen-Instanz verwandeln, die von IUnknown die Referenzzählung erbt. Das ist aber wesentlich mehr Aufwand als das bisherige „Deklarieren und Verwenden“.

Die Lösung hier zeigt sich in einem fiesen, aber einfachen Hack:

TValueListAlias = array of boolean;
TValue = object
  FList: TValueListAlias;
  SomeOtherStuff: integer;
end;
TValueList = array of TValue;

Dabei spielt es keine Rolle, welchen Basistypen man für das Alias verwendet. Es muss sich nur um ein dynamisches Array handeln. Jetzt kann man in jedem Zugriff auf das eigentliche TValueList casten und alles funktioniert. Dabei kann man die Hässlichkeiten wunderbar in Zugriffsmethoden verbergen.

SetLength(TValueList(FList), 42);

Das funktioniert auch, aber wenn TValue nun Felder die finialisiert werden müssen enthält, wird sich FastMM beschweren, dass eben diese nie freigegeben werden. Es stellt sich heraus, dass SetLength zwar den BaseType richtig speichert und auch in @DynArrayClear exakt diesen wieder beräumt, aber Felder im Gegensatz zu Variablen nicht damit, sondern mit FinalizeArray freigegeben werden. Und dieses richtet sich nach der Deklaration, nicht nach dem was wirklich passiert.

Hier kommt dann der eigentliche Grund für diesen Beitrag: wir sagen der RTL einfach: „Vertrau mir, das ist ein anderer Datentyp!“. Mit anderen Worten: wir patchen die Feld-Tabelle der betroffenen Klasse so, dass FinalizeArray dort ein TValueList (BaseType also TValue, nicht unser Dummy von weiter oben) sieht und dieses korrekt finalisiert.

Das ist zunächst einfacher als gedacht:

TI:= TypeInfo(TValue);
FT := Pointer(Integer(TI) + Byte(TI.Name[0]));
for I := FT.Count-1 downto 0 do begin
  if FT.Fields[I].TypeInfo^ = TypeInfo(TValueListAlias) then
    ppti:= FT.Fields[I].TypeInfo^:= TypeInfo(TValueList);
end;

Nur – das funktioniert so nicht 😉 Runtime Error 216 ist die Folge einer Exception beim Schreiben der neuen TypeInfo. Da hat nämlich mal jemand mitgedacht und die dazugehörige Seite als PAGE_EXECUTE_READ markiert. Was man zum Glück in seinem eigenen Prozess beliebig ändern kann, und so ergibt sich (mit allen Deklarationen) folgendes Meisterwerk:

procedure FixFieldTable(TheRecord, Find, Replace: PTypeInfo);
type
  TFieldInfo = packed record
    TypeInfo: PPTypeInfo;
    Offset: Cardinal;
  end;
 
  PFieldTable = ^TFieldTable;
  TFieldTable = packed record
    X: Word;
    Size: Cardinal;
    Count: Cardinal;
    Fields: array [0..0] of TFieldInfo;
  end;
var
  FT: PFieldTable;
  ppti: PPTypeInfo;
  I, old, dummy: cardinal;
begin
  FT := Pointer(Integer(TheRecord) + Byte(TheRecord.Name[0]));
  for I := FT.Count-1 downto 0 do begin
    if FT.Fields[I].TypeInfo^ = Find then begin
      ppti:= FT.Fields[I].TypeInfo;
      VirtualProtect(ppti,SizeOf(ppti), PAGE_READWRITE, old);
      try
        ppti^:= Replace;
      finally
        VirtualProtect(ppti, sizeof(ppti), old, dummy);
      end;
    end;
  end;
end;
{...}
 
  FixFieldTable(TypeInfo(TValue), TypeInfo(TValueListAlias), TypeInfo(TValueList));

Wer sich die Datenstruktur ansieht, wird feststellen dass TFieldTable ein Feld namens „Size“ enthält. Sollten wir das nicht anpassen? Die einfache Antwort ist: nein! In TValue selbst wird vom dynamischen Array nur ein Pointer auf den Anfang gespeichert, und der ist unabhängig vom genauen Aussehen des Arrays immer gleich groß: nämlich 32bit oder 4 Byte. Es ändert sich also nichts.
Außerdem praktisch: da SetLength den Array-Typ anlegt, den man ihm übergibt (und wir ja auf TValueList casten), werden auch untergeordnete Arrays als das freigegeben was in ihnen steckt.

Das funktioniert so bis mindestens BDS2006, ich kann mir aber vorstellen dass die neuen RTTI-Strukturen in XE hier einige Änderungen notwendig machen. Das müssen dann aber andere testen 😉

Oh, übrigens, wer es nicht erkannt hat: der Titel ist natürlich eine Referenz auf Disney’s Dschungelbuch.

Old package is old

Mal was aus der Sysadmin-Ecke 😉

Der Grund, warum ich auch für Server gerne Ubuntu statt Debian verwende, ist ja eigentlich die Idee, vom schnelleren Patch-Zyklus bei Ubuntu zu profitieren und nicht monatelang auf alter Software festzusitzen.

Und was passiert? Man sitzt auf jahrealter Software fest.

Verwendet wird hier ein 10.04.3 LTS Server und konkret problematisch war das Paket „smartmontools“. Das hat nämlich ein Problem: wenn die Platte (hier: die SSD) neuer ist als die DriveDB hat man schlechte Karten, was die Attribute betrifft. Und wenn es dann auch noch so alt ist, dass man diese nicht separat aktualisieren kann (das geht seit Ende 2009), dann ist man aufgeschmissen.
Genau das war hier der Fall. In den Lucid-Repos findet sich nur die Version 5.38 von 2008. Insgesamt scheint man nicht sehr Update-freudig gewesen zu sein, erst in den Repos für 11.04 findet sich eine Aktuelle Version: 5.41.

Wenn man dann dieses Paket nimmt (ja, alle Abhängigkeiten sind auf einem aktuellen lucid auch erfüllt) und manuell aktualisiert, funktioniert alles:

root@srv2:/root# wget http://mirror.netcologne.de/ubuntu//pool/main/s/smartmontools/smartmontools_5.41+svn3365-1_amd64.deb
root@srv2:/root# dpkg -i smartmontools_5.41+svn3365-1_amd64.deb

Und schon erfahre ich endlich, wie die SSD denn so damit lebt, Systemplatte zu sein. Gleich mit netten von Munin gemalten Diagrammen 😉

Punkteverteilung

Manchmal, nur ganz selten, verhält sich ein Code anders als man denkt, weil man was anderes programmiert hat als man denkt.

Man nehme z.B. eine KI mit Negamax+AlphaBeta, die immer irgendwelche völlig behämmerten Züge ausführt. Irgendwann ist mir dann mal gedämmert, dass man sich ja mal die Score-Werte ausgeben lassen könnte.

next move
  h1 to g1 score: 9
  h1 to g2 score: 9
  h1 to i2 score: 9
  h1 to h2 score: 9
  h1 to f1 score: 9
  h1 to f3 score: 9
  h1 to h3 score: 9
  i1 to h2 score: 9
  i1 to i2 score: 9
  i1 to g1 score: 9
  i1 to g3 score: 9
  i1 to i3 score: 9
  a9 to b8 score: 9
  a9 to b9 score: 9
  a9 to a8 score: 9
  a9 to c7 score: 9
  a9 to c9 score: 9
  a9 to a7 score: 9

Huch! Alles das gleiche. Wie kann das sein?

    function Score(ABoard: TBoard; Perspective: TField): TScore;
{...}
          if Board[c,r]=Perspective then
{...}

M( M( M( M( M( M( M(

Board ist ein Member von TGame, und damit an dieser Stelle durchaus ein gültiger Bezeichner. Nur leider nicht der den ich will, denn damit habe ich immer das Brett vom Startpunkt bewertet, niemals das nach einem Zug. Sehr schön, es sollte mal einer intelligente Compiler entwickeln, die mir sowas direkt sagen…

Stand back – I know CMD!

Ich merke das schon, Psychologie läuft nicht so gut wie Programmierung und Politik…

gnuplot hält sich für ein sehr gutes Diagramm-Plot-Programm – und das stimmt meistens auch. Wenn es darum geht, Daten aus Dateien einzulesen wird das Skript zwar sehr schnell sehr hässlich, aber es funktioniert. Zumindest, wenn man die Daten vorkocht, denn filtern kann man sie in gnuplot selbst nicht mehr vernünftig. Das ist aber grade dann von Nöten, wenn man lange Messreihen hat, aber zum Beispiel nur an den letzten N Messwerten interessiert ist. Eigentlich doch ein nicht grade seltenes Problem, aber fertige Lösungen jenseits von `tail -n` sind nicht wirklich auffindbar.
Continue reading „Stand back – I know CMD!“

Kleine Hacks erhalten die Freundschaft

Heute mal wieder was aus der Bastelkiste:

Unser Institut für Mechanik hat für Belege und Praktika ein schickes kleines Delphi-Programm, welches die Aufgabenblätter generieren kann. Das ganze hat den Zweck, dass die Aufgaben sich mit der Matrikelnummer personalisieren lassen (RandSeed) und man seine berechneten Werte mittels dieses Programms direkt auf Korrektheit prüfen lassen kann.

Nun sind die Aufgaben nicht immer so ganz ohne, und manchmal hilft es ungemein, nicht nur Eingabefelder zu haben, die einem sagen ob man richtig oder falsch lag, sondern auch mal spicken zu können, wo denn der Fehler liegt.

Aber erstmal eine kleine Anekdote.
Man sitzt also in einer Vorlesung, und macht nebenbei mit einem Kommilitonen ein solches Übungsbelegblatt. Die Gleichungen sehen auch erstmal alle ganz schön aus. Nun hat man ja einen Laptop dabei, kann also die Ergebnisse kontrollieren. Und siehe da: nix passt.
Was tut man also? Man versucht herauszubekommen, was das Programm denn als Zahlenwert erwartet. Irgendwie dachte ich mir (warum, weiß ich auch nicht; vielleicht wars ja Schicksal 😛 ) dass mir ein Hex-Editor weiterhelfen könnte. Konnte er natürlich nicht, hat aber soweit geholfen, als dass ich festgestllt habe, dass es zu jedem Aufgabenblatt 2 Definitionen gibt. Eine generiert die Aufgabenstellung, und eine generiert… Moment. Was ist das? Ein Lösungsweg!
Es gibt also nicht nur 2 Programmversionen (wie schon das _student im Dateinamen vermuten lässt), sondern zu allem Überfluss enthält die selbe Echse offenbar beide Teile. Irgendwo muss also umgeschaltet werden, was gemacht werden soll.

Nachdem man seinen IDA Pro wiedergefunden und gestartet hat, wird also mal das Programm hineingeworfen. Etwas warten, und die Programmstelle wieder finden. Das macht sich recht einfach, immerhin ist der Titel des Belegs ein sehr eindeutiges Merkmal (im Code-Segment jedenfalls). Nun muss man nur noch die XRefs zurückverfolgen, um rauszubekommen wo der wichtige Aufruf erfolgt. Der ist nämlich da, wo basierend auf einem Vergleich der eine oder andere Zweig (=Ausgabeanweisungen) aufgerufen wird. It always boils down to a simple comparision.
Der letzte Schritt (wo allerdings leider die Vorlesung zuende war; der Teil mit dem „IDA wiederfinden“ hatte etwas lange gedauert) ist es nun nur noch, die Zuweisung an die überprüfte Variable zu finden. Auch hier hilft wieder die XRefs-Funktion. Es gibt nämlich nur eine, und die schreibt eine $00, also den Ordinalwert für false. Man könnte also sagen, die betreffende Variable ist sowas wie IsTutorenVersion.
Kurzer Patch in der Exe der (wir wissen ja: es ist Delphi) eine $01 (für true) stattdessen schreibt, und fertig.

Wer das liest und ein berechtigtes Interesse hat, der hat auch Wege mich nach dem Teil zu fragen. Das ist nun grade ein Hack, bei dem ich garantiert kein Responsible Disclosure machen werde. Das wäre nämlich sehr verantwortungslos. Mir selbst gegenüber 😀

Das wars erstmal von meiner Seite,
Martok

How to not be evil [Update]

Wer kennt sie nicht, die beliebte Funktion eval()? Chrome zum Beispiel 😀

Das heißt auch, dass die bisher im Edgemonkey verwendete Kombination von eval/uneval zur (de)serialisierung von Einstellung und Speicherung dieser so in Chrome nie funktionieren wird (selbst wenn später einmal eine GM_setValue-Alternative zur Verfügung steht – stay tuned). Es muss also jedes Vorkommen davon durch die eigentlich viel schöneren und moderneren JSON.parse und JSON.stringify ersetzt werden. Was aber nicht so einfach ist, da die Migration bisheriger Daten natürlich funktionieren muss.
Was gar nicht so einfach ist – uneval erzeugt kein JSON, sondern nur etwas was so ähnlich aussieht!

Man muss also etwas tun, um die Daten in valides JSON zu konvertieren. Was nicht einfach geht, ohne uneval. Bleibt also nur, den String als solchen zu bearbeiten. And here’s how:
Continue reading „How to not be evil [Update]“

Hackfox – Foxhacks

Viele von denen, die hier lesen dürften mein Projekt EdgeMonkey kennen. Für die paar, die es nicht tun: es handelt sich um ein Userscript für Greasemonkey, welches die Entwickler-Ecke verschönert.
Jedenfalls: viele der dort eingesetzten Techniken waren schon immer Bleeding Edge, was Userscripts angeht. Deswegen wirds da auch schnell mal blutig, wenn sich irgendwo etwas ändert.

So ist das bereits mehrmals passiert – die neue Sandbox der Greasemonkey 0.8-Reihe war wohl das offensichtlichste. Aber das war ja noch einfach zu umgehen. Richtig spaßig ist das aber erst viel später auf Firefox 4 geworden, welcher mich auch dazu gebracht hat die hier besprochene Thematik mal genauer zu analysieren.
Continue reading „Hackfox – Foxhacks“