/*********************************************************************** Copyright 2002 Ben Rudiak-Gould. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA, or visit . ***********************************************************************/ #define WIN32_LEAN_AND_MEAN #include #include #include "DVDSubber.h" #include "DVDSubber-dialog.h" #include "DVDSubber-vobscan.h" #include "DVDSubber-compile.h" #include "../include/dvdsynth-filter.h" #include HINSTANCE g_hinstance; DvsDockingBayGlobal* g_bays; //scsi_result_t ScsiCommand(DvsDeviceKernel* self, const unsigned char* cdb, int cdblen, unsigned char* buffer, unsigned long* pbuflen, int inout, SenseData* sense); /******************************************************************** ********************************************************************/ struct DVDSubberUser : DvsDeviceUser { DVDSubberKernel* k; dvs_driver_handle driver_handle; DvsDockingBay* bay; HANDLE data_file_handle; }; /******************************************************************** ********************************************************************/ unsigned updcrc32(unsigned crc, unsigned char *buf, int len) { static unsigned table[256]; const unsigned poly = 0xEDB88320; if (table[255] == 0) { for (unsigned n = 0; n < 256; n++) { unsigned c = n; for (int k = 0; k < 8; k++) { if (c & 1) { c = poly ^ (c >> 1); } else { c = c >> 1; } } table[n] = c; } } crc = ~crc; for (int n = 0; n < len; n++) { crc = table[(crc ^ buf[n]) & 0xff] ^ (crc >> 8); } return ~crc; } class SlidingBuffer { DvsDeviceKernel* k; unsigned cur_sector; unsigned crc; unsigned char buf[2048]; public: SlidingBuffer(DvsDeviceKernel* _k) { k = _k; cur_sector = ~0U; crc = 0; } unsigned GetDword(unsigned base_sector, unsigned offset); unsigned GetDword(unsigned offset); unsigned GetCumulativeCRC() { return crc; } }; unsigned SlidingBuffer::GetDword(unsigned base_sector, unsigned offset) { base_sector += (offset>>11); offset &= 2047; if (base_sector != cur_sector) { static unsigned char cdb[10] = { 0x28, 0, 0, 0, 0, 0, 0, 0, 1, 0 }; for (int i=0; i<4; ++i) { cdb[5-i] = base_sector >> (i*8); } unsigned long buflen = 2048; SenseData sense; scsi_result_t result = g_bays->KernelScsiCommand(k, cdb, 10, buf, &buflen, 1, &sense); if (result != SCSIRESULT_SUCCESS) { throw result; } cur_sector = base_sector; crc = updcrc32(crc, buf, 2048); } return GetDword(offset); } unsigned SlidingBuffer::GetDword(unsigned offset) { return buf[offset]*16777216 + buf[offset+1]*65536 + buf[offset+2]*256 + buf[offset+3]; } DiscInfo* GetDiscInfo(DvsDeviceKernel* dev, DvsDockingBay* bay) { DvdVideoInfo dvd_video_info; int result = g_bays->GetDvdVideoInfo(dev, &dvd_video_info); if (result < 0) { return 0; } DiscInfo di; SlidingBuffer disc(dev); di.vmg_ifo_sector = dvd_video_info.vmg_ifo_sector; di.vmg_vobs_sector = di.vmg_ifo_sector + disc.GetDword(di.vmg_ifo_sector, 0xC0); di.vmg_bup_sector = di.vmg_ifo_sector + disc.GetDword(0x0C) - disc.GetDword(0x1C); unsigned tt_srpt_sector = disc.GetDword(0xC4); di.vmg_tt_srpt_offset = tt_srpt_sector * 2048 + 8; tt_srpt_sector += di.vmg_ifo_sector; di.num_title_sets = disc.GetDword(tt_srpt_sector, 0) >> 16; di.vmg_tt_srpt_end_offset = di.vmg_tt_srpt_offset + disc.GetDword(4) - 7; di.titlesets = (TitleSetInfo*)bay->vtable->SharedPool_Alloc(bay, di.num_title_sets * sizeof(TitleSetInfo)); di.num_title_sets = 0; memset(di.titlesets, 0, di.num_title_sets * sizeof(TitleSetInfo)); for (unsigned ofs = di.vmg_tt_srpt_offset; ofs < di.vmg_tt_srpt_end_offset; ofs += 12) { unsigned title_set = ((disc.GetDword(di.vmg_ifo_sector, ofs+4) >> 8) & 255) - 1; if (title_set < 99) { di.titlesets[title_set].vts_ifo_sector = di.vmg_ifo_sector + disc.GetDword(di.vmg_ifo_sector, ofs+8); if (title_set >= di.num_title_sets) { di.num_title_sets = title_set+1; } } } for (unsigned ts = 0; ts < di.num_title_sets; ++ts) { TitleSetInfo& tsi = di.titlesets[ts]; if (tsi.vts_ifo_sector == 0) continue; tsi.vtsm_vobs_sector = tsi.vts_ifo_sector + disc.GetDword(tsi.vts_ifo_sector, 0xC0); tsi.vts_vobs_sector = tsi.vts_ifo_sector + disc.GetDword(0xC4); tsi.vts_bup_sector = tsi.vts_ifo_sector + disc.GetDword(0x0C) - disc.GetDword(0x1C); tsi.number_of_existing_subpicture_tracks = disc.GetDword(0x254) >> 16; unsigned flags = disc.GetDword(0x200); tsi.ntsc = ~(flags >> 28) & 1; tsi.wide_aspect = (flags >> 27) & 1; unsigned pgci_sector = disc.GetDword(0xCC); unsigned tmapti_sector = disc.GetDword(0xD4); unsigned c_adt_sector = disc.GetDword(0xE0); unsigned vobu_admap_sector = disc.GetDword(0xE4); if (pgci_sector) { tsi.num_pgcs = disc.GetDword(tsi.vts_ifo_sector + pgci_sector, 0) >> 16; tsi.pgcs = (TitlePGCInfo*)bay->vtable->SharedPool_Alloc(bay, tsi.num_pgcs * sizeof(TitlePGCInfo)); unsigned pgc; for (pgc = 0; pgc < tsi.num_pgcs; ++pgc) { tsi.pgcs[pgc].subpicture_status_offset = pgci_sector*2048 + disc.GetDword(tsi.vts_ifo_sector + pgci_sector, pgc*8+12) + 0x1C; } for (pgc = 0; pgc < tsi.num_pgcs; ++pgc) { unsigned pgc_offset = tsi.pgcs[pgc].subpicture_status_offset - 0x1C; unsigned num_cells = disc.GetDword(tsi.vts_ifo_sector, pgc_offset) & 255; tsi.pgcs[pgc].cell_playback_info_table_offset = pgc_offset + (disc.GetDword(tsi.vts_ifo_sector, pgc_offset+0xE8) >> 16); tsi.pgcs[pgc].cell_playback_info_table_end_offset = tsi.pgcs[pgc].cell_playback_info_table_offset + 24 * num_cells; } } if (tmapti_sector) { tsi.num_tmaps = disc.GetDword(tsi.vts_ifo_sector+tmapti_sector, 0) >> 16; tsi.tmaps = (TitleTmapInfo*)bay->vtable->SharedPool_Alloc(bay, tsi.num_tmaps * sizeof(TitleTmapInfo)); unsigned tmap; for (tmap = 0; tmap < tsi.num_tmaps; ++tmap) { tsi.tmaps[tmap].offset = tmapti_sector * 2048 + disc.GetDword(tsi.vts_ifo_sector+tmapti_sector, 8 + tmap*4) + 4; } for (tmap = 0; tmap < tsi.num_tmaps; ++tmap) { tsi.tmaps[tmap].end_offset = tsi.tmaps[tmap].offset + (disc.GetDword(tsi.vts_ifo_sector, tsi.tmaps[tmap].offset - 4) & 65535) * 4; } } if (c_adt_sector) { tsi.vts_c_adt_offset = c_adt_sector*2048 + 8; tsi.vts_c_adt_end_offset = tsi.vts_c_adt_offset - 7 + disc.GetDword(tsi.vts_ifo_sector, tsi.vts_c_adt_offset - 4); } if (vobu_admap_sector) { tsi.vts_vobu_admap_offset = vobu_admap_sector*2048 + 4; tsi.vts_vobu_admap_end_offset = tsi.vts_vobu_admap_offset - 3 + disc.GetDword(tsi.vts_ifo_sector, tsi.vts_vobu_admap_offset - 4); } } di.disc_id = disc.GetCumulativeCRC(); DiscInfo* pdi = (DiscInfo*)bay->vtable->SharedPool_Alloc(bay, sizeof(DiscInfo)); *pdi = di; return pdi; } void PrintDiscInfo(DiscInfo* di) { printf("di->vmg_ifo_sector = %X\n", di->vmg_ifo_sector); printf("di->vmg_vobs_sector = %X\n", di->vmg_vobs_sector); printf("di->vmg_bup_sector = %X\n", di->vmg_bup_sector); printf("di->vmg_tt_srpt_offset = %X\n", di->vmg_tt_srpt_offset); printf("di->vmg_tt_srpt_end_offset = %X\n", di->vmg_tt_srpt_end_offset); printf("di->num_title_sets = %X\n", di->num_title_sets); for (unsigned i=0; inum_title_sets; ++i) { printf("di->titlesets[%d].vts_ifo_sector = %X\n", i, di->titlesets[i].vts_ifo_sector); printf("di->titlesets[%d].vtsm_vobs_sector = %X\n", i, di->titlesets[i].vtsm_vobs_sector); printf("di->titlesets[%d].vts_vobs_sector = %X\n", i, di->titlesets[i].vts_vobs_sector); printf("di->titlesets[%d].vts_bup_sector = %X\n", i, di->titlesets[i].vts_bup_sector); printf("di->titlesets[%d].number_of_existing_subpicture_tracks = %X\n", i, di->titlesets[i].number_of_existing_subpicture_tracks); printf("di->titlesets[%d].vts_c_adt_offset = %X\n", i, di->titlesets[i].vts_c_adt_offset); printf("di->titlesets[%d].vts_c_adt_end_offset = %X\n", i, di->titlesets[i].vts_c_adt_end_offset); printf("di->titlesets[%d].vts_vobu_admap_offset = %X\n", i, di->titlesets[i].vts_vobu_admap_offset); printf("di->titlesets[%d].vts_vobu_admap_end_offset = %X\n", i, di->titlesets[i].vts_vobu_admap_end_offset); printf("di->titlesets[%d].num_pgcs = %X\n", i, di->titlesets[i].num_pgcs); printf("di->titlesets[%d].num_tmaps = %X\n", i, di->titlesets[i].num_tmaps); unsigned j; for (j=0; jtitlesets[i].num_pgcs; ++j) { printf("di->titlesets[%d].pgcs[%d].subpicture_status_offset = %X\n", i, j, di->titlesets[i].pgcs[j].subpicture_status_offset); printf("di->titlesets[%d].pgcs[%d].cell_playback_info_table_offset = %X\n", i, j, di->titlesets[i].pgcs[j].cell_playback_info_table_offset); printf("di->titlesets[%d].pgcs[%d].cell_playback_info_table_end_offset = %X\n", i, j, di->titlesets[i].pgcs[j].cell_playback_info_table_end_offset); } for (j=0; jtitlesets[i].num_tmaps; ++j) { printf("di->titlesets[%d].tmaps[%d].offset = %X\n", i, j, di->titlesets[i].tmaps[j].offset); printf("di->titlesets[%d].tmaps[%d].end_offset = %X\n", i, j, di->titlesets[i].tmaps[j].end_offset); } } }; /******************************************************************** ********************************************************************/ class SubpictureReceiver : public ICompilerCallbacks, public IScannerCallbacks { CompileDialog* const compile_dialog; DVDSubberKernel* const k; DvsDockingBay* const bay; // const unsigned char* const map_data; // unsigned const map_len; HANDLE const hdatafile; unsigned datafilepos; SubpictureInfo** psi; int angle; bool got_title, got_angle; unsigned numerator, denominator; bool got_warning; unsigned char* map_data; unsigned map_pos, map_len; unsigned num_vobus; void LookupField(unsigned field, unsigned angle, bool ntsc, unsigned* plba, unsigned* pptm); void Error(const char* msg, const char* fmt, int value); void SubpictureReceiver::GrowMap(); public: SubpictureReceiver(CompileDialog* _compile_dialog, DVDSubberKernel* _k, DvsDockingBay* _bay, HANDLE _hdatafile) : compile_dialog(_compile_dialog), k(_k), bay(_bay), hdatafile(_hdatafile) { datafilepos = 0; psi = &k->subpic_inf; k->title = 0; angle = 1; got_title = false; got_angle = false; numerator = 0; denominator = 1; got_warning = false; map_data = 0; k->num_subpics = 0; k->palette_size = 0; } bool UserAborted(); bool GotWarning() { return got_warning; } void NotifySubpictureCount(unsigned num_subpictures); void NotifyDoneParsing(); bool IsNTSC(); void AddSubpicture(unsigned base_field, const unsigned char* data); int ColorLookup(unsigned rgb); void SetVTSNumber(int vts_number); void SetAngle(int angle); bool ReadSector(unsigned lba, unsigned char* buf); void AddVOBU(unsigned lba, unsigned vobu_number, unsigned num_fields, unsigned num_data_blocks); void ChangePTM(unsigned new_ptm); void ChangeAngle(unsigned new_angle); void Note(const char* msg); void Warning(const char* msg); void ErrorAtLine(const char* msg, int line); void ErrorAtTime(const char* msg, unsigned field); void ErrorAtLBA(const char* msg, unsigned lba); }; void SubpictureReceiver::LookupField(unsigned field, unsigned angle, bool ntsc, unsigned* plba, unsigned* pptm) { const unsigned char* p = map_data; const unsigned char* end = map_data + map_len; unsigned base_ptm = 0, base_field = 0, cur_field = 0, cur_sector = 0, cur_angle = 0; while (p < end) { unsigned char ch = *p++; if (ch < 0xF0 || ch == 0xFE) { unsigned fields, intervening_sectors; if (ch < 0xF0) { fields = ch >> 2; intervening_sectors = (ch&3) * 256 + (*p++); } else { fields = p[0]*256 + p[1]; intervening_sectors = p[2]*256 + p[3]; p += 4; } if (cur_angle==0 || cur_angle==angle) { cur_field += fields; if (cur_field > field) { *plba = cur_sector + 2; *pptm = base_ptm + (field-base_field) * (ntsc?3003:3600) / 2; return; } } cur_sector += intervening_sectors+1; } else if (ch == 0xFF) { if (cur_angle==0 || cur_angle==angle) { base_field = cur_field; base_ptm = 0; for (int i=0; i<4; ++i) base_ptm = base_ptm*256 + (*p++); } else { p += 4; } } else { cur_angle = ch & 15; } } // this is caught in DVDSubber-compile.cpp; not really kosher throw "Field number too large"; } void SubpictureReceiver::NotifySubpictureCount(unsigned num_subpictures) { denominator = num_subpictures; } void SubpictureReceiver::NotifyDoneParsing() { char map_file[MAX_PATH+32]; wsprintf(map_file, "%sdvdsub_%08x_%x.map", g_bays->GetDvdsynthDirectory(), k->disc_info->disc_id, k->title); HANDLE hmapfile = CreateFile(map_file, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hmapfile != INVALID_HANDLE_VALUE) { map_len = GetFileSize(hmapfile, NULL); map_data = new unsigned char[map_len]; DWORD read; ReadFile(hmapfile, map_data, map_len, &read, NULL); CloseHandle(hmapfile); } else { hmapfile = CreateFile(map_file, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hmapfile != INVALID_HANDLE_VALUE) { map_len = 10000; map_pos = 0; map_data = new unsigned char[map_len]; const TitleSetInfo& tsi = k->disc_info->titlesets[k->title]; unsigned vts_vobs_lba = tsi.vts_vobs_sector; unsigned vobu_admap_lba = tsi.vts_ifo_sector + (tsi.vts_vobu_admap_offset >> 11); num_vobus = (tsi.vts_vobu_admap_end_offset - tsi.vts_vobu_admap_offset) >> 2; if (Scan(vobu_admap_lba, num_vobus, vts_vobs_lba, this)) { map_len = map_pos; DWORD written; WriteFile(hmapfile, map_data, map_pos, &written, NULL); CloseHandle(hmapfile); } else { CloseHandle(hmapfile); DeleteFile(map_file); throw 0; } } else { Error("Could not open the cache file", " (\"%s\")", (int)map_file); throw 0; } } } bool SubpictureReceiver::UserAborted() { MSG msg; while (PeekMessage(&msg, NULL, 0, 0, TRUE)) { TranslateMessage(&msg); DispatchMessage(&msg); } return compile_dialog->ButtonPressed(); } bool SubpictureReceiver::IsNTSC() { return k->disc_info->titlesets[k->title].ntsc; } void SubpictureReceiver::AddSubpicture(unsigned base_field, const unsigned char* data) { if (*psi == 0) { *psi = (SubpictureInfo*)bay->vtable->SharedPool_Alloc(bay, sizeof(SubpictureInfo)); (*psi)->next = 0; } SubpictureInfo* si = *psi; psi = &si->next; unsigned sector; LookupField(base_field, angle, true, §or, &si->ptm); si->hard_lba = sector + k->disc_info->titlesets[k->title].vts_vobs_sector; si->file_offset = datafilepos; unsigned data_len = data[0]*256+data[1]; si->data_len = data_len; si->num_sectors = (data_len + (rest_data_packet_len-first_data_packet_len) + (rest_data_packet_len-1)) / rest_data_packet_len; DWORD written; if (!WriteFile(hdatafile, data, data_len, &written, NULL) || written != data_len) throw "Unable to write temporary file -- the disk may be full"; datafilepos += data_len; ++k->num_subpics; ++numerator; if ((numerator & 31) == (denominator & 31)) compile_dialog->SetProgress(numerator, denominator); } inline int ScaledPixelClip(int x) { x = (x + 0x8000) >> 16; return (x<=0 ? 0 : x>=255 ? 255 : x); } int RGB2YVU(int rgb) { const int cyb = int(0.114*219/255*65536+0.5); const int cyg = int(0.587*219/255*65536+0.5); const int cyr = int(0.299*219/255*65536+0.5); // y can't overflow int y = (cyb*(rgb&255) + cyg*((rgb>>8)&255) + cyr*((rgb>>16)&255) + 0x108000) >> 16; int scaled_y = (y - 16) * int(255.0/219.0*65536+0.5); int b_y = ((rgb&255) << 16) - scaled_y; int u = ScaledPixelClip((b_y >> 10) * int(1/2.018*1024+0.5) + 0x800000); int r_y = (rgb & 0xFF0000) - scaled_y; int v = ScaledPixelClip((r_y >> 10) * int(1/1.596*1024+0.5) + 0x800000); return (y*256+v)*256+u; } int SubpictureReceiver::ColorLookup(unsigned rgb) { unsigned yvu = RGB2YVU(rgb); int i; for (i = 0; i < k->palette_size; ++i) { if (k->palette[i] == yvu) break; } if (i >= k->palette_size) { if (i >= 16) { return -1; } k->palette[k->palette_size++] = yvu; } return 15 - i; } void SubpictureReceiver::SetVTSNumber(int vts_number) { if (got_title) throw "Only one command allowed"; got_title = true; if (vts_number < 1 || vts_number > int(k->disc_info->num_title_sets)) throw "VTS number out of range"; k->title = vts_number - 1; } void SubpictureReceiver::SetAngle(int _angle) { if (got_angle) throw "Only one command allowed"; got_angle = true; angle = _angle; } bool SubpictureReceiver::ReadSector(unsigned lba, unsigned char* buf) { static unsigned char cdb[10] = { 0x28, 0, 0, 0, 0, 0, 0, 0, 1, 0 }; for (int i=0; i<4; ++i) { cdb[5-i] = lba >> (i*8); } unsigned long buflen = 2048; SenseData sense; return SCSIRESULT_SUCCESS == g_bays->KernelScsiCommand(k->child, cdb, 10, buf, &buflen, 1, &sense); } void SubpictureReceiver::GrowMap() { map_len *= 2; unsigned char* new_map = new unsigned char[map_len]; memcpy(new_map, map_data, map_pos); delete[] map_data; map_data = new_map; } void SubpictureReceiver::AddVOBU(unsigned lba, unsigned vobu_number, unsigned num_fields, unsigned num_data_blocks) { if (num_fields < 60 && num_data_blocks < 0x400) { if (map_pos > map_len - 2) { GrowMap(); } map_data[map_pos] = (num_fields<<2) + (num_data_blocks>>8); map_data[map_pos+1] = num_data_blocks; map_pos += 2; } else { if (num_fields >= 0x10000 || num_data_blocks >= 0x10000) { ErrorAtLBA("Unexpectedly long VOBU", lba); throw 0; } if (map_pos > map_len - 5) { GrowMap(); } map_data[map_pos] = 0xFE; map_data[map_pos+1] = num_fields >> 8; map_data[map_pos+2] = num_fields; map_data[map_pos+3] = num_data_blocks >> 8; map_data[map_pos+4] = num_data_blocks; map_pos += 5; } compile_dialog->SetProgress(vobu_number, num_vobus); } void SubpictureReceiver::ChangePTM(unsigned new_ptm) { if (map_pos > map_len - 5) { GrowMap(); } map_data[map_pos] = 0xFF; for (int i=0; i<4; ++i) { map_data[map_pos+i+1] = new_ptm >> (24-i*8); } map_pos += 5; } void SubpictureReceiver::ChangeAngle(unsigned new_angle) { if (map_pos >= map_len) { GrowMap(); } map_data[map_pos++] = 0xF0 + new_angle; } void SubpictureReceiver::Note(const char* msg) { compile_dialog->AddMessage(msg); } void SubpictureReceiver::Warning(const char* msg) { compile_dialog->AddMessage(msg); got_warning = true; } void SubpictureReceiver::Error(const char* msg, const char* fmt, int value) { compile_dialog->AddMessage("*** ERROR:"); compile_dialog->AddMessage(msg); char buf[1024]; sprintf(buf, fmt, value); compile_dialog->AddMessage(buf); } void SubpictureReceiver::ErrorAtLBA(const char* msg, unsigned lba) { Error(msg, "(at logical block address %X)", lba); } void SubpictureReceiver::ErrorAtLine(const char* msg, int line) { Error(msg, "(at script file line %d)", line); } void SubpictureReceiver::ErrorAtTime(const char* msg, unsigned field) { Error(msg, "(at video field %d)", field); } bool ChooseFile(char* filename, const char* title, const char* filters) { OPENFILENAME ofn; ofn.lStructSize = 76; ofn.hwndOwner = NULL; ofn.hInstance = g_hinstance; ofn.lpstrFilter = filters; ofn.lpstrCustomFilter = NULL; ofn.nMaxCustFilter = 0; ofn.nFilterIndex = 2; ofn.lpstrFile = filename; ofn.nMaxFile = MAX_PATH; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 0; ofn.lpstrInitialDir = NULL; ofn.lpstrTitle = title; ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; ofn.lpstrDefExt = NULL; ofn.lpfnHook = NULL; ofn.lpTemplateName = NULL; return !!GetOpenFileName(&ofn); } bool CreateTempFile(HANDLE* puserhandle, dvs_file_handle* pkernelhandle) { char temp_path[MAX_PATH]; if (!GetTempPath(MAX_PATH, temp_path)) { MessageBox(NULL, "Could not get temporary directory.", "DVDSubber", MB_OK); return false; } char temp_name[MAX_PATH]; if (!GetTempFileName(temp_path, "DVS", 0, temp_name)) { MessageBox(NULL, "Could not get temporary file name.", "DVDSubber", MB_OK); return false; } HANDLE htemp = CreateFile(temp_name, GENERIC_WRITE, FILE_SHARE_READ, NULL, TRUNCATE_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL); if (htemp == INVALID_HANDLE_VALUE) { MessageBox(NULL, "Could not create temporary file.", "DVDSubber", MB_OK); return false; } dvs_file_handle temphandle; if (0 != g_bays->OpenFileRO(&temphandle, temp_name)) { MessageBox(NULL, "Could not create temporary file (in kernel mode).", "DVDSubber", MB_OK); CloseHandle(htemp); return false; } *puserhandle = htemp; *pkernelhandle = temphandle; return true; } class FileLRU { enum { max_files = 12 }; char* ptrs[max_files]; int num_files; char buf[max_files][MAX_PATH]; public: FileLRU() { for (int i=0; i= 0 && index < num_files ? ptrs[index] : 0; } void Promote(const char* entry) { int i; for (i=0; i0) { ptrs[i] = ptrs[i-1]; --i; } ptrs[0] = t; if (ptrs[0] != entry) { lstrcpy(ptrs[0], entry); } } void Demote(const char* entry) { int i; for (i=0; ik; // make temp. file for compiled subtitles if (k->data_file_handle == 0) { if (!CreateTempFile(&self->data_file_handle, &k->data_file_handle)) return; } // make drive appear empty while we update the disc g_bays->Driver_Call(self->driver_handle, "SetState", "pi", k, int(stateUpdating)); // remove old subtitles k->num_subpics = 0; // remove old subpicture data SetFilePointer(self->data_file_handle, 0, NULL, FILE_BEGIN); SetEndOfFile(self->data_file_handle); if (subtitle_filename) { FILE* fsub = fopen(subtitle_filename, "r"); if (!fsub) { MessageBox(NULL, "Could not open the subtitle file.", "DVDSubber", MB_OK); } else { scsi_result_t scsi_error = 0; try { k->disc_info = GetDiscInfo(k->child, self->bay); } catch (scsi_result_t result) { scsi_error = result; k->disc_info = 0; } if (scsi_error) { char buf[128]; wsprintf(buf, "Failed to get disc info: SCSI error %X.", scsi_error); MessageBox(NULL, buf, "DVDSubber", MB_OK); } else if (!k->disc_info) { MessageBox(NULL, "Failed to get disc info. (Maybe there is not a DVD-Video disc in the drive?)", "DVDSubber", MB_OK); } else { PrintDiscInfo(k->disc_info); CompileDialog* compile_dialog = new CompileDialog; SubpictureReceiver subpic_receiver(compile_dialog, k, self->bay, self->data_file_handle); if (CompileSubtitlesCatch(fsub, &subpic_receiver)) { if (subpic_receiver.GotWarning()) { subpic_receiver.Note("Compilation was successful, but there were warnings."); compile_dialog->SetButtonAutoclose("OK"); } else { compile_dialog->Destroy(); } } else { if (subpic_receiver.UserAborted()) { compile_dialog->Destroy(); } else { subpic_receiver.Note("Compilation was not successful."); compile_dialog->SetButtonAutoclose("OK"); } k->num_subpics = 0; } k->saved_scr_lba = 0xFFFFFFF0; } fclose(fsub); } } g_bays->Driver_Call(self->driver_handle, "SetState", "pi", k, int(stateUpdated)); } void OpenSubtitles(void* _self, int) { static char subtitle_filename[MAX_PATH]; if (ChooseFile(subtitle_filename, "Choose subtitle file", "DVD subber scripts (*.dvdsub)\0*.dvdsub\0")) { ApplySubtitles(_self, (int)subtitle_filename); } } void AddDeviceMenuItems(DvsDeviceUser* _self, DvsMenu* menu) { menu->vtable->BeginSubmenu(menu, "DVD subber", false); menu->vtable->AddItem(menu, "&Open...", false, OpenSubtitles, _self, 0); DVDSubberUser* self = (DVDSubberUser*)_self; if (self->k->num_subpics != 0) { menu->vtable->AddItem(menu, "&Remove current subtitles", false, ApplySubtitles, _self, 0); } // menu->vtable->AddItem(menu, "Scan disc", false, ScanDisc, _self, 0); menu->vtable->AddSeparator(menu); for (int i = 0; ; ++i) { const char* name = file_lru.Get(i); if (name==0) break; menu->vtable->AddItem(menu, name, false, ApplySubtitles, _self, int(name)); } menu->vtable->EndSubmenu(menu); } void Delete(DvsDeviceUser* _self) { DVDSubberUser* self = (DVDSubberUser*)_self; if (self->k->data_file_handle != 0) { g_bays->CloseFile(self->k->data_file_handle); } if (self->data_file_handle) { CloseHandle(self->data_file_handle); } g_bays->Driver_Unload(self->driver_handle); delete self; } DvsDeviceUser_vtable DVDSubberUser_vtable = { AddDeviceMenuItems, 0, Delete }; DvsDeviceUser* HookDevice(DvsDeviceKernel** pkernel, DvsDockingBay* bay) { DVDSubberUser* u = new DVDSubberUser; u->vtable = &DVDSubberUser_vtable; u->bay = bay; u->driver_handle = g_bays->Driver_Load("DVDSubber.kll"); if (!u->driver_handle) return 0; u->data_file_handle = NULL; DVDSubberKernel* k = u->k = (DVDSubberKernel*)bay->vtable->SharedPool_Alloc(bay, sizeof(DVDSubberKernel)); k->ScsiCommand = (dvs_scsi_func*)g_bays->Driver_Call(u->driver_handle, "GetDispatchFunc", ""); k->child = *pkernel; k->state = stateActive; k->subpic_inf = 0; k->num_subpics = 0; k->data_file_handle = 0; *pkernel = k; return u; } DvsFilterGlobal filter_global = { 0, "DVD Subtitler", DVDSYNTH_FILTER_ONLY_ONE, HookDevice, 0 }; extern "C" DvsFilterGlobal* __cdecl DvdsynthFilterPluginEntry(DvsDockingBayGlobal* bays) { g_bays = bays; return &filter_global; } extern "C" BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID) { g_hinstance = hinst; return TRUE; }