Ver Mensaje Individual
  #8  
Antiguo 18-05-2007
Avatar de seoane
[seoane] seoane is offline
Miembro Premium
 
Registrado: feb 2004
Ubicación: A Coruña, España
Posts: 3.717
Reputación: 24
seoane Va por buen camino
Bueno, sigo con el tema. Ahora que se que alguien lo lee

Vamos a sacrificar un poco de rapidez a cambio de ganar sencillez, y evitar además tener que guardar una imagen del disco. Ahora leemos directamente la información del disco y la analizamos, todo en memoria.
Código Delphi [-]
program recover;

{$APPTYPE CONSOLE}

uses Windows, SysUtils, Classes;

const
  // Aquí hacemos una estimación del tamaño del cluster.
  // Siempre tendrá que ser un múltiplo de 512 (1024, 4096, ...)
  // Cuanto mayor sea el valor que utilicemos, mas rápido sera el análisis. 
  // Pero si es demasiado grande, el análisis puede fallar. 
  // Así que por ahora lo dejamos en el valor mas pequeño posible.
  ClusterSize = 512; 

procedure Intento(Origen: TStream; FormatStr: String; var Index: Integer);
var
  Destino: TMemoryStream;
  Buffer: array[0..$FFFF] of Byte;
  Header: array[1..4] of Byte;
  Size: Integer;
begin
  Destino:= TMemoryStream.Create;
  try
    // Escribimos los 2 primeros bytes del archivo
    Header[1]:= $FF;  Header[2]:= $D8;
    Destino.WriteBuffer(Header,2);
    // En este bucle vamos copiando segmento a segmento
    while TRUE do
    begin
      Origen.ReadBuffer(Header,4);
      Size:= ((Header[3] shl 8) + Header[4]) - 2;
      case Header[2] of
        $01,$D0..$D8: begin
                        // Estos segmentos solo miden dos bytes,
                        // asi que escribimos 2 bytes y
                        Destino.WriteBuffer(Header,2);
                        // retrocedemos otros dos hacia atras.
                        Origen.Seek(-2,soFromCurrent);
                      end;
        $D9: begin
               // Este indica que llegamos al final del archivo jpeg
               Destino.WriteBuffer(Header,2);
               // Guardamos el archivo usando la plantilla
               Destino.SaveToFile(Format(FormatStr,[Index]));
               Writeln('Recuperada: ' + Format(FormatStr,[Index]));
               // Incrementamos el indice
               inc(Index);
               // Salimos del procedure
               Exit;
             end;
        $DA: begin
               // Este segmento no tiene tamaño
               Destino.WriteBuffer(Header,2);
               Origen.Seek(-2,soFromCurrent);
               // Buscamos el final byte a byte
               while TRUE do
               begin
                 Origen.ReadBuffer(Buffer,1);
                 // Posible comienzo de otro segmento
                 if Buffer[0] = $FF then
                 begin
                   Origen.ReadBuffer(Buffer,1);
                   // $FF00 = Secuencia de escape
                   // $FFD0 .. $FFD7 = Se deben de ignorar
                   if (Buffer[0] <> 0) and not (Buffer[0] in [$D0..$D7]) then
                   begin
                     // Si encontramos el comienzo de otro segmento,
                     // retrocedemos dos bytes, y volvemos al bucle principal
                     Origen.Seek(-2,soFromCurrent);
                     break;
                   end else
                   begin
                     // Si no es el comienzo de otro segmento, escribimos
                     // y continuamos
                     Header[1]:= $FF;
                     Header[2]:= Buffer[0];
                     Destino.WriteBuffer(Header,2);
                   end;  
                 end else
                   Destino.WriteBuffer(Buffer,1);
               end;
             end
        else
        begin
          // Cualquier otro segmento lo copiamos directamente
          Origen.ReadBuffer(Buffer,Size);
          Destino.WriteBuffer(Header,4);
          Destino.WriteBuffer(Buffer,Size);
        end;
      end;
    end;
  finally
    Destino.Free;
  end;
end;

procedure Scan(Stream: TStream; FormatStr: String);
var
  Buffer: PChar;
  Indice: Integer;
  Leidos: Integer;
  MemStream: TMemoryStream;
  Posicion: int64;
begin
  Indice:= 0;
  GetMem(Buffer,ClusterSize);
  try
    Leidos:= Stream.Read(Buffer^,ClusterSize);
    while Leidos > 0 do
    begin
      // Buscamos la secuencia que indica el comienzo de un archivo jpeg
      if CompareMem(Buffer,PChar(#$FF#$D8#$FF),3) then
      begin
        Posicion:= Stream.Position;
        try
          // Cargamos en un stream 8 Megas para su analisis
          MemStream:= TMemoryStream.Create;
          try
            MemStream.Write(Buffer[2],Leidos - 2);
            // Si creemos que hay imagenes de mas de 8 megas, cambiaremos este valor
            MemStream.CopyFrom(Stream,8*1024*1024);
            MemStream.Position:= 0;
            Writeln('Encontrada una posible imagen.');
            Intento(MemStream,FormatStr,Indice);
          finally
            MemStream.Free;
          end;
        except
          // Si se produce un error lo mostramos, pero no salimos del bucle
          On E: Exception do
             Writeln('Error: ' + E.Message);
        end;
        Stream.Position:= Posicion + ClusterSize;
      end;
      Leidos:= Stream.Read(Buffer^,ClusterSize);
    end;
  finally
    FreeMem(Buffer);
  end;
end;

var
  Stream: TFileStream;
  Marca: TDateTime;
begin
  try
    // Necesitamos 2 parametros
    if ParamCount = 2 then
    begin
      // Comprobamos que se pueden crear archivos en la ruta indicada
      TFileStream.Create(Format(ParamStr(2),[0]),fmCreate).Free;
      // Borramos el archivo de prueba
      DeleteFile(Format(ParamStr(2),[0]));
      Stream:= TFileStream.Create(ParamStr(1),fmOpenRead or fmShareDenyNone);
      try
        // Si llegamos hasta aqui podemos leer y escribir
        Marca:= Now;
        Scan(Stream,ParamStr(2));
        Writeln('Tiempo transcurrido: ' + TimeToStr(Now-Marca));
      finally
        Stream.Free;
      end;
    end else
      // Si no nos pasan dos parametros, imprimimos una pequeña ayuda
      Writeln('Help: ' + ExtractFilename(ParamStr(0)) + '  ');
  except
    On E: Exception do
      // Si algo sale mal, escribimos la descripcion del error
      Writeln('Error: ' + E.Message);
  end;
end.

Por ejemplo:
Código:
recover \\.\F: c:\temp\%d.jpg
Tarda un poco en realizar el análisis de todo el disco, por ejemplo, para un disco de Usb de 128 Megas tarda unos 5 Minutos. Aunque puede que sea menos si el dispositivo es rápido, y ajustamos mejor el tamaño del cluster.
Responder Con Cita