From d63d2c3c6cbbd574dda4f8b00ebe6c661735edd5 Mon Sep 17 00:00:00 2001 From: Felix Domke Date: Fri, 17 Oct 2003 15:36:42 +0000 Subject: [PATCH] import of enigma2 --- AUTHORS | 0 ChangeLog | 0 DEADJOE | 0 Makefile.am | 4 + Makefile.in | 0 NEWS | 0 README | 0 aclocal.m4 | 0 autogen.sh | 62 + autom4te.cache/output.0 | 0 autom4te.cache/requests | 0 autom4te.cache/traces.0 | 0 config.h.in | 97 ++ configure | 0 configure.ac | 42 + debug | 5 + debug.gdb | 3 + include/connection.h | 17 + include/libsig_comp.h | 33 + lib/DEADJOE | 0 lib/Makefile.am | 3 + lib/Makefile.in | 0 lib/base/Makefile.am | 9 + lib/base/Makefile.in | 0 lib/base/a | 0 lib/base/buffer.cpp | 196 +++ lib/base/buffer.h | 40 + lib/base/console.cpp | 248 ++++ lib/base/console.h | 0 lib/base/ebase.cpp | 206 +++ lib/base/ebase.h | 252 ++++ lib/base/econfig.cpp | 43 + lib/base/econfig.h | 27 + lib/base/eerror.cpp | 69 + lib/base/eerror.h | 43 + lib/base/elock.cpp | 104 ++ lib/base/elock.h | 60 + lib/base/eptrlist.h | 1338 ++++++++++++++++++++ lib/base/estring.cpp | 468 +++++++ lib/base/estring.h | 113 ++ lib/base/i18n.h | 29 + lib/base/init.cpp | 68 + lib/base/init.h | 97 ++ lib/base/init_num.h | 24 + lib/base/message.cpp | 49 + lib/base/message.h | 64 + lib/base/nconfig.cpp | 1027 +++++++++++++++ lib/base/nconfig.h | 392 ++++++ lib/base/nxml.cpp | 339 +++++ lib/base/object.h | 30 + lib/base/ringbuffer.h | 109 ++ lib/base/smartptr.cpp | 2 + lib/base/smartptr.h | 60 + lib/base/thread.cpp | 34 + lib/base/thread.h | 23 + lib/driver/Makefile.am | 7 + lib/driver/Makefile.in | 0 lib/driver/input_fake.h | 84 ++ lib/driver/rc.cpp | 245 ++++ lib/driver/rc.h | 226 ++++ lib/driver/rcdbox.h | 61 + lib/driver/rcdreambox2.h | 51 + lib/driver/rcinput.cpp | 126 ++ lib/driver/rcinput.h | 17 + lib/driver/rfmod.h | 33 + lib/driver/streamwd.h | 0 lib/dvb/Makefile.am | 8 + lib/dvb/Makefile.in | 0 lib/dvb/crc32.cpp | 164 +++ lib/dvb/crc32.h | 22 + lib/dvb/db.cpp | 274 ++++ lib/dvb/db.h | 48 + lib/dvb/decoder.cpp | 245 ++++ lib/dvb/decoder.h | 66 + lib/dvb/demux.cpp | 131 ++ lib/dvb/demux.h | 41 + lib/dvb/dvb.cpp | 211 +++ lib/dvb/dvb.h | 67 + lib/dvb/esection.cpp | 130 ++ lib/dvb/esection.h | 189 +++ lib/dvb/frontend.cpp | 465 +++++++ lib/dvb/frontend.h | 61 + lib/dvb/idvb.h | 354 ++++++ lib/dvb/isection.h | 53 + lib/dvb/list.h | 13 + lib/dvb/pmt.cpp | 142 +++ lib/dvb/pmt.h | 71 ++ lib/dvb/scan.cpp | 374 ++++++ lib/dvb/scan.h | 69 + lib/dvb/sec.cpp | 58 + lib/dvb/sec.h | 13 + lib/dvb/specs.h | 108 ++ lib/dvb/stT1RVV7 | Bin 0 -> 1490944 bytes lib/dvb_si/Makefile.am | 0 lib/dvb_si/Makefile.in | 0 lib/dvb_si/ac3_descriptor.cpp | 102 ++ lib/dvb_si/ac3_descriptor.h | 59 + lib/dvb_si/ait.cpp | 94 ++ lib/dvb_si/ait.h | 86 ++ lib/dvb_si/ancillary_data_descriptor.cpp | 33 + lib/dvb_si/ancillary_data_descriptor.h | 38 + lib/dvb_si/announcement_support_descriptor.cpp | 107 ++ lib/dvb_si/announcement_support_descriptor.h | 67 + lib/dvb_si/application_signalling_descriptor.cpp | 57 + lib/dvb_si/application_signalling_descriptor.h | 57 + lib/dvb_si/audio_stream_descriptor.cpp | 52 + lib/dvb_si/audio_stream_descriptor.h | 45 + lib/dvb_si/bat.cpp | 55 + lib/dvb_si/bat.h | 66 + lib/dvb_si/bouquet_name_descriptor.cpp | 33 + lib/dvb_si/bouquet_name_descriptor.h | 39 + lib/dvb_si/ca_descriptor.cpp | 50 + lib/dvb_si/ca_descriptor.h | 51 + lib/dvb_si/ca_identifier_descriptor.cpp | 34 + lib/dvb_si/ca_identifier_descriptor.h | 42 + lib/dvb_si/ca_system_descriptor.cpp | 33 + lib/dvb_si/ca_system_descriptor.h | 38 + lib/dvb_si/cable_delivery_system_descriptor.cpp | 80 ++ lib/dvb_si/cable_delivery_system_descriptor.h | 47 + lib/dvb_si/camt.cpp | 29 + lib/dvb_si/camt.h | 43 + lib/dvb_si/capmt.cpp | 124 ++ lib/dvb_si/capmt.h | 87 ++ lib/dvb_si/cat.cpp | 29 + lib/dvb_si/cat.h | 42 + lib/dvb_si/cell_frequency_link_descriptor.cpp | 87 ++ lib/dvb_si/cell_frequency_link_descriptor.h | 78 ++ lib/dvb_si/cell_list_descriptor.cpp | 124 ++ lib/dvb_si/cell_list_descriptor.h | 89 ++ lib/dvb_si/component_descriptor.cpp | 60 + lib/dvb_si/component_descriptor.h | 47 + lib/dvb_si/container.cpp | 289 +++++ lib/dvb_si/container.h | 39 + lib/dvb_si/content_descriptor.cpp | 68 + lib/dvb_si/content_descriptor.h | 60 + lib/dvb_si/copyright_descriptor.h | 44 + lib/dvb_si/country_availability_descriptor.cpp | 47 + lib/dvb_si/country_availability_descriptor.h | 45 + lib/dvb_si/data_broadcast_descriptor.cpp | 62 + lib/dvb_si/data_broadcast_descriptor.h | 52 + lib/dvb_si/data_broadcast_id_descriptor.cpp | 44 + lib/dvb_si/data_broadcast_id_descriptor.h | 44 + lib/dvb_si/data_stream_alignment_descriptor.h | 38 + lib/dvb_si/descriptor.cpp | 39 + lib/dvb_si/descriptor.h | 46 + lib/dvb_si/descriptor_tag.h | 127 ++ lib/dvb_si/dsng_descriptor.h | 42 + lib/dvb_si/eit.cpp | 109 ++ lib/dvb_si/eit.h | 83 ++ lib/dvb_si/element_descriptor.h | 34 + lib/dvb_si/extended_event_descriptor.cpp | 90 ++ lib/dvb_si/extended_event_descriptor.h | 70 + lib/dvb_si/frequency_list_descriptor.cpp | 44 + lib/dvb_si/frequency_list_descriptor.h | 45 + lib/dvb_si/group_descriptor.h | 34 + lib/dvb_si/hierarchy_descriptor.h | 48 + lib/dvb_si/ibp_descriptor.h | 42 + lib/dvb_si/ippv_booking_descriptor.h | 34 + lib/dvb_si/ippv_descriptor.h | 62 + lib/dvb_si/iso639_language_descriptor.cpp | 56 + lib/dvb_si/iso639_language_descriptor.h | 56 + lib/dvb_si/linkage_descriptor.cpp | 107 ++ lib/dvb_si/linkage_descriptor.h | 59 + lib/dvb_si/local_time_offset_descriptor.cpp | 88 ++ lib/dvb_si/local_time_offset_descriptor.h | 67 + lib/dvb_si/long_crc_table.cpp | 36 + lib/dvb_si/long_crc_table.h | 44 + lib/dvb_si/long_table.cpp | 88 ++ lib/dvb_si/long_table.h | 60 + lib/dvb_si/maximum_bitrate_descriptor.h | 39 + lib/dvb_si/mosaic_descriptor.cpp | 171 +++ lib/dvb_si/mosaic_descriptor.h | 96 ++ .../multilingual_bouquet_name_descriptor.cpp | 57 + lib/dvb_si/multilingual_bouquet_name_descriptor.h | 57 + lib/dvb_si/multilingual_component_descriptor.cpp | 64 + lib/dvb_si/multilingual_component_descriptor.h | 59 + .../multilingual_network_name_descriptor.cpp | 57 + lib/dvb_si/multilingual_network_name_descriptor.h | 57 + .../multilingual_service_name_descriptor.cpp | 68 + lib/dvb_si/multilingual_service_name_descriptor.h | 62 + .../multiplex_buffer_utilization_descriptor.h | 43 + lib/dvb_si/network_name_descriptor.cpp | 33 + lib/dvb_si/network_name_descriptor.h | 38 + lib/dvb_si/nit.cpp | 70 + lib/dvb_si/nit.h | 71 ++ lib/dvb_si/nvod_reference_descriptor.cpp | 63 + lib/dvb_si/nvod_reference_descriptor.h | 58 + lib/dvb_si/packet_id.h | 45 + lib/dvb_si/parental_rating_descriptor.cpp | 56 + lib/dvb_si/parental_rating_descriptor.h | 56 + lib/dvb_si/partial_transport_stream_descriptor.h | 45 + lib/dvb_si/pat.cpp | 81 ++ lib/dvb_si/pat.h | 84 ++ lib/dvb_si/pdc_descriptor.cpp | 34 + lib/dvb_si/pdc_descriptor.h | 39 + lib/dvb_si/pmt.cpp | 75 ++ lib/dvb_si/pmt.h | 77 ++ lib/dvb_si/private_data_indicator_descriptor.h | 38 + lib/dvb_si/private_data_specifier_descriptor.cpp | 33 + lib/dvb_si/private_data_specifier_descriptor.h | 38 + lib/dvb_si/registration_descriptor.h | 44 + .../satellite_delivery_system_descriptor.cpp | 91 ++ lib/dvb_si/satellite_delivery_system_descriptor.h | 50 + lib/dvb_si/sdt.cpp | 87 ++ lib/dvb_si/sdt.h | 77 ++ lib/dvb_si/service_descriptor.cpp | 47 + lib/dvb_si/service_descriptor.h | 44 + lib/dvb_si/service_list_descriptor.cpp | 56 + lib/dvb_si/service_list_descriptor.h | 56 + lib/dvb_si/service_move_descriptor.cpp | 45 + lib/dvb_si/service_move_descriptor.h | 42 + lib/dvb_si/service_type.h | 50 + lib/dvb_si/short_crc_table.cpp | 36 + lib/dvb_si/short_crc_table.h | 44 + lib/dvb_si/short_event_descriptor.cpp | 47 + lib/dvb_si/short_event_descriptor.h | 44 + lib/dvb_si/short_smoothing_buffer_descriptor.h | 41 + lib/dvb_si/short_table.cpp | 47 + lib/dvb_si/short_table.h | 58 + lib/dvb_si/smoothing_buffer_descriptor.h | 42 + lib/dvb_si/std_descriptor.h | 39 + lib/dvb_si/stream_identifier_descriptor.cpp | 33 + lib/dvb_si/stream_identifier_descriptor.h | 38 + lib/dvb_si/stream_type.h | 51 + lib/dvb_si/stuffing_descriptor.cpp | 29 + lib/dvb_si/stuffing_descriptor.h | 36 + lib/dvb_si/subtitling_descriptor.cpp | 68 + lib/dvb_si/subtitling_descriptor.h | 60 + lib/dvb_si/system_clock_descriptor.h | 44 + lib/dvb_si/table_id.h | 123 ++ lib/dvb_si/target_background_grid_descriptor.cpp | 45 + lib/dvb_si/target_background_grid_descriptor.h | 42 + lib/dvb_si/tdt.cpp | 39 + lib/dvb_si/tdt.h | 48 + lib/dvb_si/telephone_descriptor.cpp | 83 ++ lib/dvb_si/telephone_descriptor.h | 58 + lib/dvb_si/teletext_descriptor.cpp | 27 + lib/dvb_si/teletext_descriptor.h | 33 + .../terrestrial_delivery_system_descriptor.cpp | 83 ++ .../terrestrial_delivery_system_descriptor.h | 56 + lib/dvb_si/time_shifted_event_descriptor.h | 40 + lib/dvb_si/time_shifted_service_descriptor.cpp | 33 + lib/dvb_si/time_shifted_service_descriptor.h | 38 + lib/dvb_si/tot.cpp | 44 + lib/dvb_si/tot.h | 51 + lib/dvb_si/transport_stream_descriptor.h | 38 + lib/dvb_si/url_descriptor.h | 34 + lib/dvb_si/vbi_data_descriptor.cpp | 98 ++ lib/dvb_si/vbi_data_descriptor.h | 77 ++ lib/dvb_si/vbi_teletext_descriptor.cpp | 68 + lib/dvb_si/vbi_teletext_descriptor.h | 60 + lib/dvb_si/video_stream_descriptor.cpp | 73 ++ lib/dvb_si/video_stream_descriptor.h | 53 + lib/dvb_si/video_window_descriptor.cpp | 45 + lib/dvb_si/video_window_descriptor.h | 42 + lib/gdi/.cvsignore | 7 + lib/gdi/Makefile.am | 8 + lib/gdi/epng.cpp | 187 +++ lib/gdi/epng.h | 9 + lib/gdi/epoint.h | 172 +++ lib/gdi/erect.cpp | 204 +++ lib/gdi/erect.h | 229 ++++ lib/gdi/esize.h | 187 +++ lib/gdi/fb.cpp | 186 +++ lib/gdi/fb.h | 43 + lib/gdi/font.cpp | 855 +++++++++++++ lib/gdi/font.cpp-new | 787 ++++++++++++ lib/gdi/font.h | 163 +++ lib/gdi/font_arabic.cpp | 266 ++++ lib/gdi/gfbdc.cpp | 166 +++ lib/gdi/gfbdc.h | 35 + lib/gdi/glcddc.cpp | 56 + lib/gdi/glcddc.h | 26 + lib/gdi/gpixmap.cpp | 295 +++++ lib/gdi/gpixmap.h | 142 +++ lib/gdi/grc.cpp | 348 +++++ lib/gdi/grc.h | 245 ++++ lib/gdi/lcd.cpp | 210 +++ lib/gdi/lcd.h | 53 + lib/gui/.cvsignore | 7 + lib/gui/Makefile.am | 13 + lib/gui/actions.cpp | 0 lib/gui/actions.h | 0 lib/gui/combobox.cpp | 0 lib/gui/combobox.h | 0 lib/gui/decoration.cpp | 0 lib/gui/decoration.h | 0 lib/gui/ebutton.cpp | 0 lib/gui/ebutton.h | 0 lib/gui/echeckbox.cpp | 138 ++ lib/gui/echeckbox.h | 0 lib/gui/elabel.cpp | 253 ++++ lib/gui/elabel.h | 0 lib/gui/emessage.cpp | 163 +++ lib/gui/emessage.h | 0 lib/gui/enumber.cpp | 447 +++++++ lib/gui/enumber.h | 0 lib/gui/epixmap.cpp | 0 lib/gui/epixmap.h | 0 lib/gui/eprogress.cpp | 0 lib/gui/eprogress.h | 0 lib/gui/eservicegrid.cpp | 0 lib/gui/eservicegrid.h | 0 lib/gui/eskin.cpp | 760 +++++++++++ lib/gui/eskin.h | 87 ++ lib/gui/eskin_register.cpp | 0 lib/gui/eskin_register.h | 0 lib/gui/ewidget.cpp | 0 lib/gui/ewidget.h | 498 ++++++++ lib/gui/ewindow.cpp | 0 lib/gui/ewindow.h | 0 lib/gui/guiactions.cpp | 0 lib/gui/guiactions.h | 0 lib/gui/listbox.cpp | 0 lib/gui/listbox.h | 0 lib/gui/multipage.cpp | 0 lib/gui/multipage.h | 0 lib/gui/numberactions.cpp | 0 lib/gui/numberactions.h | 0 lib/gui/slider.cpp | 0 lib/gui/slider.h | 0 lib/gui/statusbar.cpp | 0 lib/gui/statusbar.h | 0 lib/gui/testpicture.cpp | 0 lib/gui/testpicture.h | 0 lib/gui/textinput.cpp | 0 lib/gui/textinput.h | 0 lib/network/Makefile.am | 7 + lib/network/Makefile.in | 0 lib/network/http_dyn.cpp | 66 + lib/network/http_dyn.h | 34 + lib/network/http_file.cpp | 228 ++++ lib/network/http_file.h | 37 + lib/network/httpd.cpp | 631 +++++++++ lib/network/httpd.h | 123 ++ lib/network/serversocket.cpp | 55 + lib/network/serversocket.h | 18 + lib/network/serversocket.lo | Bin 0 -> 191574 bytes lib/network/socket.cpp | 294 +++++ lib/network/socket.h | 63 + lib/network/socket.lo | Bin 0 -> 224253 bytes lib/network/xmlrpc.cpp | 518 ++++++++ lib/network/xmlrpc.h | 81 ++ lib/service/Makefile.am | 8 + lib/service/Makefile.in | 0 lib/service/iservice.h | 173 +++ lib/service/service.cpp | 94 ++ lib/service/service.h | 29 + lib/service/servicedvb.cpp | 154 +++ lib/service/servicedvb.h | 45 + lib/service/servicefs.cpp | 112 ++ lib/service/servicefs.h | 33 + lib/service/servicemp3.cpp | 89 ++ lib/service/servicemp3.h | 42 + main/Makefile.am | 31 + main/Makefile.in | 0 main/enigma-dvbtest.cpp | 125 ++ main/enigma-scan.cpp | 94 ++ main/enigma.cpp | 90 ++ stamp-h.in | 1 + 360 files changed, 29963 insertions(+) create mode 100644 AUTHORS create mode 100644 ChangeLog create mode 100644 DEADJOE create mode 100644 Makefile.am create mode 100644 Makefile.in create mode 100644 NEWS create mode 100644 README create mode 100644 aclocal.m4 create mode 100755 autogen.sh create mode 100644 autom4te.cache/output.0 create mode 100644 autom4te.cache/requests create mode 100644 autom4te.cache/traces.0 create mode 100644 config.h.in create mode 100755 configure create mode 100644 configure.ac create mode 100755 debug create mode 100644 debug.gdb create mode 100644 include/connection.h create mode 100644 include/libsig_comp.h create mode 100644 lib/DEADJOE create mode 100644 lib/Makefile.am create mode 100644 lib/Makefile.in create mode 100644 lib/base/Makefile.am create mode 100644 lib/base/Makefile.in create mode 100644 lib/base/a create mode 100644 lib/base/buffer.cpp create mode 100644 lib/base/buffer.h create mode 100644 lib/base/console.cpp create mode 100644 lib/base/console.h create mode 100644 lib/base/ebase.cpp create mode 100644 lib/base/ebase.h create mode 100644 lib/base/econfig.cpp create mode 100644 lib/base/econfig.h create mode 100644 lib/base/eerror.cpp create mode 100644 lib/base/eerror.h create mode 100644 lib/base/elock.cpp create mode 100644 lib/base/elock.h create mode 100644 lib/base/eptrlist.h create mode 100644 lib/base/estring.cpp create mode 100644 lib/base/estring.h create mode 100644 lib/base/i18n.h create mode 100644 lib/base/init.cpp create mode 100644 lib/base/init.h create mode 100644 lib/base/init_num.h create mode 100644 lib/base/message.cpp create mode 100644 lib/base/message.h create mode 100644 lib/base/nconfig.cpp create mode 100644 lib/base/nconfig.h create mode 100644 lib/base/nxml.cpp create mode 100644 lib/base/object.h create mode 100644 lib/base/ringbuffer.h create mode 100644 lib/base/smartptr.cpp create mode 100644 lib/base/smartptr.h create mode 100644 lib/base/thread.cpp create mode 100644 lib/base/thread.h create mode 100644 lib/driver/Makefile.am create mode 100644 lib/driver/Makefile.in create mode 100644 lib/driver/input_fake.h create mode 100644 lib/driver/rc.cpp create mode 100644 lib/driver/rc.h create mode 100644 lib/driver/rcdbox.h create mode 100644 lib/driver/rcdreambox2.h create mode 100644 lib/driver/rcinput.cpp create mode 100644 lib/driver/rcinput.h create mode 100644 lib/driver/rfmod.h create mode 100644 lib/driver/streamwd.h create mode 100644 lib/dvb/Makefile.am create mode 100644 lib/dvb/Makefile.in create mode 100644 lib/dvb/crc32.cpp create mode 100644 lib/dvb/crc32.h create mode 100644 lib/dvb/db.cpp create mode 100644 lib/dvb/db.h create mode 100644 lib/dvb/decoder.cpp create mode 100644 lib/dvb/decoder.h create mode 100644 lib/dvb/demux.cpp create mode 100644 lib/dvb/demux.h create mode 100644 lib/dvb/dvb.cpp create mode 100644 lib/dvb/dvb.h create mode 100644 lib/dvb/esection.cpp create mode 100644 lib/dvb/esection.h create mode 100644 lib/dvb/frontend.cpp create mode 100644 lib/dvb/frontend.h create mode 100644 lib/dvb/idvb.h create mode 100644 lib/dvb/isection.h create mode 100644 lib/dvb/list.h create mode 100644 lib/dvb/pmt.cpp create mode 100644 lib/dvb/pmt.h create mode 100644 lib/dvb/scan.cpp create mode 100644 lib/dvb/scan.h create mode 100644 lib/dvb/sec.cpp create mode 100644 lib/dvb/sec.h create mode 100644 lib/dvb/specs.h create mode 100644 lib/dvb/stT1RVV7 create mode 100644 lib/dvb_si/Makefile.am create mode 100644 lib/dvb_si/Makefile.in create mode 100644 lib/dvb_si/ac3_descriptor.cpp create mode 100644 lib/dvb_si/ac3_descriptor.h create mode 100644 lib/dvb_si/ait.cpp create mode 100644 lib/dvb_si/ait.h create mode 100644 lib/dvb_si/ancillary_data_descriptor.cpp create mode 100644 lib/dvb_si/ancillary_data_descriptor.h create mode 100644 lib/dvb_si/announcement_support_descriptor.cpp create mode 100644 lib/dvb_si/announcement_support_descriptor.h create mode 100644 lib/dvb_si/application_signalling_descriptor.cpp create mode 100644 lib/dvb_si/application_signalling_descriptor.h create mode 100644 lib/dvb_si/audio_stream_descriptor.cpp create mode 100644 lib/dvb_si/audio_stream_descriptor.h create mode 100644 lib/dvb_si/bat.cpp create mode 100644 lib/dvb_si/bat.h create mode 100644 lib/dvb_si/bouquet_name_descriptor.cpp create mode 100644 lib/dvb_si/bouquet_name_descriptor.h create mode 100644 lib/dvb_si/ca_descriptor.cpp create mode 100644 lib/dvb_si/ca_descriptor.h create mode 100644 lib/dvb_si/ca_identifier_descriptor.cpp create mode 100644 lib/dvb_si/ca_identifier_descriptor.h create mode 100644 lib/dvb_si/ca_system_descriptor.cpp create mode 100644 lib/dvb_si/ca_system_descriptor.h create mode 100644 lib/dvb_si/cable_delivery_system_descriptor.cpp create mode 100644 lib/dvb_si/cable_delivery_system_descriptor.h create mode 100644 lib/dvb_si/camt.cpp create mode 100644 lib/dvb_si/camt.h create mode 100644 lib/dvb_si/capmt.cpp create mode 100644 lib/dvb_si/capmt.h create mode 100644 lib/dvb_si/cat.cpp create mode 100644 lib/dvb_si/cat.h create mode 100644 lib/dvb_si/cell_frequency_link_descriptor.cpp create mode 100644 lib/dvb_si/cell_frequency_link_descriptor.h create mode 100644 lib/dvb_si/cell_list_descriptor.cpp create mode 100644 lib/dvb_si/cell_list_descriptor.h create mode 100644 lib/dvb_si/component_descriptor.cpp create mode 100644 lib/dvb_si/component_descriptor.h create mode 100644 lib/dvb_si/container.cpp create mode 100644 lib/dvb_si/container.h create mode 100644 lib/dvb_si/content_descriptor.cpp create mode 100644 lib/dvb_si/content_descriptor.h create mode 100644 lib/dvb_si/copyright_descriptor.h create mode 100644 lib/dvb_si/country_availability_descriptor.cpp create mode 100644 lib/dvb_si/country_availability_descriptor.h create mode 100644 lib/dvb_si/data_broadcast_descriptor.cpp create mode 100644 lib/dvb_si/data_broadcast_descriptor.h create mode 100644 lib/dvb_si/data_broadcast_id_descriptor.cpp create mode 100644 lib/dvb_si/data_broadcast_id_descriptor.h create mode 100644 lib/dvb_si/data_stream_alignment_descriptor.h create mode 100644 lib/dvb_si/descriptor.cpp create mode 100644 lib/dvb_si/descriptor.h create mode 100644 lib/dvb_si/descriptor_tag.h create mode 100644 lib/dvb_si/dsng_descriptor.h create mode 100644 lib/dvb_si/eit.cpp create mode 100644 lib/dvb_si/eit.h create mode 100644 lib/dvb_si/element_descriptor.h create mode 100644 lib/dvb_si/extended_event_descriptor.cpp create mode 100644 lib/dvb_si/extended_event_descriptor.h create mode 100644 lib/dvb_si/frequency_list_descriptor.cpp create mode 100644 lib/dvb_si/frequency_list_descriptor.h create mode 100644 lib/dvb_si/group_descriptor.h create mode 100644 lib/dvb_si/hierarchy_descriptor.h create mode 100644 lib/dvb_si/ibp_descriptor.h create mode 100644 lib/dvb_si/ippv_booking_descriptor.h create mode 100644 lib/dvb_si/ippv_descriptor.h create mode 100644 lib/dvb_si/iso639_language_descriptor.cpp create mode 100644 lib/dvb_si/iso639_language_descriptor.h create mode 100644 lib/dvb_si/linkage_descriptor.cpp create mode 100644 lib/dvb_si/linkage_descriptor.h create mode 100644 lib/dvb_si/local_time_offset_descriptor.cpp create mode 100644 lib/dvb_si/local_time_offset_descriptor.h create mode 100644 lib/dvb_si/long_crc_table.cpp create mode 100644 lib/dvb_si/long_crc_table.h create mode 100644 lib/dvb_si/long_table.cpp create mode 100644 lib/dvb_si/long_table.h create mode 100644 lib/dvb_si/maximum_bitrate_descriptor.h create mode 100644 lib/dvb_si/mosaic_descriptor.cpp create mode 100644 lib/dvb_si/mosaic_descriptor.h create mode 100644 lib/dvb_si/multilingual_bouquet_name_descriptor.cpp create mode 100644 lib/dvb_si/multilingual_bouquet_name_descriptor.h create mode 100644 lib/dvb_si/multilingual_component_descriptor.cpp create mode 100644 lib/dvb_si/multilingual_component_descriptor.h create mode 100644 lib/dvb_si/multilingual_network_name_descriptor.cpp create mode 100644 lib/dvb_si/multilingual_network_name_descriptor.h create mode 100644 lib/dvb_si/multilingual_service_name_descriptor.cpp create mode 100644 lib/dvb_si/multilingual_service_name_descriptor.h create mode 100644 lib/dvb_si/multiplex_buffer_utilization_descriptor.h create mode 100644 lib/dvb_si/network_name_descriptor.cpp create mode 100644 lib/dvb_si/network_name_descriptor.h create mode 100644 lib/dvb_si/nit.cpp create mode 100644 lib/dvb_si/nit.h create mode 100644 lib/dvb_si/nvod_reference_descriptor.cpp create mode 100644 lib/dvb_si/nvod_reference_descriptor.h create mode 100644 lib/dvb_si/packet_id.h create mode 100644 lib/dvb_si/parental_rating_descriptor.cpp create mode 100644 lib/dvb_si/parental_rating_descriptor.h create mode 100644 lib/dvb_si/partial_transport_stream_descriptor.h create mode 100644 lib/dvb_si/pat.cpp create mode 100644 lib/dvb_si/pat.h create mode 100644 lib/dvb_si/pdc_descriptor.cpp create mode 100644 lib/dvb_si/pdc_descriptor.h create mode 100644 lib/dvb_si/pmt.cpp create mode 100644 lib/dvb_si/pmt.h create mode 100644 lib/dvb_si/private_data_indicator_descriptor.h create mode 100644 lib/dvb_si/private_data_specifier_descriptor.cpp create mode 100644 lib/dvb_si/private_data_specifier_descriptor.h create mode 100644 lib/dvb_si/registration_descriptor.h create mode 100644 lib/dvb_si/satellite_delivery_system_descriptor.cpp create mode 100644 lib/dvb_si/satellite_delivery_system_descriptor.h create mode 100644 lib/dvb_si/sdt.cpp create mode 100644 lib/dvb_si/sdt.h create mode 100644 lib/dvb_si/service_descriptor.cpp create mode 100644 lib/dvb_si/service_descriptor.h create mode 100644 lib/dvb_si/service_list_descriptor.cpp create mode 100644 lib/dvb_si/service_list_descriptor.h create mode 100644 lib/dvb_si/service_move_descriptor.cpp create mode 100644 lib/dvb_si/service_move_descriptor.h create mode 100644 lib/dvb_si/service_type.h create mode 100644 lib/dvb_si/short_crc_table.cpp create mode 100644 lib/dvb_si/short_crc_table.h create mode 100644 lib/dvb_si/short_event_descriptor.cpp create mode 100644 lib/dvb_si/short_event_descriptor.h create mode 100644 lib/dvb_si/short_smoothing_buffer_descriptor.h create mode 100644 lib/dvb_si/short_table.cpp create mode 100644 lib/dvb_si/short_table.h create mode 100644 lib/dvb_si/smoothing_buffer_descriptor.h create mode 100644 lib/dvb_si/std_descriptor.h create mode 100644 lib/dvb_si/stream_identifier_descriptor.cpp create mode 100644 lib/dvb_si/stream_identifier_descriptor.h create mode 100644 lib/dvb_si/stream_type.h create mode 100644 lib/dvb_si/stuffing_descriptor.cpp create mode 100644 lib/dvb_si/stuffing_descriptor.h create mode 100644 lib/dvb_si/subtitling_descriptor.cpp create mode 100644 lib/dvb_si/subtitling_descriptor.h create mode 100644 lib/dvb_si/system_clock_descriptor.h create mode 100644 lib/dvb_si/table_id.h create mode 100644 lib/dvb_si/target_background_grid_descriptor.cpp create mode 100644 lib/dvb_si/target_background_grid_descriptor.h create mode 100644 lib/dvb_si/tdt.cpp create mode 100644 lib/dvb_si/tdt.h create mode 100644 lib/dvb_si/telephone_descriptor.cpp create mode 100644 lib/dvb_si/telephone_descriptor.h create mode 100644 lib/dvb_si/teletext_descriptor.cpp create mode 100644 lib/dvb_si/teletext_descriptor.h create mode 100644 lib/dvb_si/terrestrial_delivery_system_descriptor.cpp create mode 100644 lib/dvb_si/terrestrial_delivery_system_descriptor.h create mode 100644 lib/dvb_si/time_shifted_event_descriptor.h create mode 100644 lib/dvb_si/time_shifted_service_descriptor.cpp create mode 100644 lib/dvb_si/time_shifted_service_descriptor.h create mode 100644 lib/dvb_si/tot.cpp create mode 100644 lib/dvb_si/tot.h create mode 100644 lib/dvb_si/transport_stream_descriptor.h create mode 100644 lib/dvb_si/url_descriptor.h create mode 100644 lib/dvb_si/vbi_data_descriptor.cpp create mode 100644 lib/dvb_si/vbi_data_descriptor.h create mode 100644 lib/dvb_si/vbi_teletext_descriptor.cpp create mode 100644 lib/dvb_si/vbi_teletext_descriptor.h create mode 100644 lib/dvb_si/video_stream_descriptor.cpp create mode 100644 lib/dvb_si/video_stream_descriptor.h create mode 100644 lib/dvb_si/video_window_descriptor.cpp create mode 100644 lib/dvb_si/video_window_descriptor.h create mode 100644 lib/gdi/.cvsignore create mode 100644 lib/gdi/Makefile.am create mode 100644 lib/gdi/epng.cpp create mode 100644 lib/gdi/epng.h create mode 100644 lib/gdi/epoint.h create mode 100644 lib/gdi/erect.cpp create mode 100644 lib/gdi/erect.h create mode 100644 lib/gdi/esize.h create mode 100644 lib/gdi/fb.cpp create mode 100644 lib/gdi/fb.h create mode 100644 lib/gdi/font.cpp create mode 100644 lib/gdi/font.cpp-new create mode 100644 lib/gdi/font.h create mode 100644 lib/gdi/font_arabic.cpp create mode 100644 lib/gdi/gfbdc.cpp create mode 100644 lib/gdi/gfbdc.h create mode 100644 lib/gdi/glcddc.cpp create mode 100644 lib/gdi/glcddc.h create mode 100644 lib/gdi/gpixmap.cpp create mode 100644 lib/gdi/gpixmap.h create mode 100644 lib/gdi/grc.cpp create mode 100644 lib/gdi/grc.h create mode 100644 lib/gdi/lcd.cpp create mode 100644 lib/gdi/lcd.h create mode 100644 lib/gui/.cvsignore create mode 100644 lib/gui/Makefile.am create mode 100644 lib/gui/actions.cpp create mode 100644 lib/gui/actions.h create mode 100644 lib/gui/combobox.cpp create mode 100644 lib/gui/combobox.h create mode 100644 lib/gui/decoration.cpp create mode 100644 lib/gui/decoration.h create mode 100644 lib/gui/ebutton.cpp create mode 100644 lib/gui/ebutton.h create mode 100644 lib/gui/echeckbox.cpp create mode 100644 lib/gui/echeckbox.h create mode 100644 lib/gui/elabel.cpp create mode 100644 lib/gui/elabel.h create mode 100644 lib/gui/emessage.cpp create mode 100644 lib/gui/emessage.h create mode 100644 lib/gui/enumber.cpp create mode 100644 lib/gui/enumber.h create mode 100644 lib/gui/epixmap.cpp create mode 100644 lib/gui/epixmap.h create mode 100644 lib/gui/eprogress.cpp create mode 100644 lib/gui/eprogress.h create mode 100644 lib/gui/eservicegrid.cpp create mode 100644 lib/gui/eservicegrid.h create mode 100644 lib/gui/eskin.cpp create mode 100644 lib/gui/eskin.h create mode 100644 lib/gui/eskin_register.cpp create mode 100644 lib/gui/eskin_register.h create mode 100644 lib/gui/ewidget.cpp create mode 100644 lib/gui/ewidget.h create mode 100644 lib/gui/ewindow.cpp create mode 100644 lib/gui/ewindow.h create mode 100644 lib/gui/guiactions.cpp create mode 100644 lib/gui/guiactions.h create mode 100644 lib/gui/listbox.cpp create mode 100644 lib/gui/listbox.h create mode 100644 lib/gui/multipage.cpp create mode 100644 lib/gui/multipage.h create mode 100644 lib/gui/numberactions.cpp create mode 100644 lib/gui/numberactions.h create mode 100644 lib/gui/slider.cpp create mode 100644 lib/gui/slider.h create mode 100644 lib/gui/statusbar.cpp create mode 100644 lib/gui/statusbar.h create mode 100644 lib/gui/testpicture.cpp create mode 100644 lib/gui/testpicture.h create mode 100644 lib/gui/textinput.cpp create mode 100644 lib/gui/textinput.h create mode 100644 lib/network/Makefile.am create mode 100644 lib/network/Makefile.in create mode 100644 lib/network/http_dyn.cpp create mode 100644 lib/network/http_dyn.h create mode 100644 lib/network/http_file.cpp create mode 100644 lib/network/http_file.h create mode 100644 lib/network/httpd.cpp create mode 100644 lib/network/httpd.h create mode 100644 lib/network/serversocket.cpp create mode 100644 lib/network/serversocket.h create mode 100644 lib/network/serversocket.lo create mode 100644 lib/network/socket.cpp create mode 100644 lib/network/socket.h create mode 100644 lib/network/socket.lo create mode 100644 lib/network/xmlrpc.cpp create mode 100644 lib/network/xmlrpc.h create mode 100644 lib/service/Makefile.am create mode 100644 lib/service/Makefile.in create mode 100644 lib/service/iservice.h create mode 100644 lib/service/service.cpp create mode 100644 lib/service/service.h create mode 100644 lib/service/servicedvb.cpp create mode 100644 lib/service/servicedvb.h create mode 100644 lib/service/servicefs.cpp create mode 100644 lib/service/servicefs.h create mode 100644 lib/service/servicemp3.cpp create mode 100644 lib/service/servicemp3.h create mode 100644 main/Makefile.am create mode 100644 main/Makefile.in create mode 100644 main/enigma-dvbtest.cpp create mode 100644 main/enigma-scan.cpp create mode 100644 main/enigma.cpp create mode 100644 stamp-h.in diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..e69de29 diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/DEADJOE b/DEADJOE new file mode 100644 index 0000000..e69de29 diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..157daf0 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,4 @@ +AUTOMAKE_OPTIONS = gnu + +SUBDIRS = lib main + diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..e69de29 diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..e69de29 diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..e69de29 diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..d646011 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,62 @@ +#!/bin/sh + +package="tuxbox-enigma" + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +cd "$srcdir" +DIE=0 + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have autoconf installed to compile $package." + echo "Download the appropriate package for your system," + echo "or get the source from one of the GNU ftp sites" + echo "listed in http://www.gnu.org/order/ftp.html" + DIE=1 +} + +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have automake installed to compile $package." + echo "Download the appropriate package for your system," + echo "or get the source from one of the GNU ftp sites" + echo "listed in http://www.gnu.org/order/ftp.html" + DIE=1 +} + +(libtool --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have libtool installed to compile $package." + echo "Download the appropriate package for your system," + echo "or get the source from one of the GNU ftp sites" + echo "listed in http://www.gnu.org/order/ftp.html" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +if [ ! -e acinclude.m4 ]; then + for i in .. ../.. ../../..; do + if [ -e `pwd`/$i/acinclude.m4 ]; then + ln -s `pwd`/$i/acinclude.m4 . + fi + done +fi + +echo "Generating configuration files for $package, please wait...." + +echo " aclocal" +aclocal +echo " libtoolize --automake" +libtoolize --automake +echo " autoconf" +autoconf +echo " autoheader" +autoheader +echo " automake --add-missing" +automake --add-missing + diff --git a/autom4te.cache/output.0 b/autom4te.cache/output.0 new file mode 100644 index 0000000..e69de29 diff --git a/autom4te.cache/requests b/autom4te.cache/requests new file mode 100644 index 0000000..e69de29 diff --git a/autom4te.cache/traces.0 b/autom4te.cache/traces.0 new file mode 100644 index 0000000..e69de29 diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..3224a09 --- /dev/null +++ b/config.h.in @@ -0,0 +1,97 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* where to find the config files */ +#undef CONFIGDIR + +/* where to find data like icons */ +#undef DATADIR + +/* Enable debug messages */ +#undef DEBUG + +/* Define to 1 if translation of program messages to the user's native + language is requested. */ +#undef ENABLE_NLS + +/* where to find the fonts */ +#undef FONTDIR + +/* where games data is stored */ +#undef GAMESDIR + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_DVB_VERSION_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_OST_DMX_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* where to find the internal libs */ +#undef LIBDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* where to find the plugins */ +#undef PLUGINDIR + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* where to find the ucodes (firmware) */ +#undef UCODEDIR + +/* Version number of package */ +#undef VERSION + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES diff --git a/configure b/configure new file mode 100755 index 0000000..e69de29 diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..54d9789 --- /dev/null +++ b/configure.ac @@ -0,0 +1,42 @@ +AC_INIT(tuxbox-enigma,0.0.1) +AM_INIT_AUTOMAKE(tuxbox-enigma,0.0.1) + +TUXBOX_APPS +TUXBOX_APPS_DIRECTORY + +AC_PROG_CC +AC_PROG_CXX +AC_PROG_RANLIB + +TUXBOX_APPS_DVB +TUXBOX_APPS_DRIVER + +TUXBOX_APPS_LIB_CONFIG(FREETYPE,freetype-config) +TUXBOX_APPS_LIB_PKGCONFIG(FRIBIDI,fribidi) +TUXBOX_APPS_LIB_PKGCONFIG(ID3TAG,id3tag) +TUXBOX_APPS_LIB_PKGCONFIG(MAD,mad) +TUXBOX_APPS_LIB_PKGCONFIG(MD5SUM,tuxbox-md5sum) +TUXBOX_APPS_LIB_PKGCONFIG(PLUGINS,tuxbox-plugins) +TUXBOX_APPS_LIB_PKGCONFIG(PNG,libpng) +TUXBOX_APPS_LIB_PKGCONFIG(SIGC,sigc++-1.2) +TUXBOX_APPS_LIB_PKGCONFIG(XMLTREE,tuxbox-xmltree) + +CPPFLAGS="$CPPFLAGS $FREETYPE_CFLAGS $FRIBIDI_CFLAGS $ID3TAG_CFLAGS $MAD_CFLAGS $MD5SUM_CFLAGS $PLUGINS_CFLAGS $PNG_CFLAGS $SIGC_CFLAGS $XMLTREE_CFLAGS" +CXXFLAGS="$CXXFLAGS -fno-rtti -fno-exceptions" + +TUXBOX_APPS_GETTEXT + +AC_OUTPUT([ +Makefile +lib/Makefile +lib/base/Makefile +lib/driver/Makefile +lib/dvb/Makefile +lib/dvb_si/Makefile +lib/gdi/Makefile +lib/gui/Makefile +lib/network/Makefile +lib/service/Makefile +main/Makefile +]) + diff --git a/debug b/debug new file mode 100755 index 0000000..d9b8771 --- /dev/null +++ b/debug @@ -0,0 +1,5 @@ +#!/bin/sh + +powerpc-linux-gdb /dbox2/cdkroot/bin/enigma2 -x debug.gdb + + diff --git a/debug.gdb b/debug.gdb new file mode 100644 index 0000000..f2b4fca --- /dev/null +++ b/debug.gdb @@ -0,0 +1,3 @@ +set solib-absolute-prefix /dbox2/cdkroot +target remote 10.0.0.82:1234 +continue diff --git a/include/connection.h b/include/connection.h new file mode 100644 index 0000000..3ccaec1 --- /dev/null +++ b/include/connection.h @@ -0,0 +1,17 @@ +#ifndef __connection_h +#define __connection_h + +#include +#include + +class eConnection: public virtual iObject, public Connection +{ + int ref; +public: +DEFINE_REF(eConnection); +public: + eConnection(const Connection &conn): Connection(conn), ref(0) { }; + virtual ~eConnection() { disconnect(); } +}; + +#endif diff --git a/include/libsig_comp.h b/include/libsig_comp.h new file mode 100644 index 0000000..83550ed --- /dev/null +++ b/include/libsig_comp.h @@ -0,0 +1,33 @@ +#ifndef __LIBSIG_COMP_H +#define __LIBSIG_COMP_H + +#include +#include + +#ifdef SIGC_CXX_NAMESPACES +using namespace SigC; +#endif + +#define CONNECT(SENDER, EMPFAENGER) SENDER.connect(slot(*this, &EMPFAENGER)) +// use this Makro to connect with a method +// void bla::foo(int x); +// to an +// Signal testSig; +// +// CONNECT(testSig, bla::foo); +// signal and method (slot) must have the same signature + +#define CONNECT_1_0(SENDER, EMPFAENGER, PARAM) SENDER.connect( bind( slot(*this, &EMPFAENGER) ,PARAM ) ) +// use this for connect with a method +// void bla::foo(int); +// to an +// Signal0 testSig; +// CONNECT_1_0(testSig, bla:foo, 0); +// here the signal has no parameter, but the slot have an int +// the last parameter of the CONNECT_1_0 makro is the value that given to the paramater of the Slot method + +#define CONNECT_2_0(SENDER, EMPFAENGER, PARAM1, PARAM2) SENDER.connect( bind( slot(*this, &EMPFAENGER) ,PARAM1, PARAM2 ) ) + +#define CONNECT_2_1(SENDER, EMPFAENGER, PARAM) SENDER.connect( bind( slot(*this, &EMPFAENGER) ,PARAM ) ) + +#endif // __LIBSIG_COMP_H diff --git a/lib/DEADJOE b/lib/DEADJOE new file mode 100644 index 0000000..e69de29 diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..7811ca1 --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = base dvb dvb_si gdi gui network service driver + + diff --git a/lib/Makefile.in b/lib/Makefile.in new file mode 100644 index 0000000..e69de29 diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am new file mode 100644 index 0000000..3e695a3 --- /dev/null +++ b/lib/base/Makefile.am @@ -0,0 +1,9 @@ +INCLUDES = \ + -I$(top_srcdir)/include + +noinst_LIBRARIES = libenigma_base.a + +libenigma_base_a_SOURCES = \ + buffer.cpp console.cpp ebase.cpp econfig.cpp eerror.cpp elock.cpp \ + estring.cpp init.cpp message.cpp nconfig.cpp nxml.cpp thread.cpp \ + smartptr.cpp diff --git a/lib/base/Makefile.in b/lib/base/Makefile.in new file mode 100644 index 0000000..e69de29 diff --git a/lib/base/a b/lib/base/a new file mode 100644 index 0000000..e69de29 diff --git a/lib/base/buffer.cpp b/lib/base/buffer.cpp new file mode 100644 index 0000000..07e9d7f --- /dev/null +++ b/lib/base/buffer.cpp @@ -0,0 +1,196 @@ +#include +#include +#include +#include +#include + +void eIOBuffer::removeblock() +{ + ASSERT(!buffer.empty()); + eIOBufferData &b=buffer.front(); + delete[] b.data; + buffer.pop_front(); + ptr=0; +} + +eIOBuffer::eIOBufferData &eIOBuffer::addblock() +{ + eIOBufferData s; + s.data=new __u8[allocationsize]; + s.len=0; + buffer.push_back(s); + return buffer.back(); +} + +eIOBuffer::~eIOBuffer() +{ + clear(); +} + +void eIOBuffer::clear() +{ + while (!buffer.empty()) + removeblock(); +} + +int eIOBuffer::size() const +{ + int total=0; + for (std::list::const_iterator i(buffer.begin()); i != buffer.end(); ++i) + total+=i->len; + total-=ptr; + return total; +} + +int eIOBuffer::empty() const +{ + return buffer.empty(); +} + +int eIOBuffer::peek(void *dest, int len) const +{ + __u8 *dst=(__u8*)dest; + std::list::const_iterator i(buffer.begin()); + int p=ptr; + int written=0; + while (len) + { + if (i == buffer.end()) + break; + int tc=i->len-p; + if (tc > len) + tc = len; + + memcpy(dst, i->data+p, tc); + dst+=tc; + written+=tc; + + ++i; + p=0; + + len-=tc; + } + return written; +} + +void eIOBuffer::skip(int len) +{ + while (len) + { + ASSERT(! buffer.empty()); + int tn=len; + if (tn > (buffer.front().len-ptr)) + tn=buffer.front().len-ptr; + + ptr+=tn; + if (ptr == buffer.front().len) + removeblock(); + len-=tn; + } +} + +int eIOBuffer::read(void *dest, int len) +{ + __u8 *dst=(__u8*)dest; + len=peek(dst, len); + skip(len); + return len; +} + +void eIOBuffer::write(const void *source, int len) +{ + const __u8 *src=(const __u8*)source; + while (len) + { + int tc=len; + if (buffer.empty() || (allocationsize == buffer.back().len)) + addblock(); + if (tc > allocationsize-buffer.back().len) + tc=allocationsize-buffer.back().len; + memcpy(buffer.back().data+buffer.back().len, src, tc); + src+=tc; + buffer.back().len+=tc; + len-=tc; + } +} + +int eIOBuffer::fromfile(int fd, int len) +{ + int re=0; + while (len) + { + int tc=len; + int r; + if (buffer.empty() || (allocationsize == buffer.back().len)) + addblock(); + if (tc > allocationsize-buffer.back().len) + tc=allocationsize-buffer.back().len; + r=::read(fd, buffer.back().data+buffer.back().len, tc); + buffer.back().len+=r; + len-=r; + if (r < 0) + { + if (errno != EWOULDBLOCK) + eDebug("read: %m"); + r=0; + } + re+=r; + if (r != tc) + break; + } + return re; +} + +int eIOBuffer::tofile(int fd, int len) +{ + int written=0; + int w; + while (len && !buffer.empty()) + { + if (buffer.begin() == buffer.end()) + break; + int tc=buffer.front().len-ptr; + if (tc > len) + tc = len; + + w=::write(fd, buffer.front().data+ptr, tc); + if (w < 0) + { + if (errno != EWOULDBLOCK) + eDebug("write: %m"); + w=0; + } + ptr+=w; + if (ptr == buffer.front().len) + removeblock(); + written+=w; + + len-=w; + if (tc != w) + break; + } + return written; +} + +int eIOBuffer::searchchr(char ch) const +{ + std::list::const_iterator i(buffer.begin()); + int p=ptr; + int c=0; + while (1) + { + if (i == buffer.end()) + break; + while (p < i->len) + { + if (i->data[p] == ch) + return c; + else + c++, p++; + } + ++i; + p=0; + } + return -1; +} + diff --git a/lib/base/buffer.h b/lib/base/buffer.h new file mode 100644 index 0000000..9108ba8 --- /dev/null +++ b/lib/base/buffer.h @@ -0,0 +1,40 @@ +#ifndef __src_lib_base_buffer_h +#define __src_lib_base_buffer_h + +#include +#include + +/** + * IO buffer. + */ +class eIOBuffer +{ + int allocationsize; + struct eIOBufferData + { + __u8 *data; + int len; + }; + std::list buffer; + void removeblock(); + eIOBufferData &addblock(); + int ptr; +public: + eIOBuffer(int allocationsize): allocationsize(allocationsize), ptr(0) + { + } + ~eIOBuffer(); + int size() const; + int empty() const; + void clear(); + int peek(void *dest, int len) const; + void skip(int len); + int read(void *dest, int len); + void write(const void *source, int len); + int fromfile(int fd, int len); + int tofile(int fd, int len); + + int searchchr(char ch) const; +}; + +#endif diff --git a/lib/base/console.cpp b/lib/base/console.cpp new file mode 100644 index 0000000..2609582 --- /dev/null +++ b/lib/base/console.cpp @@ -0,0 +1,248 @@ +/* + * console.cpp + * + * Copyright (C) 2002 Felix Domke + * + * 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. + * + * $Id: console.cpp,v 1.1 2003-10-17 15:35:47 tmbinc Exp $ + */ + +#include + +#include +#include // for statfs +#include +#include +#include + +int bidirpipe(int pfd[], char *cmd , char *argv[]) +{ + int pfdin[2]; /* from child to parent */ + int pfdout[2]; /* from parent to child */ + int pfderr[2]; /* stderr from child to parent */ + int pid; /* child's pid */ + + if ( pipe(pfdin) == -1 || pipe(pfdout) == -1 || pipe(pfderr) == -1) + return(-1); + + if ( ( pid = fork() ) == -1 ) + return(-1); + else if (pid == 0) /* child process */ + { + if ( close(0) == -1 || close(1) == -1 || close(2) == -1 ) + _exit(0); + + if (dup(pfdout[0]) != 0 || dup(pfdin[1]) != 1 || dup(pfderr[1]) != 2 ) + _exit(0); + + if (close(pfdout[0]) == -1 || close(pfdout[1]) == -1 || + close(pfdin[0]) == -1 || close(pfdin[1]) == -1 || + close(pfderr[0]) == -1 || close(pfderr[1]) == -1 ) + _exit(0); + + execv(cmd,argv); + _exit(0); + } + if (close(pfdout[0]) == -1 || close(pfdin[1]) == -1 || close(pfderr[1]) == -1) + return(-1); + + pfd[0] = pfdin[0]; + pfd[1] = pfdout[1]; + pfd[2] = pfderr[0]; + + return(pid); +} + +eConsoleAppContainer::eConsoleAppContainer( const eString &cmd ) +:pid(-1), killstate(0), outbuf(0) +{ +// eDebug("cmd = %s", cmd.c_str() ); + memset(fd, 0, sizeof(fd) ); + int cnt=2; // path to app + terminated 0 + eString str(cmd?cmd:""); + + while( str.length() && str[0] == ' ' ) // kill spaces at beginning + str = str.mid(1); + + while( str.length() && str[str.length()-1] == ' ' ) // kill spaces at the end + str = str.left( str.length() - 1 ); + + if (!str.length()) + return; + + unsigned int idx=0; + eString path = str.left( (idx = str.find(' ')) != eString::npos ? idx : str.length() ); +// eDebug("path = %s", path.c_str() ); + + eString cmds = str.mid( path.length()+1 ); +// eDebug("cmds = %s", cmds.c_str() ); + + idx = 0; + while ( (idx = cmds.find(' ',idx) ) != eString::npos ) // count args + { + cnt++; + idx++; + } + +// eDebug("idx = %d, %d counted spaces", idx, cnt-2); + + if ( cmds.length() ) + { + cnt++; +// eDebug("increase cnt"); + } + +// eDebug("%d args", cnt-2); + char **argv = new char*[cnt]; // min two args... path and terminating 0 + argv[0] = new char[ path.length() ]; + strcpy( argv[0], path.c_str() ); + argv[cnt-1] = 0; // set terminating null + + if ( cnt > 2 ) // more then default args? + { + cnt=1; // do not overwrite path in argv[0] + + while ( (idx = cmds.find(' ')) != eString::npos ) // parse all args.. + { + argv[cnt] = new char[ idx ]; +// eDebug("idx=%d, arg = %s", idx, cmds.left(idx).c_str() ); + strcpy( argv[cnt++], cmds.left( idx ).c_str() ); + cmds = cmds.mid(idx+1); +// eDebug("str = %s", cmds.c_str() ); + } + // store the last arg + argv[cnt] = new char[ cmds.length() ]; + strcpy( argv[cnt], cmds.c_str() ); + } + + // get one read ,one write and the err pipe to the prog.. + + if ( (pid = bidirpipe(fd, argv[0], argv)) == -1 ) + { + while ( cnt-- > 0 ) + delete [] argv[cnt]; + delete [] argv; + return; + } + + while ( cnt-- > 0 ) // release heap memory + delete [] argv[cnt]; + delete [] argv; + + eDebug("pipe in = %d, out = %d, err = %d", fd[0], fd[1], fd[2]); + + in = new eSocketNotifier(eApp, fd[0], 19 ); // 19 = POLLIN, POLLPRI, POLLHUP + out = new eSocketNotifier(eApp, fd[1], eSocketNotifier::Write); // POLLOUT + err = new eSocketNotifier(eApp, fd[2], 19 ); // 19 = POLLIN, POLLPRI, POLLHUP + CONNECT(in->activated, eConsoleAppContainer::readyRead); + CONNECT(out->activated, eConsoleAppContainer::readyWrite); + CONNECT(err->activated, eConsoleAppContainer::readyErrRead); + signal(SIGCHLD, SIG_IGN); // no zombie when child killed +} + +eConsoleAppContainer::~eConsoleAppContainer() +{ + if ( running() ) + { + killstate=-1; + kill(); + } + if ( outbuf ) + delete [] outbuf; +} + +void eConsoleAppContainer::kill() +{ + killstate=-1; + system( eString().sprintf("kill %d", pid).c_str() ); + eDebug("user kill console App"); +} + +void eConsoleAppContainer::closePipes() +{ + in->stop(); + out->stop(); + err->stop(); + ::close(fd[0]); + fd[0]=0; + ::close(fd[1]); + fd[1]=0; + ::close(fd[2]); + fd[2]=0; + eDebug("pipes closed"); +} + +void eConsoleAppContainer::readyRead(int what) +{ + if (what & POLLPRI|POLLIN) + { + eDebug("what = %d"); + char buf[2048]; + int readed = read(fd[0], buf, 2048); + eDebug("%d bytes read", readed); + if ( readed != -1 && readed ) + /*emit*/ dataAvail( eString( buf ) ); + else if (readed == -1) + eDebug("readerror %d", errno); + } + if (what & eSocketNotifier::Hungup) + { + eDebug("child has terminated"); + closePipes(); + /*emit*/ appClosed(killstate); + } +} + +void eConsoleAppContainer::readyErrRead(int what) +{ + if (what & POLLPRI|POLLIN) + { + eDebug("what = %d"); + char buf[2048]; + int readed = read(fd[2], buf, 2048); + eDebug("%d bytes read", readed); + if ( readed != -1 && readed ) + /*emit*/ dataAvail( eString( buf ) ); + else if (readed == -1) + eDebug("readerror %d", errno); + } +} + +void eConsoleAppContainer::write( const eString & str ) +{ + outbuf = new char[ str.length()]; + strcpy( outbuf, str.c_str() ); +} + +void eConsoleAppContainer::readyWrite(int what) +{ + if (what == 4 && outbuf) + { + if ( ::write( fd[1], outbuf, strlen(outbuf) ) != (int) strlen(outbuf) ) + { + /* emit */ dataSent(-1); + eDebug("writeError"); + } + else + { + /* emit */ dataSent(0); + eDebug("write ok"); + } + + delete outbuf; + outbuf=0; + } +} diff --git a/lib/base/console.h b/lib/base/console.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/base/ebase.cpp b/lib/base/ebase.cpp new file mode 100644 index 0000000..3babc2e --- /dev/null +++ b/lib/base/ebase.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +#include + +eSocketNotifier::eSocketNotifier(eMainloop *context, int fd, int requested, bool startnow): context(*context), fd(fd), state(0), requested(requested) +{ + if (startnow) + start(); +} + +eSocketNotifier::~eSocketNotifier() +{ + stop(); +} + +void eSocketNotifier::start() +{ + if (state) + stop(); + + context.addSocketNotifier(this); + state=1; +} + +void eSocketNotifier::stop() +{ + if (state) + context.removeSocketNotifier(this); + + state=0; +} + + // timer +void eTimer::start(long msek, bool singleShot) +{ + if (bActive) + stop(); + + bActive = true; + bSingleShot = singleShot; + interval = msek; + gettimeofday(&nextActivation, 0); +// eDebug("this = %p\nnow sec = %d, usec = %d\nadd %d msec", this, nextActivation.tv_sec, nextActivation.tv_usec, msek); + nextActivation += (msek<0 ? 0 : msek); +// eDebug("next Activation sec = %d, usec = %d", nextActivation.tv_sec, nextActivation.tv_usec ); + context.addTimer(this); +} + +void eTimer::stop() +{ + if (bActive) + { + bActive=false; + context.removeTimer(this); + } +} + +void eTimer::changeInterval(long msek) +{ + if (bActive) // Timer is running? + { + context.removeTimer(this); // then stop + nextActivation -= interval; // sub old interval + } + else + bActive=true; // then activate Timer + + interval = msek; // set new Interval + nextActivation += interval; // calc nextActivation + + context.addTimer(this); // add Timer to context TimerList +} + +void eTimer::activate() // Internal Funktion... called from eApplication +{ + timeval now; + gettimeofday(&now, 0); +// eDebug("this = %p\nnow sec = %d, usec = %d\nnextActivation sec = %d, usec = %d", this, now.tv_sec, now.tv_usec, nextActivation.tv_sec, nextActivation.tv_usec ); +// eDebug("Timer emitted"); + context.removeTimer(this); + + if (!bSingleShot) + { + nextActivation += interval; + context.addTimer(this); + } + else + bActive=false; + + /*emit*/ timeout(); +} + +// mainloop + +void eMainloop::addSocketNotifier(eSocketNotifier *sn) +{ + notifiers.insert(std::pair (sn->getFD(), sn)); +} + +void eMainloop::removeSocketNotifier(eSocketNotifier *sn) +{ + notifiers.erase(sn->getFD()); +} + +void eMainloop::processOneEvent() +{ +// process pending timers... + long usec=0; + + while (TimerList && (usec = timeout_usec( TimerList.begin()->getNextActivation() ) ) <= 0 ) + TimerList.begin()->activate(); + + int fdAnz = notifiers.size(); + pollfd* pfd = new pollfd[fdAnz]; // make new pollfd array + +// fill pfd array + std::map::iterator it(notifiers.begin()); + for (int i=0; i < fdAnz; i++, it++) + { + pfd[i].fd = it->first; + pfd[i].events = it->second->getRequested(); + } + + int ret=poll(pfd, fdAnz, TimerList ? usec / 1000 : -1); // milli .. not micro seks + + if (ret>0) + { +// eDebug("bin aussem poll raus und da war was"); + for (int i=0; i < fdAnz ; i++) + { + if( notifiers.find(pfd[i].fd) == notifiers.end()) + continue; + + int req = notifiers[pfd[i].fd]->getRequested(); + + if ( pfd[i].revents & req ) + { + notifiers[pfd[i].fd]->activate(pfd[i].revents); + + if (!--ret) + break; + } else if (pfd[i].revents & (POLLERR|POLLHUP|POLLNVAL)) + eDebug("poll: unhandled POLLERR/HUP/NVAL for fd %d(%d)", pfd[i].fd,pfd[i].revents); + } + } + else if (ret<0) + eDebug("poll made error"); + + // check Timers... + while ( TimerList && timeout_usec( TimerList.begin()->getNextActivation() ) <= 0 ) + TimerList.begin()->activate(); + + delete [] pfd; +} + + +int eMainloop::exec() +{ + if (!loop_level) + { + app_quit_now = false; + enter_loop(); + } + return retval; +} + +void eMainloop::enter_loop() +{ + loop_level++; + + // Status der vorhandenen Loop merken + bool old_exit_loop = app_exit_loop; + + app_exit_loop = false; + + while (!app_exit_loop && !app_quit_now) + { + processOneEvent(); + } + + // wiederherstellen der vorherigen app_exit_loop + app_exit_loop = old_exit_loop; + + loop_level--; + + if (!loop_level) + { + // do something here on exit the last loop + } +} + +void eMainloop::exit_loop() // call this to leave the current loop +{ + app_exit_loop = true; +} + +void eMainloop::quit( int ret ) // call this to leave all loops +{ + retval=ret; + app_quit_now = true; +} + +eApplication* eApp = 0; diff --git a/lib/base/ebase.h b/lib/base/ebase.h new file mode 100644 index 0000000..b929cb6 --- /dev/null +++ b/lib/base/ebase.h @@ -0,0 +1,252 @@ +#ifndef __ebase_h +#define __ebase_h + +#include +#include +#include +#include +#include +#include + +#include +#include + +class eApplication; + +extern eApplication* eApp; + +static inline bool operator<( const timeval &t1, const timeval &t2 ) +{ + return t1.tv_sec < t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_usec < t2.tv_usec); +} + +static inline timeval &operator+=( timeval &t1, const timeval &t2 ) +{ + t1.tv_sec += t2.tv_sec; + if ( (t1.tv_usec += t2.tv_usec) >= 1000000 ) + { + t1.tv_sec++; + t1.tv_usec -= 1000000; + } + return t1; +} + +static inline timeval operator+( const timeval &t1, const timeval &t2 ) +{ + timeval tmp; + tmp.tv_sec = t1.tv_sec + t2.tv_sec; + if ( (tmp.tv_usec = t1.tv_usec + t2.tv_usec) >= 1000000 ) + { + tmp.tv_sec++; + tmp.tv_usec -= 1000000; + } + return tmp; +} + +static inline timeval operator-( const timeval &t1, const timeval &t2 ) +{ + timeval tmp; + tmp.tv_sec = t1.tv_sec - t2.tv_sec; + if ( (tmp.tv_usec = t1.tv_usec - t2.tv_usec) < 0 ) + { + tmp.tv_sec--; + tmp.tv_usec += 1000000; + } + return tmp; +} + +static inline timeval operator-=( timeval &t1, const timeval &t2 ) +{ + t1.tv_sec -= t2.tv_sec; + if ( (t1.tv_usec -= t2.tv_usec) < 0 ) + { + t1.tv_sec--; + t1.tv_usec += 1000000; + } + return t1; +} + +static inline timeval &operator+=( timeval &t1, const long msek ) +{ + t1.tv_sec += msek / 1000; + if ( (t1.tv_usec += (msek % 1000) * 1000) >= 1000000 ) + { + t1.tv_sec++; + t1.tv_usec -= 1000000; + } + return t1; +} + +static inline timeval operator+( const timeval &t1, const long msek ) +{ + timeval tmp; + tmp.tv_sec = t1.tv_sec + msek / 1000; + if ( (tmp.tv_usec = t1.tv_usec + (msek % 1000) * 1000) >= 1000000 ) + { + tmp.tv_sec++; + tmp.tv_usec -= 1000000; + } + return tmp; +} + +static inline timeval operator-( const timeval &t1, const long msek ) +{ + timeval tmp; + tmp.tv_sec = t1.tv_sec - msek / 1000; + if ( (tmp.tv_usec = t1.tv_usec - (msek % 1000)*1000) < 0 ) + { + tmp.tv_sec--; + tmp.tv_usec += 1000000; + } + return tmp; +} + +static inline timeval operator-=( timeval &t1, const long msek ) +{ + t1.tv_sec -= msek / 1000; + if ( (t1.tv_usec -= (msek % 1000) * 1000) < 0 ) + { + t1.tv_sec--; + t1.tv_usec += 1000000; + } + return t1; +} + +static inline timeval timeout_timeval ( const timeval & orig ) +{ + timeval now; + gettimeofday(&now,0); + + return orig-now; +} + +static inline long timeout_usec ( const timeval & orig ) +{ + timeval now; + gettimeofday(&now,0); + + return (orig-now).tv_sec*1000000 + (orig-now).tv_usec; +} + +class eMainloop; + + // die beiden signalquellen: SocketNotifier... + +/** + * \brief Gives a callback when data on a file descriptor is ready. + * + * This class emits the signal \c eSocketNotifier::activate whenever the + * event specified by \c req is available. + */ +class eSocketNotifier +{ +public: + enum { Read=POLLIN, Write=POLLOUT, Priority=POLLPRI, Error=POLLERR, Hungup=POLLHUP }; +private: + eMainloop &context; + int fd; + int state; + int requested; // requested events (POLLIN, ...) +public: + /** + * \brief Constructs a eSocketNotifier. + * \param context The thread where to bind the socketnotifier to. The signal is emitted from that thread. + * \param fd The filedescriptor to monitor. Can be a device or a socket. + * \param req The events to watch to, normally either \c Read or \c Write. You can specify any events that \c poll supports. + * \param startnow Specifies if the socketnotifier should start immediately. + */ + eSocketNotifier(eMainloop *context, int fd, int req, bool startnow=true); + ~eSocketNotifier(); + + Signal1 activated; + void activate(int what) { /*emit*/ activated(what); } + + void start(); + void stop(); + bool isRunning() { return state; } + + int getFD() { return fd; } + int getRequested() { return requested; } + void setRequested(int req) { requested=req; } +}; + + // ... und Timer +/** + * \brief Gives a callback after a specified timeout. + * + * This class emits the signal \c eTimer::timeout after the specified timeout. + */ +class eTimer +{ + eMainloop &context; + timeval nextActivation; + long interval; + bool bSingleShot; + bool bActive; +public: + /** + * \brief Constructs a timer. + * + * The timer is not yet active, it has to be started with \c start. + * \param context The thread from which the signal should be emitted. + */ + eTimer(eMainloop *context): context(*context), bActive(false) { } + ~eTimer() { if (bActive) stop(); } + + Signal0 timeout; + void activate(); + + bool isActive() { return bActive; } + timeval &getNextActivation() { return nextActivation; } + + void start(long msec, bool singleShot=false); + void stop(); + void changeInterval(long msek); + bool operator<(const eTimer& t) const { return nextActivation < t.nextActivation; } +}; + + // werden in einer mainloop verarbeitet +class eMainloop +{ + std::map notifiers; + ePtrList TimerList; + bool app_exit_loop; + bool app_quit_now; + int loop_level; + void processOneEvent(); + int retval; +public: + eMainloop():app_quit_now(0),loop_level(0),retval(0){ } + void addSocketNotifier(eSocketNotifier *sn); + void removeSocketNotifier(eSocketNotifier *sn); + void addTimer(eTimer* e) { TimerList.push_back(e); TimerList.sort(); } + void removeTimer(eTimer* e) { TimerList.remove(e); } + + int looplevel() { return loop_level; } + + int exec(); // recursive enter the loop + void quit(int ret=0); // leave all pending loops (recursive leave()) + void enter_loop(); + void exit_loop(); +}; + +/** + * \brief The application class. + * + * An application provides a mainloop, and runs in the primary thread. + * You can have other threads, too, but this is the primary one. + */ +class eApplication: public eMainloop +{ +public: + eApplication() + { + if (!eApp) + eApp = this; + } + ~eApplication() + { + eApp = 0; + } +}; +#endif diff --git a/lib/base/econfig.cpp b/lib/base/econfig.cpp new file mode 100644 index 0000000..3d51255 --- /dev/null +++ b/lib/base/econfig.cpp @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include + +eConfig *eConfig::instance; + +eConfig::eConfig() +{ + if (!instance) + instance=this; + + setName(CONFIGDIR "/enigma/registry"); + int e=open(); + if (e == NC_ERR_CORRUPT) + { + eWarning("CORRUTPED REGISTRY!"); + ::remove(CONFIGDIR "/enigma/registry"); + } + if (e) + { + if (createNew()) + { + mkdir(CONFIGDIR "/enigma", 0777); + if (createNew()) + eFatal("error while opening/creating registry - create " CONFIGDIR "/enigma"); + } + if (open()) + eFatal("still can't open configfile"); + } + locked=1; + ppin=0; + getKey("/elitedvb/pins/parentallock", ppin ); +} + +eConfig::~eConfig() +{ + if (instance==this) + instance=0; +} + +eAutoInitP0 init_eRCConfig(eAutoInitNumbers::configuration, "Configuration"); diff --git a/lib/base/econfig.h b/lib/base/econfig.h new file mode 100644 index 0000000..d9f3bec --- /dev/null +++ b/lib/base/econfig.h @@ -0,0 +1,27 @@ +#ifndef __econfig_h +#define __econfig_h + +#include + +class eConfig: public NConfig +{ + static eConfig *instance; + int ppin; +public: + int locked; + static eConfig *getInstance() { return instance; } + void setParentalPin( int pin ) + { + ppin = pin; + setKey("/elitedvb/pins/parentallock", ppin ); + } + int getParentalPin() { return ppin; } + bool pLockActive() + { + return ppin && locked; + } + eConfig(); + ~eConfig(); +}; + +#endif diff --git a/lib/base/eerror.cpp b/lib/base/eerror.cpp new file mode 100644 index 0000000..0871bb7 --- /dev/null +++ b/lib/base/eerror.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +#include + +int infatal=0; + +Signal2 logOutput; +int logOutputConsole=1; + +void eFatal(const char* fmt, ...) +{ + char buf[1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, 1024, fmt, ap); + va_end(ap); + logOutput(lvlFatal, buf); + fprintf(stderr, "%s\n",buf ); + if (!infatal) + { + infatal=1; + eMessageBox msg(buf, "FATAL ERROR", eMessageBox::iconError|eMessageBox::btOK); + msg.show(); + msg.exec(); + } + _exit(0); +} + +#ifdef DEBUG +void eDebug(const char* fmt, ...) +{ + char buf[1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, 1024, fmt, ap); + va_end(ap); + logOutput(lvlDebug, eString(buf) + "\n"); + if (logOutputConsole) + fprintf(stderr, "%s\n", buf); +} + +void eDebugNoNewLine(const char* fmt, ...) +{ + char buf[1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, 1024, fmt, ap); + va_end(ap); + logOutput(lvlDebug, buf); + if (logOutputConsole) + fprintf(stderr, "%s", buf); +} + +void eWarning(const char* fmt, ...) +{ + char buf[1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, 1024, fmt, ap); + va_end(ap); + logOutput(lvlWarning, eString(buf) + "\n"); + if (logOutputConsole) + fprintf(stderr, "%s\n", buf); +} +#endif // DEBUG diff --git a/lib/base/eerror.h b/lib/base/eerror.h new file mode 100644 index 0000000..bf91395 --- /dev/null +++ b/lib/base/eerror.h @@ -0,0 +1,43 @@ +#ifndef __E_ERROR__ +#define __E_ERROR__ + +#include "config.h" +#include +#include +#include +#include + +void eFatal(const char* fmt, ...); + +class eString; + +enum { lvlDebug=1, lvlWarning=2, lvlFatal=4 }; + +extern Signal2 logOutput; +extern int logOutputConsole; + +#ifdef ASSERT +#undef ASSERT +#endif + +#ifdef DEBUG + void eDebug(const char* fmt, ...); + void eDebugNoNewLine(const char* fmt, ...); + void eWarning(const char* fmt, ...); + #define ASSERT(x) { if (!(x)) eFatal("%s:%d ASSERTION %s FAILED!", __FILE__, __LINE__, #x); } +#else + inline void eDebug(const char* fmt, ...) + { + } + + inline void eDebugNoNewLine(const char* fmt, ...) + { + } + + inline void eWarning(const char* fmt, ...) + { + } + #define ASSERT(x) do { } while (0) +#endif //DEBUG + +#endif // __E_ERROR__ diff --git a/lib/base/elock.cpp b/lib/base/elock.cpp new file mode 100644 index 0000000..afddcc6 --- /dev/null +++ b/lib/base/elock.cpp @@ -0,0 +1,104 @@ +#include +#include + +void eLock::lock(int res) +{ + if (res>max) + res=max; + pthread_mutex_lock(&mutex); + while ((counter+res)>max) + pthread_cond_wait(&cond, &mutex); + counter+=res; + pthread_mutex_unlock(&mutex); +} + +void eLock::unlock(int res) +{ + if (res>max) + res=max; + pthread_mutex_lock(&mutex); + counter-=res; + pthread_mutex_unlock(&mutex); + pthread_cond_signal(&cond); +} + +eLock::eLock(int max): max(max) +{ + pthread_mutex_init(&mutex, 0); + pthread_cond_init(&cond, 0); + counter=0; + pid=-1; +} + +eLock::~eLock() +{ + pthread_mutex_destroy(&mutex); + pthread_cond_destroy(&cond); +} + +eLocker::eLocker(eLock &lock, int res): lock(lock), res(res) +{ + lock.lock(res); +} + +eLocker::~eLocker() +{ + lock.unlock(res); +} + +eSemaphore::eSemaphore() +{ + v=1; + pthread_mutex_init(&mutex, 0); + pthread_cond_init(&cond, 0); +} + +eSemaphore::~eSemaphore() +{ + pthread_mutex_destroy(&mutex); + pthread_cond_destroy(&cond); +} + +int eSemaphore::down() +{ + int value_after_op; + pthread_mutex_lock(&mutex); + while (v<=0) + pthread_cond_wait(&cond, &mutex); + v--; + value_after_op=v; + pthread_mutex_unlock(&mutex); + return value_after_op; +} + +int eSemaphore::decrement() +{ + int value_after_op; + pthread_mutex_lock(&mutex); + v--; + value_after_op=v; + pthread_mutex_unlock(&mutex); + pthread_cond_signal(&cond); + return value_after_op; +} + +int eSemaphore::up() +{ + int value_after_op; + pthread_mutex_lock(&mutex); + v++; + value_after_op=v; + pthread_mutex_unlock(&mutex); + pthread_cond_signal(&cond); + return value_after_op; +} + +int eSemaphore::value() +{ + int value_after_op; + pthread_mutex_lock(&mutex); + value_after_op=v; + pthread_mutex_unlock(&mutex); + return value_after_op; +} + diff --git a/lib/base/elock.h b/lib/base/elock.h new file mode 100644 index 0000000..6a47f8c --- /dev/null +++ b/lib/base/elock.h @@ -0,0 +1,60 @@ +#ifndef __elock_h +#define __elock_h + +#include + +class singleLock +{ + pthread_mutex_t &lock; +public: + singleLock( pthread_mutex_t &m ) + :lock(m) + { + pthread_mutex_lock(&lock); + } + ~singleLock() + { + pthread_mutex_unlock(&lock); + } +}; + +class eLock +{ + pthread_mutex_t mutex; + pthread_cond_t cond; + + int pid; + int counter, max; +public: + void lock(int res=100); + void unlock(int res=100); + + eLock(int max=100); + ~eLock(); +}; + +class eLocker +{ + eLock &lock; + int res; +public: + eLocker(eLock &lock, int res=100); + ~eLocker(); +}; + +class eSemaphore +{ + int v; + pthread_mutex_t mutex; + pthread_cond_t cond; +public: + eSemaphore(); + ~eSemaphore(); + + int down(); + int decrement(); + int up(); + int value(); +}; + +#endif diff --git a/lib/base/eptrlist.h b/lib/base/eptrlist.h new file mode 100644 index 0000000..f1421eb --- /dev/null +++ b/lib/base/eptrlist.h @@ -0,0 +1,1338 @@ +#ifndef _E_PTRLIST_ +#define _E_PTRLIST_ + +#include +#include +#include + +template +class ePtrList : public std::list +{ +public: + typedef typename std::list >::iterator std_list_T_iterator; // to remove compiler warnings + typedef typename std::list >::const_iterator std_list_T_const_iterator; + typedef typename std::list >::reverse_iterator std_list_T_reverse_iterator; + typedef typename std::list >::const_reverse_iterator std_list_T_const_reverse_iterator; + typedef typename ePtrList::iterator T_iterator; + typedef typename ePtrList::const_iterator T_const_iterator; + typedef typename ePtrList::reverse_iterator T_reverse_iterator; + typedef typename ePtrList::const_reverse_iterator T_const_reverse_iterator; + +// Iterator classes + class iterator; + class const_iterator; + class reverse_iterator; + class const_reverse_iterator; + +// Constructors + inline ePtrList(); + inline ePtrList(const ePtrList&); + inline ~ePtrList(); + +// overwritted sort method + inline void sort(); + +// changed methods for autodelete and current implementation + inline void remove(T* t); + inline void clear(); + inline void pop_back(); + inline void pop_front(); + inline void push_back(T*); + inline void push_front(T*); + +// added methods for current implementation + inline T* take(); + inline void take(T* t); + inline T* current(); + inline T* next(); + inline T* prev(); + inline T* first(); + inline T* last(); + inline T* setCurrent(const T*); + inline const T* current() const; + inline const T* next() const; + inline const T* prev() const; + inline const T* first() const; + inline const T* last() const; + +// added operator methods + inline operator bool(); + inline bool operator!(); + +// added methods for autodelete implementation + inline void setAutoDelete(bool b); + inline bool isAutoDelete(); + +// added compare struct ... to sort + struct less; +private: + iterator cur; + bool autoDelete; + +public: + iterator ePtrList::begin() + { + // makes implicit type conversion form std::list::iterator to ePtrList::iterator + return std::list::begin(); + } + + iterator ePtrList::end() + { + // makes implicit type conversion form std::list::iterator to ePtrList::iterator + return std::list::end(); + } + + const_iterator ePtrList::begin() const + { + // makes implicit type conversion form std::list::const_iterator to ePtrList::const_iterator + return std::list::begin(); + } + + const_iterator ePtrList::end() const + { + // makes implicit type conversion form std::list::const_iterator to ePtrList::const_iterator + return std::list::end(); + } + + reverse_iterator ePtrList::rbegin() + { + // makes implicit type conversion form std::list::reverse:_iterator to ePtrList::reverse_iterator + return std::list::rbegin(); + } + + reverse_iterator ePtrList::rend() + { + // makes implicit type conversion form std::list::reverse_iterator to ePtrList::reverse_iterator + return std::list::rend(); + } + + const_reverse_iterator ePtrList::rbegin() const + { + // makes implicit type conversion form std::list::const_reverse_iterator to ePtrList::const_reverse_iterator + return std::list::rbegin(); + } + + const_reverse_iterator ePtrList::rend() const + { + // makes implicit type conversion form std::list::const_reverse_iterator to ePtrList::const_reverse_iterator + return std::list::rend(); + } + + iterator ePtrList::erase(iterator it) + { + // Remove the item it, if auto-deletion is enabled, than the list call delete for this item + // If current is equal to the item that was removed, current is set to the next item in the list + if (autoDelete && *it) + delete *it; + + if (cur == it) + return cur = std::list::erase(it); + else + return std::list::erase(it); + } + + iterator ePtrList::erase(iterator from, iterator to) + { + // Remove all items between the to iterators from and to + // If auto-deletion is enabled, than the list call delete for all removed items + while (from != to) + from = erase(from); + + return from; + } + + operator iterator() + { + // Returns a iterator that equal to begin() of the list + return begin(); + } + + operator const_iterator() const + { + // Returns a const_iterator that equal to begin() of the list + return begin(); + } + + operator reverse_iterator() + { + // Returns a reverse_iterator that equal to rbegin() of the list + return rbegin(); + } + + operator const_reverse_iterator() const + { + // Returns a const_reverse_iterator that equal to rbegin() of the list + return rbegin(); + } + + std::vector* getVector() + { + // Creates an vector and copys all elements to this vector + // returns a pointer to this new vector ( the reserved memory must deletet from the receiver !! ) + std::vector* v=new std::vector(); + v->reserve( size() ); + for ( std_list_T_iterator it( std::list::begin() ); it != std::list::end(); it++) + v->push_back( **it ); + + return v; + } + + inline iterator insert_in_order( T* e ) + { + // added a new item to the list... in order + // returns a iterator to the new item + return insert( std::lower_bound( std::list::begin(), std::list::end(), e ), e ); + } + +}; + +/////////////////// iterator class ///////////////////////////// +template +class ePtrList::iterator : public std::list::iterator +{ +public: + // Constructors + iterator(const std_list_T_iterator& Q) : std_list_T_iterator(Q) { } + + // changed operator for pointer + T* operator->() const + { + return *std::list::iterator::operator->(); + } + + operator T&() const + { + return *operator->(); + } + + operator T*() const + { + return operator->(); + } + + iterator& operator++() + { + std::list::iterator::operator++(); + return *this; + } + + iterator operator++(int) + { + return std::list::iterator::operator++(0); + } + + iterator& operator--() + { + std::list::iterator::operator--(); + return *this; + } + + iterator operator--(int) + { + return std::list::iterator::operator--(0); + } +}; + +/////////////////// const_iterator class ///////////////////////////// +template +class ePtrList::const_iterator : public std::list::const_iterator +{ +public: + // Constructors + const_iterator(const std_list_T_const_iterator& Q) :std_list_T_const_iterator(Q) { } + + // changed operator for pointer + T* operator->() const + { + return *std::list::const_iterator::operator->(); + } + + operator T&() const + { + return *operator->(); + } + + operator T*() const + { + return operator->(); + } + + const_iterator& operator++() + { + std::list::const_iterator::operator++(); + return *this; + } + + const_iterator operator++(int) + { + return std::list::const_iterator::operator++(0); + } + + const_iterator& operator--() + { + std::list::const_iterator::operator--(); + return *this; + } + + const_iterator operator--(int) + { + return std::list::const_iterator::operator--(0); + } +}; + +/////////////////// reverse_iterator class ///////////////////////////// +template +class ePtrList::reverse_iterator : public std::list::reverse_iterator +{ +public: + // Constructors + reverse_iterator(const std_list_T_reverse_iterator& Q) :std_list_T_reverse_iterator(Q) { } + + // changed operators for pointer + T* operator->() const + { + return *std::list::reverse_iterator::operator->(); + } + + operator T&() const + { + return *operator->(); + } + + operator T*() const + { + return operator->(); + } + + reverse_iterator& operator++() + { + std::list::reverse_iterator::operator++(); + return *this; + } + + reverse_iterator operator++(int) + { + return std::list::reverse_iterator::operator++(0); + } + + reverse_iterator& operator--() + { + std::list::reverse_iterator::operator--(); + return *this; + } + + reverse_iterator operator--(int) + { + return std::list::reverse_iterator::operator--(0); + } +}; + +/////////////////// const_reverse_iterator class ///////////////////////////// +template +class ePtrList::const_reverse_iterator : public std::list::const_reverse_iterator +{ +public: + // Constructors + const_reverse_iterator(const std_list_T_const_reverse_iterator& Q) :std_list_T_const_reverse_iterator(Q) { } + + // changed operators for pointer + T* operator->() const + { + return *std::list::const_reverse_iterator::operator->(); + } + + operator T&() const + { + return *operator->(); + } + + operator T*() const + { + return operator->(); + } + + const_reverse_iterator& operator++() + { + std::list::const_reverse_iterator::operator++(); + return *this; + } + + const_reverse_iterator operator++(int) + { + return std::list::const_reverse_iterator::operator++(0); + } + + const_reverse_iterator& operator--() + { + std::list::const_reverse_iterator::operator--(); + return *this; + } + + const_reverse_iterator operator--(int) + { + return std::list::const_reverse_iterator::operator--(0); + } +}; + +/////////////////// Default Constructor ///////////////////////////// +template +ePtrList::ePtrList() + :cur(std::list::begin()), autoDelete(false) +{ + +} + +/////////////////// Copy Constructor ///////////////////////////// +template +ePtrList::ePtrList(const ePtrList& e) + :std::list(e), cur(e.cur), autoDelete( false ) +{ + if ( e.autoDelete ) + if ( e.size() ) + eDebug("Warning !! We make a Copy of a non empty ePtrList, with autoDelete enabled" + "We disable autoDelete in the new ePtrList !!"); + else + autoDelete=true; +} + +/////////////////// ePtrList Destructor ///////////////////////////// +template +inline ePtrList::~ePtrList() +{ +// if autoDelete is enabled, delete is called for all elements in the list + if (autoDelete) + for (std_list_T_iterator it(std::list::begin()); it != std::list::end(); it++) + delete *it; +} + + +/////////////////// ePtrList sort() ///////////////////////// +template +inline void ePtrList::sort() +{ +// Sorts all items in the list. +// The type T must have a operator <. + std::list::sort(ePtrList::less()); +} + +/////////////////// ePtrList remove(T*) ///////////////////////// +template +inline void ePtrList::remove(T* t) +{ +// Remove all items that, equals to t, if auto-deletion is enabled, than the list call delete for the removed items +// If current is equal to one of the removed items, current is set to the next valid item + T_iterator it(std::list::begin()); + + while (it != std::list::end()) + if (*it == t) + { + it=erase(it); + break; // one item is complete removed an deleted + } + else + it++; + + while (it != std::list::end()) + if (*it == t) + it = std::list::erase(it); // remove all other items that equals to t (no delete is called..) + else + it++; + +} + +/////////////////// ePtrList clear() ////////////////// +template +inline void ePtrList::clear() +{ +// Remove all items from the list +// If auto-deletion is enabled, than the list call delete for all items in the list + erase(std::list::begin(), std::list::end()); +} + +/////////////////// ePtrList pop_back() //////////////////// +template +inline void ePtrList::pop_back() +{ +// Removes the last item from the list. If the current item ist the last, than the current is set to the new +// last item in the list; +// The removed item is deleted if auto-deletion is enabled. + erase(--end()); +} + +/////////////////// ePtrList pop_front() //////////////////// +template +inline void ePtrList::pop_front() +{ +// Removes the first item from the list. If the current item ist the first, than the current is set to the new +// first item in the list; +// The removed item is deleted if auto-deletion is enabled. + erase(begin()); +} + +/////////////////// ePtrList push_back(T*) //////////////////// +template +inline void ePtrList::push_back(T* x) +{ +// Add a new item at the end of the list. +// The current item is set to the last item; + std::list::push_back(x); + last(); +} + +/////////////////// ePtrList push_front(T*) //////////////////// +template +inline void ePtrList::push_front(T* x) +{ +// Add a new item at the begin of the list. +// The current item is set to the first item; + std::list::push_front(x); + first(); +} + +/////////////////// ePtrList take() //////////////////// +template +inline T* ePtrList::take() +{ +// Takes the current item out of the list without deleting it (even if auto-deletion is enabled). +// Returns a pointer to the item taken out of the list, or null if the index is out of range. +// The item after the taken item becomes the new current list item if the taken item is not the last item in the list. If the last item is taken, the new last item becomes the current item. +// The current item is set to null if the list becomes empty. + T* tmp = *cur; + cur = std::list::erase(cur); + return tmp; +} + +/////////////////// ePtrList take(T*) //////////////////// +template +inline void ePtrList::take(T* t) +{ +// Takes all item with T* out of the list without deleting it (even if auto-deletion is enabled). + std::list::remove(t); +} + +/////////////////// ePtrList setCurrent(T*) //////////////////// +template +inline T* ePtrList::setCurrent(const T* t) +{ + // Sets the internal current iterator to the first element that equals to t, and returns t when a item is found, + // otherwise it returns 0 ! + for (T_iterator it(std::list::begin()); it != std::list::end(); ++it) + if (*it == t) + { + cur = it; + return *it; + } + + return 0; +} + +/////////////////// ePtrList current() //////////////////// +template +inline T* ePtrList::current() +{ +// Returns a pointer to the current list item. The current item may be null (implies that the current index is -1). + return cur==end() ? 0 : *cur; +} + +/////////////////// ePtrList next() //////////////////// +template +inline T* ePtrList::next() +{ +// Returns a pointer to the item succeeding the current item. Returns null if the current items is null or equal to the last item. +// Makes the succeeding item current. If the current item before this function call was the last item, the current item will be set to null. If the current item was null, this function does nothing. + if (cur == end()) + return 0; + else + if (++cur == end()) + return 0; + else + return *cur; +} + +/////////////////// ePtrList prev() //////////////////// +template +inline T* ePtrList::prev() +{ +// Returns a pointer to the item preceding the current item. Returns null if the current items is null or equal to the first item. +// Makes the preceding item current. If the current item before this function call was the first item, the current item will be set to null. If the current item was null, this function does nothing. + if (cur == begin()) + return 0; + else + return *--cur; +} + +/////////////////// ePtrList first() //////////////////// +template +inline T* ePtrList::first() +{ +// Returns a pointer to the first item in the list and makes this the current list item, or null if the list is empty. + return *(cur = begin()); +} + +/////////////////// ePtrList last() //////////////////// +template +inline T* ePtrList::last() +{ +// Returns a pointer to the last item in the list and makes this the current list item, or null if the list is empty. + return *(cur = --end()); +} + +/////////////////// const ePtrList current() //////////////////// +template +inline const T* ePtrList::current() const +{ +// Returns a pointer to the current list item. The current item may be null (implies that the current index is not valid) + return cur==end() ? 0 : *cur; +} + +/////////////////// const ePtrList next() //////////////////// +template +inline const T* ePtrList::next() const +{ +// Returns a pointer to the item succeeding the current item. Returns null if the current items is null or equal to the last item. +// Makes the succeeding item current. If the current item before this function call was the last item, the current item will be set to null. If the current item was null, this function does nothing. + if (cur == end()) + return 0; + else + if (++cur == end()) + return 0; + else + return *cur; +} + +/////////////////// const ePtrList prev() //////////////////// +template +inline const T* ePtrList::prev() const +{ +// Returns a pointer to the item preceding the current item. Returns null if the current items is null or equal to the first item. +// Makes the preceding item current. If the current item before this function call was the first item, the current item will be set to null. If the current item was null, this function does nothing. + if (cur == begin()) + return 0; + else + return *--cur; +} + +/////////////////// const ePtrList first() //////////////////// +template +inline const T* ePtrList::first() const +{ +// Returns a pointer to the first item in the list and makes this the current list item, or null if the list is empty. + return *(cur = begin()); +} + +/////////////////// const ePtrList last() //////////////////// +template +inline const T* ePtrList::last() const +{ +// Returns a pointer to the last item in the list and makes this the current list item, or null if the list is empty. + return *(cur = --end()); +} + +////////////////// struct less ////////////////////////////// +template +struct ePtrList::less +{ +// operator() is used internal from the list to sort them + bool operator() (const T* t1, const T* t2) + { + return (*t1 < *t2); + } +}; + +/////////////////// ePtrList operator bool //////////////////// +template +ePtrList::operator bool() +{ +// Returns a bool that contains true, when the list is NOT empty otherwise false + return !empty(); +} + +template +bool ePtrList::operator!() +{ +// Returns a bool that contains true, when the list is empty otherwise false + return empty(); +} + +template +void ePtrList::setAutoDelete(bool b) +{ +// switched autoDelete on or off +// if autoDelete is true, than the pointer list controls the heap memory behind the pointer itself +// the list calls delete for the item before it removed from the list + autoDelete=b; +} + +template +bool ePtrList::isAutoDelete() +{ +// returns a bool that contains the state of autoDelete + return autoDelete; +} + +template +class eSmartPtrList : public std::list > +{ +public: + typedef typename std::list, std::allocator > >::iterator std_list_T_iterator; // to remove compiler warnings + typedef typename std::list, std::allocator > >::const_iterator std_list_T_const_iterator; + typedef typename std::list, std::allocator > >::reverse_iterator std_list_T_reverse_iterator; + typedef typename std::list, std::allocator > >::const_reverse_iterator std_list_T_const_reverse_iterator; + typedef typename eSmartPtrList::iterator T_iterator; + typedef typename eSmartPtrList::const_iterator T_const_iterator; + typedef typename eSmartPtrList::reverse_iterator T_reverse_iterator; + typedef typename eSmartPtrList::const_reverse_iterator T_const_reverse_iterator; + +// Iterator classes + class iterator; + class const_iterator; + class reverse_iterator; + class const_reverse_iterator; + +// Constructors + inline eSmartPtrList(); + inline eSmartPtrList(const eSmartPtrList&); + inline ~eSmartPtrList(); + +// overwritted sort method + inline void sort(); + +// changed methods for autodelete and current implementation + inline void remove(T* t); + inline void clear(); + inline void pop_back(); + inline void pop_front(); + inline void push_back(T*); + inline void push_front(T*); + +// added methods for current implementation +// inline T* take(); +// inline void take(T* t); + inline T* current(); + inline T* next(); + inline T* prev(); + inline T* first(); + inline T* last(); + inline T* setCurrent(const T*); + inline const T* current() const; + inline const T* next() const; + inline const T* prev() const; + inline const T* first() const; + inline const T* last() const; + +// added operator methods + inline operator bool(); + inline bool operator!(); + +// added methods for autodelete implementation + inline void setAutoDelete(bool b); + inline bool isAutoDelete(); + +// added compare struct ... to sort + struct less; +private: + iterator cur; + bool autoDelete; + +public: + iterator eSmartPtrList::begin() + { + // makes implicit type conversion form std::list::iterator to eSmartPtrList::iterator + return std::list >::begin(); + } + + iterator eSmartPtrList::end() + { + // makes implicit type conversion form std::list::iterator to eSmartPtrList::iterator + return std::list >::end(); + } + + const_iterator eSmartPtrList::begin() const + { + // makes implicit type conversion form std::list::const_iterator to eSmartPtrList::const_iterator + return std::list >::begin(); + } + + const_iterator eSmartPtrList::end() const + { + // makes implicit type conversion form std::list::const_iterator to eSmartPtrList::const_iterator + return std::list >::end(); + } + + reverse_iterator eSmartPtrList::rbegin() + { + // makes implicit type conversion form std::list::reverse:_iterator to eSmartPtrList::reverse_iterator + return std::list >::rbegin(); + } + + reverse_iterator eSmartPtrList::rend() + { + // makes implicit type conversion form std::list::reverse_iterator to eSmartPtrList::reverse_iterator + return std::list >::rend(); + } + + const_reverse_iterator eSmartPtrList::rbegin() const + { + // makes implicit type conversion form std::list::const_reverse_iterator to eSmartPtrList::const_reverse_iterator + return std::list >::rbegin(); + } + + const_reverse_iterator eSmartPtrList::rend() const + { + // makes implicit type conversion form std::list::const_reverse_iterator to eSmartPtrList::const_reverse_iterator + return std::list >::rend(); + } + + iterator eSmartPtrList::erase(iterator it) + { + // Remove the item it, if auto-deletion is enabled, than the list call delete for this item + // If current is equal to the item that was removed, current is set to the next item in the list + if (autoDelete && *it) + delete *it; + + if (cur == it) + return cur = std::list >::erase(it); + else + return std::list >::erase(it); + } + + iterator eSmartPtrList::erase(iterator from, iterator to) + { + // Remove all items between the to iterators from and to + // If auto-deletion is enabled, than the list call delete for all removed items + while (from != to) + from = erase(from); + + return from; + } + + operator iterator() + { + // Returns a iterator that equal to begin() of the list + return begin(); + } + + operator const_iterator() const + { + // Returns a const_iterator that equal to begin() of the list + return begin(); + } + + operator reverse_iterator() + { + // Returns a reverse_iterator that equal to rbegin() of the list + return rbegin(); + } + + operator const_reverse_iterator() const + { + // Returns a const_reverse_iterator that equal to rbegin() of the list + return rbegin(); + } + + std::vector* getVector() + { + // Creates an vector and copys all elements to this vector + // returns a pointer to this new vector ( the reserved memory must deletet from the receiver !! ) + std::vector* v=new std::vector(); + v->reserve( size() ); + for ( std_list_T_iterator it( std::list >::begin() ); it != std::list >::end(); it++) + v->push_back( **it ); + + return v; + } + + inline iterator insert_in_order( T* e ) + { + // added a new item to the list... in order + // returns a iterator to the new item + return insert( std::lower_bound( std::list >::begin(), std::list >::end(), e ), e ); + } + +}; + +/////////////////// iterator class ///////////////////////////// +template +class eSmartPtrList::iterator : public std::list >::iterator +{ +public: + // Constructors + iterator(const std_list_T_iterator& Q) : std_list_T_iterator(Q) { } + + // changed operator for pointer + T* operator->() const + { + return *std::list >::iterator::operator->(); + } + + operator T&() const + { + return *operator->(); + } + + operator T*() const + { + return operator->(); + } + + iterator& operator++() + { + std::list >::iterator::operator++(); + return *this; + } + + iterator operator++(int) + { + return std::list >::iterator::operator++(0); + } + + iterator& operator--() + { + std::list >::iterator::operator--(); + return *this; + } + + iterator operator--(int) + { + return std::list >::iterator::operator--(0); + } +}; + +/////////////////// const_iterator class ///////////////////////////// +template +class eSmartPtrList::const_iterator : public std::list >::const_iterator +{ +public: + // Constructors + const_iterator(const std_list_T_const_iterator& Q) :std_list_T_const_iterator(Q) { } + + // changed operator for pointer + T* operator->() const + { + return *std::list >::const_iterator::operator->(); + } + + operator T&() const + { + return *operator->(); + } + + operator T*() const + { + return operator->(); + } + + const_iterator& operator++() + { + std::list >::const_iterator::operator++(); + return *this; + } + + const_iterator operator++(int) + { + return std::list >::const_iterator::operator++(0); + } + + const_iterator& operator--() + { + std::list >::const_iterator::operator--(); + return *this; + } + + const_iterator operator--(int) + { + return std::list >::const_iterator::operator--(0); + } +}; + +/////////////////// reverse_iterator class ///////////////////////////// +template +class eSmartPtrList::reverse_iterator : public std::list >::reverse_iterator +{ +public: + // Constructors + reverse_iterator(const std_list_T_reverse_iterator& Q) :std_list_T_reverse_iterator(Q) { } + + // changed operators for pointer + T* operator->() const + { + return *std::list >::reverse_iterator::operator->(); + } + + operator T&() const + { + return *operator->(); + } + + operator T*() const + { + return operator->(); + } + + reverse_iterator& operator++() + { + std::list >::reverse_iterator::operator++(); + return *this; + } + + reverse_iterator operator++(int) + { + return std::list >::reverse_iterator::operator++(0); + } + + reverse_iterator& operator--() + { + std::list >::reverse_iterator::operator--(); + return *this; + } + + reverse_iterator operator--(int) + { + return std::list >::reverse_iterator::operator--(0); + } +}; + +/////////////////// const_reverse_iterator class ///////////////////////////// +template +class eSmartPtrList::const_reverse_iterator : public std::list >::const_reverse_iterator +{ +public: + // Constructors + const_reverse_iterator(const std_list_T_const_reverse_iterator& Q) :std_list_T_const_reverse_iterator(Q) { } + + // changed operators for pointer + T* operator->() const + { + return *std::list >::const_reverse_iterator::operator->(); + } + + operator T&() const + { + return *operator->(); + } + + operator T*() const + { + return operator->(); + } + + const_reverse_iterator& operator++() + { + std::list >::const_reverse_iterator::operator++(); + return *this; + } + + const_reverse_iterator operator++(int) + { + return std::list >::const_reverse_iterator::operator++(0); + } + + const_reverse_iterator& operator--() + { + std::list >::const_reverse_iterator::operator--(); + return *this; + } + + const_reverse_iterator operator--(int) + { + return std::list >::const_reverse_iterator::operator--(0); + } +}; + +/////////////////// Default Constructor ///////////////////////////// +template +eSmartPtrList::eSmartPtrList() + :cur(std::list >::begin()), autoDelete(false) +{ + +} + +/////////////////// Copy Constructor ///////////////////////////// +template +eSmartPtrList::eSmartPtrList(const eSmartPtrList& e) + :std::list >(e), cur(e.cur), autoDelete( false ) +{ + if ( e.autoDelete ) + if ( e.size() ) + eDebug("Warning !! We make a Copy of a non empty eSmartPtrList, with autoDelete enabled" + "We disable autoDelete in the new eSmartPtrList !!"); + else + autoDelete=true; +} + +/////////////////// eSmartPtrList Destructor ///////////////////////////// +template +inline eSmartPtrList::~eSmartPtrList() +{ +// if autoDelete is enabled, delete is called for all elements in the list + if (autoDelete) + for (std_list_T_iterator it(std::list >::begin()); it != std::list >::end(); it++) + delete *it; +} + + +/////////////////// eSmartPtrList sort() ///////////////////////// +template +inline void eSmartPtrList::sort() +{ +// Sorts all items in the list. +// The type T must have a operator <. + std::list >::sort(eSmartPtrList::less()); +} + +/////////////////// eSmartPtrList remove(T*) ///////////////////////// +template +inline void eSmartPtrList::remove(T* t) +{ +// Remove all items that, equals to t, if auto-deletion is enabled, than the list call delete for the removed items +// If current is equal to one of the removed items, current is set to the next valid item + T_iterator it(std::list >::begin()); + + while (it != std::list >::end()) + if (*it == t) + { + it=erase(it); + break; // one item is complete removed an deleted + } + else + it++; + + while (it != std::list >::end()) + if (*it == t) + it = std::list >::erase(it); // remove all other items that equals to t (no delete is called..) + else + it++; + +} + +/////////////////// eSmartPtrList clear() ////////////////// +template +inline void eSmartPtrList::clear() +{ +// Remove all items from the list +// If auto-deletion is enabled, than the list call delete for all items in the list + erase(std::list >::begin(), std::list >::end()); +} + +/////////////////// eSmartPtrList pop_back() //////////////////// +template +inline void eSmartPtrList::pop_back() +{ +// Removes the last item from the list. If the current item ist the last, than the current is set to the new +// last item in the list; +// The removed item is deleted if auto-deletion is enabled. + erase(--end()); +} + +/////////////////// eSmartPtrList pop_front() //////////////////// +template +inline void eSmartPtrList::pop_front() +{ +// Removes the first item from the list. If the current item ist the first, than the current is set to the new +// first item in the list; +// The removed item is deleted if auto-deletion is enabled. + erase(begin()); +} + +/////////////////// eSmartPtrList push_back(T*) //////////////////// +template +inline void eSmartPtrList::push_back(T* x) +{ +// Add a new item at the end of the list. +// The current item is set to the last item; + std::list >::push_back(x); + last(); +} + +/////////////////// eSmartPtrList push_front(T*) //////////////////// +template +inline void eSmartPtrList::push_front(T* x) +{ +// Add a new item at the begin of the list. +// The current item is set to the first item; + std::list >::push_front(x); + first(); +} + +/////////////////// eSmartPtrList take() //////////////////// +//template +//inline T* eSmartPtrList::take() +//{ +//// Takes the current item out of the list without deleting it (even if auto-deletion is enabled). +//// Returns a pointer to the item taken out of the list, or null if the index is out of range. +//// The item after the taken item becomes the new current list item if the taken item is not the last item in the list. If the last item is taken, the new last item becomes the current item. +//// The current item is set to null if the list becomes empty. +// T* tmp = *cur; +// cur = std::list::erase(cur); +// return tmp; +//} + +/////////////////// eSmartPtrList take(T*) //////////////////// +//template +//inline void eSmartPtrList::take(T* t) +//{ +//// Takes all item with T* out of the list without deleting it (even if auto-deletion is enabled). +// std::list::remove(t); +//} + +/////////////////// eSmartPtrList setCurrent(T*) //////////////////// +template +inline T* eSmartPtrList::setCurrent(const T* t) +{ + // Sets the internal current iterator to the first element that equals to t, and returns t when a item is found, + // otherwise it returns 0 ! + for (T_iterator it(std::list >::begin()); it != std::list >::end(); ++it) + if (*it == t) + { + cur = it; + return *it; + } + + return 0; +} + +/////////////////// eSmartPtrList current() //////////////////// +template +inline T* eSmartPtrList::current() +{ +// Returns a pointer to the current list item. The current item may be null (implies that the current index is -1). + return cur==end() ? 0 : *cur; +} + +/////////////////// eSmartPtrList next() //////////////////// +template +inline T* eSmartPtrList::next() +{ +// Returns a pointer to the item succeeding the current item. Returns null if the current items is null or equal to the last item. +// Makes the succeeding item current. If the current item before this function call was the last item, the current item will be set to null. If the current item was null, this function does nothing. + if (cur == end()) + return 0; + else + if (++cur == end()) + return 0; + else + return *cur; +} + +/////////////////// eSmartPtrList prev() //////////////////// +template +inline T* eSmartPtrList::prev() +{ +// Returns a pointer to the item preceding the current item. Returns null if the current items is null or equal to the first item. +// Makes the preceding item current. If the current item before this function call was the first item, the current item will be set to null. If the current item was null, this function does nothing. + if (cur == begin()) + return 0; + else + return *--cur; +} + +/////////////////// eSmartPtrList first() //////////////////// +template +inline T* eSmartPtrList::first() +{ +// Returns a pointer to the first item in the list and makes this the current list item, or null if the list is empty. + return *(cur = begin()); +} + +/////////////////// eSmartPtrList last() //////////////////// +template +inline T* eSmartPtrList::last() +{ +// Returns a pointer to the last item in the list and makes this the current list item, or null if the list is empty. + return *(cur = --end()); +} + +/////////////////// const eSmartPtrList current() //////////////////// +template +inline const T* eSmartPtrList::current() const +{ +// Returns a pointer to the current list item. The current item may be null (implies that the current index is not valid) + return cur==end() ? 0 : *cur; +} + +/////////////////// const eSmartPtrList next() //////////////////// +template +inline const T* eSmartPtrList::next() const +{ +// Returns a pointer to the item succeeding the current item. Returns null if the current items is null or equal to the last item. +// Makes the succeeding item current. If the current item before this function call was the last item, the current item will be set to null. If the current item was null, this function does nothing. + if (cur == end()) + return 0; + else + if (++cur == end()) + return 0; + else + return *cur; +} + +/////////////////// const eSmartPtrList prev() //////////////////// +template +inline const T* eSmartPtrList::prev() const +{ +// Returns a pointer to the item preceding the current item. Returns null if the current items is null or equal to the first item. +// Makes the preceding item current. If the current item before this function call was the first item, the current item will be set to null. If the current item was null, this function does nothing. + if (cur == begin()) + return 0; + else + return *--cur; +} + +/////////////////// const eSmartPtrList first() //////////////////// +template +inline const T* eSmartPtrList::first() const +{ +// Returns a pointer to the first item in the list and makes this the current list item, or null if the list is empty. + return *(cur = begin()); +} + +/////////////////// const eSmartPtrList last() //////////////////// +template +inline const T* eSmartPtrList::last() const +{ +// Returns a pointer to the last item in the list and makes this the current list item, or null if the list is empty. + return *(cur = --end()); +} + +////////////////// struct less ////////////////////////////// +template +struct eSmartPtrList::less +{ +// operator() is used internal from the list to sort them + bool operator() (const T* t1, const T* t2) + { + return (*t1 < *t2); + } +}; + +/////////////////// eSmartPtrList operator bool //////////////////// +template +eSmartPtrList::operator bool() +{ +// Returns a bool that contains true, when the list is NOT empty otherwise false + return !empty(); +} + +template +bool eSmartPtrList::operator!() +{ +// Returns a bool that contains true, when the list is empty otherwise false + return empty(); +} + +template +void eSmartPtrList::setAutoDelete(bool b) +{ +// switched autoDelete on or off +// if autoDelete is true, than the pointer list controls the heap memory behind the pointer itself +// the list calls delete for the item before it removed from the list + autoDelete=b; +} + +template +bool eSmartPtrList::isAutoDelete() +{ +// returns a bool that contains the state of autoDelete + return autoDelete; +} + +#endif // _E_PTRLIST diff --git a/lib/base/estring.cpp b/lib/base/estring.cpp new file mode 100644 index 0000000..bf30d5f --- /dev/null +++ b/lib/base/estring.cpp @@ -0,0 +1,468 @@ +#include +#include +#include +#include + +static pthread_mutex_t lock=PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; + +///////////////////////////////////////// eString sprintf ///////////////////////////////////////////////// +eString& eString::sprintf(char *fmt, ...) +{ + singleLock s(lock); +// Implements the normal sprintf method, to use format strings with eString +// The max length of the result string is 1024 char. + static char buf[1024]; + va_list ap; + va_start(ap, fmt); + std::vsnprintf(buf, 1024, fmt, ap); + va_end(ap); + assign(buf); + return *this; +} + +///////////////////////////////////////// eString setNum(uint, uint) /////////////////////////////////////// +eString& eString::setNum(int val, int sys) +{ +// Returns a string that contain the value val as string +// if sys == 16 than hexadezimal if sys == 10 than decimal + char buf[12]; + + if (sys == 10) + std::snprintf(buf, 12, "%i", val); + else if (sys == 16) + std::snprintf(buf, 12, "%X", val); + + assign(buf); + return *this; +} + +///////////////////////////////////////// eString replaceChars(char, char) ///////////////////////////// +eString& eString::removeChars(char fchar) +{ +// Remove all chars that equal to fchar, and returns a reference to itself + unsigned int index=0; + + while ( ( index = find(fchar, index) ) != npos ) + erase(index, 1); + + return *this; +} + +/////////////////////////////////////// eString upper() //////////////////////////////////////////////// +eString& eString::upper() +{ +// convert all lowercase characters to uppercase, and returns a reference to itself + for (iterator it = begin(); it != end(); it++) + switch(*it) + { + case 'a' ... 'z' : + *it -= 32; + break; + + case 'ä' : + *it = 'Ä'; + break; + + case 'ü' : + *it = 'Ü'; + break; + + case 'ö' : + *it = 'Ö'; + break; + } + + return *this; +} + +eString& eString::strReplace(const char* fstr, const eString& rstr) +{ +// replace all occurrence of fstr with rstr and, and returns a reference to itself + unsigned int index=0; + unsigned int fstrlen = strlen(fstr); + int rstrlen=rstr.size(); + + while ( ( index = find(fstr, index) ) != npos ) + { + replace(index, fstrlen, rstr); + index+=rstrlen; + } + + return *this; +} + +int strnicmp(const char *s1, const char *s2, int len) +{ +// makes a case insensitive string compare with len Chars + while ( *s1 && *s2 && len-- ) + if ( tolower(*s1) != tolower(*s2) ) + return tolower(*s1) < tolower(*s2) ? -1 : 1; + else + s1++, s2++; + + return 0; +} + +/////////////////////////////////////// eString icompare(const eString&) //////////////////////////////////////////////// +int eString::icompare(const eString& s) +{ +// makes a case insensitive string compare + std::string::const_iterator p = begin(), + p2 = s.begin(); + + while ( p != end() && p2 != s.end() ) + if ( tolower(*p) != tolower(*p2) ) + return tolower(*p) < tolower(*p2) ? -1 : 1; + else + p++, p2++; + + return length() == s.length() ? 0 : length() < s.length() ? -1 : 1; +} + + // 8859-x to dvb coding tables. taken from www.unicode.org/Public/MAPPINGS/ISO8859/ +static unsigned long c88595[128]={ +0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, +0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, +0x00a0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x00ad, 0x040e, 0x040f, +0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, +0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, +0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, +0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, +0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x00a7, 0x045e, 0x045f}; + +static unsigned long c88596[128]={ +0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, +0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, +0x00a0, 0x0000, 0x0000, 0x0000, 0x00a4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x060c, 0x00ad, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x061b, 0x0000, 0x0000, 0x0000, 0x061f, +0x0000, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062a, 0x062b, 0x062c, 0x062d, 0x062e, 0x062f, +0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064a, 0x064b, 0x064c, 0x064d, 0x064e, 0x064f, +0x0650, 0x0651, 0x0652, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}; + +static unsigned long c88597[128]={ +0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, +0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, +0x00a0, 0x2018, 0x2019, 0x00a3, 0x0000, 0x0000, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x0000, 0x00ab, 0x00ac, 0x00ad, 0x0000, 0x2015, +0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x0384, 0x0385, 0x0386, 0x00b7, 0x0388, 0x0389, 0x038a, 0x00bb, 0x038c, 0x00bd, 0x038e, 0x038f, +0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, +0x03a0, 0x03a1, 0x0000, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03ae, 0x03af, +0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, +0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03cc, 0x03cd, 0x03ce, 0x0000}; + +static unsigned long c88598[128]={ +0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, +0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, +0x00a0, 0x0000, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00d7, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, +0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00f7, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2017, +0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0x05d7, 0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de, 0x05df, +0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6, 0x05e7, 0x05e8, 0x05e9, 0x05ea, 0x0000, 0x0000, 0x200e, 0x200f, 0x0000}; + +static unsigned long c88599[128]={ +0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, +0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, +0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, +0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, +0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, +0x011e, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x0130, 0x015e, 0x00df, +0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, +0x011f, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x0131, 0x015f, 0x00ff}; + + // UPC Direct / HBO strange two-character encoding. 0xC2 means acute, 0xC8 doule 'dot', 0xCA small 'circle', 0xCD double 'acute', 0xCF acute. + // many thanks to the czechs who helped me while solving this. +static inline unsigned int doCzech(int c1, int c2) +{ + switch (c1) + { + case 0xC2: // acute + switch (c2) + { + case 'A': return 0x00C1; + case 'a': return 0x00E1; + case 'E': return 0x00C9; + case 'e': return 0x00E9; + case 'I': return 0x00CD; + case 'i': return 0x00ED; + case 'O': return 0x00D3; + case 'o': return 0x00F3; // corrected, was 0x00E3 + case 'U': return 0x00DA; + case 'u': return 0x00FA; + case 'Y': return 0x00DD; + case 'y': return 0x00FD; + default: + return 0; + } + case 0xC8: // double 'dot' + switch (c2) + { + case 'A': return 0x00C4; + case 'a': return 0x00E4; + case 'E': return 0x00CB; + case 'e': return 0x00EB; + case 'O': return 0x00D6; + case 'o': return 0x00F6; + case 'U': return 0x00DC; + case 'u': return 0x00FC; + default: + return 0; + } + case 0xCA: // small 'circle' + switch (c2) + { + case 'U': return 0x016E; + case 'u': return 0x016F; + default: + return 0; + } + case 0xCD: // double 'acute' + switch (c2) + { + case 'O': return 0x0150; + case 'o': return 0x0151; + case 'U': return 0x0170; + case 'u': return 0x0171; + default: + return 0; + } + case 0xCF: // caron + switch (c2) + { + case 'C': return 0x010C; + case 'c': return 0x010D; + case 'D': return 0x010E; + case 'd': return 0x010F; + case 'E': return 0x011A; + case 'e': return 0x011B; + case 'L': return 0x013D; // not sure if they really exist. + case 'l': return 0x013E; + case 'N': return 0x0147; + case 'n': return 0x0148; + case 'R': return 0x0158; + case 'r': return 0x0159; + case 'S': return 0x0160; + case 's': return 0x0161; + case 'T': return 0x0164; + case 't': return 0x0165; + case 'Z': return 0x017D; + case 'z': return 0x017E; + default: + return 0; + } + default: + return 0; + } +} + +static inline unsigned int recode(unsigned char d, int cp) +{ + if (d < 0x80) + return d; + switch (cp) + { + case 0: // 8859-1 Latin1 <-> unicode mapping + return d; + case 1: // 8859-5 -> unicode mapping + return c88595[d-0x80]; + case 2: // 8859-6 -> unicode mapping + return c88596[d-0x80]; + case 3: // 8859-7 -> unicode mapping + return c88597[d-0x80]; + case 4: // 8859-8 -> unicode mapping + return c88598[d-0x80]; + case 5: // 8859-9 -> unicode mapping + return c88599[d-0x80]; + default: + return d; + } +} + +eString convertDVBUTF8(unsigned char *data, int len, int table) +{ + int i; + if (!len) + return ""; + + i=0; + if (data[0] <= 5) + table=data[i++]; + if ((data[0] >= 0x10) && (data[0] <= 0x12)) + return ""; + + int bytesneeded=0, t=0, s=i; + + for (; i= 0x10000) + bytesneeded++; + if (code >= 0x800) + bytesneeded++; + if (code >= 0x80) + bytesneeded++; + bytesneeded++; + } + + i=s; + + unsigned char res[bytesneeded]; + + while (i < len) + { + unsigned long code=0; + if ((table == 5) && ((data[i] == 0xC2) || (data[i] == 0xC8) || (data[i] == 0xCA) || (data[i] == 0xCD) || (data[i] == 0xCF)) && (i+1 < len)) + // braindead czech encoding... + if ((code=doCzech(data[i], data[i+1]))) + i+=2; + if (!code) + code=recode(data[i++], table); + if (!code) + continue; + // Unicode->UTF8 encoding + if (code < 0x80) // identity ascii <-> utf8 mapping + res[t++]=char(code); + else if (code < 0x800) // two byte mapping + { + res[t++]=(code>>6)|0xC0; + res[t++]=(code&0x3F)|0x80; + } else if (code < 0x10000) // three bytes mapping + { + res[t++]=(code>>12)|0xE0; + res[t++]=((code>>6)&0x3F)|0x80; + res[t++]=(code&0x3F)|0x80; + } else + { + res[t++]=(code>>18)|0xF0; + res[t++]=((code>>12)&0x3F)|0x80; + res[t++]=((code>>6)&0x3F)|0x80; + res[t++]=(code&0x3F)|0x80; + } + } + if ( t != bytesneeded) + eFatal("t: %d, bytesneeded: %d", t, bytesneeded); + return eString().assign((char*)res, t); +} + +eString convertUTF8DVB(const eString &string) +{ + eString ss=eString(); + + int len=string.length(); + for(int i=0;i=0x80){ + for(unsigned int j=0;j<128;j++){ + if(c88599[j]==c){ // now only 8859-9 .... + c=0x80+j; + break; + } + } + } + ss+=c; + } + + return ss; +} + +eString convertLatin1UTF8(const eString &string) +{ + unsigned int bytesneeded=0, t=0, i; + + unsigned int len=string.size(); + + for (i=0; i= 0x10000) + bytesneeded++; + if (code >= 0x800) + bytesneeded++; + if (code >= 0x80) + bytesneeded++; + bytesneeded++; + } + + i=0; + + unsigned char res[bytesneeded]; + + while (i < len) + { + unsigned long code=string[i++]; + // Unicode->UTF8 encoding + if (code < 0x80) // identity latin <-> utf8 mapping + res[t++]=char(code); + else if (code < 0x800) // two byte mapping + { + res[t++]=(code>>6)|0xC0; + res[t++]=(code&0x3F)|0x80; + } else if (code < 0x10000) // three bytes mapping + { + res[t++]=(code>>12)|0xE0; + res[t++]=((code>>6)&0x3F)|0x80; + res[t++]=(code&0x3F)|0x80; + } else + { + res[t++]=(code>>18)|0xF0; + res[t++]=((code>>12)&0x3F)|0x80; + res[t++]=((code>>6)&0x3F)|0x80; + res[t++]=(code&0x3F)|0x80; + } + } + if ( t != bytesneeded) + eFatal("t: %d, bytesneeded: %d", t, bytesneeded); + return eString().assign((char*)res, t); +} + +int isUTF8(const eString &string) +{ + unsigned int len=string.size(); + + for (unsigned int i=0; i < len; ++i) + { + if (!(string[i]&0x80)) // normal ASCII + continue; + if ((string[i] & 0xE0) == 0xC0) // one char following. + { + // first, length check: + if (i+1 >= len) + return 0; // certainly NOT utf-8 + i++; + if ((string[i]&0xC0) != 0x80) + return 0; // no, not UTF-8. + } else if ((string[i] & 0xF0) == 0xE0) + { + if ((i+1) >= len) + return 0; + i++; + if ((string[i]&0xC0) != 0x80) + return 0; + i++; + if ((string[i]&0xC0) != 0x80) + return 0; + } + } + return 1; // can be UTF8 (or pure ASCII, at least no non-UTF-8 8bit characters) +} + diff --git a/lib/base/estring.h b/lib/base/estring.h new file mode 100644 index 0000000..36f6636 --- /dev/null +++ b/lib/base/estring.h @@ -0,0 +1,113 @@ +#ifndef __E_STRING__ +#define __E_STRING__ + +#include +#include +#include +#include "eerror.h" + +int strnicmp(const char*, const char*, int); + +class eString : public std::string +{ +public: +// constructors + inline eString() {} + inline eString(const char* p); + inline eString(const char* p, int cnt); + inline eString(const std::string &s); +// methods + inline eString left(unsigned int len) const; + inline eString mid(unsigned int index, unsigned int len=(unsigned)-1) const; + inline eString right(unsigned int len) const; + bool isNull() const; +// operators + inline operator bool() const; + inline bool operator!() const; +// methods with implementation in estring.cpp + eString& sprintf(char *fmt, ...); + eString& setNum(int val, int sys=10); + eString& removeChars(const char fchar); + eString& strReplace(const char* fstr, const eString& rstr); + eString& upper(); + int icompare(const eString& s); +}; + +eString convertDVBUTF8(unsigned char *data, int len, int table=5); +eString convertUTF8DVB(const eString &string); // with default ISO8859-5 +eString convertLatin1UTF8(const eString &string); +int isUTF8(const eString &string); + +/////////////////////////////////////////////// Copy Constructors //////////////////////////////////////////////// +inline eString::eString(const std::string &s) + :std::string(s) +{ +} + +inline eString::eString(const char* p) + :std::string(p?p:"") // when the char* p is null, than use ""... otherwise crash... +{ +} + +inline eString::eString(const char* p, int cnt) + :std::string(p, cnt) +{ +} + +///////////////////////////////////////// eString operator bool ///////////////////////////////////////////////// +inline eString::operator bool() const +{ +// Returns a bool that contains true if the string longer than 0 Character otherwise false; + return !empty(); +} + +///////////////////////////////////////// eString operator! //////////////////////////////////////////////////// +inline bool eString::operator!() const +{ +// Returns a bool that contains true if the string ist empty otherwise false; + return empty(); +} + +///////////////////////////////////////// eString left ////////////////////////////////////////////////////////// +inline eString eString::left(unsigned int len) const +{ +// Returns a substring that contains the len leftmost characters of the string. +// The whole string is returned if len exceeds the length of the string. + return len >= length() ? *this : substr(0, len); +} + +//////////////////////////////////////// eString mid //////////////////////////////////////////////////////////// +inline eString eString::mid(unsigned int index, unsigned int len) const +{ +// Returns a substring that contains the len characters of this string, starting at position index. +// Returns a null string if the string is empty or index is out of range. Returns the whole string from index if index+len exceeds the length of the string. + register unsigned int strlen = length(); + + if (index >= strlen) + return eString(); + + if (len == (unsigned)-1) + return substr(index); + + if (strlen < index + len) + len = strlen-index; + + return substr(index, len); +} + +//////////////////////////////////////// eString right //////////////////////////////////////////////////////////// +inline eString eString::right(unsigned int len) const +{ +// Returns a substring that contains the len rightmost characters of the string. +// The whole string is returned if len exceeds the length of the string. + register unsigned int strlen = length(); + return len >= strlen ? *this : substr(strlen-len, len); +} + +inline bool eString::isNull() const +{ +// Returns a bool, that contains true, when the internal char* is null (only when a string ist empty constructed) + return !c_str(); +} + +#endif // __E_STRING__ diff --git a/lib/base/i18n.h b/lib/base/i18n.h new file mode 100644 index 0000000..96d5402 --- /dev/null +++ b/lib/base/i18n.h @@ -0,0 +1,29 @@ +/* + * i18n.h: gettext defines and includes + * + * Copyright (C) 2002 Bastian Blank + * + * 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. + * + * $Id: i18n.h,v 1.1 2003-10-17 15:35:47 tmbinc Exp $ + */ + +#ifndef __CORE__BASE__I18N_H +#define __CORE__BASE__I18N_H + +#include +#define _(string) gettext (string) + +#endif diff --git a/lib/base/init.cpp b/lib/base/init.cpp new file mode 100644 index 0000000..b9cf1de --- /dev/null +++ b/lib/base/init.cpp @@ -0,0 +1,68 @@ +#include +#include +#include + +int eInit::rl=-1; +std::list > *eInit::cl; + +void eInit::add(int trl, eAutoInit *c) +{ + if (!cl) + cl=new std::list >; + cl->push_back(std::pair(trl, c)); + if (rl>=trl) + c->initNow(); +} + +void eInit::remove(int trl, eAutoInit *c) +{ + if (!cl) + return; + cl->remove(std::pair(trl, c)); + if (rl>=trl) + c->closeNow(); +} + +eInit::eInit() +{ +} + +eInit::~eInit() +{ + setRunlevel(-1); + delete cl; + cl=0; +} + +void eInit::setRunlevel(int nrl) +{ + while (nrl>rl) + { + rl++; + + for (std::list >::iterator i(cl->begin()); i!=cl->end(); ++i) + { + if ((*i).first == rl) + { + eDebug("+ (%d) %s", rl, (*i).second->getDescription()); + (*i).second->initNow(); + } + } + } + + while (nrl >::iterator i(cl->begin()); i!=cl->end(); ++i) + if ((*i).first == rl) + { + eDebug("- (%d) %s", rl, (*i).second->getDescription()); + (*i).second->closeNow(); + } + rl--; + } + eDebug("reached rl %d", rl); +} + +eAutoInit::~eAutoInit() +{ +} diff --git a/lib/base/init.h b/lib/base/init.h new file mode 100644 index 0000000..465bac4 --- /dev/null +++ b/lib/base/init.h @@ -0,0 +1,97 @@ +#ifndef __init_h +#define __init_h + +#include +#include + +class eAutoInit; + +class eInit +{ + static std::list > *cl; + friend class eAutoInit; + static int rl; +public: + eInit(); + ~eInit(); + static void setRunlevel(int rlev); + static void add(int trl, eAutoInit *c); + static void remove(int trl, eAutoInit *c); +}; + +class eAutoInit +{ + friend class eInit; + virtual void initNow()=0; + virtual void closeNow()=0; +protected: + int rl; + char *description; +public: + eAutoInit(int rl, char *description): rl(rl), description(description) + { + } + virtual ~eAutoInit(); + const char *getDescription() const { return description; }; +}; + +template class +eAutoInitP1: protected eAutoInit +{ + T1 *t; + const T2 &arg; + void initNow() + { + t=new T1(arg); + } + void closeNow() + { + delete t; + } +public: + operator T1*() + { + return t; + } + eAutoInitP1(const T2 &arg, int runl, char *description): eAutoInit(runl, description), arg(arg) + { + eInit::add(rl, this); + } + ~eAutoInitP1() + { + eInit::remove(rl, this); + } +}; + +template class +eAutoInitP0: protected eAutoInit +{ + T1 *t; + void initNow() + { + t=new T1(); + } + void closeNow() + { + delete t; + } +public: + operator T1*() + { + return t; + } + T1 *operator->() + { + return t; + } + eAutoInitP0(int runl, char *description): eAutoInit(runl, description) + { + eInit::add(rl, this); + } + ~eAutoInitP0() + { + eInit::remove(rl, this); + } +}; + +#endif diff --git a/lib/base/init_num.h b/lib/base/init_num.h new file mode 100644 index 0000000..a9da062 --- /dev/null +++ b/lib/base/init_num.h @@ -0,0 +1,24 @@ +#ifndef __lib_system_init_num_ +#define __lib_system_init_num_ + +namespace eAutoInitNumbers +{ + enum { increment=5 }; + enum + { + configuration=0, + lowlevel=configuration+increment, + graphic=lowlevel+increment, + skin=graphic+increment, + rc=skin+increment, + guiobject=rc+increment, + actions=guiobject+increment, + dvb=actions+increment, + service=dvb+increment, + osd=service+increment, + wizard=osd+increment, + main=osd+increment*5, + }; +}; + +#endif diff --git a/lib/base/message.cpp b/lib/base/message.cpp new file mode 100644 index 0000000..dafbf3f --- /dev/null +++ b/lib/base/message.cpp @@ -0,0 +1,49 @@ +#include +#include +#include + +eMessagePump::eMessagePump(int mt): content(1024*1024), ismt(mt) +{ + pipe(fd); +} + +eMessagePump::~eMessagePump() +{ + if (ismt) + content.lock(); // blocks until all messages are processed. + close(fd[0]); + close(fd[1]); +} + +int eMessagePump::send(const void *data, int len) +{ + if (ismt) + content.lock(len); + return ::write(fd[1], data, len)<0; +} + +int eMessagePump::recv(void *data, int len) +{ + unsigned char*dst=(unsigned char*)data; + while (len) + { + if (ismt) + content.unlock(len); + int r=::read(fd[0], dst, len); + if (r<0) + return r; + dst+=r; + len-=r; + } + return 0; +} + +int eMessagePump::getInputFD() const +{ + return fd[1]; +} + +int eMessagePump::getOutputFD() const +{ + return fd[0]; +} diff --git a/lib/base/message.h b/lib/base/message.h new file mode 100644 index 0000000..6a9ff43 --- /dev/null +++ b/lib/base/message.h @@ -0,0 +1,64 @@ +#ifndef __lib_base_message_h +#define __lib_base_message_h + +#include +#include +#include + +/** + * \brief A generic messagepump. + * + * You can send and receive messages with this class. Internally a fifo is used, + * so you can use them together with a \c eMainloop. + */ +class eMessagePump +{ + int fd[2]; + eLock content; + int ismt; +public: + eMessagePump(int mt=0); + ~eMessagePump(); + int send(const void *data, int len); + int recv(void *data, int len); // blockierend + int getInputFD() const; + int getOutputFD() const; +}; + +/** + * \brief A messagepump with fixed-length packets. + * + * Based on \ref eMessagePump, with this class you can send and receive fixed size messages. + * Automatically creates a eSocketNotifier and gives you a callback. + */ +template +class eFixedMessagePump: private eMessagePump, public Object +{ + eSocketNotifier *sn; + void do_recv(int) + { + T msg; + recv(&msg, sizeof(msg)); + /*emit*/ recv_msg(msg); + } +public: + Signal1 recv_msg; + void send(const T &msg) + { + eMessagePump::send(&msg, sizeof(msg)); + } + eFixedMessagePump(eMainloop *context, int mt): eMessagePump(mt) + { + sn=new eSocketNotifier(context, getOutputFD(), eSocketNotifier::Read); + CONNECT(sn->activated, eFixedMessagePump::do_recv); + sn->start(); + } + ~eFixedMessagePump() + { + delete sn; + } + void start() { sn->start(); } + void stop() { sn->stop(); } +}; + +#endif diff --git a/lib/base/nconfig.cpp b/lib/base/nconfig.cpp new file mode 100644 index 0000000..a0e4a31 --- /dev/null +++ b/lib/base/nconfig.cpp @@ -0,0 +1,1027 @@ +// this is nconfig 0.92, a bit modified +#define NO_MAP_SHARED +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#ifndef PAGESIZE +#ifdef PAGE_SIZE +#define PAGESIZE PAGE_SIZE +#else +#define PAGESIZE 4096 +#endif +#endif + +#define SB ((struct nc_sb_s *) data) +#define CM(x) ((struct nc_chunk_s *) (data+(x))) +#define DE(x) ((struct nc_de_s *) (data+(x))) +#define IDE(x, y) (DE(((unsigned *) (data+(x)->offset))[(y)])) +#define CS(x) (((unsigned *) (data+(x)))[-1]) + +inline unsigned NConfig::crc(const char *d, unsigned len) +{ + unsigned ret = 0; + unsigned l = len / sizeof(unsigned); + + while (l) { + ret += *(const unsigned *)d; + ret = (ret << 3) & (ret >> 29); + l--; + d += sizeof(unsigned); + } + return ret; +} + +NConfig::NConfig(int protect) +{ + fd = -1; + cname = fname = data = NULL; + sb = NULL; + cdir = NULL; + chunks = NULL; + revision = update = lsize = omode = 0; + olck = 0; + lock = NC_L_NONE; + careful = protect; +} + +NConfig::~NConfig() +{ + close(); + free(fname); +} + +void NConfig::close() +{ + free(cname); + cname = NULL; + if (fd > -1) { +#ifdef NO_MAP_SHARED + if (data) { + int size=sb->size; + char *buffer=new char[size]; + memcpy(buffer, data, size); + munmap(data, size); + data = NULL; + ::lseek(fd, 0, SEEK_SET); + ::write(fd, buffer, size); + delete[] buffer; + } +#endif + ::close(fd); + fd = -1; + } + if (data) { + munmap(data, sb->size); + data = NULL; + } +} + +void NConfig::flush() +{ + close(); + open(omode); +} + +int NConfig::setName(const char *name) +{ + if (!name) + return NC_ERR_NVAL; + if (fd > -1) + return NC_ERR_PERM; + free(fname); + fname = strdup(name); + return NC_ERR_OK; +} + +int NConfig::createNew(unsigned resize, unsigned dirent, unsigned mchunks) +{ + if (fd > -1) + return NC_ERR_NVAL; + if (!access(fname, F_OK)) + return NC_ERR_PERM; + + int ff; + if ((ff = ::open(fname, O_WRONLY | O_CREAT, 0600)) == -1) + return NC_ERR_NFILE; + struct nc_sb_s bsb = {NC_SB_MAGIC, resize*PAGESIZE, + dirent, mchunks, resize, mchunks, + sizeof(struct nc_sb_s)+sizeof(struct nc_de_s)+2*sizeof(unsigned), + sizeof(struct nc_sb_s), 0}; + struct nc_de_s bde = {sizeof(struct nc_sb_s)+sizeof(struct nc_de_s), + NC_DIR, sizeof(struct nc_sb_s), 0, 0, 0}; + struct nc_chunk_s bcm; + + write(ff, &bsb, sizeof(bsb)); + write(ff, &bde, sizeof(bde)); + write(ff, "/", 2); + + lseek(ff, sizeof(unsigned)-2, SEEK_CUR); + unsigned cl = sizeof(nc_chunk_s)*mchunks+sizeof(unsigned); + write(ff, &cl, sizeof(unsigned)); + + bcm.offset = bsb.chunk + sizeof(struct nc_chunk_s)*mchunks; + bcm.size = bsb.size - bcm.offset; + + write(ff, &bcm, sizeof(bcm)); + + lseek(ff, bsb.size-1, SEEK_SET); + write(ff, "", 1); + ::close(ff); + return NC_ERR_OK; +} + + +int NConfig::open(int how) +{ + if (!fname) + return NC_ERR_NFILE; + if (how != NC_O_RO && how != NC_O_RW) + return NC_ERR_TYPE; + if (fd > -1) + close(); + + int ff; + if ((ff = ::open(fname, how)) == -1) + return NC_ERR_PERM; + + struct stat sbuf; + fstat(ff, &sbuf); + + if (!sbuf.st_size) + return NC_ERR_CORRUPT; + +#ifdef NO_MAP_SHARED + if ((data = (char *) mmap(NULL, sbuf.st_size, how == NC_O_RO ? PROT_READ : (PROT_READ|PROT_WRITE), MAP_PRIVATE, ff, 0)) == MAP_FAILED) { +#else + if ((data = (char *) mmap(NULL, sbuf.st_size, how == NC_O_RO ? PROT_READ : (PROT_READ|PROT_WRITE), MAP_SHARED, ff, 0)) == MAP_FAILED) { +#endif + ::close(ff); + return NC_ERR_NMEM; + } + if (memcmp(((struct nc_sb_s *) data)->magic, NC_SB_MAGIC, 4)) { + munmap(data, sbuf.st_size); + ::close(ff); + return NC_ERR_CORRUPT; + } + fd = ff; + omode = how; + sb = SB; + lsize = 0; + cname = strdup("/"); + + lockFile(NC_L_RO, TRUE); + rdir = DE(sb->root); + unLockFile(); + return NC_ERR_OK; +} + +void NConfig::expand(unsigned toadd) +{ + unsigned nsize = sb->size + toadd; + lseek(fd, nsize-1, SEEK_SET); + write(fd, "", 1); + _remap(sb->size, nsize); + sb->size = nsize; + cdir = getDirEnt(cname); + chunks = CM(sb->chunk); +#ifdef NC_DEBUG_ALLOC + fprintf(stderr, "Expanded from %u to %u\n", nsize-toadd, nsize); +#endif +} + +unsigned NConfig::getChunk(unsigned s) +{ + int lst = -1; + + // Make sure we get aligned data + s = alignSize(s) + sizeof(unsigned); + +#ifdef NC_DEBUG_ALLOC + fprintf(stderr, "Taking %u (total %u)\n", s, sb->chunk_ttl); +#endif + + do { + int left = 0, right = sb->chunk_ttl - 1, c; + while (left <= right) { + int diff = chunks[c = (left + right) / 2].size - s; + if (diff < 0 || diff == sizeof(unsigned)) { +#ifdef NC_DEBUG_ALLOC + if (diff > 0) + fprintf(stderr, "Rejected chunk %d (%u:%u)\n", c, chunks[c].offset, chunks[c].size); +#endif + right = c - 1; + continue; + } + lst = c; + if (!diff) + break; + left = c + 1; + } + if (lst < 0) { + unsigned ll = (s / (sb->size_inc*PAGESIZE) + 1) * PAGESIZE * sb->size_inc; + // we don't have a suitable chunk + expand(ll); + // insert new chunk into list (always succeeds) + *(unsigned *)(data+sb->size-ll) = ll; + fast_free(sb->size-ll+sizeof(unsigned)); + } + } while (lst < 0); + +#ifdef NC_DEBUG_ALLOC + fprintf(stderr, "haluz 7: off = %u size = %u\n", chunks[7].offset, chunks[7].size); + fprintf(stderr, "Got %u chunk (pos %d), taking %u\n", chunks[lst].size, lst, s); + fprintf(stderr, "chunk (%u:%u)\n", chunks[lst].offset, chunks[lst].size); +#endif + + unsigned best = chunks[lst].offset+sizeof(unsigned); + memset(data+best, 0, s-sizeof(unsigned)); + chunks[lst].size -= s; + chunks[lst].offset += s; + CS(best) = s; + + while (lst < ((signed)sb->chunk_ttl - 1) && chunks[lst].size < chunks[lst+1].size) { + unsigned b = chunks[lst].size; + unsigned i = lst + 1; + chunks[lst].size = chunks[i].size; + chunks[i].size = b; + b = chunks[lst].offset; + chunks[lst].offset = chunks[i].offset; + chunks[i].offset = b; + lst = i; + } + +#ifdef NC_DEBUG_ALLOC + fprintf(stderr, "Returned %u:%u\n", best, CS(best)); +#endif + return best; +} + +void NConfig::freeChunk(unsigned where) +{ +#ifdef NC_DEBUG_ALLOC + fprintf(stderr, "Free chunk: %u\n", CS(where)); +#endif + if (chunks[sb->chunk_ttl-2].size) { +#ifdef NC_DEBUG_ALLOC + fprintf(stderr, "Last slot available.\n"); +#endif + unsigned n = getChunk((sb->chunk_ttl+sb->chunk_inc)*sizeof(struct nc_chunk_s)); + unsigned f = sb->chunk; + memcpy(data+n, chunks, (sb->chunk_ttl-1)*sizeof(struct nc_chunk_s)); + chunks = CM(sb->chunk = n); + sb->chunk_ttl += sb->chunk_inc; + fast_free(f); + } + fast_free(where); +} + +inline unsigned NConfig::alignSize(unsigned s) +{ + unsigned of = s % sizeof(unsigned); + return of ? s + sizeof(unsigned) - of : s; +} + +void NConfig::delKey(const char *name) +{ + _delKey(name, NULL, TRUE); +} + +void NConfig::_delKey(const char *name, struct nc_de_s *p, int tosort) +{ + if (fd < 0) + return; + lockFile(NC_L_RW); + struct nc_de_s *nd = getDirEnt(name, p); + if (nd && nd != rdir && nd != cdir) { + unsigned ndo = ((char *)nd) - data; + if (nd->type == NC_DIR) + for (unsigned i=0; ipages; i++) { + struct nc_de_s *dd = IDE(nd, i); + if (dd->type) + _delKey(data+dd->name, nd, FALSE); + nd = DE(ndo); + } + sb->modtime++; + freeChunk(nd->offset); + freeChunk(DE(ndo)->name); + nd = DE(ndo); + struct nc_de_s *parent = DE(nd->parent); + memset(nd, 0, sizeof(struct nc_de_s)); + // keep parent directory sorted + if (tosort) { + unsigned i = 0; + while (i < parent->pages && IDE(parent, i) != nd) + i++; + memmove(((unsigned *)(data+parent->offset))+i, + ((unsigned *)(data+parent->offset))+i+1, + sizeof(unsigned)*(parent->pages-i-1)); + ((unsigned *)(data+parent->offset))[parent->pages-1] = ndo; + } + } + unLockFile(); +} + +int NConfig::_setKey(const char *name, const unsigned t, const char *value, const unsigned len) +{ + if (fd < 0) + return NC_ERR_NFILE; + if (omode != NC_O_RW) + return NC_ERR_RDONLY; + lockFile(NC_L_RW); + struct nc_de_s *nd = getDirEnt(name); +#ifdef NC_DEBUG_INSERT + fprintf(stderr, "Found DE %p\n", nd); +#endif + if (!nd) { + struct nc_de_s *sd = *name == '/' ? rdir : cdir; + char *parse = canonize(name), *p = parse; + + while ((nd = getDirEnt(p, sd))) + if (nd->type == NC_DIR) { + sd = nd; + p += strlen(p)+1; + } else { + free(parse); + unLockFile(); + return NC_ERR_PERM; + } + + size_t pl = 0; + struct nc_de_s ds; + unsigned sdo = ((char *)sd) - data; + while (*(p+(pl = strlen(p)+1))) { + ds.pages = ds.offset = 0; + ds.name = getChunk(pl); + memcpy(data+ds.name, p, pl); + ds.type = NC_DIR; +#ifdef NC_DEBUG_INSERT + fprintf(stderr, "Insertion parent 2: %p\n", DE(sdo)); +#endif + // FIXME: crc calculation + sdo = ((char *)insert(sdo, &ds)) - data; + p += pl; + } + ds.type = t; + memcpy(data+(ds.name = getChunk(pl)), p, pl); + ds.pages = ds.offset = 0; +#ifdef NC_DEBUG_INSERT + fprintf(stderr, "Insertion parent 1: %p\n", DE(sdo)); +#endif + nd = insert(sdo, &ds); + sb->modtime++; + free(parse); + } else + if (nd->type != t) { + unLockFile(); + return NC_ERR_TYPE; + } + unsigned ndo = ((char *)nd) - data; + if (t != NC_DIR) { + if (value) { + if (nd->offset && CS(nd->offset)-sizeof(unsigned) < len) { + freeChunk(nd->offset); + nd = DE(ndo); + nd->offset = 0; + } + if (nd->offset) { + if (CS(nd->offset)-sizeof(unsigned) > alignSize(len)+sizeof(unsigned)) { + unsigned trim = CS(nd->offset) - alignSize(len) - sizeof(unsigned); + unsigned off = nd->offset + alignSize(len) + sizeof(unsigned); + CS(off) = trim; + CS(nd->offset) -= trim; + freeChunk(off); + nd = DE(ndo); + } + } else { + unsigned off = getChunk(len); + nd = DE(ndo); + nd->offset = off; + } + memcpy(data+nd->offset, value, len); + nd->pages = len; + } else + if (nd->offset) { + freeChunk(nd->offset); + DE(ndo)->offset = 0; + } + } else + // Preallocate pages for directory + if (len > nd->pages) { + unsigned off = getChunk(sizeof(unsigned)*len); + if (DE(ndo)->offset) { + memcpy(data+off, data+DE(ndo)->offset, sizeof(unsigned)*(DE(ndo)->pages)); + freeChunk(DE(ndo)->offset); + } + DE(ndo)->offset = off; + for (unsigned al = len - DE(ndo)->pages; al; al--) { + off = getChunk(sizeof(struct nc_de_s)); + ((unsigned *)(data+DE(ndo)->offset))[DE(ndo)->pages++] = off; + } + } + unLockFile(); +#ifdef NC_DEBUG_INSERT + fprintf(stderr, "%p\n", cdir); +#endif + return NC_ERR_OK; +} + +char *NConfig::getName(const struct nc_de_s *w) +{ + if (w == rdir) + return strdup("/"); + char *parent = getName(DE(w->parent)); + unsigned l1 = strlen(parent); + unsigned l2 = strlen(data+w->name)+1; + + parent = (char *) realloc(parent, l1 + l2 + (l1 == 1 ? 0 : 1)); + if (l1 != 1) { + memcpy(parent+l1, "/", 2); + l1++; + } + memcpy(parent+l1, data+w->name, l2); + return parent; +} + +int NConfig::chDir(const char *name) +{ + if (fd < 0) + return NC_ERR_NFILE; + lockFile(NC_L_RO); + + int ret = NC_ERR_OK; + struct nc_de_s *nd = getDirEnt(name); + if (nd) { + if (nd->type == NC_DIR) { + cdir = nd; + free(cname); + cname = getName(cdir); + } else + ret = NC_ERR_NDIR; + } else + ret = NC_ERR_NEXIST; + unLockFile(); + return ret; +} + +const char *NConfig::pwDir() +{ + if (fd < 0) + return NULL; + lockFile(NC_L_RO); + struct nc_de_s *l = cdir; + char * ret = strdup(data+l->name); + while (DE(l->parent) != l) { + unsigned len = CS(l->name); + char *r = (char *) malloc(strlen(ret) + len + 2); + memcpy(r, data+l->name, len); + if (*ret != '/' && DE(l->parent) != rdir) + strcat(r, "/"); + strcat(r, ret); + free(ret); + ret = r; + l = DE(l->parent); + } + unLockFile(); + return ret; +} + +struct nc_de_s *NConfig::getDirEnt(const char *name, struct nc_de_s *cc) +{ + struct nc_de_s *ret = cc ? cc : ((*name == '/') ? rdir : cdir); + char *c = canonize(name), *can; + + if (!(can = c)) + return ret; + while (*c) { + if (!strcmp(c, "..")) + ret = DE(ret->parent); + else + if (strcmp(c, ".")) { + struct nc_de_s *re = ret; + int left = 0, right = ret->pages-1, p, r; + + ret = NULL; + while (left <= right) { + p = (left + right) / 2; + r = strcmp(c, data+IDE(re, p)->name); + if (r < 0) { + left = p + 1; + continue; + } + if (!r) { + ret = IDE(re, p); + break; + } + right = p - 1; + } + } + c += strlen(c)+1; + if (!ret || (*c && ret->type != NC_DIR)) { + ret = NULL; + break; + } + } + free(can); + return ret; +} + +char *NConfig::canonize(const char *name) +{ + if (*name == '/') + name++; + size_t i = strlen(name); + char *ret = (char *)calloc(1, i+3); + memcpy(ret, name, i); + for (size_t j=0; jname, what->type); +#endif + struct nc_de_s *w = DE(where); + if (!DE(where)->offset || IDE(w, w->pages-1)->type) { + unsigned a = getChunk((w->pages+sb->ent_inc)*sizeof(unsigned)); + w = DE(where); + if (w->offset) { + memcpy(data+a, data+w->offset, w->pages*sizeof(unsigned)); + freeChunk(w->offset); + w = DE(where); + } + w->offset = a; + for (unsigned ha = 0; haent_inc; ha++) { + unsigned off = getChunk(sizeof(struct nc_de_s)); + w = DE(where); + ((unsigned *)(data+w->offset))[w->pages] = off; + w->pages++; + } + } + int i = 0, l = 0, r = w->pages - 1, c; + while (l <= r) { + c = (l + r) / 2; + if (!IDE(w, c)->type || strcmp(data+what->name, data+IDE(w, c)->name) > 0) { + i = c; + r = c - 1; + } else + l = c + 1; + } + +#ifdef NC_DEBUG_INSERT + fprintf(stderr, "Insertion to slot %u (%s)\n", i, data+what->name); +#endif + what->parent = where; + unsigned to = ((unsigned *)(data+w->offset))[w->pages-1]; + memmove(((unsigned *)(data+w->offset))+i+1, ((unsigned *)(data+w->offset))+i, sizeof(unsigned)*(w->pages-i-1)); + ((unsigned *)(data+w->offset))[i] = to; + void *ret = memcpy(DE(to), what, sizeof(struct nc_de_s)); + sb->modtime++; + return (struct nc_de_s *)ret; +} + +void NConfig::status() +{ + if (fd < 0) + return; + lockFile(NC_L_RO); + fprintf(stderr, "Size:\t%u\n", sb->size); + unsigned low=0, hi=chunks[0].size, cnt=0, ttl=0; + for (unsigned i=0; ichunk_ttl; i++) + if (chunks[i].size > 0) { + if (!low || low > chunks[i].size) + low = chunks[i].size; + ttl += chunks[i].size; + cnt++; + } + unLockFile(); + fprintf(stderr, "Free:\t%u in %u chunk%s\n", ttl, cnt, cnt > 1 ? "s" : ""); + if (cnt > 0) + fprintf(stderr, "Min:\t%u\nAvg:\t%u\nMax:\t%u\n", low, ttl / cnt, hi); +} + +struct nc_ls_s *NConfig::ls(const char *name) +{ + if (fd < 0) + return NULL; + lockFile(NC_L_RO); + + struct nc_ls_s *rt = NULL; + unsigned count = 0; + struct nc_de_s *de = NULL; + struct nc_de_s *ds = name ? getDirEnt(name) : cdir; + + if (ds && ds->type == NC_DIR) { + for (unsigned i=0; ipages; i++) { + de = IDE(ds, i); + if (de->type && de->name) { + rt = (struct nc_ls_s *) realloc(rt, (count+2)*sizeof(nc_ls_s)); + rt[count].type = de->type; + rt[count].name = strdup(data+de->name); + rt[++count].type = 0; + rt[count].name = NULL; + } + } + } + unLockFile(); + return rt; +} + +void NConfig::fast_free(unsigned offset) +{ + unsigned s = CS(offset), i = 0; + offset -= sizeof(unsigned); + + while (1) { + if (!chunks[i].size) { +#ifdef NC_DEBUG_ALLOC + fprintf(stderr, "Inserting %u:%u to %u\n", offset, s, i); +#endif + chunks[i].offset = offset; + chunks[i].size = s; + break; + } + if (chunks[i].offset == offset + s) { +#ifdef NC_DEBUG_ALLOC + fprintf(stderr, "Prepending %u:%u to %u (%u:%u)\n", offset, s, i, chunks[i].offset, chunks[i].size); +#endif + chunks[i].offset -= s; + chunks[i].size += s; + break; + } + if (offset == chunks[i].offset + chunks[i].size) { +#ifdef NC_DEBUG_ALLOC + fprintf(stderr, "Appending %u:%u to %u (%u:%u)\n", offset, s, i, chunks[i].offset, chunks[i].size); +#endif + chunks[i].size += s; + break; + } + i++; + } + + // Keep the array sorted + while (i && chunks[i].size > chunks[i-1].size) { + unsigned b = chunks[i].size; + unsigned j = i - 1; + chunks[i].size = chunks[j].size; + chunks[j].size = b; + b = chunks[i].offset; + chunks[i].offset = chunks[j].offset; + chunks[j].offset = b; + i = j; + } +} + +int NConfig::renameKey(const char *oldname, const char *newname) +{ + if (fd < 0) + return NC_ERR_NFILE; + if (omode != NC_O_RW) + return NC_ERR_RDONLY; + lockFile(NC_L_RW); + int ret = NC_ERR_OK; + struct nc_de_s *parent, *nd = getDirEnt(newname); + if (nd) { + if ((nd = getDirEnt(oldname))) { + size_t len = strlen(newname)+1; + int inc = strcmp(oldname, newname); + unsigned i, off, pos, ndo = ((char *)nd) - data; + if (alignSize(len) != CS(nd->name)) { + freeChunk(nd->name); + off = getChunk(len); + DE(ndo)->name = off; + nd = DE(ndo); + } + memcpy(data+nd->name, newname, len); + parent = DE(nd->parent); + for (pos = 0; pos < parent->pages && IDE(parent, pos) != nd; pos++) + ; + for (i = pos; i>=0 && ipages; i += inc) + if (strcmp(data+IDE(parent, i)->name, newname) != inc) + break; + if (inc == -1) + memmove(((unsigned *)(data+parent->offset))+i+1, + ((unsigned *)(data+parent->offset))+i, + sizeof(unsigned)*(pos - i)); + else + memmove(((unsigned *)(data+parent->offset))+pos, + ((unsigned *)(data+parent->offset))+pos+1, + sizeof(unsigned)*(i-pos)); + ((unsigned *)(data+parent->offset))[i] = ndo; + sb->modtime++; + } else + ret = NC_ERR_NEXIST; + } else + ret = NC_ERR_PERM; + unLockFile(); + return NC_ERR_OK; +} + +int NConfig::createDir(const char *name, unsigned entries) +{ + return _setKey(name, NC_DIR, NULL, entries); +} + +int NConfig::setKey(const char *name, const unsigned long long value) +{ + return _setKey(name, NC_UINT, (const char *)&value, sizeof(value)); +} + +int NConfig::setKey(const char *name, const unsigned value) +{ + unsigned long long b = value; + return _setKey(name, NC_UINT, (const char *)&b, sizeof(b)); +} + +int NConfig::setKey(const char *name, const signed long long value) +{ + return _setKey(name, NC_INT, (const char *)&value, sizeof(value)); +} + +int NConfig::setKey(const char *name, const int value) +{ + signed long long b = value; + return _setKey(name, NC_INT, (const char *)&b, sizeof(b)); +} + +int NConfig::setKey(const char *name, const char *value) +{ + return _setKey(name, NC_STRING, value, strlen(value)+1); +} + +int NConfig::setKey(const char *name, const long double value) +{ + return _setKey(name, NC_DOUBLE, (const char *)&value, sizeof(value)); +} + +int NConfig::setKey(const char *name, const double value) +{ + long double b = value; + return _setKey(name, NC_DOUBLE, (const char *)&b, sizeof(b)); +} + +int NConfig::setKey(const char *name, const char *value, const unsigned len) +{ + if (!value && len) + return NC_ERR_NVAL; + if (!len) + return _setKey(name, NC_RAW, NULL, 0); + return _setKey(name, NC_RAW, value, len); +} + +int NConfig::getKey(const char *name, unsigned long long &value) +{ + if (fd < 0) + return NC_ERR_NFILE; + lockFile(NC_L_RO); + int ret = NC_ERR_OK; + struct nc_de_s *k = getDirEnt(name); + if (k) { + if (k->type == NC_UINT) { + memcpy(&value, data+k->offset, sizeof(value)); + } else + ret = NC_ERR_TYPE; + } else + ret = NC_ERR_NEXIST; + unLockFile(); + return ret; +} + +int NConfig::getKey(const char *name, unsigned &value) +{ + if (fd < 0) + return NC_ERR_NFILE; + lockFile(NC_L_RO); + int ret = NC_ERR_OK; + struct nc_de_s *k = getDirEnt(name); + if (k) { + if (k->type == NC_UINT) { + unsigned long long b; + memcpy(&b, data+k->offset, sizeof(b)); + value = b; + } else + ret = NC_ERR_TYPE; + } else + ret = NC_ERR_NEXIST; + unLockFile(); + return ret; +} + +int NConfig::getKey(const char *name, long double &value) +{ + if (fd < 0) + return NC_ERR_NFILE; + lockFile(NC_L_RO); + int ret = NC_ERR_OK; + struct nc_de_s *k = getDirEnt(name); + if (k) { + if (k->type == NC_DOUBLE) { + memcpy(&value, data+k->offset, sizeof(value)); + } else + ret = NC_ERR_TYPE; + } else + ret = NC_ERR_NEXIST; + unLockFile(); + return ret; +} + +int NConfig::getKey(const char *name, double &value) +{ + if (fd < 0) + return NC_ERR_NFILE; + lockFile(NC_L_RO); + int ret = NC_ERR_OK; + struct nc_de_s *k = getDirEnt(name); + if (k) { + if (k->type == NC_DOUBLE) { + long double b; + memcpy(&b, data+k->offset, sizeof(b)); + value = b; + } else + ret = NC_ERR_TYPE; + } else + ret = NC_ERR_NEXIST; + unLockFile(); + return ret; +} + +int NConfig::getKey(const char *name, signed long long &value) +{ + if (fd < 0) + return NC_ERR_NFILE; + lockFile(NC_L_RO); + int ret = NC_ERR_OK; + struct nc_de_s *k = getDirEnt(name); + if (k) { + if (k->type == NC_INT) { + memcpy(&value, data+k->offset, sizeof(value)); + } else + ret = NC_ERR_TYPE; + } else + ret = NC_ERR_NEXIST; + unLockFile(); + return ret; +} + +int NConfig::getKey(const char *name, int &value) +{ + if (fd < 0) + return NC_ERR_NFILE; + lockFile(NC_L_RO); + int ret = NC_ERR_OK; + struct nc_de_s *k = getDirEnt(name); + if (k) { + if (k->type == NC_INT) { + signed long long b; + memcpy(&b, data+k->offset, sizeof(b)); + value = b; + } else + ret = NC_ERR_TYPE; + } else + ret = NC_ERR_NEXIST; + unLockFile(); + return ret; +} + +int NConfig::getKey(const char *name, char *&value) +{ + if (fd < 0) + return NC_ERR_NFILE; + lockFile(NC_L_RO); + int ret = NC_ERR_OK; + struct nc_de_s *k = getDirEnt(name); + if (k) { + if (k->type == NC_STRING) { + if (k->offset) { + if (!(value = strdup(data+k->offset))) + ret = NC_ERR_NMEM; + } else + value = NULL; + } else + ret = NC_ERR_TYPE; + } else + ret = NC_ERR_NEXIST; + unLockFile(); + return ret; +} + +int NConfig::getKey(const char *name, char *&value, unsigned &len) +{ + if (fd < 0) + return NC_ERR_NFILE; + lockFile(NC_L_RO); + int ret = NC_ERR_OK; + struct nc_de_s *k = getDirEnt(name); + if (k) { + if (k->type == NC_RAW) { + if (k->offset) { + len = k->pages; + value = (char *)malloc(len); + memcpy(value, data+k->offset, len); + } else { + len = 0; + value = NULL; + } + } else + ret = NC_ERR_TYPE; + } else + ret = NC_ERR_NEXIST; + unLockFile(); + return ret; +} + +void NConfig::lockFile(int type, int force) +{ +#ifdef NC_DEBUG_LOCK + fprintf(stderr, "Lock called type=%d force=%d lock=%d olck=%u\n", type, force, lock, olck); +#endif + if (lock == NC_L_RO && type == NC_L_RW) { + fprintf(stderr, "Lock promotion is not possible.\n"); + abort(); + } + if (lock != NC_L_NONE) { + olck++; + return; + } + + struct flock flc = { type == NC_L_RW ? F_WRLCK : F_RDLCK, SEEK_SET, 0, 0, 0 }; + while (fcntl(fd, F_SETLKW, &flc)) { + sched_yield(); + flc.l_type = type == NC_L_RW ? F_WRLCK : F_RDLCK; + flc.l_whence = SEEK_SET; + flc.l_len = flc.l_start = 0; + } + +#ifdef NC_DEBUG_LOCK + fprintf(stderr, "Locked %u %u %s\n", sb->modtime, update, force ? "forced." : ""); +#endif + if (careful && type == NC_L_RW) + mprotect(data, sb->size, PROT_READ | PROT_WRITE); + lock = type; + olck = 0; + if (sb->modtime != update || force) { + // refresh memory mapping + if (lsize != sb->size) { + _remap(lsize, sb->size); + lsize = sb->size; + chunks = CM(sb->chunk); + } + cdir = getDirEnt(cname); + update = sb->modtime; + } +} + +void NConfig::unLockFile() +{ +#ifdef NC_DEBUG_LOCK + fprintf(stderr, "UnLock called lock=%u olck=%u\n", lock, olck); +#endif + if (olck) { + olck--; + return; + } + if (lock == NC_L_NONE) + return; + struct flock flc = {F_UNLCK, SEEK_SET, 0, 0, 0 }; + update = sb->modtime; +#ifdef NC_DEBUG_LOCK + fprintf(stderr, "Unlock %u\n", update); +#endif + if (careful) + mprotect(data, sb->size, PROT_READ); + fcntl(fd, F_SETLK, &flc); + lock = NC_L_NONE; + olck = 0; +} + +void NConfig::_remap(const size_t osize, const size_t nsize) +{ + data = (char *) mremap(data, osize, nsize, 1); + if (data == MAP_FAILED) { + perror("mremap"); + abort(); + } + sb = SB; + rdir = DE(sb->root); +} + +char * NConfig::version() +{ + return strdup("EliteDVB registry"); +} + diff --git a/lib/base/nconfig.h b/lib/base/nconfig.h new file mode 100644 index 0000000..12a3c6c --- /dev/null +++ b/lib/base/nconfig.h @@ -0,0 +1,392 @@ +#ifndef NC_NCONFIG_H +#define NC_NCONFIG_H 1 + +#include +#include + +/* + * Superblock definitions + */ +#define NC_SB_MAGIC ("\0\11\22") // Superblock identifier + +/* + * Key type definitions + */ +#define NC_DIR 0x01 // The key is a directory +#define NC_STRING 0x02 // The key contains a string +#define NC_INT 0x03 // The key contains a signed integer +#define NC_UINT 0x04 // The key contains an unsigned integer +#define NC_RAW 0x05 // The key contains raw data +#define NC_DOUBLE 0x06 // The key contains a double +#define NC_LINK 0x07 // The key points somewhere else + +/* + * File access definitions + */ +#define NC_O_RO 0x01 // Open file in read-only mode +#define NC_O_RW 0x02 // Open file in read-write mode + +/* + * Lock types + */ +#define NC_L_NONE 0x00 // No lock +#define NC_L_RO 0x01 // Read-only lock +#define NC_L_RW 0x02 // Read-write lock + +/* + * Error codes + */ +#define NC_ERR_OK 0 // Everything is OK +#define NC_ERR_TYPE -1 // Type mismatch +#define NC_ERR_NDIR -2 // Key is not a directory +#define NC_ERR_PERM -3 // Operation is not allowed +#define NC_ERR_NMEM -4 // Not enough memory to complete operation +#define NC_ERR_NEXIST -5 // Key does not exist +#define NC_ERR_NFILE -6 // No file is assigned/open +#define NC_ERR_CORRUPT -7 // File is corrupted +#define NC_ERR_NVAL -8 // Invalid value +#define NC_ERR_RDONLY -9 // File is open in read-only mode +#define NC_ERR_NOSUPPORT -10 // Support is not compiled-in + +/* + * Truth value definitions + */ +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/* + * Header of the config file. + */ +struct nc_sb_s { + char magic[4]; // superblock magic + unsigned size; // Current file size + unsigned ent_inc; // directory increment + unsigned chunk_inc; // Memory chunks increment + unsigned size_inc; // file size increment + unsigned chunk_ttl; // size of chunkmap + unsigned chunk; // pointer to chunkmap + unsigned root; // pointer to root direntry + unsigned modtime; // file version +}; + +/* + * Free chunk descriptor + */ +struct nc_chunk_s { + unsigned offset; + unsigned size; +}; + +/* + * In-file directory entry + */ +struct nc_de_s { + unsigned name; + unsigned type; + unsigned parent; + unsigned offset; + unsigned pages; + unsigned crc; +}; + +/* + * Ls reporting + */ +struct nc_ls_s { + const char *name; + unsigned type; +}; + +class NConfig +{ +public: + /* + * Class constructor + * pass TRUE as parameter to enable + * write protection when leaving library + */ + NConfig(int protect = FALSE); + virtual ~NConfig(); + + /* + * Set file name (prior to open) + * Errors: + * NC_ERR_PERM file is already open + * NC_ERR_NVAL no file name is given + */ + int setName(const char *name); + + /* + * Open the configuration file, re-open it + * Errors: + * NC_ERR_NFILE no file name is assigned + * NC_ERR_TYPE file open mode is invalid + * NC_ERR_PERM file cannot be opened/created + * NC_ERR_NMEM unable to mmap the file + * NC_ERR_CORRUPT superblock magic mismatch + */ + int open(const int how = NC_O_RW); + + /* + * Close the configuration file + * No errors defined + */ + void close(); + + void flush(); // flush file if not mmap'ed + + /* + * Create a new file + * resize is filesize increment is system pages + * dirent is directory increment + * mchunks is memory block increment + * Errors: + * NC_ERR_PERM file already exists + * NC_ERR_NFILE cannot create new file + * NC_ERR_NVAL file is already open + */ + int createNew(unsigned resize = 4, unsigned dirent = 32, unsigned mchunks = 32); + + /* + * Get an unsigned integer + * Errors: + * NC_ERR_NFILE no file is open + * NC_ERR_NEXIST the key does not exist + * NC_ERR_TYPE the key exists, but is of different type + */ + int getKey(const char *name, unsigned &value); + int getKey(const char *name, unsigned long long &value); + + /* + * Get a signed integer + * Errors: + * NC_ERR_NFILE no file is open + * NC_ERR_NEXIST the key does not exist + * NC_ERR_TYPE the key exists, but is of different type + */ + int getKey(const char *name, int &value); + int getKey(const char *name, signed long long &value); + + /* + * Get a string + * Errors: + * NC_ERR_NFILE no file is open + * NC_ERR_NEXIST the key does not exist + * NC_ERR_TYPE the key exists, but is of different type + */ + int getKey(const char *name, char *&value); + + /* + * Get a long double + * Errors: + * NC_ERR_NFILE no file is open + * NC_ERR_NEXIST the key does not exist + * NC_ERR_TYPE the key exists, but is of different type + */ + int getKey(const char *name, double &value); + int getKey(const char *name, long double &value); + + /* + * Get raw data + * Errors: + * NC_ERR_NFILE no file is open + * NC_ERR_NEXIST the key does not exist + * NC_ERR_TYPE the key exists, but is of different type + */ + int getKey(const char *name, char *&value, unsigned &len); + + /* + * Insert an unsigned integer + * NC_ERR_NFILE no file is open + * NC_ERR_RDONLY file is open in read-only mode + * NC_ERR_PERM intermediate key is not a directory + * NC_ERR_TYPE key already exists, but is not an usigned integer + * NC_ERR_NEXIST key does not exist (should NEVER happen) + */ + int setKey(const char *name, const unsigned value); + int setKey(const char *name, const unsigned long long value); + + /* + * Insert an integer + * NC_ERR_NFILE no file is open + * NC_ERR_RDONLY file is open in read-only mode + * NC_ERR_PERM intermediate key is not a directory + * NC_ERR_TYPE key already exists, but is not a signed integer + * NC_ERR_NEXIST key does not exist (should NEVER happen) + */ + int setKey(const char *name, const int value); + int setKey(const char *name, const signed long long value); + + /* + * Insert a string + * NC_ERR_NFILE no file is open + * NC_ERR_RDONLY file is open in read-only mode + * NC_ERR_PERM intermediate key is not a directory + * NC_ERR_TYPE key already exists, but is not a string + * NC_ERR_NEXIST key does not exist (should NEVER happen) + */ + int setKey(const char *name, const char *value); + + /* + * Insert raw data + * NC_ERR_NFILE no file is open + * NC_ERR_RDONLY file is open in read-only mode + * NC_ERR_PERM intermediate key is not a directory + * NC_ERR_TYPE key already exists, but is not raw data + * NC_ERR_NEXIST key does not exist (should NEVER happen) + */ + int setKey(const char *name, const char *value, const unsigned len); + + /* + * Insert a double + * NC_ERR_NFILE no file is open + * NC_ERR_RDONLY file is open in read-only mode + * NC_ERR_PERM intermediate key is not a directory + * NC_ERR_TYPE key already exists, but is not raw data + * NC_ERR_NEXIST key does not exist (should NEVER happen) + */ + int setKey(const char *name, const double value); + int setKey(const char *name, const long double value); + + /* + * Rename a key + * Errors: + * NC_ERR_NFILE no file is open + * NC_ERR_RDONLY file is open read-only + * NC_ERR_NEXIST the key does not exist + * NC_ERR_PERM key with specified name already exists + */ + int renameKey(const char *oldname, const char *newname); + + /* + * Delete a key + * No errors defined + */ + void delKey(const char *name); + + /* + * Create a directory + * entries parameter specifies number of direntries to preallocate + * Errors: + * NC_ERR_NFILE no file is open + * NC_ERR_RDONLY file is open in read-only mode + * NC_ERR_PERM intermediate key is not a directory + * NC_ERR_TYPE key already exists, but is not a directory + * NC_ERR_NEXIST key does not exist (should NEVER happen) + */ + int createDir(const char *name, unsigned entries = 0); + + /* + * Change working directory + * Errors: + * NC_ERR_NFILE no file is open + * NC_ERR_NDIR target key is not a directory + * NC_ERR_NEXIST target key direcotry does not exist + */ + int chDir(const char *name); + + /* + * Print working directory + * Errors: + * Returns NULL on error + */ + const char *pwDir(); + + /* + * List all keys in current/specified directory + * Result is a NULL-terminated array of nc_ls_s + * structures. + * Errors: + * Returns NULL on error + * Note: + * You need to free the returned pointer, + * as well as all the names in it. + */ + struct nc_ls_s *ls(const char *dir = NULL); + + /* + * Lock file + * This will block until lock becomes available + * type is either: + * NC_L_RO for read-only lock + * NC_L_RW for read-write lock + * No errors defined + * + * NOTE: lock may get promoted + */ + void lockFile(int type, int force = FALSE); + + /* + * Unlock file + * No errors defined + */ + void unLockFile(); + + /* + * Print out (to stderr) information about current file + * No errors defined + */ + void status(); + + /* + * Return version string + */ + static char *version(); + + /* + * Dump current file to XML + * Errors: + * NC_ERR_NFILE no file is open + * NC_ERR_PERM could not write XML output + */ + int toXML(const char *filename); + + /* + * Load XML to current file + * a file has to be open + * force can be + * TRUE - existing keys will be deleted + * FALSE - import will ignore a key if existing key type conflicts + * Errors: + * NC_ERR_NFILE no file is open + * NC_ERR_PERM file is open read-only + */ + int fromXML(const char *filename, int force = TRUE); + +protected: + int fd, omode, lock, careful; + char *fname, *data, *cname; + unsigned lsize, update; + unsigned revision, olck; + struct nc_sb_s *sb; + struct nc_de_s *rdir, *cdir; + struct nc_chunk_s *chunks; + + int _setKey(const char *, const unsigned, const char *, const unsigned); + void _delKey(const char *, struct nc_de_s *, int); + void expand(unsigned); + + void fast_free(unsigned); + + unsigned getChunk(unsigned); + void freeChunk(unsigned); + static inline unsigned alignSize(unsigned); + + struct nc_de_s *getDirEnt(const char *, struct nc_de_s * = NULL); + struct nc_de_s *insert(unsigned, struct nc_de_s *); + char *canonize(const char *); + char *getName(const struct nc_de_s *); + + void _remap(const size_t, const size_t); + + inline unsigned crc(const char *, unsigned); + void store(nc_de_s *, FILE *); + void restore(void *, int); +}; + +#endif /* NC_NCONFIG_H */ + diff --git a/lib/base/nxml.cpp b/lib/base/nxml.cpp new file mode 100644 index 0000000..f32880a --- /dev/null +++ b/lib/base/nxml.cpp @@ -0,0 +1,339 @@ +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_TIME_H +#include +#endif + +#ifdef HAVE_LIBXML2 +#include +#include +#include +#else +#define xmlChar char +#endif /* HAVE_LIBXML2 */ + +#define DE(x) ((struct nc_de_s *) (data+(x))) +#define IDE(x, y) (DE(((unsigned *) (data+(x)->offset))[(y)])) +#define XML_DE ((const xmlChar *) "dirEntry") +#define XML_NS ((const xmlChar *) "http://hq.alert.sk/projects/nconfig") +#define XML_ROOT ((const xmlChar *) "NConfigExport") + +static char *encodeXml(const char *what) +{ + unsigned p = 0, size = 6*strlen(what)+1; + char *ret = (char *)malloc(size); + for (; *what; what++) { + switch (*what) { + case '"': + ret[p++] = '&'; + ret[p++] = 'q'; + ret[p++] = 'u'; + ret[p++] = 'o'; + ret[p++] = 't'; + ret[p++] = ';'; + continue; + case '>': + ret[p++] = '&'; + ret[p++] = 'q'; + ret[p++] = 't'; + ret[p++] = ';'; + continue; + case '<': + ret[p++] = '&'; + ret[p++] = 'l'; + ret[p++] = 't'; + ret[p++] = ';'; + continue; + case '&': + ret[p++] = '&'; + ret[p++] = 'a'; + ret[p++] = 'm'; + ret[p++] = 'p'; + ret[p++] = ';'; + continue; + } + if (*what >= 0x20 || *what == '\n' || *what == '\r' || *what == '\t') + ret[p++] = *what; + else + p += sprintf(ret+p, "&#%d;", *what); + } + ret[p] = '\0'; + return ret; +} + +void NConfig::store(nc_de_s *de, FILE *f) +{ + struct nc_de_s *cc; + for (unsigned i=0; ipages; i++) + if ((cc = IDE(de, i))->type) { + char *encname = encodeXml(data+cc->name); + fprintf(f, "type); + free(encname); + switch (cc->type) { + case NC_DIR: + fprintf(f, "%u\">\n", cc->pages); + store(cc, f); + fprintf(f, "\n", XML_DE); + break; + case NC_STRING: + fprintf(f, "%s\"/>\n", encname = encodeXml(data+cc->offset)); + free(encname); + break; + case NC_INT: + fprintf(f, "%lld\"/>\n", *((signed long long *) (data+cc->offset))); + break; + case NC_UINT: + fprintf(f, "%llu\"/>\n", *((unsigned long long *) (data+cc->offset))); + break; + case NC_DOUBLE: + fprintf(f, "%La\"/>\n", *((long double *) (data+cc->offset))); + break; + case NC_RAW: + { + const char *raw = data+cc->offset; + for (unsigned j=0; jpages; j++) + fprintf(f, "%d%d%d", raw[j] / 100, (raw[j] % 100) / 10, raw[j] % 10); + fprintf(f, "\"/>\n"); + } + } + } +} + +int NConfig::toXML(const char *filename) +{ + if (fd < 0) + return NC_ERR_NFILE; + + FILE *f = fopen(filename, "w"); + if (!f) + return NC_ERR_PERM; + + fprintf(f, "%s", "\n"); + fprintf(f, "\n"); + lockFile(NC_L_RO); + + store(rdir, f); + + unLockFile(); + fprintf(f, "\n", XML_ROOT); + fclose(f); + return NC_ERR_OK; +} + +#ifdef HAVE_LIBXML2 +static xmlSAXHandler sh; +enum stateEnum {noRoot = 0, inRoot, inDir, inEnt, unknown}; + +struct ncParseState { + stateEnum state, pState; + xmlChar *ns; + unsigned depth; + unsigned unDepth; + unsigned force; + NConfig *which; +}; + +static int ncXmlSAXParseFile(xmlSAXHandlerPtr sax, void *user_data, const char *filename) +{ + int ret = 0; + xmlParserCtxtPtr ctxt = xmlCreateFileParserCtxt(filename); + if (!ctxt) + return -1; + ctxt->sax = sax; + ctxt->userData = user_data; + xmlParseDocument(ctxt); + ret = ctxt->wellFormed ? 0 : -1; + if (sax) + ctxt->sax = NULL; + xmlFreeParserCtxt(ctxt); + return ret; +} + +static xmlEntityPtr ncXmlGetEntity(void *user_data, const CHAR *name) +{ + return xmlGetPredefinedEntity(name); +} + +static void ncXmlStartElement(void *user_data, const CHAR *name, const CHAR **attrs) +{ + struct ncParseState *p = (struct ncParseState *)user_data; +#ifdef NC_DEBUG_XML + fprintf(stderr, "New element %s state=%d %s\n", name, p->state, p->ns); +#endif + if (p->state == unknown) { + p->unDepth++; + return; + } + if (p->state == noRoot) { + while (*attrs) { + if (!xmlStrncmp(*attrs, (const xmlChar *) "xmlns:", 6)) { + if (!xmlStrcmp(attrs[1], XML_NS)) { + p->ns = xmlStrdup((*attrs)+6); + break; + } + } + attrs += 2; + } + char *b = (char *) malloc(xmlStrlen(p->ns)+xmlStrlen(XML_ROOT)+2); + sprintf(b, "%s:%s", p->ns, XML_ROOT); + if (xmlStrcmp(name, (xmlChar *)b)) { +#ifdef NC_DEBUG_XML + fprintf(stderr, "NewElement, entering unknown %s\n", name); +#endif + p->pState = p->state; + p->state = unknown; + } else + p->state = inRoot; + free(b); + return; + } + if (p->state == inRoot || p->state == inDir) { + const xmlChar *value = NULL, *n = NULL; + int type = 0; + while (*attrs) { + if (!xmlStrcmp(*attrs, (const xmlChar *)"value")) + value = attrs[1]; + if (!xmlStrcmp(*attrs, (const xmlChar *)"name")) + n = attrs[1]; + if (!xmlStrcmp(*attrs, (const xmlChar *)"type")) + type = atoi(attrs[1]); + attrs += 2; + } +#ifdef NC_DEBUG_XML + fprintf(stderr, "%s %s %s %d %d\n", name, n, value, type, p->state); +#endif + char *b = (char *) malloc(xmlStrlen(p->ns)+xmlStrlen(XML_DE)+2); + sprintf(b, "%s:%s", p->ns, XML_DE); + if (xmlStrcmp(name, (xmlChar *)b) || !type || !value || !n) { +#ifdef NC_DEBUG_XML + fprintf(stderr, "NewElement, entering unknown on mismatch\n"); +#endif + p->pState = p->state; + p->state = unknown; + free(b); + return; + } + free(b); + if (p->force) + p->which->delKey((const char *)n); + + switch (type) { + case NC_DIR: + if (p->which->createDir((const char *)n, strtoul((const char *)value, NULL, 0)) != NC_ERR_OK) { + p->pState = p->state; + p->state = unknown; +#ifdef NC_DEBUG_XML + fprintf(stderr, "NewElement, entering unknown on failed mkdir\n"); +#endif + return; + } + p->which->chDir((const char *)n); + break; + case NC_STRING: + p->which->setKey((const char *)n, (const char *)value); + break; + case NC_INT: + p->which->setKey((const char *)n, strtoll((const char *)value, NULL, 0)); + break; + case NC_UINT: + p->which->setKey((const char *)n, strtoull((const char *)value, NULL, 0)); + break; + case NC_DOUBLE: + { + long double c; + sscanf((const char *)value, "%La", &c); + p->which->setKey((const char *)n, c); + } + break; + case NC_RAW: + { + unsigned size = xmlStrlen(value) / 3; + char *dec = NULL; + if (size) { + dec = (char *)malloc(size); + for (unsigned i=0, k=0; iwhich->setKey((const char *)n, dec, size); + free(dec); + } + } + if (type == NC_DIR) { + p->state = inDir; + p->depth++; + } else { + p->pState = p->state; + p->state = inEnt; + } + return; + } +} + +static void ncXmlEndElement(void *user_data, const CHAR *name) +{ + struct ncParseState *p = (struct ncParseState *)user_data; +#ifdef NC_DEBUG_XML + fprintf(stderr, "EndElement %s %s %d\n", name, p->ns, p->state); +#endif + if (p->state == inEnt) { + p->state = p->pState; + return; + } + if (p->state == unknown) { + if (p->unDepth) + p->unDepth--; + else + p->state = p->pState; + return; + } + if (p->state == inRoot) { + p->state = noRoot; + free(p->ns); + p->ns = NULL; + return; + } + if (p->state == inDir) { + p->depth--; + if (!p->depth) + p->state = inRoot; + p->which->chDir(".."); + } +} +#endif /* HAVE_LIBXML2 */ + +int NConfig::fromXML(const char *filename, int force) +{ + if (fd < 0) + return NC_ERR_NFILE; + if (omode != NC_O_RW) + return NC_ERR_PERM; +#ifndef HAVE_LIBXML2 + return NC_ERR_NOSUPPORT; +#else + struct ncParseState state = { noRoot, noRoot, NULL, 0, 0, force, this }; + sh.getEntity = ncXmlGetEntity; + sh.startElement = ncXmlStartElement; + sh.endElement = ncXmlEndElement; + + lockFile(NC_L_RW); + cdir = rdir; + int ret = ncXmlSAXParseFile(&sh, &state, filename); + cdir = rdir; + unLockFile(); + + return ret < 0 ? NC_ERR_NVAL : NC_ERR_OK; +#endif /* HAVE_LIBXML2 */ +} + diff --git a/lib/base/object.h b/lib/base/object.h new file mode 100644 index 0000000..744bff1 --- /dev/null +++ b/lib/base/object.h @@ -0,0 +1,30 @@ +#ifndef __base_object_h +#define __base_object_h + +#include + +// #define OBJECT_DEBUG + +#include +#ifdef OBJECT_DEBUG +#include +#endif + +typedef int RESULT; + +class iObject +{ +public: + virtual void AddRef()=0; + virtual void Release()=0; +}; + +#define DECLARE_REF private: int ref; public: void AddRef(); void Release(); +#ifdef OBJECT_DEBUG +extern int object_total_remaining; +#define DEFINE_REF(c) void c::AddRef() { ++object_total_remaining; ++ref; eDebug("OBJECT_DEBUG " #c "+%p now %d", this, ref); } void c::Release() { --object_total_remaining; eDebug("OBJECT_DEBUG " #c "-%p now %d", this, ref-1); if (!--ref) delete this; } +#else +#define DEFINE_REF(c) void c::AddRef() { ++ref; } void c::Release() { if (!--ref) delete this; } +#endif + +#endif diff --git a/lib/base/ringbuffer.h b/lib/base/ringbuffer.h new file mode 100644 index 0000000..f2cd905 --- /dev/null +++ b/lib/base/ringbuffer.h @@ -0,0 +1,109 @@ +#ifndef QueueRingBufferH +#define QueueRingBufferH + +template +class queueRingBuffer +{ + template + struct link + { + link ( const A &val ) + :value(val) + {} + A value; + link *nextLink; + link *prevLink; + }; + + link *lastFilled; + link *lastFree; + unsigned int max; + int count; +public: + queueRingBuffer( unsigned int max ); + ~queueRingBuffer(); + int size() { return count; } + T& queueRingBuffer::dequeue(); + T& queueRingBuffer::current(); + void queueRingBuffer::enqueue( const T &val ); +}; + +template +queueRingBuffer::queueRingBuffer( unsigned int max ) +{ + // constructor for queues based on ring buffers + // create the first link + T initialvalue; + lastFree = new link( initialvalue ); + lastFilled = lastFree; + // make value point to itself + lastFilled->nextLink = lastFilled; + lastFilled->prevLink = lastFilled; + // now add the remainder of the elements + while ( max-- > 0 ) + { + link * newLink = new link( initialvalue ); + newLink->prevLink = lastFilled; + newLink->nextLink = lastFilled->nextLink; + lastFilled->nextLink->prevLink = newLink; + lastFilled->nextLink = newLink; + } +} + +template +queueRingBuffer::~queueRingBuffer() +{ + // delete all memory associated with ring buffer + link * p = lastFree; + link * next; + + // walk around the circle deleting nodes + while( p->nextLink != lastFree ) + { + next = p->nextLink; + delete p; + p = next; + } +} + +template +T& queueRingBuffer::dequeue() +{ + // remove element form front of queue + // advance last free position + lastFree = lastFree->nextLink; + count--; + // return value stored in last free position + return lastFree->value; +} + +template +T& queueRingBuffer::current() +{ + // return value stored in current + return lastFree->nextLink->value; +} + +template +void queueRingBuffer::enqueue( const T &val ) +{ + // add new element to end of queue buffer + // first check for potential overflow + if( lastFilled->nextLink == lastFree ) + { + eDebug("increase size %d", count); + link * newLink = new link( val ); + newLink->prevLink = lastFilled; + newLink->nextLink = lastFilled->nextLink; + lastFilled->nextLink->prevLink = newLink; + lastFilled->nextLink = newLink; + } + else + { + // simply advance the last filled pointer + lastFilled = lastFilled->nextLink; + lastFilled->value = val; + } + count++; +} +#endif diff --git a/lib/base/smartptr.cpp b/lib/base/smartptr.cpp new file mode 100644 index 0000000..95269d7 --- /dev/null +++ b/lib/base/smartptr.cpp @@ -0,0 +1,2 @@ +#include "smartptr.h" +#include diff --git a/lib/base/smartptr.h b/lib/base/smartptr.h new file mode 100644 index 0000000..029fd1d --- /dev/null +++ b/lib/base/smartptr.h @@ -0,0 +1,60 @@ +#ifndef __smartptr_h +#define __smartptr_h + +#include "object.h" +#include + +template +class ePtr +{ +protected: + T *ptr; +public: + T &operator*() { return *ptr; } + ePtr(): ptr(0) + { + } + ePtr(T *c): ptr(c) + { + if (c) + c->AddRef(); + } + ePtr(const ePtr &c) + { + ptr=c.ptr; + if (ptr) + ptr->AddRef(); + } + ePtr &operator=(T *c) + { + if (ptr) + ptr->Release(); + ptr=c; + if (ptr) + ptr->AddRef(); + return *this; + } + + ePtr &operator=(ePtr &c) + { + if (ptr) + ptr->Release(); + ptr=c.ptr; + if (ptr) + ptr->AddRef(); + return *this; + } + + ~ePtr() + { + if (ptr) + ptr->Release(); + } + T* &ptrref() { assert(!ptr); return ptr; } + T* operator->() { assert(ptr); return ptr; } + const T* operator->() const { assert(ptr); return ptr; } + operator T*() const { return this->ptr; } +}; + + +#endif diff --git a/lib/base/thread.cpp b/lib/base/thread.cpp new file mode 100644 index 0000000..4cff925 --- /dev/null +++ b/lib/base/thread.cpp @@ -0,0 +1,34 @@ +#include +#include +#include + +void *eThread::wrapper(void *ptr) +{ + ((eThread*)ptr)->thread(); + pthread_exit(0); +} + +eThread::eThread() +{ + alive=0; +} + +void eThread::run() +{ + alive=1; + pthread_create(&the_thread, 0, wrapper, this); +} + +eThread::~eThread() +{ + if (alive) + kill(); +} + +void eThread::kill() +{ + alive=0; + eDebug("waiting for thread shutdown"); + pthread_join(the_thread, 0); + eDebug("ok"); +} diff --git a/lib/base/thread.h b/lib/base/thread.h new file mode 100644 index 0000000..56b74be --- /dev/null +++ b/lib/base/thread.h @@ -0,0 +1,23 @@ +#ifndef __lib_base_thread_h +#define __lib_base_thread_h + +#include + +class eThread +{ + pthread_t the_thread; + static void *wrapper(void *ptr); + int alive; +public: + bool thread_running() { return alive; } + eThread(); + virtual ~eThread(); + + void run(); + + virtual void thread()=0; + + void kill(); +}; + +#endif diff --git a/lib/driver/Makefile.am b/lib/driver/Makefile.am new file mode 100644 index 0000000..eff4486 --- /dev/null +++ b/lib/driver/Makefile.am @@ -0,0 +1,7 @@ +INCLUDES = \ + -I$(top_srcdir)/include + +noinst_LIBRARIES = libenigma_driver.a + +libenigma_driver_a_SOURCES = \ + rc.cpp rcinput.cpp \ No newline at end of file diff --git a/lib/driver/Makefile.in b/lib/driver/Makefile.in new file mode 100644 index 0000000..e69de29 diff --git a/lib/driver/input_fake.h b/lib/driver/input_fake.h new file mode 100644 index 0000000..7aecc9f --- /dev/null +++ b/lib/driver/input_fake.h @@ -0,0 +1,84 @@ +#ifndef _INPUT_FAKE_H +#define _INPUT_FAKE_H + +#include + + +#if !defined(KEY_OK) + +/** + * define some additional remote control keys in case they + * were not already defined above in + */ + +#define KEY_OK 0x160 +#define KEY_SELECT 0x161 +#define KEY_GOTO 0x162 +#define KEY_CLEAR 0x163 +#define KEY_POWER2 0x164 +#define KEY_OPTION 0x165 +#define KEY_INFO 0x166 +#define KEY_TIME 0x167 +#define KEY_VENDOR 0x168 +#define KEY_ARCHIVE 0x169 +#define KEY_PROGRAM 0x16a +#define KEY_CHANNEL 0x16b +#define KEY_FAVORITES 0x16c +#define KEY_EPG 0x16d +#define KEY_PVR 0x16e +#define KEY_MHP 0x16f +#define KEY_LANGUAGE 0x170 +#define KEY_TITLE 0x171 +#define KEY_SUBTITLE 0x172 +#define KEY_ANGLE 0x173 +#define KEY_ZOOM 0x174 +#define KEY_MODE 0x175 +#define KEY_KEYBOARD 0x176 +#define KEY_SCREEN 0x177 +#define KEY_PC 0x178 +#define KEY_TV 0x179 +#define KEY_TV2 0x17a +#define KEY_VCR 0x17b +#define KEY_VCR2 0x17c +#define KEY_SAT 0x17d +#define KEY_SAT2 0x17e +#define KEY_CD 0x17f +#define KEY_TAPE 0x180 +#define KEY_RADIO 0x181 +#define KEY_TUNER 0x182 +#define KEY_PLAYER 0x183 +#define KEY_TEXT 0x184 +#define KEY_DVD 0x185 +#define KEY_AUX 0x186 +#define KEY_MP3 0x187 +#define KEY_AUDIO 0x188 +#define KEY_VIDEO 0x189 +#define KEY_DIRECTORY 0x18a +#define KEY_LIST 0x18b +#define KEY_MEMO 0x18c +#define KEY_CALENDAR 0x18d +#define KEY_RED 0x18e +#define KEY_GREEN 0x18f +#define KEY_YELLOW 0x190 +#define KEY_BLUE 0x191 +#define KEY_CHANNELUP 0x192 +#define KEY_CHANNELDOWN 0x193 +#define KEY_FIRST 0x194 +#define KEY_LAST 0x195 +#define KEY_AB 0x196 +#define KEY_PLAY 0x197 +#define KEY_RESTART 0x198 +#define KEY_SLOW 0x199 +#define KEY_SHUFFLE 0x19a +#define KEY_FASTFORWARD 0x19b +#define KEY_PREVIOUS 0x19c +#define KEY_NEXT 0x19d +#define KEY_DIGITS 0x19e +#define KEY_TEEN 0x19f +#define KEY_TWEN 0x1a0 +#define KEY_BREAK 0x1a1 + + +#endif /* !defined(KEY_OK) */ +#endif /* _INPUT_FAKE_H */ + diff --git a/lib/driver/rc.cpp b/lib/driver/rc.cpp new file mode 100644 index 0000000..790c5f6 --- /dev/null +++ b/lib/driver/rc.cpp @@ -0,0 +1,245 @@ +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * note on the enigma input layer: + * the enigma input layer (rc*) supports n different devices which + * all have completely different interfaces, mapped down to 32bit + + * make/break/release codes mapped down (via xml files) to "actions". + * this was necessary to support multiple remote controls with proprietary + * interfaces. now everybody is using input devices, and thus adding + * another input layer seems to be a bit overkill. BUT: + * image a remote control with two hundred buttons. each and every function + * in enigma can be bound to a button. no need to use them twice. + * for example, you would have KEY_MENU assigned to a menu for setup etc., + * but no audio and video settings, since you have special keys for that, + * and you don't want to display a big menu with entries that are available + * with another single key. + * then image a remote control with ten buttons. do you really want to waste + * KEY_MENU for a simple menu? you need the audio/video settings there too. + * take this just as a (bad) example. another (better) example might be front- + * button-keys. usually you have KEY_UP, KEY_DOWN, KEY_POWER. you don't want + * them to behave like the remote-control-KEY_UP, KEY_DOWN and KEY_POWER, + * don't you? + * so here we can map same keys of different input devices to different + * actions. have fun. + */ + +int eRCDevice::getKeyCompatibleCode(const eRCKey &) const +{ + return -1; +} + +eRCDevice::eRCDevice(eString id, eRCDriver *driver): driver(driver), id(id) +{ + input=driver->getInput(); + driver->addCodeListener(this); + eRCInput::getInstance()->addDevice(id, this); +} + +eRCDevice::~eRCDevice() +{ + driver->removeCodeListener(this); + eRCInput::getInstance()->removeDevice(id.c_str()); +} + +eRCDriver::eRCDriver(eRCInput *input): input(input), enabled(1) +{ +} + +eRCDriver::~eRCDriver() +{ + for (std::list::iterator i=listeners.begin(); i!=listeners.end(); ++i) + delete *i; +} + +void eRCShortDriver::keyPressed(int) +{ + __u16 rccode; + while (1) + { + if (read(handle, &rccode, 2)!=2) + break; + if (enabled && !input->islocked()) + for (std::list::iterator i(listeners.begin()); i!=listeners.end(); ++i) + (*i)->handleCode(rccode); + } +} + +eRCShortDriver::eRCShortDriver(const char *filename): eRCDriver(eRCInput::getInstance()) +{ + handle=open(filename, O_RDONLY|O_NONBLOCK); + if (handle<0) + { + eDebug("failed to open %s", filename); + sn=0; + } else + { + sn=new eSocketNotifier(eApp, handle, eSocketNotifier::Read); + CONNECT(sn->activated, eRCShortDriver::keyPressed); + eRCInput::getInstance()->setFile(handle); + } +} + +eRCShortDriver::~eRCShortDriver() +{ + if (handle>=0) + close(handle); + if (sn) + delete sn; +} + +void eRCInputEventDriver::keyPressed(int) +{ + struct input_event ev; + while (1) + { + if (read(handle, &ev, sizeof(struct input_event))!=sizeof(struct input_event)) + break; + if (enabled && !input->islocked()) + for (std::list::iterator i(listeners.begin()); i!=listeners.end(); ++i) + (*i)->handleCode((int)&ev); + } +} + +eRCInputEventDriver::eRCInputEventDriver(const char *filename): eRCDriver(eRCInput::getInstance()) +{ + handle=open(filename, O_RDONLY|O_NONBLOCK); + if (handle<0) + { + eDebug("failed to open %s", filename); + sn=0; + } else + { + sn=new eSocketNotifier(eApp, handle, eSocketNotifier::Read); + CONNECT(sn->activated, eRCInputEventDriver::keyPressed); + eRCInput::getInstance()->setFile(handle); + } +} + +eString eRCInputEventDriver::getDeviceName() +{ + char name[128]=""; + if (handle >= 0) + ::ioctl(handle, EVIOCGNAME(128), name); + return name; +} + +eRCInputEventDriver::~eRCInputEventDriver() +{ + if (handle>=0) + close(handle); + if (sn) + delete sn; +} + +eRCConfig::eRCConfig() +{ + reload(); +} + +eRCConfig::~eRCConfig() +{ + save(); +} + +void eRCConfig::set( int delay, int repeat ) +{ + rdelay = delay; + rrate = repeat; +} + +void eRCConfig::reload() +{ + rdelay=500; + rrate=100; + if ( eConfig::getInstance()->getKey("/ezap/rc/repeatRate", rrate) ) + save(); + eConfig::getInstance()->getKey("/ezap/rc/repeatDelay", rdelay); +} + +void eRCConfig::save() +{ + eConfig::getInstance()->setKey("/ezap/rc/repeatRate", rrate); + eConfig::getInstance()->setKey("/ezap/rc/repeatDelay", rdelay); +} + +eRCInput *eRCInput::instance; + +eRCInput::eRCInput() +{ + ASSERT( !instance); + instance=this; + handle = -1; + locked = 0; +} + +eRCInput::~eRCInput() +{ +} + +void eRCInput::close() +{ +} + +bool eRCInput::open() +{ + return false; +} + +int eRCInput::lock() +{ + locked=1; + return handle; +} + +void eRCInput::unlock() +{ + if (locked) + locked=0; +} + +void eRCInput::setFile(int newh) +{ + handle=newh; +} + +void eRCInput::addDevice(const eString &id, eRCDevice *dev) +{ + devices.insert(std::pair(id, dev)); +} + +void eRCInput::removeDevice(const eString &id) +{ + devices.erase(id); +} + +eRCDevice *eRCInput::getDevice(const eString &id) +{ + std::map::iterator i=devices.find(id); + if (i == devices.end()) + { + eDebug("failed, possible choices are:"); + for (std::map::iterator i=devices.begin(); i != devices.end(); ++i) + eDebug("%s", i->first.c_str()); + return 0; + } + return i->second; +} + +std::map &eRCInput::getDevices() +{ + return devices; +} + +eAutoInitP0 init_rcinput(eAutoInitNumbers::rc, "RC Input layer"); diff --git a/lib/driver/rc.h b/lib/driver/rc.h new file mode 100644 index 0000000..557be5e --- /dev/null +++ b/lib/driver/rc.h @@ -0,0 +1,226 @@ +#ifndef __rc_h +#define __rc_h + +#include +#include + +#include +#include +#include + +class eRCInput; +class eRCDriver; +class eRCKey; + +/** + * \brief A remote control. + * + * Handles one remote control. Gets codes from a \ref eRCDriver. Produces events in \ref eRCInput. + */ +class eRCDevice: public Object +{ +protected: + eRCInput *input; + eRCDriver *driver; + eString id; +public: + /** + * \brief Constructs a new remote control. + * + * \param id The identifier of the RC, for use in settings. + * \param input The \ref eRCDriver where this remote gets its codes from. + */ + eRCDevice(eString id, eRCDriver *input); + ~eRCDevice(); + /** + * \brief Handles a device specific code. + * + * Generates events in \ref eRCInput. code is highly device- and driver dependant. + * For Example, it might be 16bit codes with one bit make/break or special codes + * for repeat. + */ + virtual void handleCode(int code)=0; + /** + * \brief Get user readable description. + * \result The description. + */ + virtual const char *getDescription() const=0; + const eString getIdentifier() const { return id; } + /** + * \brief Get a description for a specific key. + * \param key The key to get the description for. + * \result User readable description of given key. + */ + virtual const char *getKeyDescription(const eRCKey &key) const=0; + /** + * \brief Get a dbox2-compatible keycode. + * + * THIS IS DEPRECATED! DON'T USE IT UNLESS YOU NEED IT! + * \param key The key to get the compatible code for. + * \result The dbox2-compatible code. (new RC as defined in enum). + */ + virtual int getKeyCompatibleCode(const eRCKey &key) const; +}; + +/** + * Receives codes from one or more remote controls. + */ +class eRCDriver: public Object +{ +protected: + std::list listeners; + eRCInput *input; + int enabled; +public: + /** + * \brief Constructs a driver. + * + * \param input The RCInput to bind this driver to. + */ + eRCDriver(eRCInput *input); + /** + * \brief Get pointer to key-consumer. + */ + eRCInput *getInput() const { return input; } + /** + * \brief Adds a code lister + */ + void addCodeListener(eRCDevice *dev) + { + listeners.push_back(dev); + } + void removeCodeListener(eRCDevice *dev) + { + listeners.remove(dev); + } + ~eRCDriver(); + + void enable(int en) { enabled=en; } +}; + +class eRCShortDriver: public eRCDriver +{ +protected: + int handle; + eSocketNotifier *sn; + void keyPressed(int); +public: + eRCShortDriver(const char *filename); + ~eRCShortDriver(); +}; + +class eRCInputEventDriver: public eRCDriver +{ +protected: + int handle; + eSocketNotifier *sn; + void keyPressed(int); +public: + eString getDeviceName(); + eRCInputEventDriver(const char *filename); + ~eRCInputEventDriver(); +}; + +class eRCKey +{ +public: + eRCDevice *producer; + int code, flags; + + eRCKey(eRCDevice *producer, int code, int flags): + producer(producer), code(code), flags(flags) + { + } + enum + { + flagBreak=1, + flagRepeat=2 + }; + + bool operator<(const eRCKey &r) const + { + if (r.producer == producer) + { + if (r.code == code) + { + if (r.flags < flags) + return 1; + else + return 0; + } else if (r.code < code) + return 1; + else + return 0; + } else if (r.producer < producer) + return 1; + else + return 0; + } +}; + +class eRCConfig +{ +public: + eRCConfig(); + ~eRCConfig(); + void reload(); + void save(); + void set(int delay, int repeat); + int rdelay, // keypress delay after first keypress to begin of repeat (in ms) + rrate; // repeat rate (in ms) +}; + +class eRCInput: public Object +{ + int locked; + int handle; + static eRCInput *instance; + +public: + struct lstr + { + bool operator()(const eString &a, const eString &b) const + { + return a devices; +public: + Signal1 keyEvent; + enum + { + RC_0=0, RC_1=0x1, RC_2=0x2, RC_3=0x3, RC_4=0x4, RC_5=0x5, RC_6=0x6, RC_7=0x7, + RC_8=0x8, RC_9=0x9, + RC_RIGHT=10, RC_LEFT=11, RC_UP=12, RC_DOWN=13, RC_OK=14, RC_MUTE=15, + RC_STANDBY=16, RC_GREEN=17, RC_YELLOW=18, RC_RED=19, RC_BLUE=20, RC_PLUS=21, RC_MINUS=22, + RC_HELP=23, RC_DBOX=24, + RC_UP_LEFT=27, RC_UP_RIGHT=28, RC_DOWN_LEFT=29, RC_DOWN_RIGHT=30, RC_HOME=31 + }; + eRCInput(); + ~eRCInput(); + + int lock(); + void unlock(); + int islocked() { return locked; } + void close(); + bool open(); + + void setFile(int handle); + + void keyPressed(const eRCKey &key) + { + /*emit*/ keyEvent(key); + } + + void addDevice(const eString &id, eRCDevice *dev); + void removeDevice(const eString &id); + eRCDevice *getDevice(const eString &id); + std::map &getDevices(); + + static eRCInput *getInstance() { return instance; } + + eRCConfig config; +}; + +#endif diff --git a/lib/driver/rcdbox.h b/lib/driver/rcdbox.h new file mode 100644 index 0000000..bd361f6 --- /dev/null +++ b/lib/driver/rcdbox.h @@ -0,0 +1,61 @@ +#ifndef DISABLE_DBOX_RC + +#ifndef __rcdbox_h +#define __rcdbox_h + +#include + +class eRCDeviceDBoxOld: public eRCDevice +{ + int last, ccode; + eTimer timeout, repeattimer; +private: + void timeOut(); + void repeat(); +public: + void handleCode(int code); + eRCDeviceDBoxOld(eRCDriver *driver); + const char *getDescription() const; + const char *getKeyDescription(const eRCKey &key) const; + int getKeyCompatibleCode(const eRCKey &key) const; +}; + +class eRCDeviceDBoxNew: public eRCDevice +{ + int last, ccode; + eTimer timeout, repeattimer; +private: + void timeOut(); + void repeat(); +public: + void handleCode(int code); + eRCDeviceDBoxNew(eRCDriver *driver); + const char *getDescription() const; + const char *getKeyDescription(const eRCKey &key) const; + int getKeyCompatibleCode(const eRCKey &key) const; +}; + +class eRCDeviceDBoxButton: public eRCDevice +{ + int last; + eTimer repeattimer; +private: + void repeat(); +public: + void handleCode(int code); + eRCDeviceDBoxButton(eRCDriver *driver); + const char *getDescription() const; + + const char *getKeyDescription(const eRCKey &key) const; + int getKeyCompatibleCode(const eRCKey &key) const; +}; + +class eRCDBoxDriver: public eRCShortDriver +{ +public: + eRCDBoxDriver(); +}; + +#endif + +#endif // DISABLE_DBOX_RC diff --git a/lib/driver/rcdreambox2.h b/lib/driver/rcdreambox2.h new file mode 100644 index 0000000..e77beb4 --- /dev/null +++ b/lib/driver/rcdreambox2.h @@ -0,0 +1,51 @@ +#ifndef DISABLE_DREAMBOX_RC + +#ifndef __rcdreambox2_h +#define __rcdreambox2_h + +#include + +class eRCDeviceDreambox2: public eRCDevice +{ + int last, ccode; + eTimer timeout, repeattimer; +private: + void timeOut(); + void repeat(); +public: + void handleCode(int code); + eRCDeviceDreambox2(eRCDriver *driver); + const char *getDescription() const; + const char *getKeyDescription(const eRCKey &key) const; + int getKeyCompatibleCode(const eRCKey &key) const; +}; + +class eRCDreamboxDriver2: public eRCShortDriver +{ +public: + eRCDreamboxDriver2(); +}; + +class eRCDeviceDreamboxButton: public eRCDevice +{ + int last; + eTimer repeattimer; +private: + void repeat(); +public: + void handleCode(int code); + eRCDeviceDreamboxButton(eRCDriver *driver); + const char *getDescription() const; + + const char *getKeyDescription(const eRCKey &key) const; + int getKeyCompatibleCode(const eRCKey &key) const; +}; + +class eRCDreamboxButtonDriver: public eRCShortDriver +{ +public: + eRCDreamboxButtonDriver(); +}; +#endif + +#endif // DISABLE_DREAMBOX_RC diff --git a/lib/driver/rcinput.cpp b/lib/driver/rcinput.cpp new file mode 100644 index 0000000..041330a --- /dev/null +++ b/lib/driver/rcinput.cpp @@ -0,0 +1,126 @@ +#include + +#include +#include +#include + +#include +#include +#include +#include + +void eRCDeviceInputDev::handleCode(int rccode) +{ + struct input_event *ev = (struct input_event *)rccode; + if (ev->type!=EV_KEY) + return; + eDebug("%x %x %x", ev->value, ev->code, ev->type); + switch (ev->value) + { + case 0: + /*emit*/ input->keyPressed(eRCKey(this, ev->code, eRCKey::flagBreak)); + break; + case 1: + /*emit*/ input->keyPressed(eRCKey(this, ev->code, 0)); + break; + case 2: + /*emit*/ input->keyPressed(eRCKey(this, ev->code, eRCKey::flagRepeat)); + break; + } +} + +eRCDeviceInputDev::eRCDeviceInputDev(eRCInputEventDriver *driver): eRCDevice(driver->getDeviceName(), driver) +{ +} + +const char *eRCDeviceInputDev::getDescription() const +{ + return id.c_str(); +} + +const char *eRCDeviceInputDev::getKeyDescription(const eRCKey &key) const +{ + switch (key.code) + { + case KEY_0: return "0"; + case KEY_1: return "1"; + case KEY_2: return "2"; + case KEY_3: return "3"; + case KEY_4: return "4"; + case KEY_5: return "5"; + case KEY_6: return "6"; + case KEY_7: return "7"; + case KEY_8: return "8"; + case KEY_9: return "9"; + case KEY_RIGHT: return "rechts"; + case KEY_LEFT: return "links"; + case KEY_UP: return "oben"; + case KEY_DOWN: return "unten"; + case KEY_OK: return "ok"; + case KEY_MUTE: return "mute"; + case KEY_POWER: return "power"; + case KEY_GREEN: return "gruen"; + case KEY_YELLOW: return "gelb"; + case KEY_RED: return "rot"; + case KEY_BLUE: return "blau"; + case KEY_VOLUMEUP: return "Lautstaerke plus"; + case KEY_VOLUMEDOWN: return "Lautstaerke minus"; + case KEY_HELP: return "?"; + case KEY_SETUP: return "d-Box"; +#if 0 + case KEY_TOPLEFT: return "oben links"; + case KEY_TOPRIGHT: return "oben rechts"; + case KEY_BOTTOMLEFT: return "unten links"; + case KEY_BOTTOMRIGHT: return "unten rechts"; +#endif + case KEY_HOME: return "home"; + default: return 0; + } +} + +int eRCDeviceInputDev::getKeyCompatibleCode(const eRCKey &key) const +{ + switch (key.code) + { + case KEY_0: return eRCInput::RC_0; + case KEY_1: return eRCInput::RC_1; + case KEY_2: return eRCInput::RC_2; + case KEY_3: return eRCInput::RC_3; + case KEY_4: return eRCInput::RC_4; + case KEY_5: return eRCInput::RC_5; + case KEY_6: return eRCInput::RC_6; + case KEY_7: return eRCInput::RC_7; + case KEY_8: return eRCInput::RC_8; + case KEY_9: return eRCInput::RC_9; + case KEY_RIGHT: return eRCInput::RC_RIGHT; + case KEY_LEFT: return eRCInput::RC_LEFT; + case KEY_UP: return eRCInput::RC_UP; + case KEY_DOWN: return eRCInput::RC_DOWN; + case KEY_OK: return eRCInput::RC_OK; + case KEY_MUTE: return eRCInput::RC_MUTE; + case KEY_POWER: return eRCInput::RC_STANDBY; + case KEY_GREEN: return eRCInput::RC_GREEN; + case KEY_YELLOW: return eRCInput::RC_YELLOW; + case KEY_RED: return eRCInput::RC_RED; + case KEY_VOLUMEUP: return eRCInput::RC_PLUS; + case KEY_BLUE: return eRCInput::RC_BLUE; + case KEY_VOLUMEDOWN: return eRCInput::RC_MINUS; + case KEY_HELP: return eRCInput::RC_HELP; + case KEY_SETUP: return eRCInput::RC_DBOX; + case KEY_HOME: return eRCInput::RC_HOME; + } + return -1; +} + + +class eInputDeviceInit +{ + eRCInputEventDriver driver; + eRCDeviceInputDev deviceInputDev; +public: + eInputDeviceInit(): driver("/dev/input/event0"), deviceInputDev(&driver) + { + } +}; + +eAutoInitP0 init_rcinputdev(eAutoInitNumbers::rc+1, "input device driver"); diff --git a/lib/driver/rcinput.h b/lib/driver/rcinput.h new file mode 100644 index 0000000..c13eafb --- /dev/null +++ b/lib/driver/rcinput.h @@ -0,0 +1,17 @@ +#ifndef __rcdbox_h +#define __rcdbox_h + +#include + +class eRCDeviceInputDev: public eRCDevice +{ +public: + void handleCode(int code); + eRCDeviceInputDev(eRCInputEventDriver *driver); + const char *getDescription() const; + + const char *getKeyDescription(const eRCKey &key) const; + int getKeyCompatibleCode(const eRCKey &key) const; +}; + +#endif diff --git a/lib/driver/rfmod.h b/lib/driver/rfmod.h new file mode 100644 index 0000000..e3acc47 --- /dev/null +++ b/lib/driver/rfmod.h @@ -0,0 +1,33 @@ +#ifdef ENABLE_RFMOD + +#ifndef __erfmod_h +#define __erfmod_h + +#include + +class eRFmod: public Object +{ + static eRFmod *instance; + + int rfmodfd; + int channel,soundsubcarrier,soundenable,finetune; + +public: + eRFmod(); + ~eRFmod(); + + void init(); + + static eRFmod *getInstance(); + + int save(); + + int setChannel(int channel); + int setSoundSubCarrier(int val); + int setSoundEnable(int val); + int setFinetune(int val); + int setTestPattern(int val); +}; +#endif + +#endif // ENABLE_RFMOD diff --git a/lib/driver/streamwd.h b/lib/driver/streamwd.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/dvb/Makefile.am b/lib/dvb/Makefile.am new file mode 100644 index 0000000..a3b81bc --- /dev/null +++ b/lib/dvb/Makefile.am @@ -0,0 +1,8 @@ +INCLUDES = \ + -I$(top_srcdir)/include + +noinst_LIBRARIES = libenigma_dvb.a + +libenigma_dvb_a_SOURCES = dvb.cpp demux.cpp frontend.cpp esection.cpp db.cpp \ + sec.cpp scan.cpp crc32.cpp pmt.cpp decoder.cpp + \ No newline at end of file diff --git a/lib/dvb/Makefile.in b/lib/dvb/Makefile.in new file mode 100644 index 0000000..e69de29 diff --git a/lib/dvb/crc32.cpp b/lib/dvb/crc32.cpp new file mode 100644 index 0000000..049af37 --- /dev/null +++ b/lib/dvb/crc32.cpp @@ -0,0 +1,164 @@ +/* + * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + * + * First, the polynomial itself and its table of feedback terms. The + * polynomial is + * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 + * + * Note that we take it "backwards" and put the highest-order term in + * the lowest-order bit. The X^32 term is "implied"; the LSB is the + * X^31 term, etc. The X^0 term (usually shown as "+1") results in + * the MSB being 1 + * + * Note that the usual hardware shift register implementation, which + * is what we're using (we're merely optimizing it by doing eight-bit + * chunks at a time) shifts bits into the lowest-order term. In our + * implementation, that means shifting towards the right. Why do we + * do it this way? Because the calculated CRC must be transmitted in + * order from highest-order term to lowest-order term. UARTs transmit + * characters in order from LSB to MSB. By storing the CRC this way + * we hand it to the UART in the order low-byte to high-byte; the UART + * sends each low-bit to hight-bit; and the result is transmission bit + * by bit from highest- to lowest-order term without requiring any bit + * shuffling on our part. Reception works similarly + * + * The feedback terms table consists of 256, 32-bit entries. Notes + * + * The table can be generated at runtime if desired; code to do so + * is shown later. It might not be obvious, but the feedback + * terms simply represent the results of eight shift/xor opera + * tions for all combinations of data and CRC register values + * + * The values must be right-shifted by eight bits by the "updcrc + * logic; the shift must be unsigned (bring in zeroes). On some + * hardware you could probably optimize the shift in assembler by + * using byte-swap instructions + * polynomial $edb88320 + */ + +/* $Id: crc32.cpp,v 1.1 2003-10-17 15:35:50 tmbinc Exp $ */ + +#include "crc32.h" +#if 0 +const uint32_t crc32_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; +#else +const uint32_t crc32_table[256] = { + 0, 0x4C11DB7, 0x9823B6E, 0xD4326D9, + 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005, + 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61, + 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, + 0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, + 0x5F15ADAC, 0x5BD4B01B, 0x569796C2, 0x52568B75, + 0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, + 0x791D4014, 0x7DDC5DA3, 0x709F7B7A, 0x745E66CD, + 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039, + 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, + 0xBE2B5B58, 0xBAEA46EF, 0xB7A96036, 0xB3687D81, + 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D, + 0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, + 0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95, + 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, + 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, + 0x34867077, 0x30476DC0, 0x3D044B19, 0x39C556AE, + 0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072, + 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, + 0x18AEB13, 0x54BF6A4, 0x808D07D, 0xCC9CDCA, + 0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE, + 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02, + 0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, 0x53DC6066, + 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA, + 0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, + 0xBFA1B04B, 0xBB60ADFC, 0xB6238B25, 0xB2E29692, + 0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6, + 0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A, + 0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E, + 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2, + 0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, + 0xD5B88683, 0xD1799B34, 0xDC3ABDED, 0xD8FBA05A, + 0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637, + 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, + 0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, + 0x5C007B8A, 0x58C1663D, 0x558240E4, 0x51435D53, + 0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, + 0x36194D42, 0x32D850F5, 0x3F9B762C, 0x3B5A6B9B, + 0x315D626, 0x7D4CB91, 0xA97ED48, 0xE56F0FF, + 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, + 0xF12F560E, 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, + 0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B, + 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, + 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3, + 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, + 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, + 0x9B3660C6, 0x9FF77D71, 0x92B45BA8, 0x9675461F, + 0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3, + 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, + 0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C, + 0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8, + 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24, + 0x119B4BE9, 0x155A565E, 0x18197087, 0x1CD86D30, + 0x29F3D35, 0x65E2082, 0xB1D065B, 0xFDC1BEC, + 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, + 0x2497D08D, 0x2056CD3A, 0x2D15EBE3, 0x29D4F654, + 0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0, + 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C, + 0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18, + 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4, + 0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, + 0x9ABC8BD5, 0x9E7D9662, 0x933EB0BB, 0x97FFAD0C, + 0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668, + 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4}; +#endif diff --git a/lib/dvb/crc32.h b/lib/dvb/crc32.h new file mode 100644 index 0000000..9925453 --- /dev/null +++ b/lib/dvb/crc32.h @@ -0,0 +1,22 @@ +#ifndef CRC32_H +#define CRC32_H + +/* $Id: crc32.h,v 1.1 2003-10-17 15:35:49 tmbinc Exp $ */ + +typedef unsigned int uint32_t; + +extern const uint32_t crc32_table[256]; + +/* Return a 32-bit CRC of the contents of the buffer. */ + +static inline uint32_t +crc32(uint32_t val, const void *ss, int len) +{ + const unsigned char *s =(const unsigned char *) ss; + while (--len >= 0) +// val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8); + val = (val << 8) ^ crc32_table[(val >> 24) ^ *s++]; + return val; +} + +#endif diff --git a/lib/dvb/db.cpp b/lib/dvb/db.cpp new file mode 100644 index 0000000..f8233a6 --- /dev/null +++ b/lib/dvb/db.cpp @@ -0,0 +1,274 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +DEFINE_REF(eDVBService); + +eDVBService::eDVBService(): ref(0) +{ +} + +eDVBService::~eDVBService() +{ +} + +DEFINE_REF(eDVBDB); + +eDVBDB::eDVBDB() +{ + eDebug("---- opening lame channel db"); + FILE *f=fopen("lamedb", "rt"); + if (!f) + return; + char line[256]; + if ((!fgets(line, 256, f)) || strncmp(line, "eDVB services", 13)) + { + eDebug("not a servicefile"); + fclose(f); + return; + } + eDebug("reading services"); + if ((!fgets(line, 256, f)) || strcmp(line, "transponders\n")) + { + eDebug("services invalid, no transponders"); + fclose(f); + return; + } + + // clear all transponders + + while (!feof(f)) + { + if (!fgets(line, 256, f)) + break; + if (!strcmp(line, "end\n")) + break; + int dvb_namespace=-1, transport_stream_id=-1, original_network_id=-1; + sscanf(line, "%x:%x:%x", &dvb_namespace, &transport_stream_id, &original_network_id); + if (original_network_id == -1) + continue; + eDVBChannelID channelid = eDVBChannelID( + eDVBNamespace(dvb_namespace), + eTransportStreamID(transport_stream_id), + eOriginalNetworkID(original_network_id)); + + ePtr feparm = new eDVBFrontendParameters; + while (!feof(f)) + { + fgets(line, 256, f); + if (!strcmp(line, "/\n")) + break; + if (line[1]=='s') + { + eDVBFrontendParametersSatellite sat; + int frequency, symbol_rate, polarisation, fec, orbital_position, inversion; + sscanf(line+2, "%d:%d:%d:%d:%d:%d", &frequency, &symbol_rate, &polarisation, &fec, &orbital_position, &inversion); + sat.frequency = frequency; + sat.symbol_rate = symbol_rate; + sat.polarisation = polarisation; + sat.fec = fec; + sat.orbital_position = orbital_position; + sat.inversion = inversion; + // ... +// t.setSatellite(frequency, symbol_rate, polarisation, fec, sat, inversion); + feparm->setDVBS(sat); + } + if (line[1]=='c') + { + int frequency, symbol_rate, inversion=0, modulation=3; + sscanf(line+2, "%d:%d:%d:%d", &frequency, &symbol_rate, &inversion, &modulation); +// t.setCable(frequency, symbol_rate, inversion, modulation); + } + } + addChannelToList(channelid, feparm); + } + + if ((!fgets(line, 256, f)) || strcmp(line, "services\n")) + { + eDebug("services invalid, no services"); + return; + } + + // clear all services + + int count=0; + + while (!feof(f)) + { + if (!fgets(line, 256, f)) + break; + if (!strcmp(line, "end\n")) + break; + + int service_id=-1, dvb_namespace, transport_stream_id=-1, original_network_id=-1, service_type=-1, service_number=-1; + sscanf(line, "%x:%x:%x:%x:%d:%d", &service_id, &dvb_namespace, &transport_stream_id, &original_network_id, &service_type, &service_number); + if (service_number == -1) + continue; + ePtr s = new eDVBService; + eServiceReferenceDVB ref = + eServiceReferenceDVB( + eDVBNamespace(dvb_namespace), + eTransportStreamID(transport_stream_id), + eOriginalNetworkID(original_network_id), + eServiceID(service_id), + service_type); + count++; + fgets(line, 256, f); + if (strlen(line)) + line[strlen(line)-1]=0; + s->m_service_name=line; + fgets(line, 256, f); + if (strlen(line)) + line[strlen(line)-1]=0; + + eString str=line; + + if (str[1]!=':') // old ... (only service_provider) + { + s->m_provider_name=line; + } else + while ((!str.empty()) && str[1]==':') // new: p:, f:, c:%02d... + { + unsigned int c=str.find(','); + char p=str[0]; + eString v; + if (c == eString::npos) + { + v=str.mid(2); + str=""; + } else + { + v=str.mid(2, c-2); + str=str.mid(c+1); + } +// eDebug("%c ... %s", p, v.c_str()); + if (p == 'p') + s->m_provider_name=v; + else if (p == 'f') + { + sscanf(v.c_str(), "%x", &s->m_flags); + } else if (p == 'c') + { + int cid, val; + sscanf(v.c_str(), "%02d%04x", &cid, &val); + s->m_cache[cid]=val; + } else if (p == 'C') + { + int val; + sscanf(v.c_str(), "%04x", &val); + s->m_ca.insert(val); + } + } + addService(ref, s); + } + + eDebug("loaded %d services", count); + + fclose(f); + +} + +eDVBDB::~eDVBDB() +{ + eDebug("---- saving lame channel db"); + FILE *f=fopen("lamedb", "wt"); + int channels=0, services=0; + if (!f) + eFatal("couldn't save lame channel db!"); + fprintf(f, "eDVB services /3/\n"); + fprintf(f, "transponders\n"); + for (std::map::const_iterator i(m_channels.begin()); + i != m_channels.end(); ++i) + { + const eDVBChannelID &chid = i->first; + const channel &ch = i->second; + + fprintf(f, "%08x:%04x:%04x\n", chid.dvbnamespace.get(), + chid.transport_stream_id.get(), chid.original_network_id.get()); + eDVBFrontendParametersSatellite sat; + if (!ch.m_frontendParameters->getDVBS(sat)) + { + fprintf(f, "\ts %d:%d:%d:%d:%d:%d\n", + sat.frequency, sat.symbol_rate, + sat.polarisation, sat.fec, sat.inversion, + sat.orbital_position); + } + fprintf(f, "/\n"); + channels++; + } + fprintf(f, "end\nservices\n"); + + for (std::map >::iterator i(m_services.begin()); + i != m_services.end(); ++i) + { + const eServiceReferenceDVB &s = i->first; + fprintf(f, "%04x:%08x:%04x:%04x:%d:%d\n", + s.getServiceID().get(), s.getDVBNamespace().get(), + s.getOriginalNetworkID().get(), s.getTransportStreamID().get(), + s.getServiceType(), + 0); + + fprintf(f, "%s\n", i->second->m_service_name.c_str()); + fprintf(f, "p=%s", i->second->m_provider_name.c_str()); + for (std::set::const_iterator ca(i->second->m_ca.begin()); + ca != i->second->m_ca.end(); ++ca) + fprintf(f, ",C=%04x", *ca); + fprintf(f, "\n"); + services++; + } + fprintf(f, "end\nHave a lot of bugs!\n"); + eDebug("saved %d channels and %d services!", channels, services); + fclose(f); +} + +RESULT eDVBDB::addChannelToList(const eDVBChannelID &id, iDVBFrontendParameters *feparm) +{ + channel ch; + assert(feparm); + ch.m_frontendParameters = feparm; + m_channels.insert(std::pair(id, ch)); + return 0; +} + +RESULT eDVBDB::removeChannel(const eDVBChannelID &id) +{ + m_channels.erase(id); + return 0; +} + +RESULT eDVBDB::getChannelFrontendData(const eDVBChannelID &id, ePtr &parm) +{ + std::map::iterator i = m_channels.find(id); + if (i == m_channels.end()) + { + parm = 0; + return -ENOENT; + } + parm = i->second.m_frontendParameters; + return 0; +} + +RESULT eDVBDB::addService(const eServiceReferenceDVB &serviceref, eDVBService *service) +{ + m_services.insert(std::pair >(serviceref, service)); + return 0; +} + +RESULT eDVBDB::getService(const eServiceReferenceDVB &reference, ePtr &service) +{ + std::map >::iterator i; + i = m_services.find(reference); + if (i == m_services.end()) + { + service = 0; + return -ENOENT; + } + service = i->second; + return 0; +} + diff --git a/lib/dvb/db.h b/lib/dvb/db.h new file mode 100644 index 0000000..763df7e --- /dev/null +++ b/lib/dvb/db.h @@ -0,0 +1,48 @@ +#ifndef __db_h +#define __db_h + +#include +#include + +class eDVBService: public iObject +{ + DECLARE_REF; +public: + eDVBService(); + eString m_service_name; + eString m_provider_name; + + int m_flags; + std::set m_ca; + std::map m_cache; + virtual ~eDVBService(); +}; + +class ServiceDescriptionTable; + +class eDVBDB: public virtual iDVBChannelList +{ +DECLARE_REF; +private: + struct channel + { + ePtr m_frontendParameters; + }; + + std::map m_channels; + + std::map > m_services; +public: + eDVBDB(); + virtual ~eDVBDB(); + + RESULT addChannelToList(const eDVBChannelID &id, iDVBFrontendParameters *feparm); + RESULT removeChannel(const eDVBChannelID &id); + + RESULT getChannelFrontendData(const eDVBChannelID &id, ePtr &parm); + + RESULT addService(const eServiceReferenceDVB &service, eDVBService *service); + RESULT getService(const eServiceReferenceDVB &reference, ePtr &service); +}; + +#endif diff --git a/lib/dvb/decoder.cpp b/lib/dvb/decoder.cpp new file mode 100644 index 0000000..e21e456 --- /dev/null +++ b/lib/dvb/decoder.cpp @@ -0,0 +1,245 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +DEFINE_REF(eDVBAudio); + +eDVBAudio::eDVBAudio(eDVBDemux *demux, int dev): ref(0), m_demux(demux) +{ + char filename[128]; + sprintf(filename, "/dev/dvb/adapter%d/audio%d", demux->adapter, dev); + m_fd = ::open(filename, O_RDWR); + if (m_fd < 0) + eWarning("%s: %m", filename); + sprintf(filename, "/dev/dvb/adapter%d/demux%d", demux->adapter, demux->demux); + m_fd_demux = ::open(filename, O_RDWR); + if (m_fd_demux < 0) + eWarning("%s: %m", filename); +} + +int eDVBAudio::startPid(int pid) +{ + eDebug("setting audio pid to %x", pid); + if ((m_fd < 0) || (m_fd_demux < 0)) + return -1; + dmx_pes_filter_params pes; + + pes.pid = pid; + pes.input = DMX_IN_FRONTEND; + pes.output = DMX_OUT_DECODER; + pes.pes_type = DMX_PES_AUDIO0; + pes.flags = 0; + if (::ioctl(m_fd_demux, DMX_SET_PES_FILTER, &pes) < 0) + { + eWarning("audio: DMX_SET_PES_FILTER: %m"); + return -errno; + } + if (::ioctl(m_fd_demux, DMX_START, &pes) < 0) + { + eWarning("audio: DMX_START: %m"); + return -errno; + } + + if (::ioctl(m_fd, AUDIO_PLAY) < 0) + eWarning("audio: AUDIO_PLAY: %m"); + return 0; +} + +void eDVBAudio::stop() +{ + if (::ioctl(m_fd, AUDIO_STOP) < 0) + eWarning("audio: AUDIO_STOP: %m"); + if (::ioctl(m_fd_demux, DMX_STOP) < 0) + eWarning("audio: DMX_STOP: %m"); +} + +eDVBAudio::~eDVBAudio() +{ + if (m_fd >= 0) + ::close(m_fd); + if (m_fd_demux >= 0) + ::close(m_fd_demux); +} + +DEFINE_REF(eDVBVideo); + +eDVBVideo::eDVBVideo(eDVBDemux *demux, int dev): ref(0), m_demux(demux) +{ + char filename[128]; + sprintf(filename, "/dev/dvb/adapter%d/video%d", demux->adapter, dev); + m_fd = ::open(filename, O_RDWR); + if (m_fd < 0) + eWarning("%s: %m", filename); + sprintf(filename, "/dev/dvb/adapter%d/demux%d", demux->adapter, demux->demux); + m_fd_demux = ::open(filename, O_RDWR); + if (m_fd_demux < 0) + eWarning("%s: %m", filename); +} + +int eDVBVideo::startPid(int pid) +{ + if ((m_fd < 0) || (m_fd_demux < 0)) + return -1; + dmx_pes_filter_params pes; + + pes.pid = pid; + pes.input = DMX_IN_FRONTEND; + pes.output = DMX_OUT_DECODER; + pes.pes_type = DMX_PES_VIDEO0; + pes.flags = 0; + if (::ioctl(m_fd_demux, DMX_SET_PES_FILTER, &pes) < 0) + { + eWarning("video: DMX_SET_PES_FILTER: %m"); + return -errno; + } + if (::ioctl(m_fd_demux, DMX_START, &pes) < 0) + { + eWarning("video: DMX_START: %m"); + return -errno; + } + + if (::ioctl(m_fd, VIDEO_PLAY) < 0) + eWarning("video: VIDEO_PLAY: %m"); + else + eDebug("video ok"); + return 0; +} + +void eDVBVideo::stop() +{ + if (::ioctl(m_fd, VIDEO_STOP) < 0) + eWarning("video: VIDEO_STOP: %m"); + if (::ioctl(m_fd_demux, DMX_STOP) < 0) + eWarning("video: DMX_STOP: %m"); +} + +eDVBVideo::~eDVBVideo() +{ + if (m_fd >= 0) + ::close(m_fd); + if (m_fd_demux >= 0) + ::close(m_fd_demux); +} + +DEFINE_REF(eTSMPEGDecoder); + +int eTSMPEGDecoder::setState() +{ + int res = 0; + eDebug("changed %x", m_changed); + if (m_changed & changeAudio) + { + if (m_audio) + m_audio->stop(); + m_audio = 0; + m_audio = new eDVBAudio(m_demux, 0); + if (m_audio->startPid(m_apid)) + { + eWarning("audio: startpid failed!"); + res = -1; + } + m_changed &= ~changeAudio; + } + if (m_changed & changeVideo) + { + if (m_video) + m_video->stop(); + m_video = 0; + m_video = new eDVBVideo(m_demux, 0); + if (m_video->startPid(m_vpid)) + { + eWarning("video: startpid failed!"); + res = -1; + } + m_changed &= ~changeVideo; + } + return res; +} + +eTSMPEGDecoder::eTSMPEGDecoder(eDVBDemux *demux, int decoder): m_demux(demux) +{ +} + +eTSMPEGDecoder::~eTSMPEGDecoder() +{ + m_vpid = m_apid = m_pcrpid = pidNone; + m_changed = -1; + setState(); +} + +RESULT eTSMPEGDecoder::setVideoPID(int vpid) +{ + if (m_vpid != vpid) + { + m_changed |= changeVideo; + m_vpid = vpid; + } + return 0; +} + +RESULT eTSMPEGDecoder::setAudioPID(int apid, int type) +{ + if ((m_apid != apid) || (m_atype != type)) + { + m_changed |= changeAudio; + m_atype = type; + m_apid = apid; + } + return 0; +} + +RESULT eTSMPEGDecoder::setSyncPCR(int pcrpid) +{ + if (m_pcrpid != pcrpid) + { + m_changed |= changeAudio; + m_pcrpid = pcrpid; + } + return -1; +} + +RESULT eTSMPEGDecoder::setSyncMaster(int who) +{ + return -1; +} + +RESULT eTSMPEGDecoder::start() +{ + return setState(); +} + +RESULT eTSMPEGDecoder::freeze(int cont) +{ + return -1; +} + +RESULT eTSMPEGDecoder::unfreeze() +{ + return -1; +} + +RESULT eTSMPEGDecoder::setSinglePictureMode(int when) +{ + return -1; +} + +RESULT eTSMPEGDecoder::setPictureSkipMode(int what) +{ + return -1; +} + +RESULT eTSMPEGDecoder::setSlowMotion(int repeat) +{ + return -1; +} + +RESULT eTSMPEGDecoder::setZoom(int what) +{ + return -1; +} diff --git a/lib/dvb/decoder.h b/lib/dvb/decoder.h new file mode 100644 index 0000000..6694e9f --- /dev/null +++ b/lib/dvb/decoder.h @@ -0,0 +1,66 @@ +#ifndef __decoder_h +#define __decoder_h + +#include +#include + +class eDVBAudio: public virtual iObject +{ +DECLARE_REF; +private: + ePtr m_demux; + int m_fd, m_fd_demux; +public: + eDVBAudio(eDVBDemux *demux, int dev); + int startPid(int pid); + void stop(); + virtual ~eDVBAudio(); +}; + +class eDVBVideo: public virtual iObject +{ +DECLARE_REF; +private: + ePtr m_demux; + int m_fd, m_fd_demux; +public: + eDVBVideo(eDVBDemux *demux, int dev); + int startPid(int pid); + void stop(); + virtual ~eDVBVideo(); +}; + +class eTSMPEGDecoder: public virtual iTSMPEGDecoder +{ +DECLARE_REF; +private: + ePtr m_demux; + ePtr m_audio; + ePtr m_video; + + int m_vpid, m_apid, m_atype, m_pcrpid; + enum + { + changeVideo = 1, + changeAudio = 2, + changePCR = 4 + }; + int m_changed; + int setState(); +public: + enum { pidNone = -1 }; + eTSMPEGDecoder(eDVBDemux *demux, int decoder); + virtual ~eTSMPEGDecoder(); + RESULT setVideoPID(int vpid); + RESULT setAudioPID(int apid, int type); + RESULT setSyncPCR(int pcrpid); + RESULT setSyncMaster(int who); + RESULT start(); + RESULT freeze(int cont); + RESULT unfreeze(); + RESULT setSinglePictureMode(int when); + RESULT setPictureSkipMode(int what); + RESULT setSlowMotion(int repeat); + RESULT setZoom(int what); +}; +#endif diff --git a/lib/dvb/demux.cpp b/lib/dvb/demux.cpp new file mode 100644 index 0000000..e1d8bba --- /dev/null +++ b/lib/dvb/demux.cpp @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include + +#include +#include "crc32.h" + +#include +#include +#include +#include +#include + +eDVBDemux::eDVBDemux(int adapter, int demux): adapter(adapter), demux(demux), ref(0) +{ +} + +eDVBDemux::~eDVBDemux() +{ +} + +DEFINE_REF(eDVBDemux) + +RESULT eDVBDemux::createSectionReader(eMainloop *context, ePtr &reader) +{ + RESULT res; + reader = new eDVBSectionReader(this, context, res); + if (res) + reader = 0; + return res; +} + +RESULT eDVBDemux::getMPEGDecoder(ePtr &decoder) +{ + decoder = new eTSMPEGDecoder(this, 0); + return 0; +} + +void eDVBSectionReader::data(int) +{ + __u8 data[4096]; // max. section size + int r; + r = ::read(fd, data, 4096); + if(r < 0) + { + eWarning("ERROR reading section - %m\n"); + return; + } + if (checkcrc) + { + // this check should never happen unless the driver is crappy! + unsigned int c; + if ((c = crc32((unsigned)-1, data, r))) + eFatal("crc32 failed! is %x\n", c); + } + read(data); +} + +eDVBSectionReader::eDVBSectionReader(eDVBDemux *demux, eMainloop *context, RESULT &res): ref(0), demux(demux) +{ + char filename[128]; + sprintf(filename, "/dev/dvb/adapter%d/demux%d", demux->adapter, demux->demux); + fd = ::open(filename, O_RDWR); + + if (fd >= 0) + { + notifier=new eSocketNotifier(context, fd, eSocketNotifier::Read); + CONNECT(notifier->activated, eDVBSectionReader::data); + res = 0; + } else + { + perror(filename); + res = errno; + } +} + +DEFINE_REF(eDVBSectionReader) + +eDVBSectionReader::~eDVBSectionReader() +{ + if (notifier) + delete notifier; + if (fd >= 0) + ::close(fd); +} + +RESULT eDVBSectionReader::start(const eDVBSectionFilterMask &mask) +{ + RESULT res; + if (fd < 0) + return -ENODEV; + + dmx_sct_filter_params sct; + + sct.pid = mask.pid; + sct.timeout = 0; + sct.flags = DMX_IMMEDIATE_START; + if (mask.flags & eDVBSectionFilterMask::rfCRC) + { + sct.flags |= DMX_CHECK_CRC; + checkcrc = 1; + } else + checkcrc = 0; + + memcpy(sct.filter.filter, mask.data, DMX_FILTER_SIZE); + memcpy(sct.filter.mask, mask.mask, DMX_FILTER_SIZE); + memcpy(sct.filter.mode, mask.mode, DMX_FILTER_SIZE); + + res = ::ioctl(fd, DMX_SET_FILTER, &sct); + if (!res) + active = 1; + return res; +} + +RESULT eDVBSectionReader::stop() +{ + if (!active) + return -1; + + ::ioctl(fd, DMX_STOP); + + return 0; +} + +RESULT eDVBSectionReader::connectRead(const Slot1 &r, ePtr &conn) +{ + conn = new eConnection(read.connect(r)); + return 0; +} diff --git a/lib/dvb/demux.h b/lib/dvb/demux.h new file mode 100644 index 0000000..3b4cbed --- /dev/null +++ b/lib/dvb/demux.h @@ -0,0 +1,41 @@ +#ifndef __dvb_demux_h +#define __dvb_demux_h + +#include +#include + +class eDVBDemux: public virtual iDVBDemux +{ + int adapter, demux; + friend class eDVBSectionReader; + friend class eDVBAudio; + friend class eDVBVideo; +public: + DECLARE_REF + eDVBDemux(int adapter, int demux); + virtual ~eDVBDemux(); + RESULT createSectionReader(eMainloop *context, ePtr &reader); + RESULT getMPEGDecoder(ePtr &reader); +}; + +class eDVBSectionReader: public virtual iDVBSectionReader, public Object +{ + DECLARE_REF +private: + int fd; + Signal1 read; + ePtr demux; + int active; + int checkcrc; + void data(int); + eSocketNotifier *notifier; +public: + + eDVBSectionReader(eDVBDemux *demux, eMainloop *context, RESULT &res); + virtual ~eDVBSectionReader(); + RESULT start(const eDVBSectionFilterMask &mask); + RESULT stop(); + RESULT connectRead(const Slot1 &read, ePtr &conn); +}; + +#endif diff --git a/lib/dvb/dvb.cpp b/lib/dvb/dvb.cpp new file mode 100644 index 0000000..176b07c --- /dev/null +++ b/lib/dvb/dvb.cpp @@ -0,0 +1,211 @@ +#include +#include +#include +#include +#include + +DEFINE_REF(eDVBResourceManager); + +eDVBResourceManager *eDVBResourceManager::instance; + +eDVBResourceManager::eDVBResourceManager(): ref(0) +{ + avail = 1; + busy = 0; + m_sec = new eDVBSatelliteEquipmentControl; + if (!instance) + instance = this; +} + +eDVBResourceManager::~eDVBResourceManager() +{ + if (instance == this) + instance = 0; +} + +RESULT eDVBResourceManager::setChannelList(iDVBChannelList *list) +{ + m_list = list; + return 0; +} + +RESULT eDVBResourceManager::getChannelList(ePtr &list) +{ + list = m_list; + if (list) + return 0; + else + return -ENOENT; +} + + +RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, ePtr &channel) +{ + RESULT res; + eDVBChannel *ch; + channel = ch = new eDVBChannel(this, 0, 0, 0); + + ePtr fe; + if (!channel->getFrontend(fe)) + fe->setSEC(m_sec); + + res = ch->setChannel(channelid); + if (res) + { + channel = 0; + return res; + } + return 0; +} + +RESULT eDVBResourceManager::allocateRawChannel(ePtr &channel) +{ + channel = new eDVBChannel(this, 0, 0, 0); + ePtr fe; + if (!channel->getFrontend(fe)) + fe->setSEC(m_sec); + + return 0; +} + +RESULT eDVBResourceManager::allocatePVRChannel(int caps) +{ + return -1; // will nicht, mag nicht, und das interface ist auch kaputt +} + +RESULT eDVBResourceManager::addChannel(const eDVBChannelID &chid, eDVBChannel *ch) +{ + eDebug("add channel %p", ch); + m_active_channels.insert(std::pair(chid, ch)); + return 0; +} + +RESULT eDVBResourceManager::removeChannel(const eDVBChannelID &chid, eDVBChannel *) +{ + int cnt = m_active_channels.erase(chid); + eDebug("remove channel: removed %d channels", cnt); + ASSERT(cnt <= 1); + if (cnt == 1) + return 0; + return -ENOENT; +} + +eDVBChannel::eDVBChannel(eDVBResourceManager *mgr, int adapter, int frontend, int demux): eDVBDemux(adapter, demux), m_state(state_idle), m_mgr(mgr) +{ + if (frontend >= 0) + { + int ok; + m_frontend = new eDVBFrontend(adapter, frontend, ok); + if (!ok) + { + eDebug("warning, frontend failed"); + m_frontend = 0; + return; + } + m_frontend->connectStateChange(slot(*this, &eDVBChannel::frontendStateChanged), m_conn_frontendStateChanged); + } +} + +eDVBChannel::~eDVBChannel() +{ + if (m_channel_id) + m_mgr->removeChannel(m_channel_id, this); +} + +void eDVBChannel::frontendStateChanged(iDVBFrontend*fe) +{ + eDebug("fe state changed!"); + int state, ourstate = 0; + if (fe->getState(state)) + return; + + if (state == iDVBFrontend::stateLock) + { + eDebug("OURSTATE: ok"); + ourstate = state_ok; + } else if (state == iDVBFrontend::stateTuning) + { + eDebug("OURSTATE: tuning"); + ourstate = state_tuning; + } else if (state == iDVBFrontend::stateFailed) + { + eDebug("OURSTATE: failed/unavailable"); + ourstate = state_unavailable; + } else + eFatal("state unknown"); + + if (ourstate != m_state) + { + m_state = ourstate; + m_stateChanged(this); + } +} + +RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid) +{ + ePtr list; + + if (m_mgr->getChannelList(list)) + { + eDebug("no channel list set!"); + return -ENOENT; + } + + eDebug("tuning to chid: ns: %08x tsid %04x onid %04x", + channelid.dvbnamespace.get(), channelid.transport_stream_id.get(), channelid.original_network_id.get()); + + + ePtr feparm; + if (list->getChannelFrontendData(channelid, feparm)) + { + eDebug("channel not found!"); + return -ENOENT; + } + eDebug("allocateChannel: channel found.."); + + if (!m_frontend) + { + eDebug("no frontend to tune!"); + return -ENODEV; + } + + if (m_channel_id) + m_mgr->removeChannel(m_channel_id, this); + m_channel_id = channelid; + m_mgr->addChannel(m_channel_id, this); + m_state = state_tuning; + eDebug("%p", &*feparm); + return m_frontend->tune(*feparm); +} + +RESULT eDVBChannel::connectStateChange(const Slot1 &stateChange, ePtr &connection) +{ + connection = new eConnection( m_stateChanged.connect(stateChange) ); + return 0; +} + +RESULT eDVBChannel::getState(int &state) +{ + state = m_state; + return 0; +} + +RESULT eDVBChannel::setCIRouting(const eDVBCIRouting &routing) +{ + return -1; +} + +RESULT eDVBChannel::getDemux(ePtr &demux) +{ + demux = this; + return 0; +} + +RESULT eDVBChannel::getFrontend(ePtr &frontend) +{ + frontend = m_frontend; + if (frontend) + return 0; + else + return -ENODEV; +} diff --git a/lib/dvb/dvb.h b/lib/dvb/dvb.h new file mode 100644 index 0000000..f6aae23 --- /dev/null +++ b/lib/dvb/dvb.h @@ -0,0 +1,67 @@ +#ifndef __dvb_dvb_h +#define __dvb_dvb_h + +#include +#include +#include +#include + +class eDVBChannel; + +class eDVBResourceManager: public virtual iDVBResourceManager +{ + DECLARE_REF; + int avail, busy; + struct adapter + { + eSmartPtrList fe; + eSmartPtrList demux; + }; + std::multimap m_active_channels; + ePtr m_list; + ePtr m_sec; + static eDVBResourceManager *instance; +public: + eDVBResourceManager(); + virtual ~eDVBResourceManager(); + + static RESULT getInstance(ePtr &ptr) { if (instance) { ptr = instance; return 0; } return -1; } + + RESULT setChannelList(iDVBChannelList *list); + RESULT getChannelList(ePtr &list); + + RESULT allocateChannel(const eDVBChannelID &channelid, ePtr &channel); + RESULT allocateRawChannel(ePtr &channel); + RESULT allocatePVRChannel(int caps); + + RESULT addChannel(const eDVBChannelID &chid, eDVBChannel *ch); + RESULT removeChannel(const eDVBChannelID &chid, eDVBChannel *ch); +}; + +class eDVBChannel: public virtual iDVBChannel, public virtual eDVBDemux, public Object +{ + ePtr m_frontend; + ePtr m_current_frontend_parameters; + eDVBChannelID m_channel_id; + Signal1 m_stateChanged; + int m_state; + ePtr m_mgr; + + void frontendStateChanged(iDVBFrontend*fe); + ePtr m_conn_frontendStateChanged; +public: + eDVBChannel(eDVBResourceManager *mgr, int adapter, int frontend, int demux); + virtual ~eDVBChannel(); + + /* only for managed channels */ + RESULT setChannel(const eDVBChannelID &id); + + RESULT connectStateChange(const Slot1 &stateChange, ePtr &connection); + RESULT getState(int &state); + + RESULT setCIRouting(const eDVBCIRouting &routing); + RESULT getDemux(ePtr &demux); + RESULT getFrontend(ePtr &frontend); +}; + +#endif diff --git a/lib/dvb/esection.cpp b/lib/dvb/esection.cpp new file mode 100644 index 0000000..08cb49b --- /dev/null +++ b/lib/dvb/esection.cpp @@ -0,0 +1,130 @@ +#include +#include + +void eGTable::sectionRead(const __u8 *d) +{ + unsigned int last_section_number = d[7]; + m_table.flags &= ~eDVBTableSpec::tfAnyVersion; + m_table.flags |= eDVBTableSpec::tfThisVersion; + m_table.version = (d[5]>>1)&0x1F; + + + if (createTable(d[6], d, last_section_number + 1)) + { + if (m_timeout) + m_timeout->stop(); + m_reader->stop(); + ready = 1; + tableReady(error); + } else if ((m_table.flags & eDVBTableSpec::tfHaveTimeout) && m_timeout) + m_timeout->start(m_table.timeout, 1); // reset timeout +} + +void eGTable::timeout() +{ + eDebug("timeout!"); + m_reader->stop(); + ready = 1; + error = -1; + tableReady(error); +} + +eGTable::eGTable(): + ref(0), m_timeout(0), error(0) +{ +} + +DEFINE_REF(eGTable); + +RESULT eGTable::start(iDVBSectionReader *reader, const eDVBTableSpec &table) +{ + RESULT res; + m_table = table; + + m_reader = reader; + m_reader->connectRead(slot(*this, &eGTable::sectionRead), m_sectionRead_conn); + + // setup filter struct + eDVBSectionFilterMask mask; + + memset(&mask, 0, sizeof(mask)); + mask.pid = m_table.pid; + mask.flags = 0; + + if (m_table.flags & eDVBTableSpec::tfCheckCRC) + mask.flags |= eDVBSectionFilterMask::rfCRC; + + if (m_table.flags & eDVBTableSpec::tfHaveTID) + { + mask.data[0] = m_table.tid; + mask.mask[0] = 0xFF; + } + + if (m_table.flags & eDVBTableSpec::tfHaveTIDExt) + { + mask.data[1] = m_table.tidext >> 8; + mask.data[2] = m_table.tidext; + mask.mask[1] = 0xFF; + mask.mask[2] = 0xFF; + } + + if (!(m_table.flags & eDVBTableSpec::tfAnyVersion)) + { + eDebug("doing version filtering"); + mask.data[3] |= (m_table.version << 1)|1; + mask.mask[3] |= 0x3f; + if (!(m_table.flags & eDVBTableSpec::tfThisVersion)) + mask.mode[3] |= 0x3e; // negative filtering + } else + eDebug("no version filtering"); + + eDebug("%04x: %02x %02x %02x %02x %02x %02x", + mask.pid, + mask.data[0], mask.data[1], mask.data[2], + mask.data[3], mask.data[4], mask.data[5]); + eDebug("mask: %02x %02x %02x %02x %02x %02x", + mask.mask[0], mask.mask[1], mask.mask[2], + mask.mask[3], mask.mask[4], mask.mask[5]); + eDebug("mode: %02x %02x %02x %02x %02x %02x", + mask.mode[0], mask.mode[1], mask.mode[2], + mask.mode[3], mask.mode[4], mask.mode[5]); + + if ((res = m_reader->start(mask))) + { + eDebug("reader failed to start."); + return res; + } + + if (m_table.flags & eDVBTableSpec::tfHaveTimeout) + { + eDebug("have timeout, %d", m_table.timeout); + if (m_timeout) + delete m_timeout; + m_timeout = new eTimer(eApp); + m_timeout->start(m_table.timeout, 1); // begin timeout + CONNECT(m_timeout->timeout, eGTable::timeout); + } + + return 0; +} + +RESULT eGTable::start(iDVBDemux *demux, const eDVBTableSpec &table) +{ + int res; + ePtr reader; + res = demux->createSectionReader(eApp, reader); + if (res) + return res; + return start(reader, table); +} + +eGTable::~eGTable() +{ + if (m_timeout) + delete m_timeout; +} + +void eAUGTable::slotTableReady(int error) +{ + getNext(error); +} diff --git a/lib/dvb/esection.h b/lib/dvb/esection.h new file mode 100644 index 0000000..ca63c18 --- /dev/null +++ b/lib/dvb/esection.h @@ -0,0 +1,189 @@ +#ifndef __esection_h +#define __esection_h + +#include +#include + +class eGTable: public virtual iObject, public Object +{ +DECLARE_REF; +private: + ePtr m_reader; + eDVBTableSpec m_table; + + eTimer *m_timeout; + + void sectionRead(const __u8 *data); + void timeout(); + ePtr m_sectionRead_conn; +protected: + virtual int createTable(int nr, const __u8 *data, unsigned int max)=0; +public: + Signal1 tableReady; + eGTable(); + RESULT start(iDVBSectionReader *reader, const eDVBTableSpec &table); + RESULT start(iDVBDemux *reader, const eDVBTableSpec &table); + RESULT getSpec(eDVBTableSpec &spec) { spec = m_table; return 0; } + virtual ~eGTable(); + int error; + int ready; +}; + +template +class eTable: public eGTable +{ +private: + std::vector sections; + std::set avail; +protected: + int createTable(int nr, const __u8 *data, unsigned int max) + { + if (avail.find(nr) != avail.end()) + delete sections[nr]; + sections.resize(max); + + + sections[nr] = new Section(data); + avail.insert(nr); + + for (unsigned int i = 0; i < max; ++i) + if (avail.find(i) != avail.end()) + printf("+"); + else + printf("-"); + + printf(" %d/%d\n", avail.size(), max); + + if (avail.size() == max) + return 1; + else + return 0; + } +public: + std::vector &getSections() { return sections; } + eTable(): eGTable() + { + } + ~eTable() + { + for (typename std::vector::iterator i(sections.begin()); i != sections.end(); ++i) + delete *i; + } +}; + +class eAUGTable: public Object +{ +protected: + void slotTableReady(int); +public: + Signal1 tableReady; + virtual void getNext(int err)=0; +}; + +template +class eAUTable: public eAUGTable +{ + ePtr current, next; // current is READY AND ERRORFREE, next is not yet ready + int first; + ePtr m_demux; + eMainloop *ml; +public: + + eAUTable() + { + } + + ~eAUTable() + { + current=next=0; + } + + int begin(eMainloop *m, const eDVBTableSpec &spec, ePtr demux) + { + ml = m; + m_demux = demux; + first= 1; + current = 0; + next = new Table(); + CONNECT(next->tableReady, eAUTable::slotTableReady); + next->start(demux, spec); + return 0; + } + + int get() + { + if (current) + { + /*emit*/ tableReady(0); + return 0; + } else if (!next) + { + /*emit*/ tableReady(-1); + return 0; + } else + return 1; + } + + RESULT getCurrent(ePtr
&ptr) + { + if (!current) + return -1; + ptr = current; + return 0; + } + +#if 0 + void abort() + { + eDebug("eAUTable: aborted!"); + if (next) + next->abort(); + delete next; + next=0; + } +#endif + + int ready() + { + return !!current; + } + + void inject(Table *t) + { + next=t; + getNext(0); + } + + void getNext(int error) + { + current = 0; + if (error) + { + next=0; + if (first) + /*emit*/ tableReady(error); + first=0; + return; + } else + current=next; + + next=0; + first=0; + + assert(current->ready); + + /*emit*/ tableReady(0); + + eDVBTableSpec spec; + + if (current && (!current->getSpec(spec))) + { + next = new Table(); + CONNECT(next->tableReady, eAUTable::slotTableReady); + spec.flags &= ~(eDVBTableSpec::tfAnyVersion|eDVBTableSpec::tfThisVersion|eDVBTableSpec::tfHaveTimeout); + next->eGTable::start(m_demux, spec); + } + } +}; + +#endif diff --git a/lib/dvb/frontend.cpp b/lib/dvb/frontend.cpp new file mode 100644 index 0000000..c51eeea --- /dev/null +++ b/lib/dvb/frontend.cpp @@ -0,0 +1,465 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +void eDVBFrontendParametersSatellite::set(const SatelliteDeliverySystemDescriptor &descriptor) +{ + frequency = descriptor.getFrequency() * 10; + symbol_rate = descriptor.getSymbolRate() * 100; + switch (descriptor.getPolarization()) + { + case 0: + polarisation = Polarisation::Horizontal; + break; + case 1: + polarisation = Polarisation::Vertical; + break; + case 2: + polarisation = Polarisation::CircularLeft; + break; + case 3: + polarisation = Polarisation::CircularRight; + break; + } + switch (descriptor.getFecInner()) + { + case 1: + fec = FEC::f1_2; + break; + case 2: + fec = FEC::f2_3; + break; + case 3: + fec = FEC::f3_4; + break; + case 4: + fec = FEC::f5_6; + break; + case 5: + fec = FEC::f7_8; + break; + case 0xF: + fec = FEC::fNone; + break; + default: + fec = FEC::fAuto; + break; + } + inversion = Inversion::Unknown; + orbital_position = ((descriptor.getOrbitalPosition() >> 12) & 0xF) * 1000; + orbital_position += ((descriptor.getOrbitalPosition() >> 8) & 0xF) * 100; + orbital_position += ((descriptor.getOrbitalPosition() >> 4) & 0xF) * 10; + orbital_position += ((descriptor.getOrbitalPosition()) & 0xF); + if (orbital_position && (!descriptor.getWestEastFlag())) + orbital_position = 3600 - orbital_position; +} + +void eDVBFrontendParametersCable::set(const CableDeliverySystemDescriptor &descriptor) +{ + eFatal("nyi"); +} + +void eDVBFrontendParametersTerrestrial::set(const TerrestrialDeliverySystemDescriptor &) +{ + eFatal("nyi"); +} + +eDVBFrontendParameters::eDVBFrontendParameters(): ref(0), m_type(-1) +{ +} + +DEFINE_REF(eDVBFrontendParameters); + +RESULT eDVBFrontendParameters::getSystem(int &t) const +{ + if (m_type == -1) + return -1; + t = m_type; + return 0; +} + +RESULT eDVBFrontendParameters::getDVBS(eDVBFrontendParametersSatellite &p) const +{ + if (m_type != iDVBFrontend::feSatellite) + return -1; + p = sat; + return 0; +} + +RESULT eDVBFrontendParameters::getDVBC(eDVBFrontendParametersCable &p) const +{ + if (m_type != iDVBFrontend::feCable) + return -1; + p = cable; + return 0; +} + +RESULT eDVBFrontendParameters::getDVBT(eDVBFrontendParametersTerrestrial &p) const +{ + if (m_type != iDVBFrontend::feTerrestrial) + return -1; + p = terrestrial; + return 0; +} + +RESULT eDVBFrontendParameters::setDVBS(eDVBFrontendParametersSatellite &p) +{ + sat = p; + m_type = iDVBFrontend::feSatellite; + return 0; +} + +RESULT eDVBFrontendParameters::setDVBC(eDVBFrontendParametersCable &p) +{ + cable = p; + m_type = iDVBFrontend::feCable; + return 0; +} + +RESULT eDVBFrontendParameters::setDVBT(eDVBFrontendParametersTerrestrial &p) +{ + terrestrial = p; + m_type = iDVBFrontend::feTerrestrial; + return 0; +} + +RESULT eDVBFrontendParameters::calculateDifference(const iDVBFrontendParameters *parm, int &diff) const +{ + if (!parm) + return -1; + int type; + if (parm->getSystem(type)) + return -1; + if (type != m_type) + { + diff = 1<<30; // big difference + return 0; + } + + switch (type) + { + case iDVBFrontend::feSatellite: + { + eDVBFrontendParametersSatellite osat; + if (parm->getDVBS(osat)) + return -2; + + if (sat.orbital_position != osat.orbital_position) + diff = 1<<29; + else if (sat.polarisation != osat.polarisation) + diff = 1<<28; + else + diff = abs(sat.frequency - osat.frequency); + return 0; + } + case iDVBFrontend::feCable: + case iDVBFrontend::feTerrestrial: + default: + return -1; + } + return 0; +} + +RESULT eDVBFrontendParameters::getHash(unsigned long &hash) const +{ + switch (m_type) + { + case iDVBFrontend::feSatellite: + { + hash = sat.frequency & 0xFFFF; + hash |= sat.orbital_position << 16; + return 0; + } + case iDVBFrontend::feCable: + case iDVBFrontend::feTerrestrial: + default: + return -1; + } +} + +DEFINE_REF(eDVBFrontend); + +eDVBFrontend::eDVBFrontend(int adap, int fe, int &ok): ref(0), m_type(-1) +{ + char filename[128]; + int result; + dvb_frontend_info fe_info; + + m_sn = 0; + m_timeout = 0; + + sprintf(filename, "/dev/dvb/adapter%d/frontend%d", adap, fe); + eDebug("opening frontend."); + m_fd = ::open(filename, O_RDWR|O_NONBLOCK); + if (m_fd < 0) + { + eWarning("failed! (%s) %m", filename); + ok = 0; + return; + } + + result = ::ioctl(m_fd, FE_GET_INFO, &fe_info); + + if (result < 0) { + eWarning("ioctl FE_GET_INFO failed"); + ::close(m_fd); + m_fd = -1; + ok = 0; + return; + } + + switch (fe_info.type) + { + case FE_QPSK: + m_type = feSatellite; + break; + case FE_QAM: + m_type = feCable; + break; + case FE_OFDM: + m_type = feTerrestrial; + break; + default: + eWarning("unknown frontend type."); + ::close(m_fd); + m_fd = -1; + ok = 0; + return; + } + eDebug("detected %s frontend", "satellite\0cable\0 terrestrial"+feSatellite*9); + ok = 1; + + m_sn = new eSocketNotifier(eApp, m_fd, eSocketNotifier::Read); + CONNECT(m_sn->activated, eDVBFrontend::feEvent); + m_sn->start(); + + m_timeout = new eTimer(eApp); + CONNECT(m_timeout->timeout, eDVBFrontend::timeout); + + return; +} + +eDVBFrontend::~eDVBFrontend() +{ + if (m_fd >= 0) + ::close(m_fd); + if (m_sn) + delete m_sn; + if (m_timeout) + delete m_timeout; +} + +void eDVBFrontend::feEvent(int w) +{ + while (1) + { + dvb_frontend_event event; + int res; + int state; + res = ::ioctl(m_fd, FE_GET_EVENT, &event); + + if (res && (errno == EAGAIN)) + break; + + if (res) + { + eWarning("FE_GET_EVENT failed! %m"); + return; + } + + if (w < 0) + continue; + + eDebug("fe event: status %x, inversion %s", event.status, (event.parameters.inversion == INVERSION_ON) ? "on" : "off"); + if (event.status & FE_HAS_LOCK) + { + state = stateLock; + } else + { + if (m_tuning) + state = stateTuning; + else + state = stateFailed; + } + if (m_state != state) + { + m_state = state; + m_stateChanged(this); + } + } +} + +void eDVBFrontend::timeout() +{ + int state; + if (m_state == stateTuning) + { + state = stateFailed; + eDebug("DVBFrontend: timeout"); + if (m_state != state) + { + m_state = state; + m_stateChanged(this); + } + } else + m_tuning = 0; +} + +RESULT eDVBFrontend::getFrontendType(int &t) +{ + if (m_type == -1) + return -ENODEV; + t = m_type; + return 0; +} + +RESULT eDVBFrontend::tune(const iDVBFrontendParameters &where) +{ + if (m_type == -1) + return -ENODEV; + + dvb_frontend_parameters parm; + + feEvent(-1); + + switch (m_type) + { + case feSatellite: + { + int res; + eDVBFrontendParametersSatellite feparm; + if (where.getDVBS(feparm)) + { + eDebug("no dvbs data!"); + return -EINVAL; + } + if (!m_sec) + { + eWarning("no SEC module active!"); + return -ENOENT; + } + + res = m_sec->prepare(*this, parm, feparm); + if (res) + return res; + + eDebug("tuning to %d mhz", parm.frequency/1000); + break; + } + case feCable: + { + eDVBFrontendParametersCable feparm; + if (where.getDVBC(feparm)) + return -EINVAL; + eFatal("cable tuning nyi"); + } + case feTerrestrial: + { + eDVBFrontendParametersTerrestrial feparm; + if (where.getDVBT(feparm)) + return -EINVAL; + eFatal("terrestrial tuning nyi"); + } + } + + if (ioctl(m_fd, FE_SET_FRONTEND, &parm) == -1) + { + perror("FE_SET_FRONTEND failed"); + return errno; + } + + if (m_state != stateTuning) + { + m_tuning = 1; + m_state = stateTuning; + m_stateChanged(this); + } + + m_timeout->start(5000, 1); // 5 sec timeout. TODO: symbolrate dependent + + return 0; +} + +RESULT eDVBFrontend::connectStateChange(const Slot1 &stateChange, ePtr &connection) +{ + connection = new eConnection(m_stateChanged.connect(stateChange)); + return 0; +} + +RESULT eDVBFrontend::setVoltage(int voltage) +{ + fe_sec_voltage_t vlt; + + switch (voltage) + { + case voltageOff: + vlt = SEC_VOLTAGE_OFF; + break; + case voltage13: + vlt = SEC_VOLTAGE_13; + break; + case voltage18: + vlt = SEC_VOLTAGE_18; + break; + default: + return -ENODEV; + } + + return ::ioctl(m_fd, FE_SET_VOLTAGE, vlt); +} + +RESULT eDVBFrontend::getState(int &state) +{ + state = m_state; + return 0; +} + +RESULT eDVBFrontend::setTone(int t) +{ + fe_sec_tone_mode_t tone; + + switch (t) + { + case toneOn: + tone = SEC_TONE_ON; + break; + case toneOff: + tone = SEC_TONE_OFF; + break; + default: + return -ENODEV; + } + + return ::ioctl(m_fd, FE_SET_TONE, tone); +} + +RESULT eDVBFrontend::sendDiseqc(const eDVBDiseqcCommand &diseqc) +{ + struct dvb_diseqc_master_cmd cmd; + if (::ioctl(m_fd, FE_SET_TONE, SEC_TONE_OFF)) + return -EINVAL; + usleep(15 * 1000); + memcpy(cmd.msg, diseqc.data, diseqc.len); + cmd.msg_len = diseqc.len; + + if (::ioctl(m_fd, FE_DISEQC_SEND_MASTER_CMD, &cmd)) + return -EINVAL; + usleep(15 * 1000); + eDebug("diseqc ok"); + return 0; +} + +RESULT eDVBFrontend::setSEC(iDVBSatelliteEquipmentControl *sec) +{ + m_sec = sec; + return 0; +} diff --git a/lib/dvb/frontend.h b/lib/dvb/frontend.h new file mode 100644 index 0000000..ee7f945 --- /dev/null +++ b/lib/dvb/frontend.h @@ -0,0 +1,61 @@ +#ifndef __dvb_frontend_h +#define __dvb_frontend_h + +#include + +class eDVBFrontendParameters: public virtual iDVBFrontendParameters +{ + DECLARE_REF; + union + { + eDVBFrontendParametersSatellite sat; + eDVBFrontendParametersCable cable; + eDVBFrontendParametersTerrestrial terrestrial; + }; + int m_type; +public: + eDVBFrontendParameters(); + + RESULT getSystem(int &type) const; + RESULT getDVBS(eDVBFrontendParametersSatellite &p) const; + RESULT getDVBC(eDVBFrontendParametersCable &p) const; + RESULT getDVBT(eDVBFrontendParametersTerrestrial &p) const; + + RESULT setDVBS(eDVBFrontendParametersSatellite &p); + RESULT setDVBC(eDVBFrontendParametersCable &p); + RESULT setDVBT(eDVBFrontendParametersTerrestrial &p); + + RESULT calculateDifference(const iDVBFrontendParameters *parm, int &diff) const; + + RESULT getHash(unsigned long &hash) const; +}; + +class eDVBFrontend: public virtual iDVBFrontend, public Object +{ + DECLARE_REF; + int m_type; + int m_fd; + int m_state; + Signal1 m_stateChanged; + ePtr m_sec; + eSocketNotifier *m_sn; + int m_tuning; + eTimer *m_timeout; + + void feEvent(int); + void timeout(); +public: + eDVBFrontend(int adap, int fe, int &ok); + virtual ~eDVBFrontend(); + + RESULT getFrontendType(int &type); + RESULT tune(const iDVBFrontendParameters &where); + RESULT connectStateChange(const Slot1 &stateChange, ePtr &connection); + RESULT getState(int &state); + RESULT setTone(int tone); + RESULT setVoltage(int voltage); + RESULT sendDiseqc(const eDVBDiseqcCommand &diseqc); + RESULT setSEC(iDVBSatelliteEquipmentControl *sec); +}; + +#endif diff --git a/lib/dvb/idvb.h b/lib/dvb/idvb.h new file mode 100644 index 0000000..c4ae488 --- /dev/null +++ b/lib/dvb/idvb.h @@ -0,0 +1,354 @@ +#ifndef __dvb_idvb_h +#define __dvb_idvb_h + +#include +#include +#include +#include +#include + + // bitte KEINE operator int() definieren, sonst bringt das ganze nix! +struct eTransportStreamID +{ +private: + int v; +public: + int get() const { return v; } + eTransportStreamID(int i): v(i) { } + eTransportStreamID(): v(-1) { } + bool operator == (const eTransportStreamID &c) const { return v == c.v; } + bool operator != (const eTransportStreamID &c) const { return v != c.v; } + bool operator < (const eTransportStreamID &c) const { return v < c.v; } + bool operator > (const eTransportStreamID &c) const { return v > c.v; } +}; + +struct eServiceID +{ +private: + int v; +public: + int get() const { return v; } + eServiceID(int i): v(i) { } + eServiceID(): v(-1) { } + bool operator == (const eServiceID &c) const { return v == c.v; } + bool operator != (const eServiceID &c) const { return v != c.v; } + bool operator < (const eServiceID &c) const { return v < c.v; } + bool operator > (const eServiceID &c) const { return v > c.v; } +}; + +struct eOriginalNetworkID +{ +private: + int v; +public: + int get() const { return v; } + eOriginalNetworkID(int i): v(i) { } + eOriginalNetworkID(): v(-1) { } + bool operator == (const eOriginalNetworkID &c) const { return v == c.v; } + bool operator != (const eOriginalNetworkID &c) const { return v != c.v; } + bool operator < (const eOriginalNetworkID &c) const { return v < c.v; } + bool operator > (const eOriginalNetworkID &c) const { return v > c.v; } +}; + +struct eDVBNamespace +{ +private: + int v; +public: + int get() const { return v; } + eDVBNamespace(int i): v(i) { } + eDVBNamespace(): v(-1) { } + bool operator == (const eDVBNamespace &c) const { return v == c.v; } + bool operator != (const eDVBNamespace &c) const { return v != c.v; } + bool operator < (const eDVBNamespace &c) const { return v < c.v; } + bool operator > (const eDVBNamespace &c) const { return v > c.v; } +}; + +struct eDVBChannelID +{ + eDVBNamespace dvbnamespace; + eTransportStreamID transport_stream_id; + eOriginalNetworkID original_network_id; + bool operator<(const eDVBChannelID &c) const + { + if (dvbnamespace < c.dvbnamespace) + return 1; + else if (dvbnamespace == c.dvbnamespace) + { + if (original_network_id < c.original_network_id) + return 1; + else if (original_network_id == c.original_network_id) + if (transport_stream_id < c.transport_stream_id) + return 1; + } + return 0; + } + eDVBChannelID(eDVBNamespace dvbnamespace, eTransportStreamID tsid, eOriginalNetworkID onid): + dvbnamespace(dvbnamespace), transport_stream_id(tsid), original_network_id(onid) + { + } + eDVBChannelID(): + dvbnamespace(-1), transport_stream_id(-1), original_network_id(-1) + { + } + operator bool() const + { + return (dvbnamespace != -1) && (transport_stream_id != -1) && (original_network_id != -1); + } +}; + +struct eServiceReferenceDVB: public eServiceReference +{ + int getServiceType() const { return data[0]; } + void setServiceType(int service_type) { data[0]=service_type; } + + eServiceID getServiceID() const { return eServiceID(data[1]); } + void setServiceID(eServiceID service_id) { data[1]=service_id.get(); } + + eTransportStreamID getTransportStreamID() const { return eTransportStreamID(data[2]); } + void setTransportStreamID(eTransportStreamID transport_stream_id) { data[2]=transport_stream_id.get(); } + + eOriginalNetworkID getOriginalNetworkID() const { return eOriginalNetworkID(data[3]); } + void setOriginalNetworkID(eOriginalNetworkID original_network_id) { data[3]=original_network_id.get(); } + + eDVBNamespace getDVBNamespace() const { return eDVBNamespace(data[4]); } + void setDVBNamespace(eDVBNamespace dvbnamespace) { data[4]=dvbnamespace.get(); } + + eServiceReferenceDVB(eDVBNamespace dvbnamespace, eTransportStreamID transport_stream_id, eOriginalNetworkID original_network_id, eServiceID service_id, int service_type) + :eServiceReference(eServiceReference::idDVB, 0) + { + setTransportStreamID(transport_stream_id); + setOriginalNetworkID(original_network_id); + setDVBNamespace(dvbnamespace); + setServiceID(service_id); + setServiceType(service_type); + } + + void set(const eDVBChannelID &chid) + { + setDVBNamespace(chid.dvbnamespace); + setOriginalNetworkID(chid.original_network_id); + setTransportStreamID(chid.transport_stream_id); + } + + void getChannelID(eDVBChannelID &chid) + { + chid = eDVBChannelID(getDVBNamespace(), getTransportStreamID(), getOriginalNetworkID()); + } + + eServiceReferenceDVB() + :eServiceReference(eServiceReference::idDVB, 0) + { + } +}; + + +class iDVBChannel; +class iDVBDemux; +class iDVBFrontendParameters; + +class iDVBChannelList: public virtual iObject +{ +public: + virtual RESULT getChannelFrontendData(const eDVBChannelID &id, ePtr &parm)=0; +}; + +class iDVBResourceManager: public virtual iObject +{ +public: + /* + solange rumloopen bis eine resource gefunden wurde, die eine frequenz + tunen will + + wenn natuerlich sowas schon vorhanden ist, dann einfach ne ref darauf + geben. (zwei services auf dem gleichen transponder teilen sich einen + channel) + */ + virtual RESULT setChannelList(iDVBChannelList *list)=0; + virtual RESULT getChannelList(ePtr &list)=0; + virtual RESULT allocateChannel(const eDVBChannelID &channel, ePtr &channel)=0; + virtual RESULT allocateRawChannel(ePtr &channel)=0; + virtual RESULT allocatePVRChannel(int caps)=0; +}; + +class SatelliteDeliverySystemDescriptor; +class CableDeliverySystemDescriptor; +class TerrestrialDeliverySystemDescriptor; + +struct eDVBFrontendParametersSatellite +{ + struct Polarisation + { + enum { + Horizontal, Vertical, CircularLeft, CircularRight + }; + }; + struct Inversion + { + enum { + On, Off, Unknown + }; + }; + struct FEC + { + enum { + fNone, f1_2, f2_3, f3_4, f5_6, f7_8, fAuto + }; + }; + unsigned int frequency, symbol_rate; + int polarisation, fec, inversion, orbital_position; + + void set(const SatelliteDeliverySystemDescriptor &); +}; + +struct eDVBFrontendParametersCable +{ + unsigned int frequency, symbol_rate; + int modulation, inversion, fec_inner; + void set(const CableDeliverySystemDescriptor &); +}; + +struct eDVBFrontendParametersTerrestrial +{ + int unknown; + void set(const TerrestrialDeliverySystemDescriptor &); +}; + +class iDVBFrontendParameters: public virtual iObject +{ +public: + virtual RESULT getSystem(int &type) const = 0; + virtual RESULT getDVBS(eDVBFrontendParametersSatellite &p) const = 0; + virtual RESULT getDVBC(eDVBFrontendParametersCable &p) const = 0; + virtual RESULT getDVBT(eDVBFrontendParametersTerrestrial &p) const = 0; + + virtual RESULT calculateDifference(const iDVBFrontendParameters *parm, int &diff) const = 0; + virtual RESULT getHash(unsigned long &hash) const = 0; +}; + +#define MAX_DISEQC_LENGTH 16 + +struct eDVBDiseqcCommand +{ + int len; + __u8 data[MAX_DISEQC_LENGTH]; +}; + +class iDVBSatelliteEquipmentControl; + +class iDVBFrontend: public virtual iObject +{ +public: + enum { + feSatellite, feCable, feTerrestrial + }; + virtual RESULT getFrontendType(int &type)=0; + virtual RESULT tune(const iDVBFrontendParameters &where)=0; + virtual RESULT connectStateChange(const Slot1 &stateChange, ePtr &connection)=0; + enum { + stateIdle = 0, + stateTuning = 1, + stateFailed = 2, + stateLock = 3 + }; + virtual RESULT getState(int &state)=0; + enum { + toneOn, toneOff + }; + virtual RESULT setTone(int tone)=0; + enum { + voltageOff, voltage13, voltage18 + }; + virtual RESULT setVoltage(int voltage)=0; + virtual RESULT sendDiseqc(const eDVBDiseqcCommand &diseqc)=0; + virtual RESULT setSEC(iDVBSatelliteEquipmentControl *sec)=0; +}; + +class iDVBSatelliteEquipmentControl: public iObject +{ +public: + virtual RESULT prepare(iDVBFrontend &frontend, struct dvb_frontend_parameters &parm, eDVBFrontendParametersSatellite &sat)=0; +}; + +struct eDVBCIRouting +{ + int enabled; +}; + +class iDVBChannel: public virtual iObject +{ +public: + enum + { + state_idle, /* not yet tuned */ + state_tuning, /* currently tuning (first time) */ + state_unavailable, /* currently unavailable, will be back without further interaction */ + state_ok /* ok */ + }; + virtual RESULT connectStateChange(const Slot1 &stateChange, ePtr &connection)=0; + virtual RESULT getState(int &state)=0; + enum + { + cap_decode, + cap_ci + }; + virtual RESULT setCIRouting(const eDVBCIRouting &routing)=0; + virtual RESULT getDemux(ePtr &demux)=0; + + /* direct frontend access for raw channels and/or status inquiries. */ + virtual RESULT getFrontend(ePtr &frontend)=0; +}; + +class iDVBSectionReader; +class iTSMPEGDecoder; + +class iDVBDemux: public virtual iObject +{ +public: + virtual RESULT createSectionReader(eMainloop *context, ePtr &reader)=0; + virtual RESULT getMPEGDecoder(ePtr &reader)=0; +}; + +class iTSMPEGDecoder: public iObject +{ +public: + enum { pidDisabled = -1 }; + /** Set Displayed Video PID */ + virtual RESULT setVideoPID(int vpid)=0; + + enum { af_MPEG, af_AC3, af_DTS }; + /** Set Displayed Audio PID and type */ + virtual RESULT setAudioPID(int apid, int type)=0; + + /** Set Sync mode to PCR */ + virtual RESULT setSyncPCR(int pcrpid)=0; + enum { sm_Audio, sm_Video }; + /** Set Sync mode to either audio or video master */ + virtual RESULT setSyncMaster(int who)=0; + + /** Apply settings */ + virtual RESULT start()=0; + + /** Freeze frame. Either continue decoding (without display) or halt. */ + virtual RESULT freeze(int cont)=0; + /** Continue after freeze. */ + virtual RESULT unfreeze()=0; + + // stop on .. Picture + enum { spm_I, spm_Ref, spm_Any }; + /** Stop on specific decoded picture. For I-Frame display. */ + virtual RESULT setSinglePictureMode(int when)=0; + + enum { pkm_B, pkm_PB }; + /** Fast forward by skipping either B or P/B pictures */ + virtual RESULT setPictureSkipMode(int what)=0; + + /** Slow Motion by repeating pictures */ + virtual RESULT setSlowMotion(int repeat)=0; + + enum { zoom_Normal, zoom_PanScan, zoom_Letterbox, zoom_Fullscreen }; + /** Set Zoom. mode *must* be fitting. */ + virtual RESULT setZoom(int what)=0; +}; + +#endif diff --git a/lib/dvb/isection.h b/lib/dvb/isection.h new file mode 100644 index 0000000..04b50f5 --- /dev/null +++ b/lib/dvb/isection.h @@ -0,0 +1,53 @@ +#ifndef __dvb_isection_h +#define __dvb_isection_h + +#include + +#ifndef DMX_FILTER_SIZE +#define DMX_FILTER_SIZE 16 +#endif + +struct eDVBSectionFilterMask +{ + int pid; + /* mode is 0 for positive, 1 for negative filtering */ + __u8 data[DMX_FILTER_SIZE], mask[DMX_FILTER_SIZE], mode[DMX_FILTER_SIZE]; + enum { + rfCRC=1, + rfNoAbort=2 + }; + int flags; +}; + +struct eDVBTableSpec +{ + int pid, tid, tidext; + int version; + int timeout; /* timeout in ms */ + enum + { + tfInOrder=1, + /* + tfAnyVersion filter ANY version + 0 filter all EXCEPT given version (negative filtering) + tfThisVersion filter only THIS version + */ + tfAnyVersion=2, + tfThisVersion=4, + tfHaveTID=8, + tfHaveTIDExt=16, + tfCheckCRC=32, + tfHaveTimeout=64, + }; + int flags; +}; + +class iDVBSectionReader: public virtual iObject +{ +public: + virtual RESULT start(const eDVBSectionFilterMask &mask)=0; + virtual RESULT stop()=0; + virtual RESULT connectRead(const Slot1 &read, ePtr &conn)=0; +}; + +#endif diff --git a/lib/dvb/list.h b/lib/dvb/list.h new file mode 100644 index 0000000..6df4980 --- /dev/null +++ b/lib/dvb/list.h @@ -0,0 +1,13 @@ +#ifndef __list_h +#define __list_h + +class eDVBTransponderList: iDVBChannelList +{ + DECLARE_REF; +private: + std::map > channels; +public: + virtual RESULT getChannelFrontendData(const eDVBChannelID &id, ePtr &parm)=0; +}; + +#endif diff --git a/lib/dvb/pmt.cpp b/lib/dvb/pmt.cpp new file mode 100644 index 0000000..4ab551f --- /dev/null +++ b/lib/dvb/pmt.cpp @@ -0,0 +1,142 @@ +#include +#include +#include + +eDVBServicePMTHandler::eDVBServicePMTHandler() +{ + ePtr mgr; + eDVBResourceManager::getInstance(mgr); + m_resourceManager = mgr; + CONNECT(m_PMT.tableReady, eDVBServicePMTHandler::PMTready); + CONNECT(m_PAT.tableReady, eDVBServicePMTHandler::PATready); +} + +void eDVBServicePMTHandler::channelStateChanged(iDVBChannel *channel) +{ + int state; + channel->getState(state); + + if ((m_last_channel_state != iDVBChannel::state_ok) + && (state == iDVBChannel::state_ok) && (!m_demux)) + { + if (m_channel) + if (m_channel->getDemux(m_demux)) + eDebug("shit it failed.. again."); + + if (m_demux) + { + eDebug("ok ... now we start!!"); + m_PAT.begin(eApp, eDVBPATSpec(), m_demux); + } + } +} + +void eDVBServicePMTHandler::PMTready(int error) +{ + if (error) + serviceEvent(eventNoPMT); + else + serviceEvent(eventNewProgramInfo); +} + +void eDVBServicePMTHandler::PATready(int) +{ + ePtr > ptr; + if (!m_PAT.getCurrent(ptr)) + { + int pmtpid = -1; + ProgramAssociationTableConstIterator i; + for (i = ptr->getSections().begin(); i != ptr->getSections().end(); ++i) + { + const ProgramAssociationTable &pat = **i; + ProgramAssociationConstIterator program; + for (program = pat.getPrograms()->begin(); program != pat.getPrograms()->end(); ++program) + if (eServiceID((*program)->getProgramNumber()) == m_reference.getServiceID()) + pmtpid = (*program)->getProgramMapPid(); + } + if (pmtpid == -1) + serviceEvent(eventNoPATEntry); + else + m_PMT.begin(eApp, eDVBPMTSpec(pmtpid, m_reference.getServiceID().get()), m_demux); + } else + serviceEvent(eventNoPAT); +} + +int eDVBServicePMTHandler::getProgramInfo(struct program &program) +{ + ePtr > ptr; + + program.videoStreams.clear(); + program.audioStreams.clear(); + program.pcrPid = -1; + + if (!m_PMT.getCurrent(ptr)) + { + ProgramMapTableConstIterator i; + for (i = ptr->getSections().begin(); i != ptr->getSections().end(); ++i) + { + const ProgramMapTable &pmt = **i; + program.pcrPid = pmt.getPcrPid(); + + ElementaryStreamInfoConstIterator es; + for (es = pmt.getEsInfo()->begin(); es != pmt.getEsInfo()->end(); ++es) + { + int isaudio = 0, isvideo = 0; + videoStream video; + audioStream audio; + + video.pid = (*es)->getPid(); + audio.pid = (*es)->getPid(); + + switch ((*es)->getType()) + { + case 0x01: // MPEG 1 video + case 0x02: // MPEG 2 video + isvideo = 1; + break; + case 0x03: // MPEG 1 audio + case 0x04: // MPEG 2 audio: + isaudio = 1; + audio.type = audioStream::atMPEG; + break; + } + if (isaudio) + program.audioStreams.push_back(audio); + if (isvideo) + program.videoStreams.push_back(video); + } + } + return 0; + } else + return -1; +} + +int eDVBServicePMTHandler::getDemux(ePtr &demux) +{ + demux = m_demux; + if (demux) + return 0; + else + return -1; +} + +int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref) +{ + RESULT res; + m_channel = 0; + m_channelStateChanged_connection = 0; + m_reference = ref; + eDVBChannelID chid; + ref.getChannelID(chid); + res = m_resourceManager->allocateChannel(chid, m_channel); + eDebug("eDVBServicePMTHandler: tune %d", res); + if (m_channel) + { + m_channel->connectStateChange( + slot(*this, &eDVBServicePMTHandler::channelStateChanged), + m_channelStateChanged_connection); + m_last_channel_state = -1; + channelStateChanged(m_channel); + } + return res; +} diff --git a/lib/dvb/pmt.h b/lib/dvb/pmt.h new file mode 100644 index 0000000..ee2040a --- /dev/null +++ b/lib/dvb/pmt.h @@ -0,0 +1,71 @@ +#ifndef __lib_dvb_dvbmid_h +#define __lib_dvb_dvbmid_h + +#include +#include +#include +#include +#include + +class eDVBServicePMTHandler: public Object +{ + eServiceReferenceDVB m_reference; +// ePtr m_service; + + int m_last_channel_state; + + eAUTable > m_PMT; + eAUTable > m_PAT; + + ePtr m_channel; + ePtr m_resourceManager; + ePtr m_demux; + + void channelStateChanged(iDVBChannel *); + ePtr m_channelStateChanged_connection; + + void PMTready(int error); + void PATready(int error); + +public: + eDVBServicePMTHandler(); + + enum + { + eventNoResources, // a requested resource couldn't be allocated + eventNoPAT, // no pat could be received (timeout) + eventNoPATEntry, // no pat entry for the corresponding SID could be found + eventNoPMT, // no pmt could be received (timeout) + eventNewProgramInfo // we just received a PMT + }; + + Signal1 serviceEvent; + + struct videoStream + { + int pid; + }; + + struct audioStream + { + int pid; + enum { atMPEG, atAC3, atDTS }; + int type; // mpeg2, ac3, dts, ... + // language code, ... + }; + + struct program + { + std::vector videoStreams; + std::vector audioStreams; + // ca info + int pcrPid; + }; + + int getProgramInfo(struct program &program); + int getDemux(ePtr &demux); + + int tune(eServiceReferenceDVB &ref); +}; + +#endif diff --git a/lib/dvb/scan.cpp b/lib/dvb/scan.cpp new file mode 100644 index 0000000..68121c4 --- /dev/null +++ b/lib/dvb/scan.cpp @@ -0,0 +1,374 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +eDVBScan::eDVBScan(iDVBChannel *channel): m_channel(channel) +{ + if (m_channel->getDemux(m_demux)) + eDebug("scan: failed to allocate demux!"); + m_channel->connectStateChange(slot(*this, &eDVBScan::stateChange), m_stateChanged_connection); +} + +eDVBScan::~eDVBScan() +{ +} + +int eDVBScan::isValidONIDTSID(eOriginalNetworkID onid, eTransportStreamID tsid) +{ + switch (onid.get()) + { + case 0: + case 0xFFFF: + case 0x1111: + return 0; + case 1: + return tsid>1; + case 0x00B1: + return tsid != 0x00B0; + case 0x0002: + return tsid != 0x07E8; + default: + return 1; + } +} + +eDVBNamespace eDVBScan::buildNamespace(eOriginalNetworkID onid, eTransportStreamID tsid, unsigned long hash) +{ + // on valid ONIDs, ignore frequency ("sub network") part + if (isValidONIDTSID(onid, tsid)) + hash &= ~0xFFFF; + return eDVBNamespace(hash); +} + +void eDVBScan::stateChange(iDVBChannel *ch) +{ + int state; + if (ch->getState(state)) + return; + if (m_channel_state == state) + return; + + if (state == iDVBChannel::state_ok) + { + startFilter(); + m_channel_state = state; + } else if (state == iDVBChannel::state_unavailable) + { + m_ch_unavailable.push_back(m_ch_current); + nextChannel(); + } +} + +RESULT eDVBScan::nextChannel() +{ + ePtr fe; + + m_SDT = 0; m_BAT = 0; m_NIT = 0; + + m_ready = readyBAT; + if (m_ch_toScan.empty()) + { + eDebug("no channels left to scan."); + eDebug("%d channels scanned, %d were unavailable.", + m_ch_scanned.size(), m_ch_unavailable.size()); + eDebug("%d channels in database.", m_new_channels.size()); + m_event(evtFinish); + return -ENOENT; + } + + m_ch_current = m_ch_toScan.front(); + m_ch_toScan.pop_front(); + + if (m_channel->getFrontend(fe)) + return -ENOTSUP; + + m_channel_state = iDVBChannel::state_idle; + if (fe->tune(*m_ch_current)) + return -EINVAL; + + m_event(evtUpdate); + return 0; +} + +RESULT eDVBScan::startFilter() +{ + assert(m_demux); + + m_SDT = new eTable(); + if (m_SDT->start(m_demux, eDVBSDTSpec())) + return -1; + CONNECT(m_SDT->tableReady, eDVBScan::SDTready); + + m_NIT = 0; + m_NIT = new eTable(); + if (m_NIT->start(m_demux, eDVBNITSpec())) + return -1; + CONNECT(m_NIT->tableReady, eDVBScan::NITready); + + m_BAT = new eTable(); + if (m_BAT->start(m_demux, eDVBBATSpec())) + return -1; + CONNECT(m_BAT->tableReady, eDVBScan::BATready); + + return 0; +} + +void eDVBScan::SDTready(int err) +{ + eDebug("got sdt"); + m_ready |= readySDT; + if (!err) + m_ready |= validSDT; + channelDone(); +} + +void eDVBScan::NITready(int err) +{ + eDebug("got nit, err %d", err); + m_ready |= readyNIT; + if (!err) + m_ready |= validNIT; + channelDone(); +} + +void eDVBScan::BATready(int err) +{ + eDebug("got bat"); + m_ready |= readyBAT; + if (!err) + m_ready |= validBAT; + channelDone(); +} + +void eDVBScan::addChannel(const eDVBChannelID &chid, iDVBFrontendParameters *feparm) +{ + /* add it to the list of known channels. */ + if (chid) + m_new_channels.insert(std::pair >(chid, feparm)); + + /* check if we don't already have that channel ... */ + + /* ... in the list of channels to scan */ + for (std::list >::const_iterator i(m_ch_toScan.begin()); i != m_ch_toScan.end(); ++i) + if (sameChannel(*i, feparm)) + return; + + /* ... in the list of successfully scanned channels */ + for (std::list >::const_iterator i(m_ch_scanned.begin()); i != m_ch_scanned.end(); ++i) + if (sameChannel(*i, feparm)) + return; + + /* ... in the list of unavailable channels */ + for (std::list >::const_iterator i(m_ch_unavailable.begin()); i != m_ch_unavailable.end(); ++i) + if (sameChannel(*i, feparm)) + return; + + /* ... on the current channel */ + if (sameChannel(m_ch_current, feparm)) + return; + + /* otherwise, add it to the todo list. */ + m_ch_toScan.push_back(feparm); +} + +int eDVBScan::sameChannel(iDVBFrontendParameters *ch1, iDVBFrontendParameters *ch2) const +{ + int diff; + if (ch1->calculateDifference(ch2, diff)) + return 0; + if (diff < 4000) // more than 4mhz difference? + return 1; + return 0; +} + +void eDVBScan::channelDone() +{ + if (m_ready & validSDT) + { + unsigned long hash = 0; + m_ch_current->getHash(hash); + + eDVBNamespace dvbnamespace = buildNamespace( + (**m_SDT->getSections().begin()).getOriginalNetworkId(), + (**m_SDT->getSections().begin()).getTransportStreamId(), + hash); + + eDebug("SDT: "); + ServiceDescriptionTableConstIterator i; + for (i = m_SDT->getSections().begin(); i != m_SDT->getSections().end(); ++i) + processSDT(dvbnamespace, **i); + m_ready &= ~validSDT; + } + + if (m_ready & validNIT) + { + eDebug("dumping NIT"); + NetworkInformationTableConstIterator i; + for (i = m_NIT->getSections().begin(); i != m_NIT->getSections().end(); ++i) + { + const TransportStreamInfoVector &tsinfovec = *(*i)->getTsInfo(); + + for (TransportStreamInfoConstIterator tsinfo(tsinfovec.begin()); + tsinfo != tsinfovec.end(); ++tsinfo) + { + eDebug("TSID: %04x ONID: %04x", (*tsinfo)->getTransportStreamId(), + (*tsinfo)->getOriginalNetworkId()); + + eOriginalNetworkID onid = (*tsinfo)->getOriginalNetworkId(); + eTransportStreamID tsid = (*tsinfo)->getTransportStreamId(); + + for (DescriptorConstIterator desc = (*tsinfo)->getDescriptors()->begin(); + desc != (*tsinfo)->getDescriptors()->end(); ++desc) + { + switch ((*desc)->getTag()) + { +// case SERVICE_LIST_DESCRIPTOR: + case SATELLITE_DELIVERY_SYSTEM_DESCRIPTOR: + { + SatelliteDeliverySystemDescriptor &d = (SatelliteDeliverySystemDescriptor&)**desc; + eDebug("%d kHz, %d%d%d.%d%c %s MOD:%d %d symb/s, fec %d", + d.getFrequency(), + (d.getOrbitalPosition()>>12)&0xF, + (d.getOrbitalPosition()>>8)&0xF, + (d.getOrbitalPosition()>>4)&0xF, + d.getOrbitalPosition()&0xF, d.getWestEastFlag()?'E':'W', + d.getPolarization() ? "hor" : "vert", + d.getModulation(), d.getSymbolRate(), d.getFecInner()); + + /* some sanity checking: below 100MHz is invalid */ + if (d.getFrequency() < 10000) + break; + + ePtr feparm = new eDVBFrontendParameters; + eDVBFrontendParametersSatellite sat; + sat.set(d); + feparm->setDVBS(sat); + unsigned long hash=0; + feparm->getHash(hash); + + eDVBNamespace ns = buildNamespace(onid, tsid, hash); + + addChannel( + eDVBChannelID(ns, tsid, onid), + feparm); + break; + } + default: + eDebug("descr<%x>", (*desc)->getTag()); + break; + } + } + + } + } + m_ready &= ~validNIT; + } + + if ((m_ready & readyAll) != readyAll) + return; + eDebug("channel done!"); + m_ch_scanned.push_back(m_ch_current); + nextChannel(); +} + +void eDVBScan::start(const std::list > &known_transponders) +{ + m_ch_toScan.clear(); + m_ch_scanned.clear(); + m_ch_unavailable.clear(); + m_new_channels.clear(); + m_new_services.clear(); + m_ch_toScan.insert(m_ch_toScan.end(), known_transponders.begin(), known_transponders.end()); + nextChannel(); +} + +void eDVBScan::insertInto(eDVBDB *db) +{ + for (std::map >::const_iterator + ch(m_new_channels.begin()); ch != m_new_channels.end(); ++ch) + db->addChannelToList(ch->first, ch->second); + for (std::map >::const_iterator + service(m_new_services.begin()); service != m_new_services.end(); ++service) + { + ePtr dvb_service; + if (!db->getService(service->first, dvb_service)) + *dvb_service = *service->second; + else + db->addService(service->first, service->second); + } +} + +RESULT eDVBScan::processSDT(eDVBNamespace dvbnamespace, const ServiceDescriptionTable &sdt) +{ + const ServiceDescriptionVector &services = *sdt.getDescriptions(); + eDebug("ONID: %04x", sdt.getOriginalNetworkId()); + eDVBChannelID chid(dvbnamespace, sdt.getTransportStreamId(), sdt.getOriginalNetworkId()); + + for (ServiceDescriptionConstIterator s(services.begin()); s != services.end(); ++s) + { + eDebugNoNewLine("SID %04x: ", (*s)->getServiceId()); + + eServiceReferenceDVB ref; + ePtr service = new eDVBService; + + ref.set(chid); + ref.setServiceID((*s)->getServiceId()); + + for (DescriptorConstIterator desc = (*s)->getDescriptors()->begin(); + desc != (*s)->getDescriptors()->end(); ++desc) + if ((*desc)->getTag() == SERVICE_DESCRIPTOR) + ref.setServiceType(((ServiceDescriptor&)**desc).getServiceType()); + + for (DescriptorConstIterator desc = (*s)->getDescriptors()->begin(); + desc != (*s)->getDescriptors()->end(); ++desc) + { + switch ((*desc)->getTag()) + { + case SERVICE_DESCRIPTOR: + { + ServiceDescriptor &d = (ServiceDescriptor&)**desc; + eDebug("name '%s', provider_name '%s'", d.getServiceName().c_str(), d.getServiceProviderName().c_str()); + service->m_service_name = d.getServiceName(); + service->m_provider_name = d.getServiceProviderName(); + break; + } + case CA_IDENTIFIER_DESCRIPTOR: + { + CaIdentifierDescriptor &d = (CaIdentifierDescriptor&)**desc; + const CaSystemIdVector &caids = *d.getCaSystemIds(); + eDebugNoNewLine("CA "); + for (CaSystemIdVector::const_iterator i(caids.begin()); i != caids.end(); ++i) + { + eDebugNoNewLine("%04x ", *i); + service->m_ca.insert(*i); + } + eDebug(""); + break; + } + default: + eDebug("descr<%x>", (*desc)->getTag()); + break; + } + } + + m_new_services.insert(std::pair >(ref, service)); + } + return 0; +} + +RESULT eDVBScan::connectEvent(const Slot1 &event, ePtr &connection) +{ + connection = new eConnection(m_event.connect(event)); + return 0; +} diff --git a/lib/dvb/scan.h b/lib/dvb/scan.h new file mode 100644 index 0000000..196e52f --- /dev/null +++ b/lib/dvb/scan.h @@ -0,0 +1,69 @@ +#ifndef __lib_dvb_scan_h +#define __lib_dvb_scan_h + +#include +#include +#include +#include + +class eDVBScan: public Object +{ + /* chid helper functions: */ + + /* heuristically determine if onid/tsid is valid */ + int isValidONIDTSID(eOriginalNetworkID onid, eTransportStreamID tsid); + /* build dvb namespace */ + eDVBNamespace buildNamespace(eOriginalNetworkID onid, eTransportStreamID tsid, unsigned long hash); + + /* scan resources */ + ePtr m_channel; + ePtr m_demux; + + /* infrastructure */ + void stateChange(iDVBChannel *); + ePtr m_stateChanged_connection; + + /* state handling */ + RESULT nextChannel(); + + RESULT startFilter(); + enum { readySDT=1, readyNIT=2, readyBAT=4, readyAll=7, + validSDT=8, validNIT=16, validBAT=32}; + + /* scan state variables */ + int m_channel_state; + int m_ready; + + std::map > m_new_channels; + std::map > m_new_services; + + std::list > m_ch_toScan, m_ch_scanned, m_ch_unavailable; + ePtr m_ch_current; + + ePtr > m_SDT; + ePtr > m_NIT; + ePtr > m_BAT; + + void SDTready(int err); + void NITready(int err); + void BATready(int err); + + void addChannel(const eDVBChannelID &chid, iDVBFrontendParameters *feparm); + int sameChannel(iDVBFrontendParameters *ch1, iDVBFrontendParameters *ch2) const; + + void channelDone(); + + Signal1 m_event; + RESULT processSDT(eDVBNamespace dvbnamespace, const ServiceDescriptionTable &sdt); +public: + eDVBScan(iDVBChannel *channel); + ~eDVBScan(); + + void start(const std::list > &known_transponders); + + enum { evtUpdate, evtFinish }; + RESULT connectEvent(const Slot1 &event, ePtr &connection); + void insertInto(eDVBDB *db); +}; + +#endif diff --git a/lib/dvb/sec.cpp b/lib/dvb/sec.cpp new file mode 100644 index 0000000..99e39ea --- /dev/null +++ b/lib/dvb/sec.cpp @@ -0,0 +1,58 @@ +#include +#include +#include + +DEFINE_REF(eDVBSatelliteEquipmentControl); + +RESULT eDVBSatelliteEquipmentControl::prepare(iDVBFrontend &frontend, struct dvb_frontend_parameters &parm, eDVBFrontendParametersSatellite &sat) +{ + int hi; + eDebug("(very) ugly and hardcoded eDVBSatelliteEquipmentControl"); + + if (sat.frequency > 11700000) + hi = 1; + else + hi = 0; + + if (hi) + parm.frequency = sat.frequency - 10600000; + else + parm.frequency = sat.frequency - 9750000; + +// frontend.sentDiseqc(...); + + parm.inversion = sat.inversion ? INVERSION_ON : INVERSION_OFF; + + switch (sat.fec) + { +// case 1: +// case ...: + default: + parm.u.qpsk.fec_inner = FEC_AUTO; + break; + } + parm.u.qpsk.symbol_rate = sat.symbol_rate; + + + frontend.setVoltage((sat.polarisation == eDVBFrontendParametersSatellite::Polarisation::Vertical) ? iDVBFrontend::voltage13 : iDVBFrontend::voltage18); + + eDVBDiseqcCommand diseqc; + + diseqc.len = 4; + diseqc.data[0] = 0xe0; + diseqc.data[1] = 0x10; + diseqc.data[2] = 0x38; + diseqc.data[3] = 0xF0; + + if (hi) + diseqc.data[3] |= 1; + + if (sat.polarisation == eDVBFrontendParametersSatellite::Polarisation::Horizontal) + diseqc.data[3] |= 2; + + frontend.sendDiseqc(diseqc); + frontend.setTone(hi ? iDVBFrontend::toneOn : iDVBFrontend::toneOff); + + return 0; +} + diff --git a/lib/dvb/sec.h b/lib/dvb/sec.h new file mode 100644 index 0000000..e4b2d1d --- /dev/null +++ b/lib/dvb/sec.h @@ -0,0 +1,13 @@ +#ifndef __dvb_sec_h +#define __dvb_sec_h + +#include + +class eDVBSatelliteEquipmentControl: public iDVBSatelliteEquipmentControl +{ +public: + DECLARE_REF; + RESULT prepare(iDVBFrontend &frontend, struct dvb_frontend_parameters &parm, eDVBFrontendParametersSatellite &sat); +}; + +#endif diff --git a/lib/dvb/specs.h b/lib/dvb/specs.h new file mode 100644 index 0000000..3c6908c --- /dev/null +++ b/lib/dvb/specs.h @@ -0,0 +1,108 @@ +#ifndef __lib_dvb_specs_h +#define __lib_dvb_specs_h + +#include +#include +#include +#include +#include +#include +#include + +struct eDVBPMTSpec +{ + eDVBTableSpec m_spec; +public: + eDVBPMTSpec(int pid, int sid) + { + m_spec.pid = pid; + m_spec.tid = ProgramMapTable::TID; + m_spec.tidext = sid; + m_spec.timeout = ProgramMapTable::TIMEOUT; + m_spec.flags = eDVBTableSpec::tfAnyVersion | + eDVBTableSpec::tfHaveTID | eDVBTableSpec::tfHaveTIDExt | + eDVBTableSpec::tfCheckCRC | eDVBTableSpec::tfHaveTimeout; + } + operator eDVBTableSpec &() + { + return m_spec; + } +}; + +struct eDVBSDTSpec +{ + eDVBTableSpec m_spec; +public: + eDVBSDTSpec() + { + m_spec.pid = ServiceDescriptionTable::PID; + m_spec.tid = ServiceDescriptionTable::TID; + m_spec.timeout = ServiceDescriptionTable::TIMEOUT; + m_spec.flags = eDVBTableSpec::tfAnyVersion | + eDVBTableSpec::tfHaveTID | eDVBTableSpec::tfCheckCRC | + eDVBTableSpec::tfHaveTimeout; + } + operator eDVBTableSpec &() + { + return m_spec; + } +}; + +struct eDVBNITSpec +{ + eDVBTableSpec m_spec; +public: + eDVBNITSpec() + { + m_spec.pid = NetworkInformationTable::PID; + m_spec.tid = NetworkInformationTable::TID; + m_spec.timeout = NetworkInformationTable::TIMEOUT; + m_spec.flags = eDVBTableSpec::tfAnyVersion | + eDVBTableSpec::tfHaveTID | eDVBTableSpec::tfCheckCRC | + eDVBTableSpec::tfHaveTimeout; + } + operator eDVBTableSpec &() + { + return m_spec; + } +}; + +struct eDVBBATSpec +{ + eDVBTableSpec m_spec; +public: + eDVBBATSpec() + { + m_spec.pid = BouquetAssociationTable::PID; + m_spec.tid = BouquetAssociationTable::TID; + m_spec.timeout = BouquetAssociationTable::TIMEOUT; + m_spec.flags = eDVBTableSpec::tfAnyVersion | + eDVBTableSpec::tfHaveTID | eDVBTableSpec::tfCheckCRC | + eDVBTableSpec::tfHaveTimeout; + } + operator eDVBTableSpec &() + { + return m_spec; + } +}; + +struct eDVBPATSpec +{ + eDVBTableSpec m_spec; +public: + eDVBPATSpec() + { + m_spec.pid = ProgramAssociationTable::PID; + m_spec.tid = ProgramAssociationTable::TID; + m_spec.timeout = ProgramAssociationTable::TIMEOUT; + m_spec.flags = eDVBTableSpec::tfAnyVersion | + eDVBTableSpec::tfHaveTID | eDVBTableSpec::tfCheckCRC | + eDVBTableSpec::tfHaveTimeout; + } + operator eDVBTableSpec &() + { + return m_spec; + } +}; + +#endif diff --git a/lib/dvb/stT1RVV7 b/lib/dvb/stT1RVV7 new file mode 100644 index 0000000000000000000000000000000000000000..a9d6d3dc47e9ae10e7a41fef91c935494f657a6c GIT binary patch literal 1490944 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

Zsn@>@z{#v2|4~r|6&z9r%_MG6?MEB` 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�yHeS&EfO_)xbQN%PPlz0$bkQ^L?~>Gl z_QZYH=bnljqx6|=LMmzKV!6r{h_G@6L3jcPD_1~&TXg{d-KQgUm)c6*TCQ=l@Z?{S z&N%FAE(3R+4g<;Yt&ena@R`qY2oP=)l0Mo!(!m|O55r?E$!XI%(ix@HiyNiQGNHb5 zBhnd+LjzberJX~&O|QAFg ze&0tQS#^O_@@XNQ=5tl{|CQxY%|o?U>#OuD-E^dL9O15!FF@c3=2Y2nkxajmLHScqy

8BW(5`A=FSA`;U8br< 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%0AvO^Go7roBz1mT(@9 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 + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +Ac3Descriptor::Ac3Descriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + if (buffer[1] >= 1) + { + ac3TypeFlag = (buffer[2] >> 7) & 0x01; + bsidFlag = (buffer[2] >> 6) & 0x01; + mainidFlag = (buffer[2] >> 5) & 0x01; + asvcFlag = (buffer[2] >> 4) & 0x01; + reserved = buffer[2] & 0x0F; + if (ac3TypeFlag == 1) + ac3Type = buffer[3]; + + if (bsidFlag == 1) + bsid = buffer[ac3TypeFlag + 3]; + + if (mainidFlag == 1) + mainid = buffer[ac3TypeFlag + mainidFlag + 3]; + + if (asvcFlag == 1) + avsc = buffer[ac3TypeFlag + bsidFlag + mainidFlag + 3]; + + if (descriptorLength > ac3TypeFlag + bsidFlag + mainidFlag + asvcFlag) + for (uint16_t i = 0; i < descriptorLength - ac3TypeFlag - bsidFlag - mainidFlag - asvcFlag - 1; ++i) + additionalInfo.push_back(buffer[ac3TypeFlag + bsidFlag + mainidFlag + asvcFlag + i + 3]); + } else + { + ac3TypeFlag = 0; + bsidFlag = 0; + mainidFlag = 0; + asvcFlag = 0; + reserved = 0; + } +} + +uint8_t Ac3Descriptor::getAc3TypeFlag(void) const +{ + return ac3TypeFlag; +} + +uint8_t Ac3Descriptor::getBsidFlag(void) const +{ + return bsidFlag; +} + +uint8_t Ac3Descriptor::getMainidFlag(void) const +{ + return mainidFlag; +} + +uint8_t Ac3Descriptor::getAsvcFlag(void) const +{ + return asvcFlag; +} + +uint8_t Ac3Descriptor::getAc3Type(void) const +{ + return ac3Type; +} + +uint8_t Ac3Descriptor::getBsid(void) const +{ + return bsid; +} + +uint8_t Ac3Descriptor::getMainid(void) const +{ + return mainid; +} + +uint8_t Ac3Descriptor::getAvsc(void) const +{ + return avsc; +} + +const AdditionalInfoVector *Ac3Descriptor::getAdditionalInfo(void) const +{ + return &additionalInfo; +} + diff --git a/lib/dvb_si/ac3_descriptor.h b/lib/dvb_si/ac3_descriptor.h new file mode 100644 index 0000000..d5eaf83 --- /dev/null +++ b/lib/dvb_si/ac3_descriptor.h @@ -0,0 +1,59 @@ +/* + * $Id: ac3_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_ac3_descriptor_h__ +#define __dvb_descriptor_ac3_descriptor_h__ + +#include "descriptor.h" + +typedef std::vector AdditionalInfoVector; +typedef AdditionalInfoVector::iterator AdditionalInfoIterator; +typedef AdditionalInfoVector::const_iterator AdditionalInfoConstIterator; + +class Ac3Descriptor : public Descriptor +{ + protected: + unsigned ac3TypeFlag : 1; + unsigned bsidFlag : 1; + unsigned mainidFlag : 1; + unsigned asvcFlag : 1; + unsigned reserved : 4; + unsigned ac3Type : 8; + unsigned bsid : 8; + unsigned mainid : 8; + unsigned avsc : 8; + AdditionalInfoVector additionalInfo; + + public: + Ac3Descriptor(const uint8_t * const buffer); + + uint8_t getAc3TypeFlag(void) const; + uint8_t getBsidFlag(void) const; + uint8_t getMainidFlag(void) const; + uint8_t getAsvcFlag(void) const; + uint8_t getAc3Type(void) const; + uint8_t getBsid(void) const; + uint8_t getMainid(void) const; + uint8_t getAvsc(void) const; + const AdditionalInfoVector *getAdditionalInfo(void) const; +}; + +#endif /* __dvb_descriptor_ac3_descriptor_h__ */ diff --git a/lib/dvb_si/ait.cpp b/lib/dvb_si/ait.cpp new file mode 100644 index 0000000..74e38ca --- /dev/null +++ b/lib/dvb_si/ait.cpp @@ -0,0 +1,94 @@ +/* + * $Id: ait.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +ApplicationIdentifier::ApplicationIdentifier(const uint8_t * const buffer) +{ + organisationId = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; + applicationId = (buffer[4] << 8) | buffer[5]; +} + +uint32_t ApplicationIdentifier::getOrganisationId(void) const +{ + return organisationId; +} + +uint16_t ApplicationIdentifier::getApplicationId(void) const +{ + return applicationId; +} + +ApplicationInformation::ApplicationInformation(const uint8_t * const buffer) +{ + applicationIdentifier = new ApplicationIdentifier(&buffer[0]); + applicationControlCode = buffer[6]; + reserved = (buffer[7] >> 4) & 0x0f; + applicationDescriptorsLoopLength = ((buffer[7] & 0x0f) << 8) | buffer[8]; + + for (uint16_t i = 0; i < applicationDescriptorsLoopLength; i += buffer[i + 10] + 2) + descriptor(&buffer[i + 9]); +} + +ApplicationInformation::~ApplicationInformation(void) +{ + delete applicationIdentifier; +} + +const ApplicationIdentifier *ApplicationInformation::getApplicationIdentifier(void) const +{ + return applicationIdentifier; +} + +uint8_t ApplicationInformation::getApplicationControlCode(void) const +{ + return applicationControlCode; +} + +ApplicationInformationTable::ApplicationInformationTable(const uint8_t * const buffer) : LongCrcTable(buffer) +{ + reserved4 = (buffer[8] >> 4) & 0x0f; + commonDescriptorsLength = ((buffer[8] & 0x0f) << 8) | buffer[9]; + + for (uint16_t i = 0; i < commonDescriptorsLength; i += buffer[i + 11] + 2) + descriptor(&buffer[i + 10]); + + reserved5 = (buffer[commonDescriptorsLength + 10] >> 4) & 0x0f; + applicationLoopLength = ((buffer[commonDescriptorsLength + 10] & 0x0f) << 8) | buffer[commonDescriptorsLength + 11]; + + for (uint16_t i = 0; i < applicationLoopLength; i += 9) { + ApplicationInformation *a = new ApplicationInformation(&buffer[commonDescriptorsLength + 12]); + applicationInformation.push_back(a); + i += a->applicationDescriptorsLoopLength; + } +} + +ApplicationInformationTable::~ApplicationInformationTable(void) +{ + for (ApplicationInformationIterator i = applicationInformation.begin(); i != applicationInformation.end(); ++i) + delete *i; +} + +const ApplicationInformationVector *ApplicationInformationTable::getApplicationInformation(void) const +{ + return &applicationInformation; +} + diff --git a/lib/dvb_si/ait.h b/lib/dvb_si/ait.h new file mode 100644 index 0000000..8dd71f6 --- /dev/null +++ b/lib/dvb_si/ait.h @@ -0,0 +1,86 @@ +/* + * $Id: ait.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_table_ait_h__ +#define __dvb_table_ait_h__ + +#include +#include "long_crc_table.h" + +class ApplicationIdentifier +{ + protected: + unsigned organisationId : 32; + unsigned applicationId : 16; + + public: + ApplicationIdentifier(const uint8_t * const buffer); + + uint32_t getOrganisationId(void) const; + uint16_t getApplicationId(void) const; +}; + +class ApplicationInformation : public DescriptorContainer +{ + protected: + ApplicationIdentifier *applicationIdentifier; + unsigned applicationControlCode : 8; + unsigned reserved : 4; + unsigned applicationDescriptorsLoopLength : 12; + + public: + ApplicationInformation(const uint8_t * const buffer); + ~ApplicationInformation(void); + + const ApplicationIdentifier *getApplicationIdentifier(void) const; + uint8_t getApplicationControlCode(void) const; + + friend class ApplicationInformationTable; +}; + +typedef std::vector ApplicationInformationVector; +typedef ApplicationInformationVector::iterator ApplicationInformationIterator; +typedef ApplicationInformationVector::const_iterator ApplicationInformationConstIterator; + +class ApplicationInformationTable : public LongCrcTable, public DescriptorContainer +{ + protected: + unsigned reserved4 : 4; + unsigned commonDescriptorsLength : 12; + unsigned reserved5 : 4; + unsigned applicationLoopLength : 12; + ApplicationInformationVector applicationInformation; + + public: + ApplicationInformationTable(const uint8_t * const buffer); + ~ApplicationInformationTable(void); + + static const enum TableId TID = TID_AIT; + static const uint32_t TIMEOUT = 12000; + + const ApplicationInformationVector *getApplicationInformation(void) const; +}; + +typedef std::vector ApplicationInformationTableVector; +typedef ApplicationInformationTableVector::iterator ApplicationInformationTableIterator; +typedef ApplicationInformationTableVector::const_iterator ApplicationInformationTableConstIterator; + +#endif /* __dvb_table_ait_h__ */ diff --git a/lib/dvb_si/ancillary_data_descriptor.cpp b/lib/dvb_si/ancillary_data_descriptor.cpp new file mode 100644 index 0000000..33cb85c --- /dev/null +++ b/lib/dvb_si/ancillary_data_descriptor.cpp @@ -0,0 +1,33 @@ +/* + * $Id: ancillary_data_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +AncillaryDataDescriptor::AncillaryDataDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + ancillaryDataIdentifier = buffer[2]; +} + +uint8_t AncillaryDataDescriptor::getAncillaryDataIdentifier(void) const +{ + return ancillaryDataIdentifier; +} + diff --git a/lib/dvb_si/ancillary_data_descriptor.h b/lib/dvb_si/ancillary_data_descriptor.h new file mode 100644 index 0000000..f6ddd11 --- /dev/null +++ b/lib/dvb_si/ancillary_data_descriptor.h @@ -0,0 +1,38 @@ +/* + * $Id: ancillary_data_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_ancillary_data_descriptor_h__ +#define __dvb_descriptor_ancillary_data_descriptor_h__ + +#include "descriptor.h" + +class AncillaryDataDescriptor : public Descriptor +{ + protected: + unsigned ancillaryDataIdentifier : 8; + + public: + AncillaryDataDescriptor(const uint8_t * const buffer); + + uint8_t getAncillaryDataIdentifier(void) const; +}; + +#endif /* __dvb_descriptor_ancillary_data_descriptor_h__ */ diff --git a/lib/dvb_si/announcement_support_descriptor.cpp b/lib/dvb_si/announcement_support_descriptor.cpp new file mode 100644 index 0000000..5cc13f7 --- /dev/null +++ b/lib/dvb_si/announcement_support_descriptor.cpp @@ -0,0 +1,107 @@ +/* + * $Id: announcement_support_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +Announcement::Announcement(const uint8_t * const buffer) +{ + announcementType = (buffer[0] >> 4) & 0x0f; + reserved = (buffer[0] >> 3) & 0x01; + referenceType = buffer[0] & 0x07; + + if ((referenceType >= 0x01) && (referenceType <= 0x03)) { + originalNetworkId = (buffer[1] << 8) | buffer[2]; + transportStreamId = (buffer[3] << 8) | buffer[4]; + serviceId = (buffer[5] << 8) | buffer[6]; + componentTag = buffer[7]; + } +} + +uint8_t Announcement::getAnnouncementType(void) const +{ + return announcementType; +} + +uint8_t Announcement::getReferenceType(void) const +{ + return referenceType; +} + +uint16_t Announcement::getOriginalNetworkId(void) const +{ + return originalNetworkId; +} + +uint16_t Announcement::getTransportStreamId(void) const +{ + return transportStreamId; +} + +uint16_t Announcement::getServiceId(void) const +{ + return serviceId; +} + +uint8_t Announcement::getComponentTag(void) const +{ + return componentTag; +} + +AnnouncementSupportDescriptor::AnnouncementSupportDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + Announcement *a; + + announcementSupportIndicator = (buffer[2] << 8) | buffer[3]; + + if (descriptorLength < 2) + return; + + for (uint16_t i = 0; i < descriptorLength - 2; ++i) { + a = new Announcement(&buffer[i + 4]); + announcements.push_back(a); + switch (a->getReferenceType()) { + case 0x01: + case 0x02: + case 0x03: + i += 7; + break; + default: + break; + } + } +} + +AnnouncementSupportDescriptor::~AnnouncementSupportDescriptor(void) +{ + for (AnnouncementIterator i = announcements.begin(); i != announcements.end(); ++i) + delete *i; +} + +uint16_t AnnouncementSupportDescriptor::getAnnouncementSupportIndicator(void) const +{ + return announcementSupportIndicator; +} + +const AnnouncementVector *AnnouncementSupportDescriptor::getAnnouncements(void) const +{ + return &announcements; +} + diff --git a/lib/dvb_si/announcement_support_descriptor.h b/lib/dvb_si/announcement_support_descriptor.h new file mode 100644 index 0000000..ce80cbe --- /dev/null +++ b/lib/dvb_si/announcement_support_descriptor.h @@ -0,0 +1,67 @@ +/* + * $Id: announcement_support_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_announcement_support_descriptor_h__ +#define __dvb_descriptor_announcement_support_descriptor_h__ + +#include "descriptor.h" + +class Announcement +{ + protected: + unsigned announcementType : 4; + unsigned reserved : 1; + unsigned referenceType : 3; + unsigned originalNetworkId : 16; + unsigned transportStreamId : 16; + unsigned serviceId : 16; + unsigned componentTag : 8; + + public: + Announcement(const uint8_t * const buffer); + + uint8_t getAnnouncementType(void) const; + uint8_t getReferenceType(void) const; + uint16_t getOriginalNetworkId(void) const; + uint16_t getTransportStreamId(void) const; + uint16_t getServiceId(void) const; + uint8_t getComponentTag(void) const; +}; + +typedef std::vector AnnouncementVector; +typedef AnnouncementVector::iterator AnnouncementIterator; +typedef AnnouncementVector::const_iterator AnnouncementConstIterator; + +class AnnouncementSupportDescriptor : public Descriptor +{ + protected: + unsigned announcementSupportIndicator : 16; + AnnouncementVector announcements; + + public: + AnnouncementSupportDescriptor(const uint8_t * const buffer); + ~AnnouncementSupportDescriptor(void); + + uint16_t getAnnouncementSupportIndicator(void) const; + const AnnouncementVector *getAnnouncements(void) const; +}; + +#endif /* __dvb_descriptor_announcement_support_descriptor_h__ */ diff --git a/lib/dvb_si/application_signalling_descriptor.cpp b/lib/dvb_si/application_signalling_descriptor.cpp new file mode 100644 index 0000000..978780f --- /dev/null +++ b/lib/dvb_si/application_signalling_descriptor.cpp @@ -0,0 +1,57 @@ +/* + * $Id: application_signalling_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +ApplicationSignalling::ApplicationSignalling(const uint8_t * const buffer) +{ + applicationType = (buffer[0] << 8) | buffer[1]; + reserved = (buffer[2] >> 5) & 0x07; + aitVersionNumber = buffer[2] & 0x1f; +} + +uint16_t ApplicationSignalling::getApplicationType(void) const +{ + return applicationType; +} + +uint8_t ApplicationSignalling::getAitVersionNumber(void) const +{ + return aitVersionNumber; +} + +ApplicationSignallingDescriptor::ApplicationSignallingDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + for (uint16_t i = 0; i < descriptorLength; i += 3) + applicationSignallings.push_back(new ApplicationSignalling(&buffer[i + 2])); +} + +ApplicationSignallingDescriptor::~ApplicationSignallingDescriptor(void) +{ + for (ApplicationSignallingIterator i = applicationSignallings.begin(); i != applicationSignallings.end(); ++i) + delete *i; +} + +const ApplicationSignallingVector *ApplicationSignallingDescriptor::getApplicationSignallings(void) const +{ + return &applicationSignallings; +} + diff --git a/lib/dvb_si/application_signalling_descriptor.h b/lib/dvb_si/application_signalling_descriptor.h new file mode 100644 index 0000000..c4533e1 --- /dev/null +++ b/lib/dvb_si/application_signalling_descriptor.h @@ -0,0 +1,57 @@ +/* + * $Id: application_signalling_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_application_signalling_descriptor_h__ +#define __dvb_descriptor_application_signalling_descriptor_h__ + +#include "descriptor.h" + +class ApplicationSignalling +{ + protected: + unsigned applicationType : 16; + unsigned reserved : 3; + unsigned aitVersionNumber : 5; + + public: + ApplicationSignalling(const uint8_t * const buffer); + + uint16_t getApplicationType(void) const; + uint8_t getAitVersionNumber(void) const; +}; + +typedef std::vector ApplicationSignallingVector; +typedef ApplicationSignallingVector::iterator ApplicationSignallingIterator; +typedef ApplicationSignallingVector::const_iterator ApplicationSignallingConstIterator; + +class ApplicationSignallingDescriptor : public Descriptor +{ + protected: + ApplicationSignallingVector applicationSignallings; + + public: + ApplicationSignallingDescriptor(const uint8_t * const buffer); + ~ApplicationSignallingDescriptor(void); + + const ApplicationSignallingVector *getApplicationSignallings(void) const; +}; + +#endif /* __dvb_descriptor_application_signalling_descriptor_h__ */ diff --git a/lib/dvb_si/audio_stream_descriptor.cpp b/lib/dvb_si/audio_stream_descriptor.cpp new file mode 100644 index 0000000..6a27af1 --- /dev/null +++ b/lib/dvb_si/audio_stream_descriptor.cpp @@ -0,0 +1,52 @@ +/* + * $Id: audio_stream_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +AudioStreamDescriptor::AudioStreamDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + freeFormatFlag = (buffer[2] >> 7) & 0x01; + id = (buffer[2] >> 6) & 0x01; + layer = (buffer[2] >> 4) & 0x03; + variableRateAudioIndicator = (buffer[2] >> 3) & 0x01; + reserved = buffer[2] & 0x07; +} + +uint8_t AudioStreamDescriptor::getFreeFormatFlag(void) const +{ + return freeFormatFlag; +} + +uint8_t AudioStreamDescriptor::getId(void) const +{ + return id; +} + +uint8_t AudioStreamDescriptor::getLayer(void) const +{ + return layer; +} + +uint8_t AudioStreamDescriptor::getVariableRateAudioIndicator(void) const +{ + return variableRateAudioIndicator; +} + diff --git a/lib/dvb_si/audio_stream_descriptor.h b/lib/dvb_si/audio_stream_descriptor.h new file mode 100644 index 0000000..17876b7 --- /dev/null +++ b/lib/dvb_si/audio_stream_descriptor.h @@ -0,0 +1,45 @@ +/* + * $Id: audio_stream_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_audio_stream_descriptor_h__ +#define __dvb_descriptor_audio_stream_descriptor_h__ + +#include "descriptor.h" + +class AudioStreamDescriptor : public Descriptor +{ + protected: + unsigned freeFormatFlag : 1; + unsigned id : 1; + unsigned layer : 2; + unsigned variableRateAudioIndicator : 1; + unsigned reserved : 3; + + public: + AudioStreamDescriptor(const uint8_t * const buffer); + + uint8_t getFreeFormatFlag(void) const; + uint8_t getId(void) const; + uint8_t getLayer(void) const; + uint8_t getVariableRateAudioIndicator(void) const; +}; + +#endif /* __dvb_descriptor_audio_stream_descriptor_h__ */ diff --git a/lib/dvb_si/bat.cpp b/lib/dvb_si/bat.cpp new file mode 100644 index 0000000..a3763f7 --- /dev/null +++ b/lib/dvb_si/bat.cpp @@ -0,0 +1,55 @@ +/* + * $Id: bat.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +BouquetAssociation::BouquetAssociation(const uint8_t * const buffer) +{ + transportStreamId = (buffer[0] << 8) | buffer[1]; + originalNetworkId = (buffer[2] << 8) | buffer[3]; + reserved = (buffer[4] >> 4) & 0x0f; + transportStreamLoopLength = ((buffer[4] & 0x0f) << 8) | buffer[5]; + + for (uint16_t i = 6; i < transportStreamLoopLength + 6; i += buffer[i + 1] + 2) + descriptor(&buffer[i]); +} + +BouquetAssociationTable::BouquetAssociationTable(const uint8_t * const buffer) : LongCrcTable(buffer) +{ + reserved4 = (buffer[8] >> 4) & 0x0f; + bouquetDescriptorsLength = ((buffer[8] & 0x0f) << 8) | buffer[9]; + + for (uint16_t i = 10; i < bouquetDescriptorsLength + 10; i += buffer[i + 1] + 2) + descriptor(&buffer[i]); + + reserved5 = (buffer[bouquetDescriptorsLength + 10] >> 4) & 0x0f; + transportStreamLoopLength = ((buffer[bouquetDescriptorsLength + 10] & 0x0f) << 8) | buffer[bouquetDescriptorsLength + 11]; + + for (uint16_t i = bouquetDescriptorsLength + 12; i < sectionLength - 1; i += ((buffer[i + 4] & 0x0f) | buffer[i + 5]) + 6) + bouquet.push_back(new BouquetAssociation(&buffer[i])); +} + +BouquetAssociationTable::~BouquetAssociationTable(void) +{ + for (BouquetAssociationIterator b = bouquet.begin(); b != bouquet.end(); ++b) + delete *b; +} + diff --git a/lib/dvb_si/bat.h b/lib/dvb_si/bat.h new file mode 100644 index 0000000..41fd97f --- /dev/null +++ b/lib/dvb_si/bat.h @@ -0,0 +1,66 @@ +/* + * $Id: bat.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_table_bat_h__ +#define __dvb_table_bat_h__ + +#include +#include "long_crc_table.h" + +class BouquetAssociation : public DescriptorContainer +{ + protected: + unsigned transportStreamId : 16; + unsigned originalNetworkId : 16; + unsigned reserved : 4; + unsigned transportStreamLoopLength : 12; + + public: + BouquetAssociation(const uint8_t * const buffer); +}; + +typedef std::vector BouquetAssociationVector; +typedef BouquetAssociationVector::iterator BouquetAssociationIterator; +typedef BouquetAssociationVector::const_iterator BouquetAssociationConstIterator; + +class BouquetAssociationTable : public LongCrcTable , public DescriptorContainer +{ + protected: + unsigned reserved4 : 4; + unsigned bouquetDescriptorsLength : 12; + unsigned reserved5 : 4; + unsigned transportStreamLoopLength : 12; + BouquetAssociationVector bouquet; + + public: + BouquetAssociationTable(const uint8_t * const buffer); + ~BouquetAssociationTable(void); + + static const enum PacketId PID = PID_BAT; + static const enum TableId TID = TID_BAT; + static const uint32_t TIMEOUT = 12000; +}; + +typedef std::vector BouquetAssociationTableVector; +typedef BouquetAssociationTableVector::iterator BouquetAssociationTableIterator; +typedef BouquetAssociationTableVector::const_iterator BouquetAssociationTableConstIterator; + +#endif /* __dvb_table_bat_h__ */ diff --git a/lib/dvb_si/bouquet_name_descriptor.cpp b/lib/dvb_si/bouquet_name_descriptor.cpp new file mode 100644 index 0000000..e11a208 --- /dev/null +++ b/lib/dvb_si/bouquet_name_descriptor.cpp @@ -0,0 +1,33 @@ +/* + * $Id: bouquet_name_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +BouquetNameDescriptor::BouquetNameDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + bouquetName.assign((char *)&buffer[2], descriptorLength); +} + +std::string BouquetNameDescriptor::getBouquetName(void) const +{ + return bouquetName; +} + diff --git a/lib/dvb_si/bouquet_name_descriptor.h b/lib/dvb_si/bouquet_name_descriptor.h new file mode 100644 index 0000000..5f608bc --- /dev/null +++ b/lib/dvb_si/bouquet_name_descriptor.h @@ -0,0 +1,39 @@ +/* + * $Id: bouquet_name_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_bouquet_name_descriptor_h__ +#define __dvb_descriptor_bouquet_name_descriptor_h__ + +#include "descriptor.h" + +class BouquetNameDescriptor : public Descriptor +{ + protected: + std::string bouquetName; + + public: + BouquetNameDescriptor(const uint8_t * const buffer); + ~BouquetNameDescriptor(void); + + std::string getBouquetName(void) const; +}; + +#endif /* __dvb_descriptor_bouquet_name_descriptor_h__ */ diff --git a/lib/dvb_si/ca_descriptor.cpp b/lib/dvb_si/ca_descriptor.cpp new file mode 100644 index 0000000..019855d --- /dev/null +++ b/lib/dvb_si/ca_descriptor.cpp @@ -0,0 +1,50 @@ +/* + * $Id: ca_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +CaDescriptor::CaDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + caSystemId = (buffer[2] << 8) | buffer[3]; + reserved = buffer[4] >> 5; + caPid = ((buffer[4] & 0x1F) << 8) | buffer[5]; + + if (descriptorLength < 4) + return; + for (uint16_t i = 0; i < descriptorLength - 4; ++i) + privateDataBytes.push_back(buffer[i + 6]); +} + +uint16_t CaDescriptor::getCaSystemId(void) const +{ + return caSystemId; +} + +uint16_t CaDescriptor::getCaPid(void) const +{ + return caPid; +} + +const PrivateDataByteVector *CaDescriptor::getPrivateDataBytes(void) const +{ + return &privateDataBytes; +} + diff --git a/lib/dvb_si/ca_descriptor.h b/lib/dvb_si/ca_descriptor.h new file mode 100644 index 0000000..b02add9 --- /dev/null +++ b/lib/dvb_si/ca_descriptor.h @@ -0,0 +1,51 @@ +/* + * $Id: ca_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_ca_descriptor_h__ +#define __dvb_descriptor_ca_descriptor_h__ + +#include "descriptor.h" + +typedef std::vector PrivateDataByteVector; +typedef PrivateDataByteVector::iterator PrivateDataByteIterator; +typedef PrivateDataByteVector::const_iterator PrivateDataByteConstIterator; + +class CaDescriptor : public Descriptor +{ + protected: + unsigned caSystemId : 16; + unsigned reserved : 3; + unsigned caPid : 13; + PrivateDataByteVector privateDataBytes; + + public: + CaDescriptor(const uint8_t * const buffer); + + uint16_t getCaSystemId(void) const; + uint16_t getCaPid(void) const; + const PrivateDataByteVector *getPrivateDataBytes(void) const; +}; + +typedef std::vector CaDescriptorVector; +typedef CaDescriptorVector::iterator CaDescriptorIterator; +typedef CaDescriptorVector::const_iterator CaDescriptorConstIterator; + +#endif /* __dvb_descriptor_ca_descriptor_h__ */ diff --git a/lib/dvb_si/ca_identifier_descriptor.cpp b/lib/dvb_si/ca_identifier_descriptor.cpp new file mode 100644 index 0000000..5804c5d --- /dev/null +++ b/lib/dvb_si/ca_identifier_descriptor.cpp @@ -0,0 +1,34 @@ +/* + * $Id: ca_identifier_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +CaIdentifierDescriptor::CaIdentifierDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + for (uint16_t i = 0; i < descriptorLength; i += 2) + caSystemIds.push_back((buffer[i + 2] << 8) | buffer[i + 3]); +} + +const CaSystemIdVector *CaIdentifierDescriptor::getCaSystemIds(void) const +{ + return &caSystemIds; +} + diff --git a/lib/dvb_si/ca_identifier_descriptor.h b/lib/dvb_si/ca_identifier_descriptor.h new file mode 100644 index 0000000..432d8d5 --- /dev/null +++ b/lib/dvb_si/ca_identifier_descriptor.h @@ -0,0 +1,42 @@ +/* + * $Id: ca_identifier_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_ca_identifier_descriptor_h__ +#define __dvb_descriptor_ca_identifier_descriptor_h__ + +#include "descriptor.h" + +typedef std::vector CaSystemIdVector; +typedef CaSystemIdVector::iterator CaSystemIdIterator; +typedef CaSystemIdVector::const_iterator CaSystemIdConstIterator; + +class CaIdentifierDescriptor : public Descriptor +{ + protected: + CaSystemIdVector caSystemIds; + + public: + CaIdentifierDescriptor(const uint8_t * const buffer); + + const CaSystemIdVector *getCaSystemIds(void) const; +}; + +#endif /* __dvb_descriptor_ca_identifier_descriptor_h__ */ diff --git a/lib/dvb_si/ca_system_descriptor.cpp b/lib/dvb_si/ca_system_descriptor.cpp new file mode 100644 index 0000000..acae51f --- /dev/null +++ b/lib/dvb_si/ca_system_descriptor.cpp @@ -0,0 +1,33 @@ +/* + * $Id: ca_system_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +CaSystemDescriptor::CaSystemDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + scramblingCode = buffer[2]; +} + +uint8_t CaSystemDescriptor::getScramblingCode(void) const +{ + return scramblingCode; +} + diff --git a/lib/dvb_si/ca_system_descriptor.h b/lib/dvb_si/ca_system_descriptor.h new file mode 100644 index 0000000..0149197 --- /dev/null +++ b/lib/dvb_si/ca_system_descriptor.h @@ -0,0 +1,38 @@ +/* + * $Id: ca_system_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_ca_system_descriptor_h__ +#define __dvb_descriptor_ca_system_descriptor_h__ + +#include "descriptor.h" + +class CaSystemDescriptor : public Descriptor +{ + protected: + unsigned scramblingCode : 8; + + public: + CaSystemDescriptor(const uint8_t * const buffer); + + uint8_t getScramblingCode(void) const; +}; + +#endif /* __dvb_descriptor_ca_system_descriptor_h__ */ diff --git a/lib/dvb_si/cable_delivery_system_descriptor.cpp b/lib/dvb_si/cable_delivery_system_descriptor.cpp new file mode 100644 index 0000000..4d07080 --- /dev/null +++ b/lib/dvb_si/cable_delivery_system_descriptor.cpp @@ -0,0 +1,80 @@ +/* + * $Id: cable_delivery_system_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +CableDeliverySystemDescriptor::CableDeliverySystemDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + frequency = + ( + ((buffer[2] >> 4) * 10000000) + + ((buffer[2] & 0x0F) * 1000000) + + ((buffer[3] >> 4) * 100000) + + ((buffer[3] & 0x0F) * 10000) + + ((buffer[4] >> 4) * 1000) + + ((buffer[4] & 0x0F) * 100) + + ((buffer[5] >> 4) * 10) + + ((buffer[5] & 0x0F) * 1) + ); + + reserved = (buffer[6] << 4) | ((buffer[7] >> 4) & 0x0F); + fecOuter = buffer[7] & 0x0F; + modulation = buffer[8]; + + symbolRate = + ( + ((buffer[9] >> 4) * 1000000) + + ((buffer[9] & 0x0F) * 100000) + + ((buffer[10] >> 4) * 10000) + + ((buffer[10] & 0x0F) * 1000) + + ((buffer[11] >> 4) * 100) + + ((buffer[11] & 0x0F) * 10) + + ((buffer[12] >> 4) * 1) + ); + + fecInner = buffer[12] & 0x0F; +} + +uint32_t CableDeliverySystemDescriptor::getFrequency(void) const +{ + return frequency; +} + +uint8_t CableDeliverySystemDescriptor::getFecOuter(void) const +{ + return fecOuter; +} + +uint8_t CableDeliverySystemDescriptor::getModulation(void) const +{ + return modulation; +} + +uint32_t CableDeliverySystemDescriptor::getSymbolRate(void) const +{ + return symbolRate; +} + +uint8_t CableDeliverySystemDescriptor::getFecInner(void) const +{ + return fecInner; +} + diff --git a/lib/dvb_si/cable_delivery_system_descriptor.h b/lib/dvb_si/cable_delivery_system_descriptor.h new file mode 100644 index 0000000..c063222 --- /dev/null +++ b/lib/dvb_si/cable_delivery_system_descriptor.h @@ -0,0 +1,47 @@ +/* + * $Id: cable_delivery_system_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_cable_delivery_system_descriptor_h__ +#define __dvb_descriptor_cable_delivery_system_descriptor_h__ + +#include "descriptor.h" + +class CableDeliverySystemDescriptor : public Descriptor +{ + protected: + unsigned frequency : 32; + unsigned reserved : 12; + unsigned fecOuter : 4; + unsigned modulation : 8; + unsigned symbolRate : 28; + unsigned fecInner : 4; + + public: + CableDeliverySystemDescriptor(const uint8_t * const buffer); + + uint32_t getFrequency(void) const; + uint8_t getFecOuter(void) const; + uint8_t getModulation(void) const; + uint32_t getSymbolRate(void) const; + uint8_t getFecInner(void) const; +}; + +#endif /* __dvb_descriptor_cable_delivery_system_descriptor_h__ */ diff --git a/lib/dvb_si/camt.cpp b/lib/dvb_si/camt.cpp new file mode 100644 index 0000000..7383a21 --- /dev/null +++ b/lib/dvb_si/camt.cpp @@ -0,0 +1,29 @@ +/* + * $Id: camt.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +ConditionalAccessMessageTable::ConditionalAccessMessageTable(const uint8_t * const buffer) : ShortTable(buffer) +{ + for (uint16_t i = 8; i < sectionLength - 1; ++i) + caDataByte.push_back(buffer[i]); +} + diff --git a/lib/dvb_si/camt.h b/lib/dvb_si/camt.h new file mode 100644 index 0000000..e01b806 --- /dev/null +++ b/lib/dvb_si/camt.h @@ -0,0 +1,43 @@ +/* + * $Id: camt.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_table_camt_h__ +#define __dvb_table_camt_h__ + +#include "short_table.h" + +class ConditionalAccessMessageTable : public ShortTable +{ + protected: + std::vector caDataByte; + + public: + ConditionalAccessMessageTable(const uint8_t * const buffer); + + static const uint16_t LENGTH = 256; + static const enum TableId TID = TID_CAMT_ECM_0; +}; + +typedef std::vector ConditionalAccessMessageTableVector; +typedef ConditionalAccessMessageTableVector::iterator ConditionalAccessMessageTableIterator; +typedef ConditionalAccessMessageTableVector::const_iterator ConditionalAccessMessageTableConstIterator; + +#endif /* __dvb_table_camt_h__ */ diff --git a/lib/dvb_si/capmt.cpp b/lib/dvb_si/capmt.cpp new file mode 100644 index 0000000..c5a6ab9 --- /dev/null +++ b/lib/dvb_si/capmt.cpp @@ -0,0 +1,124 @@ +/* + * $Id: capmt.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +CaLengthField::CaLengthField(const uint64_t length) +{ + if (length < 0x80) { + sizeIndicator = 0; + lengthValue = length; + } + + else { + uint64_t mask = 0xFF; + + sizeIndicator = 1; + lengthFieldSize = 1; + + while ((length & mask) != length) { + lengthFieldSize++; + mask = ((uint64_t)(mask << 8)) | ((uint64_t)0xFFULL); + } + + for (uint8_t i = lengthFieldSize; i > 0; i--) + lengthValueByte.push_back((length >> ((i - 1) << 3)) & 0xFF); + } +} + +CaElementaryStreamInfo::CaElementaryStreamInfo(const ElementaryStreamInfo * const info, const uint8_t cmdId) +{ + streamType = info->streamType; + reserved1 = info->reserved1; + elementaryPid = info->elementaryPid; + reserved2 = info->reserved2; + esInfoLength = 0; + + for (DescriptorConstIterator i = info->getDescriptors()->begin(); i != info->getDescriptors()->end(); ++i) + if ((*i)->getTag() == CA_DESCRIPTOR) { + descriptors.push_back(new CaDescriptor(*(CaDescriptor *)*i)); + esInfoLength += (*i)->getLength() + 2; + } + + if (esInfoLength) { + caPmtCmdId = cmdId; + esInfoLength++; + } +} + +CaElementaryStreamInfo::~CaElementaryStreamInfo(void) +{ + for (CaDescriptorIterator i = descriptors.begin(); i != descriptors.end(); ++i) + delete *i; +} + +uint16_t CaElementaryStreamInfo::getLength(void) const +{ + return esInfoLength + 5; +} + +CaProgramMapTable::CaProgramMapTable(const ProgramMapTable * const pmt, const uint8_t listManagement, const uint8_t cmdId) +{ + uint64_t length = 6; + + caPmtTag = 0x9F80C3; + caPmtListManagement = listManagement; + + programNumber = pmt->tableIdExtension; + reserved1 = pmt->reserved3; + versionNumber = pmt->versionNumber; + currentNextIndicator = pmt->currentNextIndicator; + reserved2 = pmt->reserved5; + programInfoLength = 0; + + for (DescriptorConstIterator i = pmt->getDescriptors()->begin(); i != pmt->getDescriptors()->end(); ++i) + if ((*i)->getTag() == CA_DESCRIPTOR) { + descriptors.push_back(new CaDescriptor(*(CaDescriptor *)*i)); + programInfoLength += (*i)->getLength() + 2; + } + + if (programInfoLength) { + caPmtCmdId = cmdId; + programInfoLength++; + length += programInfoLength; + } + + for (ElementaryStreamInfoConstIterator i = pmt->esInfo.begin(); i != pmt->esInfo.end(); ++i) { + CaElementaryStreamInfo *info = new CaElementaryStreamInfo(*i, cmdId); + esInfo.push_back(info); + length += info->getLength(); + } + + lengthField = new CaLengthField(length); +} + +CaProgramMapTable::~CaProgramMapTable(void) +{ + for (CaDescriptorIterator i = descriptors.begin(); i != descriptors.end(); ++i) + delete *i; + + for (CaElementaryStreamInfoIterator i = esInfo.begin(); i != esInfo.end(); ++i) + delete *i; + + delete lengthField; +} + diff --git a/lib/dvb_si/capmt.h b/lib/dvb_si/capmt.h new file mode 100644 index 0000000..6d8dc43 --- /dev/null +++ b/lib/dvb_si/capmt.h @@ -0,0 +1,87 @@ +/* + * $Id: capmt.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_table_capmt_h__ +#define __dvb_table_capmt_h__ + +#include +#include "pmt.h" + +class CaLengthField +{ + protected: + unsigned sizeIndicator : 1; + unsigned lengthValue : 7; + unsigned lengthFieldSize : 7; + std::vector lengthValueByte; + + public: + CaLengthField(const uint64_t length); +}; + +class CaElementaryStreamInfo +{ + protected: + unsigned streamType : 8; + unsigned reserved1 : 3; + unsigned elementaryPid : 13; + unsigned reserved2 : 4; + unsigned esInfoLength : 12; + unsigned caPmtCmdId : 8; + CaDescriptorVector descriptors; + + public: + CaElementaryStreamInfo(const ElementaryStreamInfo * const info, const uint8_t cmdId); + ~CaElementaryStreamInfo(void); + + uint16_t getLength(void) const; +}; + +typedef std::vector CaElementaryStreamInfoVector; +typedef CaElementaryStreamInfoVector::iterator CaElementaryStreamInfoIterator; +typedef CaElementaryStreamInfoVector::const_iterator CaElementaryStreamInfoConstIterator; + +class CaProgramMapTable +{ + protected: + unsigned caPmtTag : 24; + CaLengthField *lengthField; + unsigned caPmtListManagement : 8; + unsigned programNumber : 16; + unsigned reserved1 : 2; + unsigned versionNumber : 5; + unsigned currentNextIndicator : 1; + unsigned reserved2 : 4; + unsigned programInfoLength : 12; + unsigned caPmtCmdId : 8; + CaDescriptorVector descriptors; + CaElementaryStreamInfoVector esInfo; + + public: + CaProgramMapTable(const ProgramMapTable * const pmt, const uint8_t listManagement, const uint8_t cmdId); + ~CaProgramMapTable(void); +}; + +typedef std::vector CaProgramMapTableVector; +typedef CaProgramMapTableVector::iterator CaProgramMapTableIterator; +typedef CaProgramMapTableVector::const_iterator CaProgramMapTableConstIterator; + +#endif /* __dvb_table_capmt_h__ */ diff --git a/lib/dvb_si/cat.cpp b/lib/dvb_si/cat.cpp new file mode 100644 index 0000000..de69230 --- /dev/null +++ b/lib/dvb_si/cat.cpp @@ -0,0 +1,29 @@ +/* + * $Id: cat.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +ConditionalAccessTable::ConditionalAccessTable(const uint8_t * const buffer) : LongCrcTable(buffer) +{ + for (uint16_t i = 8; i < sectionLength - 1; i += buffer[i + 1] + 2) + descriptor(&buffer[i]); +} + diff --git a/lib/dvb_si/cat.h b/lib/dvb_si/cat.h new file mode 100644 index 0000000..3c3e3cd --- /dev/null +++ b/lib/dvb_si/cat.h @@ -0,0 +1,42 @@ +/* + * $Id: cat.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_table_cat_h__ +#define __dvb_table_cat_h__ + +#include +#include "long_crc_table.h" + +class ConditionalAccessTable : public LongCrcTable, public DescriptorContainer +{ + public: + ConditionalAccessTable(const uint8_t * const buffer); + + static const enum PacketId PID = PID_CAT; + static const enum TableId TID = TID_CAT; + static const uint32_t TIMEOUT = 200; +}; + +typedef std::vector ConditionalAccessTableVector; +typedef ConditionalAccessTableVector::iterator ConditionalAccessTableIterator; +typedef ConditionalAccessTableVector::const_iterator ConditionalAccessTableConstIterator; + +#endif /* __dvb_table_cat_h__ */ diff --git a/lib/dvb_si/cell_frequency_link_descriptor.cpp b/lib/dvb_si/cell_frequency_link_descriptor.cpp new file mode 100644 index 0000000..f4c4451 --- /dev/null +++ b/lib/dvb_si/cell_frequency_link_descriptor.cpp @@ -0,0 +1,87 @@ +/* + * $Id: cell_frequency_link_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +SubcellInfo::SubcellInfo(const uint8_t * const buffer) +{ + cellIdExtenstion = buffer[0]; + transposerFrequency = (buffer[1] << 24) | (buffer[2] << 16) | (buffer[3] << 8) | buffer[4]; +} + +uint8_t SubcellInfo::getCellIdExtension(void) const +{ + return cellIdExtenstion; +} + +uint32_t SubcellInfo::getTransposerFrequency(void) const +{ + return transposerFrequency; +} + +CellFrequencyLink::CellFrequencyLink(const uint8_t * const buffer) +{ + cellId = (buffer[0] << 8) | buffer[1]; + frequency = (buffer[2] << 24) | (buffer[3] << 16) | (buffer[4] << 8) | buffer[5]; + subcellInfoLoopLength = buffer[6]; + + for (uint16_t i = 0; i < subcellInfoLoopLength; i += 5) + subcells.push_back(new SubcellInfo(&buffer[i + 7])); +} + +CellFrequencyLink::~CellFrequencyLink(void) +{ + for (SubcellInfoIterator i = subcells.begin(); i != subcells.end(); ++i) + delete *i; +} + +uint16_t CellFrequencyLink::getCellId(void) const +{ + return cellId; +} + +uint32_t CellFrequencyLink::getFrequency(void) const +{ + return frequency; +} + +const SubcellInfoVector *CellFrequencyLink::getSubcells(void) const +{ + return &subcells; +} + +CellFrequencyLinkDescriptor::CellFrequencyLinkDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + for (uint16_t i = 0; i < descriptorLength; i += buffer[i + 10] + 6) + cellFrequencyLinks.push_back(new CellFrequencyLink(&buffer[i + 2])); +} + +CellFrequencyLinkDescriptor::~CellFrequencyLinkDescriptor(void) +{ + for (CellFrequencyLinkIterator i = cellFrequencyLinks.begin(); i != cellFrequencyLinks.end(); ++i) + delete *i; +} + +const CellFrequencyLinkVector *CellFrequencyLinkDescriptor::getCellFrequencyLinks(void) const +{ + return &cellFrequencyLinks; +} + diff --git a/lib/dvb_si/cell_frequency_link_descriptor.h b/lib/dvb_si/cell_frequency_link_descriptor.h new file mode 100644 index 0000000..29395eb --- /dev/null +++ b/lib/dvb_si/cell_frequency_link_descriptor.h @@ -0,0 +1,78 @@ +/* + * $Id: cell_frequency_link_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_cell_frequency_link_descriptor_h__ +#define __dvb_descriptor_cell_frequency_link_descriptor_h__ + +#include "descriptor.h" + +class SubcellInfo +{ + protected: + unsigned cellIdExtenstion : 8; + unsigned transposerFrequency : 32; + + public: + SubcellInfo(const uint8_t * const buffer); + + uint8_t getCellIdExtension(void) const; + uint32_t getTransposerFrequency(void) const; +}; + +typedef std::vector SubcellInfoVector; +typedef SubcellInfoVector::iterator SubcellInfoIterator; +typedef SubcellInfoVector::const_iterator SubcellInfoConstIterator; + +class CellFrequencyLink +{ + protected: + unsigned cellId : 16; + unsigned frequency : 32; + unsigned subcellInfoLoopLength : 8; + SubcellInfoVector subcells; + + public: + CellFrequencyLink(const uint8_t * const buffer); + ~CellFrequencyLink(void); + + uint16_t getCellId(void) const; + uint32_t getFrequency(void) const; + const SubcellInfoVector *getSubcells(void) const; + +}; + +typedef std::vector CellFrequencyLinkVector; +typedef CellFrequencyLinkVector::iterator CellFrequencyLinkIterator; +typedef CellFrequencyLinkVector::const_iterator CellFrequencyLinkConstIterator; + +class CellFrequencyLinkDescriptor : public Descriptor +{ + protected: + CellFrequencyLinkVector cellFrequencyLinks; + + public: + CellFrequencyLinkDescriptor(const uint8_t * const buffer); + ~CellFrequencyLinkDescriptor(void); + + const CellFrequencyLinkVector *getCellFrequencyLinks(void) const; +}; + +#endif /* __dvb_descriptor_cell_frequency_link_descriptor_h__ */ diff --git a/lib/dvb_si/cell_list_descriptor.cpp b/lib/dvb_si/cell_list_descriptor.cpp new file mode 100644 index 0000000..c8588ae --- /dev/null +++ b/lib/dvb_si/cell_list_descriptor.cpp @@ -0,0 +1,124 @@ +/* + * $Id: cell_list_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include + +Subcell::Subcell(const uint8_t * const buffer) +{ + cellIdExtension = buffer[0]; + subcellLatitude = (buffer[1] << 8) | buffer[2]; + subcellLongitude = (buffer[3] << 8) | buffer[4]; + subcellExtendOfLatitude = (buffer[5] << 4) | ((buffer[6] >> 4) & 0x0f); + subcellExtendOfLongitude = ((buffer[6] & 0x0f) << 8) | buffer[7]; +} + +uint8_t Subcell::getCellIdExtension(void) const +{ + return cellIdExtension; +} + +uint16_t Subcell::getSubcellLatitude(void) const +{ + return subcellLatitude; +} + +uint16_t Subcell::getSubcellLongtitude(void) const +{ + return subcellLongitude; +} + +uint16_t Subcell::getSubcellExtendOfLatitude(void) const +{ + return subcellExtendOfLatitude; +} + +uint16_t Subcell::getSubcellExtendOfLongtitude(void) const +{ + return subcellExtendOfLongitude; +} + +Cell::Cell(const uint8_t * const buffer) +{ + cellId = (buffer[0] << 8) | buffer[1]; + cellLatitude = (buffer[2] << 8) | buffer[3]; + cellLongtitude = (buffer[4] << 8) | buffer[5]; + cellExtendOfLatitude = (buffer[6] << 4) | ((buffer[7] >> 4) & 0x0f); + cellExtendOfLongtitude = ((buffer[7] & 0x0f) << 8) | buffer[8]; + subcellInfoLoopLength = buffer[9]; + + for (uint16_t i = 0; i < subcellInfoLoopLength; i += 8) + subcells.push_back(new Subcell(&buffer[i + 10])); +} + +Cell::~Cell(void) +{ + for (SubcellIterator i = subcells.begin(); i != subcells.end(); ++i) + delete *i; +} + +uint16_t Cell::getCellId(void) const +{ + return cellId; +} + +uint16_t Cell::getCellLatitude(void) const +{ + return cellLatitude; +} + +uint16_t Cell::getCellLongtitude(void) const +{ + return cellLongtitude; +} + +uint16_t Cell::getCellExtendOfLatitude(void) const +{ + return cellExtendOfLatitude; +} + +uint16_t Cell::getCellExtendOfLongtitude(void) const +{ + return cellExtendOfLongtitude; +} + +const SubcellVector *Cell::getSubcells(void) const +{ + return &subcells; +} + +CellListDescriptor::CellListDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + for (uint16_t i = 0; i < descriptorLength; i += buffer[i + 11] + 10) + cells.push_back(new Cell(&buffer[i + 2])); +} + +CellListDescriptor::~CellListDescriptor(void) +{ + for (CellIterator i = cells.begin(); i != cells.end(); ++i) + delete *i; +} + +const CellVector *CellListDescriptor::getCells(void) const +{ + return &cells; +} + diff --git a/lib/dvb_si/cell_list_descriptor.h b/lib/dvb_si/cell_list_descriptor.h new file mode 100644 index 0000000..cfe93d9 --- /dev/null +++ b/lib/dvb_si/cell_list_descriptor.h @@ -0,0 +1,89 @@ +/* + * $Id: cell_list_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_cell_list_descriptor_h__ +#define __dvb_descriptor_cell_list_descriptor_h__ + +#include "descriptor.h" + +class Subcell +{ + protected: + unsigned cellIdExtension : 8; + unsigned subcellLatitude : 16; + unsigned subcellLongitude : 16; + unsigned subcellExtendOfLatitude : 12; + unsigned subcellExtendOfLongitude : 12; + + public: + Subcell(const uint8_t * const buffer); + + uint8_t getCellIdExtension(void) const; + uint16_t getSubcellLatitude(void) const; + uint16_t getSubcellLongtitude(void) const; + uint16_t getSubcellExtendOfLatitude(void) const; + uint16_t getSubcellExtendOfLongtitude(void) const; +}; + +typedef std::vector SubcellVector; +typedef SubcellVector::iterator SubcellIterator; +typedef SubcellVector::const_iterator SubcellConstIterator; + +class Cell +{ + protected: + unsigned cellId : 16; + unsigned cellLatitude : 16; + unsigned cellLongtitude : 16; + unsigned cellExtendOfLatitude : 12; + unsigned cellExtendOfLongtitude : 12; + unsigned subcellInfoLoopLength : 8; + SubcellVector subcells; + + public: + Cell(const uint8_t * const buffer); + ~Cell(void); + + uint16_t getCellId(void) const; + uint16_t getCellLatitude(void) const; + uint16_t getCellLongtitude(void) const; + uint16_t getCellExtendOfLatitude(void) const; + uint16_t getCellExtendOfLongtitude(void) const; + const SubcellVector *getSubcells(void) const; +}; + +typedef std::vector CellVector; +typedef CellVector::iterator CellIterator; +typedef CellVector::const_iterator CellConstIterator; + +class CellListDescriptor : public Descriptor +{ + protected: + CellVector cells; + + public: + CellListDescriptor(const uint8_t * const buffer); + ~CellListDescriptor(void); + + const CellVector *getCells(void) const; +}; + +#endif /* __dvb_descriptor_cell_list_descriptor_h__ */ diff --git a/lib/dvb_si/component_descriptor.cpp b/lib/dvb_si/component_descriptor.cpp new file mode 100644 index 0000000..8b043e1 --- /dev/null +++ b/lib/dvb_si/component_descriptor.cpp @@ -0,0 +1,60 @@ +/* + * $Id: component_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +ComponentDescriptor::ComponentDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + reserved = (buffer[2] >> 4) & 0x0f; + streamContent = buffer[2] & 0x0f; + componentType = buffer[3]; + componentTag = buffer[4]; + iso639LanguageCode.assign((char *) &buffer[5], 3); + if (descriptorLength < 6) + return; + text.assign((char *) &buffer[8], descriptorLength - 6); +} + +uint8_t ComponentDescriptor::getStreamContent(void) const +{ + return streamContent; +} + +uint8_t ComponentDescriptor::getComponentType(void) const +{ + return componentType; +} + +uint8_t ComponentDescriptor::getComponentTag(void) const +{ + return componentTag; +} + +std::string ComponentDescriptor::getIso639LanguageCode(void) const +{ + return iso639LanguageCode; +} + +std::string ComponentDescriptor::getText(void) const +{ + return text; +} + diff --git a/lib/dvb_si/component_descriptor.h b/lib/dvb_si/component_descriptor.h new file mode 100644 index 0000000..c910ad4 --- /dev/null +++ b/lib/dvb_si/component_descriptor.h @@ -0,0 +1,47 @@ +/* + * $Id: component_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_component_descriptor_h__ +#define __dvb_descriptor_component_descriptor_h__ + +#include "descriptor.h" + +class ComponentDescriptor : public Descriptor +{ + protected: + unsigned reserved : 4; + unsigned streamContent : 4; + unsigned componentType : 8; + unsigned componentTag : 8; + std::string iso639LanguageCode; + std::string text; + + public: + ComponentDescriptor(const uint8_t * const buffer); + + uint8_t getStreamContent(void) const; + uint8_t getComponentType(void) const; + uint8_t getComponentTag(void) const; + std::string getIso639LanguageCode(void) const; + std::string getText(void) const; +}; + +#endif /* __dvb_descriptor_component_descriptor_h__ */ diff --git a/lib/dvb_si/container.cpp b/lib/dvb_si/container.cpp new file mode 100644 index 0000000..1c14c2f --- /dev/null +++ b/lib/dvb_si/container.cpp @@ -0,0 +1,289 @@ +/* + * $Id: container.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DescriptorContainer::~DescriptorContainer(void) +{ + for (DescriptorIterator i = descriptorVector.begin(); i != descriptorVector.end(); ++i) + delete *i; +} + +void DescriptorContainer::descriptor(const uint8_t * const buffer) +{ + switch (buffer[0]) { + case VIDEO_STREAM_DESCRIPTOR: + descriptorVector.push_back(new VideoStreamDescriptor(buffer)); + break; + + case AUDIO_STREAM_DESCRIPTOR: + descriptorVector.push_back(new AudioStreamDescriptor(buffer)); + break; + + case TARGET_BACKGROUND_GRID_DESCRIPTOR: + descriptorVector.push_back(new TargetBackgroundGridDescriptor(buffer)); + break; + + case VIDEO_WINDOW_DESCRIPTOR: + descriptorVector.push_back(new VideoWindowDescriptor(buffer)); + break; + + case CA_DESCRIPTOR: + descriptorVector.push_back(new CaDescriptor(buffer)); + break; + + case ISO_639_LANGUAGE_DESCRIPTOR: + descriptorVector.push_back(new Iso639LanguageDescriptor(buffer)); + break; + + case NETWORK_NAME_DESCRIPTOR: + descriptorVector.push_back(new NetworkNameDescriptor(buffer)); + break; + + case SERVICE_LIST_DESCRIPTOR: + descriptorVector.push_back(new ServiceListDescriptor(buffer)); + break; + + case STUFFING_DESCRIPTOR: + descriptorVector.push_back(new StuffingDescriptor(buffer)); + break; + + case SATELLITE_DELIVERY_SYSTEM_DESCRIPTOR: + descriptorVector.push_back(new SatelliteDeliverySystemDescriptor(buffer)); + break; + + case CABLE_DELIVERY_SYSTEM_DESCRIPTOR: + descriptorVector.push_back(new CableDeliverySystemDescriptor(buffer)); + break; + + case VBI_DATA_DESCRIPTOR: + descriptorVector.push_back(new VbiDataDescriptor(buffer)); + break; + + case VBI_TELETEXT_DESCRIPTOR: + descriptorVector.push_back(new VbiTeletextDescriptor(buffer)); + break; + + case BOUQUET_NAME_DESCRIPTOR: + descriptorVector.push_back(new BouquetNameDescriptor(buffer)); + break; + + case SERVICE_DESCRIPTOR: + descriptorVector.push_back(new ServiceDescriptor(buffer)); + break; + + case COUNTRY_AVAILABILITY_DESCRIPTOR: + descriptorVector.push_back(new CountryAvailabilityDescriptor(buffer)); + break; + + case LINKAGE_DESCRIPTOR: + descriptorVector.push_back(new LinkageDescriptor(buffer)); + break; + + case NVOD_REFERENCE_DESCRIPTOR: + descriptorVector.push_back(new NvodReferenceDescriptor(buffer)); + break; + + case TIME_SHIFTED_SERVICE_DESCRIPTOR: + descriptorVector.push_back(new TimeShiftedServiceDescriptor(buffer)); + break; + + case SHORT_EVENT_DESCRIPTOR: + descriptorVector.push_back(new ShortEventDescriptor(buffer)); + break; + + case EXTENDED_EVENT_DESCRIPTOR: + descriptorVector.push_back(new ExtendedEventDescriptor(buffer)); + break; + + case COMPONENT_DESCRIPTOR: + descriptorVector.push_back(new ComponentDescriptor(buffer)); + break; + + case MOSAIC_DESCRIPTOR: + descriptorVector.push_back(new MosaicDescriptor(buffer)); + break; + + case STREAM_IDENTIFIER_DESCRIPTOR: + descriptorVector.push_back(new StreamIdentifierDescriptor(buffer)); + break; + + case CA_IDENTIFIER_DESCRIPTOR: + descriptorVector.push_back(new CaIdentifierDescriptor(buffer)); + break; + + case CONTENT_DESCRIPTOR: + descriptorVector.push_back(new ContentDescriptor(buffer)); + break; + + case PARENTAL_RATING_DESCRIPTOR: + descriptorVector.push_back(new ParentalRatingDescriptor(buffer)); + break; + + case TELETEXT_DESCRIPTOR: + descriptorVector.push_back(new TeletextDescriptor(buffer)); + break; + + case TELEPHONE_DESCRIPTOR: + descriptorVector.push_back(new TelephoneDescriptor(buffer)); + break; + + case LOCAL_TIME_OFFSET_DESCRIPTOR: + descriptorVector.push_back(new LocalTimeOffsetDescriptor(buffer)); + break; + + case SUBTITLING_DESCRIPTOR: + descriptorVector.push_back(new SubtitlingDescriptor(buffer)); + break; + + case TERRESTRIAL_DELIVERY_SYSTEM_DESCRIPTOR: + descriptorVector.push_back(new TerrestrialDeliverySystemDescriptor(buffer)); + break; + + case MULTILINGUAL_NETWORK_NAME_DESCRIPTOR: + descriptorVector.push_back(new MultilingualNetworkNameDescriptor(buffer)); + break; + + case MULTILINGUAL_BOUQUET_NAME_DESCRIPTOR: + descriptorVector.push_back(new MultilingualBouquetNameDescriptor(buffer)); + break; + + case MULTILINGUAL_SERVICE_NAME_DESCRIPTOR: + descriptorVector.push_back(new MultilingualServiceNameDescriptor(buffer)); + break; + + case MULTILINGUAL_COMPONENT_DESCRIPTOR: + descriptorVector.push_back(new MultilingualComponentDescriptor(buffer)); + break; + + case PRIVATE_DATA_SPECIFIER_DESCRIPTOR: + descriptorVector.push_back(new PrivateDataSpecifierDescriptor(buffer)); + break; + + case SERVICE_MOVE_DESCRIPTOR: + descriptorVector.push_back(new ServiceMoveDescriptor(buffer)); + break; + + case FREQUENCY_LIST_DESCRIPTOR: + descriptorVector.push_back(new FrequencyListDescriptor(buffer)); + break; + + case DATA_BROADCAST_DESCRIPTOR: + descriptorVector.push_back(new DataBroadcastDescriptor(buffer)); + break; + + case CA_SYSTEM_DESCRIPTOR: + descriptorVector.push_back(new CaSystemDescriptor(buffer)); + break; + + case DATA_BROADCAST_ID_DESCRIPTOR: + descriptorVector.push_back(new DataBroadcastIdDescriptor(buffer)); + break; + + case PDC_DESCRIPTOR: + descriptorVector.push_back(new PdcDescriptor(buffer)); + break; + + case AC3_DESCRIPTOR: + descriptorVector.push_back(new Ac3Descriptor(buffer)); + break; + + case ANCILLARY_DATA_DESCRIPTOR: + descriptorVector.push_back(new AncillaryDataDescriptor(buffer)); + break; + + case CELL_LIST_DESCRIPTOR: + descriptorVector.push_back(new CellListDescriptor(buffer)); + break; + + case CELL_FREQUENCY_LINK_DESCRIPTOR: + descriptorVector.push_back(new CellFrequencyLinkDescriptor(buffer)); + break; + + case ANNOUNCEMENT_SUPPORT_DESCRIPTOR: + descriptorVector.push_back(new AnnouncementSupportDescriptor(buffer)); + break; + + case APPLICATION_SIGNALLING_DESCRIPTOR: + descriptorVector.push_back(new ApplicationSignallingDescriptor(buffer)); + break; + + default: + descriptorVector.push_back(new Descriptor(buffer)); + break; + } +} + +const DescriptorVector *DescriptorContainer::getDescriptors(void) const +{ + return &descriptorVector; +} + diff --git a/lib/dvb_si/container.h b/lib/dvb_si/container.h new file mode 100644 index 0000000..9d1d5e3 --- /dev/null +++ b/lib/dvb_si/container.h @@ -0,0 +1,39 @@ +/* + * $Id: container.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_container_h__ +#define __dvb_descriptor_container_h__ + +#include "descriptor.h" + +class DescriptorContainer +{ + protected: + void descriptor(const uint8_t * const buffer); + DescriptorVector descriptorVector; + + public: + ~DescriptorContainer(void); + + const DescriptorVector *getDescriptors(void) const; +}; + +#endif /* __dvb_descriptor_container_h__ */ diff --git a/lib/dvb_si/content_descriptor.cpp b/lib/dvb_si/content_descriptor.cpp new file mode 100644 index 0000000..b8996a4 --- /dev/null +++ b/lib/dvb_si/content_descriptor.cpp @@ -0,0 +1,68 @@ +/* + * $Id: content_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +ContentClassification::ContentClassification(const uint8_t * const buffer) +{ + contentNibbleLevel1 = (buffer[0] >> 4) & 0x0f; + contentNibbleLevel2 = buffer[0] & 0x0f; + userNibble1 = (buffer[1] >> 4) & 0x0f; + userNibble2 = buffer[1] & 0x0f; +} + +uint8_t ContentClassification::getContentNibbleLevel1(void) const +{ + return contentNibbleLevel1; +} + +uint8_t ContentClassification::getContentNibbleLevel2(void) const +{ + return contentNibbleLevel2; +} + +uint8_t ContentClassification::getUserNibble1(void) const +{ + return userNibble1; +} + +uint8_t ContentClassification::getUserNibble2(void) const +{ + return userNibble2; +} + +ContentDescriptor::ContentDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + for (uint16_t i = 0; i < descriptorLength; i += 2) + classifications.push_back(new ContentClassification(&buffer[i + 2])); +} + +ContentDescriptor::~ContentDescriptor(void) +{ + for (ContentClassificationVector::iterator i = classifications.begin(); i != classifications.end(); ++i) + delete *i; +} + +const ContentClassificationVector *ContentDescriptor::getClassifications(void) const +{ + return &classifications; +} + diff --git a/lib/dvb_si/content_descriptor.h b/lib/dvb_si/content_descriptor.h new file mode 100644 index 0000000..31f4e71 --- /dev/null +++ b/lib/dvb_si/content_descriptor.h @@ -0,0 +1,60 @@ +/* + * $Id: content_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_content_descriptor_h__ +#define __dvb_descriptor_content_descriptor_h__ + +#include "descriptor.h" + +class ContentClassification +{ + protected: + unsigned contentNibbleLevel1 : 4; + unsigned contentNibbleLevel2 : 4; + unsigned userNibble1 : 4; + unsigned userNibble2 : 4; + + public: + ContentClassification(const uint8_t * const buffer); + + uint8_t getContentNibbleLevel1(void) const; + uint8_t getContentNibbleLevel2(void) const; + uint8_t getUserNibble1(void) const; + uint8_t getUserNibble2(void) const; +}; + +typedef std::vector ContentClassificationVector; +typedef ContentClassificationVector::iterator ContentClassificationIterator; +typedef ContentClassificationVector::const_iterator ContentClassificationConstIterator; + +class ContentDescriptor : public Descriptor +{ + protected: + ContentClassificationVector classifications; + + public: + ContentDescriptor(const uint8_t * const buffer); + ~ContentDescriptor(void); + + const ContentClassificationVector *getClassifications(void) const; +}; + +#endif /* __dvb_descriptor_content_descriptor_h__ */ diff --git a/lib/dvb_si/copyright_descriptor.h b/lib/dvb_si/copyright_descriptor.h new file mode 100644 index 0000000..89d0c28 --- /dev/null +++ b/lib/dvb_si/copyright_descriptor.h @@ -0,0 +1,44 @@ +/* + * $Id: copyright_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_copyright_descriptor_h__ +#define __dvb_descriptor_copyright_descriptor_h__ + +#include "descriptor.h" + +typedef std::vector AdditionalCopyrightInfoVector; +typedef AdditionalCopyrightInfoVector::iterator AdditionalCopyrightInfoIterator; +typedef AdditionalCopyrightInfoVector::const_iterator AdditionalCopyrightInfoConstIterator; + +class CopyrightDescriptor : public Descriptor +{ + protected: + unsigned copyrightIdentifier : 32; + AdditionalCopyrightInfoVector additionalCopyrightInfo; + + public: + CopyrightDescriptor(const uint8_t * const buffer); + + uint32_t getCopyrightIdentifier(void) const; + const AdditionalCopyrightInfoVector *getAdditionalCopyrightInfo(void) const; +}; + +#endif /* __dvb_descriptor_copyright_descriptor_h__ */ diff --git a/lib/dvb_si/country_availability_descriptor.cpp b/lib/dvb_si/country_availability_descriptor.cpp new file mode 100644 index 0000000..7aa37ff --- /dev/null +++ b/lib/dvb_si/country_availability_descriptor.cpp @@ -0,0 +1,47 @@ +/* + * $Id: country_availability_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +CountryAvailabilityDescriptor::CountryAvailabilityDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + std::string countryCode; + countryAvailabilityFlag = (buffer[2] >> 7) & 0x01; + + if (descriptorLength < 1) + return; + + for (uint16_t i = 0; i < descriptorLength - 1; i += 3) { + countryCode.assign((char *)&buffer[i + 3], 3); + countryCodes.push_back(countryCode); + } +} + +uint8_t CountryAvailabilityDescriptor::getCountryAvailabilityFlag(void) const +{ + return countryAvailabilityFlag; +} + +const CountryCodeVector *CountryAvailabilityDescriptor::getCountryCodes(void) const +{ + return &countryCodes; +} + diff --git a/lib/dvb_si/country_availability_descriptor.h b/lib/dvb_si/country_availability_descriptor.h new file mode 100644 index 0000000..baa0b84 --- /dev/null +++ b/lib/dvb_si/country_availability_descriptor.h @@ -0,0 +1,45 @@ +/* + * $Id: country_availability_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_country_availability_descriptor_h__ +#define __dvb_descriptor_country_availability_descriptor_h__ + +#include "descriptor.h" + +typedef std::vector CountryCodeVector; +typedef CountryCodeVector::iterator CountryCodeIterator; +typedef CountryCodeVector::const_iterator CountryCodeConstIterator; + +class CountryAvailabilityDescriptor : public Descriptor +{ + protected: + unsigned countryAvailabilityFlag : 1; + unsigned reserved : 7; + CountryCodeVector countryCodes; + + public: + CountryAvailabilityDescriptor(const uint8_t * const buffer); + + uint8_t getCountryAvailabilityFlag(void) const; + const CountryCodeVector *getCountryCodes(void) const; +}; + +#endif /* __dvb_descriptor_country_availability_descriptor_h__ */ diff --git a/lib/dvb_si/data_broadcast_descriptor.cpp b/lib/dvb_si/data_broadcast_descriptor.cpp new file mode 100644 index 0000000..b5f8b2a --- /dev/null +++ b/lib/dvb_si/data_broadcast_descriptor.cpp @@ -0,0 +1,62 @@ +/* + * $Id: data_broadcast_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +DataBroadcastDescriptor::DataBroadcastDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + dataBroadcastId = (buffer[2] << 8) | buffer[3]; + componentTag = buffer[3]; + selectorLength = buffer[4]; + + for (uint16_t i = 0; i < selectorLength; ++i) + selectorBytes.push_back(buffer[i + 5]); + + iso639LanguageCode.assign((char *)&buffer[selectorLength + 5], 3); + textLength = buffer[selectorLength + 8]; + text.assign((char *)&buffer[selectorLength + 9], textLength); +} + +uint16_t DataBroadcastDescriptor::getDataBroadcastId(void) const +{ + return dataBroadcastId; +} + +uint8_t DataBroadcastDescriptor::getComponentTag(void) const +{ + return componentTag; +} + +const selectorByteVector *DataBroadcastDescriptor::getSelectorBytes(void) const +{ + return &selectorBytes; +} + +std::string DataBroadcastDescriptor::getIso639LanguageCode(void) const +{ + return iso639LanguageCode; +} + +std::string DataBroadcastDescriptor::getText(void) const +{ + return text; +} + diff --git a/lib/dvb_si/data_broadcast_descriptor.h b/lib/dvb_si/data_broadcast_descriptor.h new file mode 100644 index 0000000..aa5bb5d --- /dev/null +++ b/lib/dvb_si/data_broadcast_descriptor.h @@ -0,0 +1,52 @@ +/* + * $Id: data_broadcast_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_data_broadcast_descriptor_h__ +#define __dvb_descriptor_data_broadcast_descriptor_h__ + +#include "descriptor.h" + +typedef std::vector selectorByteVector; +typedef selectorByteVector::iterator selectorByteIterator; +typedef selectorByteVector::const_iterator selectorByteConstIterator; + +class DataBroadcastDescriptor : public Descriptor +{ + protected: + unsigned dataBroadcastId : 16; + unsigned componentTag : 8; + unsigned selectorLength : 8; + selectorByteVector selectorBytes; + std::string iso639LanguageCode; + unsigned textLength : 8; + std::string text; + + public: + DataBroadcastDescriptor(const uint8_t * const buffer); + + uint16_t getDataBroadcastId(void) const; + uint8_t getComponentTag(void) const; + const selectorByteVector *getSelectorBytes(void) const; + std::string getIso639LanguageCode(void) const; + std::string getText(void) const; +}; + +#endif /* __dvb_descriptor_data_broadcast_descriptor_h__ */ diff --git a/lib/dvb_si/data_broadcast_id_descriptor.cpp b/lib/dvb_si/data_broadcast_id_descriptor.cpp new file mode 100644 index 0000000..2e058c4 --- /dev/null +++ b/lib/dvb_si/data_broadcast_id_descriptor.cpp @@ -0,0 +1,44 @@ +/* + * $Id: data_broadcast_id_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +DataBroadcastIdDescriptor::DataBroadcastIdDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + if (descriptorLength < 2) + return; + + dataBroadcastId = (buffer[2] << 8) | buffer[3]; + + for (uint16_t i = 0; i < descriptorLength - 2; ++i) + idSelectorBytes.push_back(buffer[i + 4]); +} + +uint16_t DataBroadcastIdDescriptor::getDataBroadcastId(void) const +{ + return dataBroadcastId; +} + +const IdSelectorByteVector *DataBroadcastIdDescriptor::getIdSelectorBytes(void) const +{ + return &idSelectorBytes; +} + diff --git a/lib/dvb_si/data_broadcast_id_descriptor.h b/lib/dvb_si/data_broadcast_id_descriptor.h new file mode 100644 index 0000000..a933c6e --- /dev/null +++ b/lib/dvb_si/data_broadcast_id_descriptor.h @@ -0,0 +1,44 @@ +/* + * $Id: data_broadcast_id_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_data_broadcast_id_descriptor_h__ +#define __dvb_descriptor_data_broadcast_id_descriptor_h__ + +#include "descriptor.h" + +typedef std::vector IdSelectorByteVector; +typedef IdSelectorByteVector::iterator IdSelectorByteIterator; +typedef IdSelectorByteVector::const_iterator IdSelectorByteConstIterator; + +class DataBroadcastIdDescriptor : public Descriptor +{ + protected: + unsigned dataBroadcastId : 16; + IdSelectorByteVector idSelectorBytes; + + public: + DataBroadcastIdDescriptor(const uint8_t * const buffer); + + uint16_t getDataBroadcastId(void) const; + const IdSelectorByteVector *getIdSelectorBytes(void) const; +}; + +#endif /* __dvb_descriptor_data_broadcast_id_descriptor_h__ */ diff --git a/lib/dvb_si/data_stream_alignment_descriptor.h b/lib/dvb_si/data_stream_alignment_descriptor.h new file mode 100644 index 0000000..988cb6d --- /dev/null +++ b/lib/dvb_si/data_stream_alignment_descriptor.h @@ -0,0 +1,38 @@ +/* + * $Id: data_stream_alignment_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_data_stream_alignment_descriptor_h__ +#define __dvb_descriptor_data_stream_alignment_descriptor_h__ + +#include "descriptor.h" + +class DataStreamAlignmentDescriptor : public Descriptor +{ + protected: + unsigned alignmentType : 8; + + private: + DataStreamAlignmentDescriptor(const uint8_t * const buffer); + + uint8_t getAlignmentType(void) const; +}; + +#endif /* __dvb_descriptor_data_stream_alignment_descriptor_h__ */ diff --git a/lib/dvb_si/descriptor.cpp b/lib/dvb_si/descriptor.cpp new file mode 100644 index 0000000..f81ed9a --- /dev/null +++ b/lib/dvb_si/descriptor.cpp @@ -0,0 +1,39 @@ +/* + * $Id: descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +Descriptor::Descriptor(const uint8_t * const buffer) +{ + descriptorTag = buffer[0]; + descriptorLength = buffer[1]; +} + +uint8_t Descriptor::getTag(void) const +{ + return descriptorTag; +} + +uint8_t Descriptor::getLength(void) const +{ + return descriptorLength; +} + diff --git a/lib/dvb_si/descriptor.h b/lib/dvb_si/descriptor.h new file mode 100644 index 0000000..3572cce --- /dev/null +++ b/lib/dvb_si/descriptor.h @@ -0,0 +1,46 @@ +/* + * $Id: descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_descriptor_h__ +#define __dvb_descriptor_descriptor_h__ + +#include +#include +#include + +class Descriptor +{ + protected: + unsigned descriptorTag : 8; + unsigned descriptorLength : 8; + + public: + Descriptor(const uint8_t * const buffer); + + uint8_t getTag(void) const; + uint8_t getLength(void) const; +}; + +typedef std::vector DescriptorVector; +typedef DescriptorVector::iterator DescriptorIterator; +typedef DescriptorVector::const_iterator DescriptorConstIterator; + +#endif /* __dvb_descriptor_descriptor_h__ */ diff --git a/lib/dvb_si/descriptor_tag.h b/lib/dvb_si/descriptor_tag.h new file mode 100644 index 0000000..c3f8327 --- /dev/null +++ b/lib/dvb_si/descriptor_tag.h @@ -0,0 +1,127 @@ +/* + * $Id: descriptor_tag.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_id_descriptor_tag_h__ +#define __dvb_id_descriptor_tag_h__ + +enum DescriptorTag { + /* 0x00 - 0x3F: ITU-T Rec. H.222.0 | ISO/IEC 13818-1 */ + VIDEO_STREAM_DESCRIPTOR = 0x02, + AUDIO_STREAM_DESCRIPTOR = 0x03, + HIERARCHY_DESCRIPTOR = 0x04, + REGISTRATION_DESCRIPTOR = 0x05, + DATA_STREAM_ALIGNMENT_DESCRIPTOR = 0x06, + TARGET_BACKGROUND_GRID_DESCRIPTOR = 0x07, + VIDEO_WINDOW_DESCRIPTOR = 0x08, + CA_DESCRIPTOR = 0x09, + ISO_639_LANGUAGE_DESCRIPTOR = 0x0A, + SYSTEM_CLOCK_DESCRIPTOR = 0x0B, + MULTIPLEX_BUFFER_UTILIZATION_DESCRIPTOR = 0x0C, + COPYRIGHT_DESCRIPTOR = 0x0D, + MAXIMUM_BITRATE_DESCRIPTOR = 0x0E, + PRIVATE_DATA_INDICATOR_DESCRIPTOR = 0x0F, + SMOOTHING_BUFFER_DESCRIPTOR = 0x10, + STD_DESCRIPTOR = 0x11, + IBP_DESCRIPTOR = 0x12, + CAROUSEL_IDENTIFIER_DESCRIPTOR = 0x13, + /* 0x40 - 0x7F: ETSI EN 300 468 V1.5.1 (2003-01) */ + NETWORK_NAME_DESCRIPTOR = 0x40, + SERVICE_LIST_DESCRIPTOR = 0x41, + STUFFING_DESCRIPTOR = 0x42, + SATELLITE_DELIVERY_SYSTEM_DESCRIPTOR = 0x43, + CABLE_DELIVERY_SYSTEM_DESCRIPTOR = 0x44, + VBI_DATA_DESCRIPTOR = 0x45, + VBI_TELETEXT_DESCRIPTOR = 0x46, + BOUQUET_NAME_DESCRIPTOR = 0x47, + SERVICE_DESCRIPTOR = 0x48, + COUNTRY_AVAILABILITY_DESCRIPTOR = 0x49, + LINKAGE_DESCRIPTOR = 0x4A, + NVOD_REFERENCE_DESCRIPTOR = 0x4B, + TIME_SHIFTED_SERVICE_DESCRIPTOR = 0x4C, + SHORT_EVENT_DESCRIPTOR = 0x4D, + EXTENDED_EVENT_DESCRIPTOR = 0x4E, + TIME_SHIFTED_EVENT_DESCRIPTOR = 0x4F, + COMPONENT_DESCRIPTOR = 0x50, + MOSAIC_DESCRIPTOR = 0x51, + STREAM_IDENTIFIER_DESCRIPTOR = 0x52, + CA_IDENTIFIER_DESCRIPTOR = 0x53, + CONTENT_DESCRIPTOR = 0x54, + PARENTAL_RATING_DESCRIPTOR = 0x55, + TELETEXT_DESCRIPTOR = 0x56, + TELEPHONE_DESCRIPTOR = 0x57, + LOCAL_TIME_OFFSET_DESCRIPTOR = 0x58, + SUBTITLING_DESCRIPTOR = 0x59, + TERRESTRIAL_DELIVERY_SYSTEM_DESCRIPTOR = 0x5A, + MULTILINGUAL_NETWORK_NAME_DESCRIPTOR = 0x5B, + MULTILINGUAL_BOUQUET_NAME_DESCRIPTOR = 0x5C, + MULTILINGUAL_SERVICE_NAME_DESCRIPTOR = 0x5D, + MULTILINGUAL_COMPONENT_DESCRIPTOR = 0x5E, + PRIVATE_DATA_SPECIFIER_DESCRIPTOR = 0x5F, + SERVICE_MOVE_DESCRIPTOR = 0x60, + SHORT_SMOOTHING_BUFFER_DESCRIPTOR = 0x61, + FREQUENCY_LIST_DESCRIPTOR = 0x62, + PARTIAL_TRANSPORT_STREAM_DESCRIPTOR = 0x63, + DATA_BROADCAST_DESCRIPTOR = 0x64, + CA_SYSTEM_DESCRIPTOR = 0x65, + DATA_BROADCAST_ID_DESCRIPTOR = 0x66, + TRANSPORT_STREAM_DESCRIPTOR = 0x67, + DSNG_DESCRIPTOR = 0x68, + PDC_DESCRIPTOR = 0x69, + AC3_DESCRIPTOR = 0x6A, + ANCILLARY_DATA_DESCRIPTOR = 0x6B, + CELL_LIST_DESCRIPTOR = 0x6C, + CELL_FREQUENCY_LINK_DESCRIPTOR = 0x6D, + ANNOUNCEMENT_SUPPORT_DESCRIPTOR = 0x6E, + APPLICATION_SIGNALLING_DESCRIPTOR = 0x6F, + ADAPTATION_FIELD_DATA_DESCRIPTOR = 0x70, + SERVICE_IDENTIFIER_DESCRIPTOR = 0x71, + SERVICE_AVAILABILITY_DESCRIPTOR = 0x72, + /* 0x80 - 0xFE: user defined */ + VIASAT_LOGIC_CHANNEL_DESCRIPTOR = 0x82, + NORDIG_LOGIC_CHANNEL_DESCRIPTOR = 0x83, + EACEM_LOGIC_CHANNEL_DESCRIPTOR = 0x83, + EACEM_PREFERRED_NAME_LIST_DESCRIPTOR = 0x84, + EACEM_PREFERRED_NAME_IDENTIFIER_DESCRIPTOR = 0x85, + EACEM_STREAM_IDENTIFIER_DESCRIPTOR = 0x86, + SENDA_CHANNEL_LIST_DESCRIPTOR = 0xF1, + /* 0xFF: Forbidden */ + FORBIDDEN_DESCRIPTOR = 0xFF +}; + +enum MhpDescriptorTag { + /* ETSI TS 101 812 V1.2.1 (2002-06) */ + APPLICATION_DESCRIPTOR = 0x00, + APPLICATION_NAME_DESCRIPTOR = 0x01, + TRANSPORT_PROTOCOL_DESCRIPTOR = 0x02, + DVB_J_APPLICATION_DESCRIPTOR = 0x03, + DVB_J_APPLICATION_LOCATION_DESCRIPTOR = 0x04, + EXTERNAL_APPLICATION_AUTHORISATION_DESCRIPTOR = 0x05, + ROUTING_DESCRIPTOR_IP4 = 0x06, + ROUTING_DESCRIPTOR_IP6 = 0x07, + DVB_HTML_APPLICATION_DESCRIPTOR = 0x08, + DVB_HTML_APPLICATION_LOCATION_DESCRIPTOR = 0x09, + DVB_HTML_APPLICATION_BOUNDARY_DESCRIPTOR = 0x0A, + APPLICATION_ICONS_DESCRIPTOR = 0x0B, + PREFETCH_DESCRIPTOR = 0x0C, + DII_LOCATION_DESCRIPTOR = 0x0D, +}; + +#endif /* __dvb_id_descriptor_tag_h__ */ diff --git a/lib/dvb_si/dsng_descriptor.h b/lib/dvb_si/dsng_descriptor.h new file mode 100644 index 0000000..f12d554 --- /dev/null +++ b/lib/dvb_si/dsng_descriptor.h @@ -0,0 +1,42 @@ +/* + * $Id: dsng_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_dsng_descriptor_h__ +#define __dvb_descriptor_dsng_descriptor_h__ + +#include "descriptor.h" + +typedef std::vector ByteVector; +typedef ByteVector::iterator ByteIterator; +typedef ByteVector::const_iterator ByteConstIterator; + +class DsngDescriptor : public Descriptor +{ + protected: + ByteVector bytes; + + public: + DsngDescriptor(const uint8_t * const buffer); + + const ByteVector *getBytes(void) const; +}; + +#endif /* __dvb_descriptor_dsng_descriptor_h__ */ diff --git a/lib/dvb_si/eit.cpp b/lib/dvb_si/eit.cpp new file mode 100644 index 0000000..cf7c02b --- /dev/null +++ b/lib/dvb_si/eit.cpp @@ -0,0 +1,109 @@ +/* + * $Id: eit.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +Event::Event(const uint8_t * const buffer) +{ + eventId = (buffer[0] << 8) | buffer[1]; + startTimeMjd = (buffer[2] << 8) | buffer[3]; + startTimeBcd = (buffer[4] << 16) | (buffer[5] << 8) | buffer[6]; + duration = (buffer[7] << 16) | (buffer[8] << 8) | buffer[9]; + runningStatus = (buffer[10] >> 5) & 0x07; + freeCaMode = (buffer[10] >> 4) & 0x01; + descriptorsLoopLength = ((buffer[10] & 0x0f) << 8) | buffer[11]; + + for (uint16_t i = 12; i < descriptorsLoopLength + 12; i += buffer[i + 1] + 2) + descriptor(&buffer[i]); +} + +uint16_t Event::getEventId(void) const +{ + return eventId; +} + +uint16_t Event::getStartTimeMjd(void) const +{ + return startTimeMjd; +} + +uint32_t Event::getStartTimeBcd(void) const +{ + return startTimeBcd; +} + +uint32_t Event::getDuration(void) const +{ + return duration; +} + +uint8_t Event::getRunningStatus(void) const +{ + return runningStatus; +} + +uint8_t Event::getFreeCaMode(void) const +{ + return freeCaMode; +} + +EventInformationTable::EventInformationTable(const uint8_t * const buffer) : LongCrcTable(buffer) +{ + transportStreamId = (buffer[8] << 8) | buffer[9]; + originalNetworkId = (buffer[10] << 8) | buffer[11]; + segmentLastSectionNumber = buffer[12]; + lastTableId = buffer[13]; + + for (uint16_t i = 14; i < sectionLength - 1; i += (((buffer[i + 10] & 0x0f) << 8) | buffer[i + 11]) + 12) + events.push_back(new Event(&buffer[i])); +} + +EventInformationTable::~EventInformationTable(void) +{ + for (EventIterator i = events.begin(); i != events.end(); ++i) + delete *i; +} + +uint16_t EventInformationTable::getTransportStreamId(void) const +{ + return transportStreamId; +} + +uint16_t EventInformationTable::getOriginalNetworkId(void) const +{ + return originalNetworkId; +} + +uint8_t EventInformationTable::getLastSectionNumber(void) const +{ + return lastSectionNumber; +} + +uint8_t EventInformationTable::getLastTableId(void) const +{ + return lastTableId; +} + +const EventVector *EventInformationTable::getEvents(void) const +{ + return &events; +} + diff --git a/lib/dvb_si/eit.h b/lib/dvb_si/eit.h new file mode 100644 index 0000000..1b278ca --- /dev/null +++ b/lib/dvb_si/eit.h @@ -0,0 +1,83 @@ +/* + * $Id: eit.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_table_eit_h__ +#define __dvb_table_eit_h__ + +#include +#include "long_crc_table.h" + +class Event : public DescriptorContainer +{ + protected: + unsigned eventId : 16; + unsigned startTimeMjd : 16; + unsigned startTimeBcd : 24; + unsigned duration : 24; + unsigned runningStatus : 3; + unsigned freeCaMode : 1; + unsigned descriptorsLoopLength : 12; + + public: + Event(const uint8_t * const buffer); + + uint16_t getEventId(void) const; + uint16_t getStartTimeMjd(void) const; + uint32_t getStartTimeBcd(void) const; + uint32_t getDuration(void) const; + uint8_t getRunningStatus(void) const; + uint8_t getFreeCaMode(void) const; +}; + +typedef std::vector EventVector; +typedef EventVector::iterator EventIterator; +typedef EventVector::const_iterator EventConstIterator; + +class EventInformationTable : public LongCrcTable +{ + protected: + unsigned transportStreamId : 16; + unsigned originalNetworkId : 16; + unsigned segmentLastSectionNumber : 8; + unsigned lastTableId : 8; + EventVector events; + + public: + EventInformationTable(const uint8_t * const buffer); + ~EventInformationTable(void); + + static const uint16_t LENGTH = 4096; + static const enum PacketId PID = PID_EIT; + static const enum TableId TID = TID_EIT_ACTUAL; + static const uint32_t TIMEOUT = 3000; + + uint16_t getTransportStreamId(void) const; + uint16_t getOriginalNetworkId(void) const; + uint8_t getLastSectionNumber(void) const; + uint8_t getLastTableId(void) const; + const EventVector *getEvents(void) const; +}; + +typedef std::vector EventInformationTableVector; +typedef EventInformationTableVector::iterator EventInformationTableIterator; +typedef EventInformationTableVector::const_iterator EventInformationTableConstIterator; + +#endif /* __dvb_table_eit_h__ */ diff --git a/lib/dvb_si/element_descriptor.h b/lib/dvb_si/element_descriptor.h new file mode 100644 index 0000000..3587569 --- /dev/null +++ b/lib/dvb_si/element_descriptor.h @@ -0,0 +1,34 @@ +/* + * $Id: element_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_element_descriptor_h__ +#define __dvb_descriptor_element_descriptor_h__ + +#include "descriptor.h" + +/* 0x92 */ +class ElementDescriptor : public Descriptor +{ + public: + ElementDescriptor(const uint8_t * const buffer); +}; + +#endif /* __dvb_descriptor_element_descriptor_h__ */ diff --git a/lib/dvb_si/extended_event_descriptor.cpp b/lib/dvb_si/extended_event_descriptor.cpp new file mode 100644 index 0000000..11b639a --- /dev/null +++ b/lib/dvb_si/extended_event_descriptor.cpp @@ -0,0 +1,90 @@ +/* + * $Id: extended_event_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +ExtendedEvent::ExtendedEvent(const uint8_t * const buffer) +{ + itemDescriptionLength = buffer[0]; + itemDescription.assign((char *)&buffer[1], itemDescriptionLength); + itemLength = buffer[itemDescriptionLength + 1]; + item.assign((char *)&buffer[itemDescriptionLength + 2], itemLength); +} + +std::string ExtendedEvent::getItemDescription(void) const +{ + return itemDescription; +} + +std::string ExtendedEvent::getItem(void) const +{ + return item; +} + +ExtendedEventDescriptor::ExtendedEventDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + descriptorNumber = (buffer[2] >> 4) & 0x0f; + lastDescriptorNumber = buffer[2] & 0x0f; + iso639LanguageCode.assign((char *)&buffer[3], 3); + lengthOfItems = buffer[6]; + + ExtendedEvent *e; + + for (uint16_t i = 0; i < lengthOfItems; i += e->itemDescriptionLength + e->itemLength + 2) { + e = new ExtendedEvent(&buffer[i + 7]); + items.push_back(e); + } + + textLength = buffer[lengthOfItems + 7]; + text.assign((char *)&buffer[lengthOfItems + 8], textLength); +} + +ExtendedEventDescriptor::~ExtendedEventDescriptor(void) +{ + for (ExtendedEventIterator i = items.begin(); i != items.end(); ++i) + delete *i; +} + +uint8_t ExtendedEventDescriptor::getDescriptorNumber(void) const +{ + return descriptorNumber; +} + +uint8_t ExtendedEventDescriptor::getLastDescriptorNumber(void) const +{ + return lastDescriptorNumber; +} + +std::string ExtendedEventDescriptor::getIso639LanguageCode(void) const +{ + return iso639LanguageCode; +} + +const ExtendedEventVector *ExtendedEventDescriptor::getItems(void) const +{ + return &items; +} + +std::string ExtendedEventDescriptor::getText(void) const +{ + return text; +} + diff --git a/lib/dvb_si/extended_event_descriptor.h b/lib/dvb_si/extended_event_descriptor.h new file mode 100644 index 0000000..9b170d0 --- /dev/null +++ b/lib/dvb_si/extended_event_descriptor.h @@ -0,0 +1,70 @@ +/* + * $Id: extended_event_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_extended_event_descriptor_h__ +#define __dvb_descriptor_extended_event_descriptor_h__ + +#include "descriptor.h" + +class ExtendedEvent +{ + protected: + unsigned itemDescriptionLength : 8; + std::string itemDescription; + unsigned itemLength : 8; + std::string item; + + public: + ExtendedEvent(const uint8_t * const buffer); + + std::string getItemDescription(void) const; + std::string getItem(void) const; + + friend class ExtendedEventDescriptor; +}; + +typedef std::vector ExtendedEventVector; +typedef ExtendedEventVector::iterator ExtendedEventIterator; +typedef ExtendedEventVector::const_iterator ExtendedEventConstIterator; + +class ExtendedEventDescriptor : public Descriptor +{ + protected: + unsigned descriptorNumber : 4; + unsigned lastDescriptorNumber : 4; + std::string iso639LanguageCode; + unsigned lengthOfItems : 8; + ExtendedEventVector items; + unsigned textLength : 8; + std::string text; + + public: + ExtendedEventDescriptor(const uint8_t * const buffer); + ~ExtendedEventDescriptor(void); + + uint8_t getDescriptorNumber(void) const; + uint8_t getLastDescriptorNumber(void) const; + std::string getIso639LanguageCode(void) const; + const ExtendedEventVector *getItems(void) const; + std::string getText(void) const; +}; + +#endif /* __dvb_descriptor_extended_event_descriptor_h__ */ diff --git a/lib/dvb_si/frequency_list_descriptor.cpp b/lib/dvb_si/frequency_list_descriptor.cpp new file mode 100644 index 0000000..fe6ea32 --- /dev/null +++ b/lib/dvb_si/frequency_list_descriptor.cpp @@ -0,0 +1,44 @@ +/* + * $Id: frequency_list_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +FrequencyListDescriptor::FrequencyListDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + if (descriptorLength < 1) + return; + reserved = (buffer[2] >> 2) & 0x3f; + codingType = buffer[2] & 0x03; + + for (uint16_t i = 0; i < descriptorLength - 1; i += 4) + centreFrequencies.push_back((buffer[i + 3] << 24) | (buffer[i + 4] << 16) | (buffer[i + 5] << 8) | buffer[i + 6]); +} + +uint8_t FrequencyListDescriptor::getCodingType(void) const +{ + return codingType; +} + +const CentreFrequencyVector *FrequencyListDescriptor::getCentreFrequencies(void) const +{ + return ¢reFrequencies; +} + diff --git a/lib/dvb_si/frequency_list_descriptor.h b/lib/dvb_si/frequency_list_descriptor.h new file mode 100644 index 0000000..83fdf92 --- /dev/null +++ b/lib/dvb_si/frequency_list_descriptor.h @@ -0,0 +1,45 @@ +/* + * $Id: frequency_list_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_frequency_list_descriptor_h__ +#define __dvb_descriptor_frequency_list_descriptor_h__ + +#include "descriptor.h" + +typedef std::vector CentreFrequencyVector; +typedef CentreFrequencyVector::iterator CentreFrequencyIterator; +typedef CentreFrequencyVector::const_iterator CentreFrequencyConstIterator; + +class FrequencyListDescriptor : public Descriptor +{ + protected: + unsigned reserved : 6; + unsigned codingType : 2; + CentreFrequencyVector centreFrequencies; + + public: + FrequencyListDescriptor(const uint8_t * const buffer); + + uint8_t getCodingType(void) const; + const CentreFrequencyVector *getCentreFrequencies(void) const; +}; + +#endif /* __dvb_descriptor_frequency_list_descriptor_h__ */ diff --git a/lib/dvb_si/group_descriptor.h b/lib/dvb_si/group_descriptor.h new file mode 100644 index 0000000..fd3292d --- /dev/null +++ b/lib/dvb_si/group_descriptor.h @@ -0,0 +1,34 @@ +/* + * $Id: group_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_group_descriptor_h__ +#define __dvb_descriptor_group_descriptor_h__ + +#include "descriptor.h" + +/* 0x91 */ +class GroupDescriptor : public Descriptor +{ + public: + GroupDescriptor(const uint8_t * const buffer); +}; + +#endif /* __dvb_descriptor_group_descriptor_h__ */ diff --git a/lib/dvb_si/hierarchy_descriptor.h b/lib/dvb_si/hierarchy_descriptor.h new file mode 100644 index 0000000..3c990fc --- /dev/null +++ b/lib/dvb_si/hierarchy_descriptor.h @@ -0,0 +1,48 @@ +/* + * $Id: hierarchy_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_hierarchy_descriptor_h__ +#define __dvb_descriptor_hierarchy_descriptor_h__ + +#include "descriptor.h" + +class HierarchyDescriptor : public Descriptor +{ + protected: + unsigned reserved : 4; + unsigned hierarchyType : 4; + unsigned reserved2 : 2; + unsigned hierarchyLayerIndex : 6; + unsigned reserved3 : 2; + unsigned hierarchyEmbeddedLayerIndex : 6; + unsigned reserved4 : 2; + unsigned hierarchyChannel : 6; + + private: + HierarchyDescriptor(const uint8_t * const buffer); + + uint8_t getHierarchyType(void) const; + uint8_t getHierarchyLayerIndex(void) const; + uint8_t getHierarchyEmbeddedLayerIndex(void) const; + uint8_t getHierarchyChannel(void) const; +}; + +#endif /* __dvb_descriptor_hierarchy_descriptor_h__ */ diff --git a/lib/dvb_si/ibp_descriptor.h b/lib/dvb_si/ibp_descriptor.h new file mode 100644 index 0000000..d601fe4 --- /dev/null +++ b/lib/dvb_si/ibp_descriptor.h @@ -0,0 +1,42 @@ +/* + * $Id: ibp_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_ibp_descriptor_h__ +#define __dvb_descriptor_ibp_descriptor_h__ + +#include "descriptor.h" + +class IbpDescriptor : public Descriptor +{ + protected: + unsigned closedGopFlag : 1; + unsigned identicalGopFlag : 1; + unsigned maxGopLength : 14; + + public: + IbpDescriptor(const uint8_t * const buffer); + + uint8_t getClosedGopFlag(void) const; + uint8_t getIdenticalGopFlag(void) const; + uint16_t getMaxGopLength(void) const; +}; + +#endif /* __dvb_descriptor_ibp_descriptor_h__ */ diff --git a/lib/dvb_si/ippv_booking_descriptor.h b/lib/dvb_si/ippv_booking_descriptor.h new file mode 100644 index 0000000..950bb45 --- /dev/null +++ b/lib/dvb_si/ippv_booking_descriptor.h @@ -0,0 +1,34 @@ +/* + * $Id: ippv_booking_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_ippv_booking_descriptor_h__ +#define __dvb_descriptor_ippv_booking_descriptor_h__ + +#include "descriptor.h" + +/* 0x81 */ +class IppvBookingDescriptor : public Descriptor +{ + public: + IppvBookingDescriptor(const uint8_t * const buffer); +}; + +#endif /* __dvb_descriptor_ippv_booking_descriptor_h__ */ diff --git a/lib/dvb_si/ippv_descriptor.h b/lib/dvb_si/ippv_descriptor.h new file mode 100644 index 0000000..967c9b5 --- /dev/null +++ b/lib/dvb_si/ippv_descriptor.h @@ -0,0 +1,62 @@ +/* + * $Id: ippv_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_ippv_descriptor_h__ +#define __dvb_descriptor_ippv_descriptor_h__ + +#include "descriptor.h" + +/* 0xF0 */ +class CurrencyEntry +{ + public: + CurrencyEntry(const uint8_t * const buffer); +}; + +class CountryEntry +{ + protected: + unsigned country : 24; + unsigned unknown : 5; + unsigned currencyAndCostDetail : 3; + // if (currencyAndCostDetail & 1) + unsigned bcdCost : 32; + unsigned length : 8; + std::vector currency; + + public: + CountryEntry(const uint8_t * const buffer); +}; + +class IppvDescriptor : public Descriptor +{ + protected: + unsigned unknown1 : 16; + unsigned unknown2 : 16; + unsigned unknown3 : 16; + unsigned IppvEventId : 16; + std::vector country; + + private: + IppvDescriptor(const uint8_t * const buffer); +}; + +#endif /* __dvb_descriptor_ippv_descriptor_h__ */ diff --git a/lib/dvb_si/iso639_language_descriptor.cpp b/lib/dvb_si/iso639_language_descriptor.cpp new file mode 100644 index 0000000..26ca1e0 --- /dev/null +++ b/lib/dvb_si/iso639_language_descriptor.cpp @@ -0,0 +1,56 @@ +/* + * $Id: iso639_language_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +Iso639Language::Iso639Language(const uint8_t * const buffer) +{ + iso639LanguageCode.assign((char *)&buffer[0], 3); + audioType = buffer[3]; +} + +std::string Iso639Language::getIso639LanguageCode(void) const +{ + return iso639LanguageCode; +} + +uint8_t Iso639Language::getAudioType(void) const +{ + return audioType; +} + +Iso639LanguageDescriptor::Iso639LanguageDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + for (uint16_t i = 0; i < descriptorLength; i += 4) + iso639Languages.push_back(new Iso639Language(&buffer[i + 2])); +} + +Iso639LanguageDescriptor::~Iso639LanguageDescriptor(void) +{ + for (Iso639LanguageIterator i = iso639Languages.begin(); i != iso639Languages.end(); ++i) + delete *i; +} + +const Iso639LanguageVector *Iso639LanguageDescriptor::getIso639Languages(void) const +{ + return &iso639Languages; +} + diff --git a/lib/dvb_si/iso639_language_descriptor.h b/lib/dvb_si/iso639_language_descriptor.h new file mode 100644 index 0000000..8983639 --- /dev/null +++ b/lib/dvb_si/iso639_language_descriptor.h @@ -0,0 +1,56 @@ +/* + * $Id: iso639_language_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_iso639_language_descriptor_h__ +#define __dvb_descriptor_iso639_language_descriptor_h__ + +#include "descriptor.h" + +class Iso639Language +{ + protected: + std::string iso639LanguageCode; + unsigned audioType : 8; + + public: + Iso639Language(const uint8_t * const buffer); + + std::string getIso639LanguageCode(void) const; + uint8_t getAudioType(void) const; +}; + +typedef std::vector Iso639LanguageVector; +typedef Iso639LanguageVector::iterator Iso639LanguageIterator; +typedef Iso639LanguageVector::const_iterator Iso639LanguageConstIterator; + +class Iso639LanguageDescriptor : public Descriptor +{ + protected: + Iso639LanguageVector iso639Languages; + + public: + Iso639LanguageDescriptor(const uint8_t * const buffer); + ~Iso639LanguageDescriptor(void); + + const Iso639LanguageVector *getIso639Languages(void) const; +}; + +#endif /* __dvb_descriptor_iso639_language_descriptor_h__ */ diff --git a/lib/dvb_si/linkage_descriptor.cpp b/lib/dvb_si/linkage_descriptor.cpp new file mode 100644 index 0000000..77b991e --- /dev/null +++ b/lib/dvb_si/linkage_descriptor.cpp @@ -0,0 +1,107 @@ +/* + * $Id: linkage_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +LinkageDescriptor::LinkageDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + transportStreamId = (buffer[2] << 8) | buffer[3]; + originalNetworkId = (buffer[4] << 8) | buffer[5]; + serviceId = (buffer[6] << 8) | buffer[7]; + linkageType = buffer[8]; + + if (linkageType != 0x08) + { + if (descriptorLength < 7) + return; + + for (uint16_t i = 0; i < descriptorLength - 7; ++i) + privateDataBytes.push_back(buffer[i + 9]); + } + + else { + handOverType = (buffer[9] >> 4) & 0x0f; + reserved = (buffer[9] >> 1) & 0x07; + originType = buffer[9] & 0x01; + + uint8_t offset = 0; + + if ((handOverType >= 0x01) && (handOverType <= 0x03)) { + networkId = (buffer[10] << 8) | buffer[11]; + offset += 2; + } + + if (originType == 0x00) { + initialServiceId = (buffer[offset + 10] << 8) | buffer[offset + 11]; + offset += 2; + } + + if (descriptorLength >= (unsigned)(offset+8)) + for (uint16_t i = 0; i < descriptorLength - (offset + 8); ++i) + privateDataBytes.push_back(buffer[i + offset + 10]); + } +} + +uint16_t LinkageDescriptor::getTransportStreamId(void) const +{ + return transportStreamId; +} + +uint16_t LinkageDescriptor::getOriginalNetworkId(void) const +{ + return originalNetworkId; +} + +uint16_t LinkageDescriptor::getServiceId(void) const +{ + return serviceId; +} + +uint8_t LinkageDescriptor::getLinkageType(void) const +{ + return linkageType; +} + +const PrivateDataByteVector *LinkageDescriptor::getPrivateDataBytes(void) const +{ + return &privateDataBytes; +} + +uint8_t LinkageDescriptor::getHandOverType(void) const +{ + return handOverType; +} + +uint8_t LinkageDescriptor::getOriginType(void) const +{ + return originType; +} + +uint16_t LinkageDescriptor::getNetworkId(void) const +{ + return networkId; +} + +uint16_t LinkageDescriptor::getInitialServiceId(void) const +{ + return initialServiceId; +} + diff --git a/lib/dvb_si/linkage_descriptor.h b/lib/dvb_si/linkage_descriptor.h new file mode 100644 index 0000000..cfecf50 --- /dev/null +++ b/lib/dvb_si/linkage_descriptor.h @@ -0,0 +1,59 @@ +/* + * $Id: linkage_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_linkage_descriptor_h__ +#define __dvb_descriptor_linkage_descriptor_h__ + +#include "descriptor.h" + +typedef std::vector PrivateDataByteVector; +typedef PrivateDataByteVector::iterator PrivateDataByteIterator; +typedef PrivateDataByteVector::const_iterator PrivateDataByteConstIterator; + +class LinkageDescriptor : public Descriptor +{ + protected: + unsigned transportStreamId : 16; + unsigned originalNetworkId : 16; + unsigned serviceId : 16; + unsigned linkageType : 8; + PrivateDataByteVector privateDataBytes; + unsigned handOverType : 4; + unsigned reserved : 3; + unsigned originType : 1; + unsigned networkId : 16; + unsigned initialServiceId : 16; + + public: + LinkageDescriptor(const uint8_t * const buffer); + + uint16_t getTransportStreamId(void) const; + uint16_t getOriginalNetworkId(void) const; + uint16_t getServiceId(void) const; + uint8_t getLinkageType(void) const; + const PrivateDataByteVector *getPrivateDataBytes(void) const; + uint8_t getHandOverType(void) const; + uint8_t getOriginType(void) const; + uint16_t getNetworkId(void) const; + uint16_t getInitialServiceId(void) const; +}; + +#endif /* __dvb_descriptor_linkage_descriptor_h__ */ diff --git a/lib/dvb_si/local_time_offset_descriptor.cpp b/lib/dvb_si/local_time_offset_descriptor.cpp new file mode 100644 index 0000000..72fc53b --- /dev/null +++ b/lib/dvb_si/local_time_offset_descriptor.cpp @@ -0,0 +1,88 @@ +/* + * $Id: local_time_offset_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +LocalTimeOffset::LocalTimeOffset(const uint8_t * const buffer) +{ + countryCode.assign((char *)&buffer[0], 3); + countryRegionId = (buffer[3] >> 2) & 0x3f; + reserved = (buffer[3] >> 1) & 0x01; + localTimeOffsetPolarity = buffer[3] & 0x01; + localTimeOffset = (buffer[4] << 8) | buffer[5]; + timeOfChangeMjd = (buffer[6] << 8) | buffer[7]; + timeOfChangeBcd = (buffer[8] << 16) | (buffer[9] << 8) | buffer[10]; + nextTimeOffset = (buffer[11] << 8) | buffer[12]; +} + +std::string LocalTimeOffset::getCountryCode(void) const +{ + return countryCode; +} + +uint8_t LocalTimeOffset::getCountryRegionId(void) const +{ + return countryRegionId; +} + +uint8_t LocalTimeOffset::getLocalTimeOffsetPolarity(void) const +{ + return localTimeOffsetPolarity; +} + +uint16_t LocalTimeOffset::getLocalTimeOffset(void) const +{ + return localTimeOffset; +} + +uint16_t LocalTimeOffset::getTimeOfChangeMjd(void) const +{ + return timeOfChangeMjd; +} + +uint32_t LocalTimeOffset::getTimeOfChangeBcd(void) const +{ + return timeOfChangeBcd; +} + +uint16_t LocalTimeOffset::getNextTimeOffset(void) const +{ + return nextTimeOffset; +} + +LocalTimeOffsetDescriptor::LocalTimeOffsetDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + for (uint16_t i = 0; i < descriptorLength; i += 13) + localTimeOffsets.push_back(new LocalTimeOffset(&buffer[i + 2])); +} + + +LocalTimeOffsetDescriptor::~LocalTimeOffsetDescriptor(void) +{ + for (LocalTimeOffsetIterator i = localTimeOffsets.begin(); i != localTimeOffsets.end(); ++i) + delete *i; +} + +const LocalTimeOffsetVector *LocalTimeOffsetDescriptor::getLocalTimeOffsets(void) const +{ + return &localTimeOffsets; +} + diff --git a/lib/dvb_si/local_time_offset_descriptor.h b/lib/dvb_si/local_time_offset_descriptor.h new file mode 100644 index 0000000..85373f7 --- /dev/null +++ b/lib/dvb_si/local_time_offset_descriptor.h @@ -0,0 +1,67 @@ +/* + * $Id: local_time_offset_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_local_time_offset_descriptor_h__ +#define __dvb_descriptor_local_time_offset_descriptor_h__ + +#include "descriptor.h" + +class LocalTimeOffset +{ + protected: + std::string countryCode; + unsigned countryRegionId : 6; + unsigned reserved : 1; + unsigned localTimeOffsetPolarity : 1; + unsigned localTimeOffset : 16; + unsigned timeOfChangeMjd : 16; + unsigned timeOfChangeBcd : 24; + unsigned nextTimeOffset : 16; + + public: + LocalTimeOffset(const uint8_t * const buffer); + + std::string getCountryCode(void) const; + uint8_t getCountryRegionId(void) const; + uint8_t getLocalTimeOffsetPolarity(void) const; + uint16_t getLocalTimeOffset(void) const; + uint16_t getTimeOfChangeMjd(void) const; + uint32_t getTimeOfChangeBcd(void) const; + uint16_t getNextTimeOffset(void) const; +}; + +typedef std::vector LocalTimeOffsetVector; +typedef LocalTimeOffsetVector::iterator LocalTimeOffsetIterator; +typedef LocalTimeOffsetVector::const_iterator LocalTimeOffsetConstIterator; + +class LocalTimeOffsetDescriptor : public Descriptor +{ + protected: + LocalTimeOffsetVector localTimeOffsets; + + public: + LocalTimeOffsetDescriptor(const uint8_t * const buffer); + ~LocalTimeOffsetDescriptor(void); + + const LocalTimeOffsetVector *getLocalTimeOffsets(void) const; +}; + +#endif /* __dvb_descriptor_local_time_offset_descriptor_h__ */ diff --git a/lib/dvb_si/long_crc_table.cpp b/lib/dvb_si/long_crc_table.cpp new file mode 100644 index 0000000..2f91652 --- /dev/null +++ b/lib/dvb_si/long_crc_table.cpp @@ -0,0 +1,36 @@ +/* + * $Id: long_crc_table.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +LongCrcTable::LongCrcTable(const uint8_t * const buffer) : LongTable(buffer) +{ + crc32 = (buffer[sectionLength - 1] << 24) | + (buffer[sectionLength + 0] << 16) | + (buffer[sectionLength + 1] << 8) | + (buffer[sectionLength + 2]); +} + +uint32_t LongCrcTable::getCrc32(void) const +{ + return crc32; +} + diff --git a/lib/dvb_si/long_crc_table.h b/lib/dvb_si/long_crc_table.h new file mode 100644 index 0000000..14f4ded --- /dev/null +++ b/lib/dvb_si/long_crc_table.h @@ -0,0 +1,44 @@ +/* + * $Id: long_crc_table.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_table_long_crc_table_h__ +#define __dvb_table_long_crc_table_h__ + +#include "long_table.h" + +class LongCrcTable : public LongTable +{ + protected: + unsigned crc32 : 32; + + public: + LongCrcTable(const uint8_t * const buffer); + + static const uint8_t CRC32 = 1; + + uint32_t getCrc32(void) const; +}; + +typedef std::vector LongCrcTableVector; +typedef LongCrcTableVector::iterator LongCrcTableIterator; +typedef LongCrcTableVector::const_iterator LongCrcTableConstIterator; + +#endif /* __dvb_table_long_crc_table_h__ */ diff --git a/lib/dvb_si/long_table.cpp b/lib/dvb_si/long_table.cpp new file mode 100644 index 0000000..f6bcbdf --- /dev/null +++ b/lib/dvb_si/long_table.cpp @@ -0,0 +1,88 @@ +/* + * $Id: long_table.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +LongTable::LongTable(const uint8_t * const buffer) : ShortTable(buffer) +{ + tableIdExtension = (buffer[3] << 8) | buffer[4]; + reserved3 = (buffer[5] >> 6) & 0x03; + versionNumber = (buffer[5] >> 1) & 0x1F; + currentNextIndicator = buffer[5] & 0x01; + sectionNumber = buffer[6]; + lastSectionNumber = buffer[7]; +} + +uint16_t LongTable::getTableIdExtension(void) const +{ + return tableIdExtension; +} + +uint8_t LongTable::getVersionNumber(void) const +{ + return versionNumber; +} + +uint8_t LongTable::getCurrentNextIndicator(void) const +{ + return currentNextIndicator; +} + +uint8_t LongTable::getSectionNumber(void) const +{ + return sectionNumber; +} + +uint8_t LongTable::getLastSectionNumber(void) const +{ + return lastSectionNumber; +} + +bool LongTable::operator< (const LongTable &t) const +{ + return (sectionNumber < t.sectionNumber); +} + +bool LongTable::operator> (const LongTable &t) const +{ + return (sectionNumber > t.sectionNumber); +} + +bool LongTable::operator<= (const LongTable &t) const +{ + return (sectionNumber <= t.sectionNumber); +} + +bool LongTable::operator>= (const LongTable &t) const +{ + return (sectionNumber >= t.sectionNumber); +} + +bool LongTable::operator== (const LongTable &t) const +{ + return (sectionNumber == t.sectionNumber); +} + +bool LongTable::operator!= (const LongTable &t) const +{ + return (sectionNumber != t.sectionNumber); +} + diff --git a/lib/dvb_si/long_table.h b/lib/dvb_si/long_table.h new file mode 100644 index 0000000..31341d4 --- /dev/null +++ b/lib/dvb_si/long_table.h @@ -0,0 +1,60 @@ +/* + * $Id: long_table.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_table_long_table_h__ +#define __dvb_table_long_table_h__ + +#include "short_table.h" + +class LongTable : public ShortTable +{ + protected: + unsigned tableIdExtension : 16; + unsigned reserved3 : 2; + unsigned versionNumber : 5; + unsigned currentNextIndicator : 1; + unsigned sectionNumber : 8; + unsigned lastSectionNumber : 8; + + public: + LongTable(const uint8_t * const buffer); + + static const uint8_t SYNTAX = 1; + + uint16_t getTableIdExtension(void) const; + uint8_t getVersionNumber(void) const; + uint8_t getCurrentNextIndicator(void) const; + uint8_t getSectionNumber(void) const; + uint8_t getLastSectionNumber(void) const; + + bool operator< (const LongTable &t) const; + bool operator> (const LongTable &t) const; + bool operator<= (const LongTable &t) const; + bool operator>= (const LongTable &t) const; + bool operator== (const LongTable &t) const; + bool operator!= (const LongTable &t) const; +}; + +typedef std::vector LongTableVector; +typedef LongTableVector::iterator LongTableIterator; +typedef LongTableVector::const_iterator LongTableConstIterator; + +#endif /* __dvb_table_long_table_h__ */ diff --git a/lib/dvb_si/maximum_bitrate_descriptor.h b/lib/dvb_si/maximum_bitrate_descriptor.h new file mode 100644 index 0000000..54ed29c --- /dev/null +++ b/lib/dvb_si/maximum_bitrate_descriptor.h @@ -0,0 +1,39 @@ +/* + * $Id: maximum_bitrate_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_maximum_bitrate_descriptor_h__ +#define __dvb_descriptor_maximum_bitrate_descriptor_h__ + +#include "descriptor.h" + +class MaximumBitrateDescriptor : public Descriptor +{ + protected: + unsigned reserved : 2; + unsigned maximumBitrate : 22; + + public: + MaximumBitrateDescriptor(const uint8_t * const buffer); + + uint32_t getMaximumBitrate(void) const; +}; + +#endif /* __dvb_descriptor_maximum_bitrate_descriptor_h__ */ diff --git a/lib/dvb_si/mosaic_descriptor.cpp b/lib/dvb_si/mosaic_descriptor.cpp new file mode 100644 index 0000000..f13bccf --- /dev/null +++ b/lib/dvb_si/mosaic_descriptor.cpp @@ -0,0 +1,171 @@ +/* + * $Id: mosaic_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +ElementaryCellField::ElementaryCellField (const uint8_t * const buffer) +{ + reserved = (buffer[0] >> 6) & 0x03; + elementaryCellId = buffer[0] & 0x3F; +} + +uint8_t ElementaryCellField::getElementaryCellId(void) const +{ + return elementaryCellId; +} + +MosaicCell::MosaicCell (const uint8_t * const buffer) +{ + logicalCellId = (buffer[0] >> 2) & 0x3F; + reserved = (((buffer[0] & 0x03) << 8) | (buffer[1] & 0xF1)) >> 3; + logicalCellPresentationInfo = buffer[1] & 0x07; + elementaryCellFieldLength = buffer[2]; + + for (uint16_t i = 0; i < elementaryCellFieldLength; ++i) + elementaryCellFields.push_back(new ElementaryCellField(&buffer[i + 3])); + + cellLinkageInfo = buffer[elementaryCellFieldLength + 3]; + + switch (cellLinkageInfo) { + case 0x01: + bouquetId = (buffer[elementaryCellFieldLength + 4] << 8) | buffer[elementaryCellFieldLength + 5]; + break; + case 0x02: + case 0x03: + case 0x04: + originalNetworkId = (buffer[elementaryCellFieldLength + 4] << 8) | buffer[elementaryCellFieldLength + 5]; + transportStreamId = (buffer[elementaryCellFieldLength + 6] << 8) | buffer[elementaryCellFieldLength + 7]; + serviceId = (buffer[elementaryCellFieldLength + 8] << 8) | buffer[elementaryCellFieldLength + 9]; + break; + default: + break; + } + + if (cellLinkageInfo == 0x04) + eventId = (buffer[elementaryCellFieldLength + 10] << 8) | buffer[elementaryCellFieldLength + 11]; +} + +MosaicCell::~MosaicCell(void) +{ + for (ElementaryCellFieldIterator i = elementaryCellFields.begin(); i != elementaryCellFields.end(); ++i) + delete *i; +} + +uint8_t MosaicCell::getLogicalCellId(void) const +{ + return logicalCellId; +} + +uint8_t MosaicCell::getLogicalCellPresentationInfo(void) const +{ + return logicalCellPresentationInfo; +} + +const ElementaryCellFieldVector *MosaicCell::getElementaryCellFields(void) const +{ + return &elementaryCellFields; +} + +uint8_t MosaicCell::getCellLinkageInfo(void) const +{ + return cellLinkageInfo; +} + +uint16_t MosaicCell::getBouquetId(void) const +{ + return bouquetId; +} + +uint16_t MosaicCell::getOriginalNetworkId(void) const +{ + return originalNetworkId; +} + +uint16_t MosaicCell::getTransportStreamId(void) const +{ + return transportStreamId; +} + +uint16_t MosaicCell::getServiceId(void) const +{ + return serviceId; +} + +uint16_t MosaicCell::getEventId(void) const +{ + return eventId; +} + +MosaicDescriptor::MosaicDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + if (descriptorLength < 1) + return; + + mosaicEntryPoint = (buffer[2] >> 7) & 0x01; + numberOfHorizontalElementaryCells = (buffer[2] >> 4) & 0x07; + reserved = (buffer[2] >> 3) & 0x01; + numberOfVerticalElementaryCells = buffer[2] & 0x07; + + for (uint16_t i = 0; i < descriptorLength - 1; i += buffer[i + 6] + 2) { + mosaicCells.push_back(new MosaicCell(&buffer[i + 1])); + switch (buffer[i + 6 + buffer[i + 6] + 1]) { + case 0x01: + i += 2; + break; + case 0x02: + case 0x03: + i += 6; + break; + case 0x04: + i += 8; + break; + default: + break; + } + } +} + +MosaicDescriptor::~MosaicDescriptor(void) +{ + for (MosaicCellIterator i = mosaicCells.begin(); i != mosaicCells.end(); ++i) + delete *i; +} + +uint8_t MosaicDescriptor::getMosaicEntryPoint(void) const +{ + return mosaicEntryPoint; +} + +uint8_t MosaicDescriptor::getNumberOfHorizontalElementaryCells(void) const +{ + return numberOfHorizontalElementaryCells; +} + +uint8_t MosaicDescriptor::getNumberOfVerticalElementaryCells(void) const +{ + return numberOfVerticalElementaryCells; +} + +const MosaicCellVector *MosaicDescriptor::getMosaicCells(void) const +{ + return &mosaicCells; +} + diff --git a/lib/dvb_si/mosaic_descriptor.h b/lib/dvb_si/mosaic_descriptor.h new file mode 100644 index 0000000..e9f112e --- /dev/null +++ b/lib/dvb_si/mosaic_descriptor.h @@ -0,0 +1,96 @@ +/* + * $Id: mosaic_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_mosaic_descriptor_h__ +#define __dvb_descriptor_mosaic_descriptor_h__ + +#include "descriptor.h" + +class ElementaryCellField +{ + protected: + unsigned reserved : 2; + unsigned elementaryCellId : 6; + + public: + ElementaryCellField(const uint8_t * const buffer); + + uint8_t getElementaryCellId(void) const; +}; + +typedef std::vector ElementaryCellFieldVector; +typedef ElementaryCellFieldVector::iterator ElementaryCellFieldIterator; +typedef ElementaryCellFieldVector::const_iterator ElementaryCellFieldConstIterator; + +class MosaicCell +{ + protected: + unsigned logicalCellId : 6; + unsigned reserved : 7; + unsigned logicalCellPresentationInfo : 3; + unsigned elementaryCellFieldLength : 8; + ElementaryCellFieldVector elementaryCellFields; + unsigned cellLinkageInfo : 8; + unsigned bouquetId : 16; + unsigned originalNetworkId : 16; + unsigned transportStreamId : 16; + unsigned serviceId : 16; + unsigned eventId : 16; + + public: + MosaicCell(const uint8_t * const buffer); + ~MosaicCell(void); + + uint8_t getLogicalCellId(void) const; + uint8_t getLogicalCellPresentationInfo(void) const; + const ElementaryCellFieldVector *getElementaryCellFields(void) const; + uint8_t getCellLinkageInfo(void) const; + uint16_t getBouquetId(void) const; + uint16_t getOriginalNetworkId(void) const; + uint16_t getTransportStreamId(void) const; + uint16_t getServiceId(void) const; + uint16_t getEventId(void) const; +}; + +typedef std::vector MosaicCellVector; +typedef MosaicCellVector::iterator MosaicCellIterator; +typedef MosaicCellVector::const_iterator MosaicCellConstIterator; + +class MosaicDescriptor : public Descriptor +{ + protected: + unsigned mosaicEntryPoint : 1; + unsigned numberOfHorizontalElementaryCells : 3; + unsigned reserved : 1; + unsigned numberOfVerticalElementaryCells : 3; + MosaicCellVector mosaicCells; + + public: + MosaicDescriptor(const uint8_t * const buffer); + ~MosaicDescriptor(void); + + uint8_t getMosaicEntryPoint(void) const; + uint8_t getNumberOfHorizontalElementaryCells(void) const; + uint8_t getNumberOfVerticalElementaryCells(void) const; + const MosaicCellVector *getMosaicCells(void) const; +}; + +#endif /* __dvb_descriptor_mosaic_descriptor_h__ */ diff --git a/lib/dvb_si/multilingual_bouquet_name_descriptor.cpp b/lib/dvb_si/multilingual_bouquet_name_descriptor.cpp new file mode 100644 index 0000000..d628dc0 --- /dev/null +++ b/lib/dvb_si/multilingual_bouquet_name_descriptor.cpp @@ -0,0 +1,57 @@ +/* + * $Id: multilingual_bouquet_name_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +MultilingualBouquetName::MultilingualBouquetName(const uint8_t * const buffer) +{ + iso639LanguageCode.assign((char *)&buffer[0], 3); + bouquetNameLength = buffer[3]; + bouquetName.assign((char *)&buffer[4], bouquetNameLength); +} + +std::string MultilingualBouquetName::getIso639LanguageCode(void) const +{ + return iso639LanguageCode; +} + +std::string MultilingualBouquetName::getBouquetName(void) const +{ + return bouquetName; +} + +MultilingualBouquetNameDescriptor::MultilingualBouquetNameDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + for (uint16_t i = 0; i < descriptorLength; i += buffer[i + 3] + 2) + multilingualBouquetNames.push_back(new MultilingualBouquetName(&buffer[i + 2])); +} + +MultilingualBouquetNameDescriptor::~MultilingualBouquetNameDescriptor(void) +{ + for (MultilingualBouquetNameIterator i = multilingualBouquetNames.begin(); i != multilingualBouquetNames.end(); ++i) + delete *i; +} + +const MultilingualBouquetNameVector *MultilingualBouquetNameDescriptor::getMultilingualBouquetNames(void) const +{ + return &multilingualBouquetNames; +} + diff --git a/lib/dvb_si/multilingual_bouquet_name_descriptor.h b/lib/dvb_si/multilingual_bouquet_name_descriptor.h new file mode 100644 index 0000000..3864f74 --- /dev/null +++ b/lib/dvb_si/multilingual_bouquet_name_descriptor.h @@ -0,0 +1,57 @@ +/* + * $Id: multilingual_bouquet_name_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_multilingual_bouquet_name_descriptor_h__ +#define __dvb_descriptor_multilingual_bouquet_name_descriptor_h__ + +#include "descriptor.h" + +class MultilingualBouquetName +{ + protected: + std::string iso639LanguageCode; + unsigned bouquetNameLength : 8; + std::string bouquetName; + + public: + MultilingualBouquetName(const uint8_t * const buffer); + + std::string getIso639LanguageCode(void) const; + std::string getBouquetName(void) const; +}; + +typedef std::vector MultilingualBouquetNameVector; +typedef MultilingualBouquetNameVector::iterator MultilingualBouquetNameIterator; +typedef MultilingualBouquetNameVector::const_iterator MultilingualBouquetNameConstIterator; + +class MultilingualBouquetNameDescriptor : public Descriptor +{ + protected: + MultilingualBouquetNameVector multilingualBouquetNames; + + public: + MultilingualBouquetNameDescriptor(const uint8_t * const buffer); + ~MultilingualBouquetNameDescriptor(void); + + const MultilingualBouquetNameVector *getMultilingualBouquetNames(void) const; +}; + +#endif /* __dvb_descriptor_multilingual_bouquet_name_descriptor_h__ */ diff --git a/lib/dvb_si/multilingual_component_descriptor.cpp b/lib/dvb_si/multilingual_component_descriptor.cpp new file mode 100644 index 0000000..ecd4950 --- /dev/null +++ b/lib/dvb_si/multilingual_component_descriptor.cpp @@ -0,0 +1,64 @@ +/* + * $Id: multilingual_component_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +MultilingualComponent::MultilingualComponent(const uint8_t * const buffer) +{ + iso639LanguageCode.assign((char *)&buffer[0], 3); + textDescriptionLength = buffer[3]; + text.assign((char *)&buffer[4], textDescriptionLength); +} + +std::string MultilingualComponent::getIso639LanguageCode(void) const +{ + return iso639LanguageCode; +} + +std::string MultilingualComponent::getText(void) const +{ + return text; +} + +MultilingualComponentDescriptor::MultilingualComponentDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + componentTag = buffer[2]; + + for (uint16_t i = 0; i < descriptorLength - 1; i += buffer[i + 4] + 2) + multilingualComponents.push_back(new MultilingualComponent(&buffer[i + 3])); +} + +MultilingualComponentDescriptor::~MultilingualComponentDescriptor(void) +{ + for (MultilingualComponentIterator i = multilingualComponents.begin(); i != multilingualComponents.end(); ++i) + delete *i; +} + +uint8_t MultilingualComponentDescriptor::getComponentTag(void) const +{ + return componentTag; +} + +const MultilingualComponentVector *MultilingualComponentDescriptor::getMultilingualComponents(void) const +{ + return &multilingualComponents; +} + diff --git a/lib/dvb_si/multilingual_component_descriptor.h b/lib/dvb_si/multilingual_component_descriptor.h new file mode 100644 index 0000000..d7d2101 --- /dev/null +++ b/lib/dvb_si/multilingual_component_descriptor.h @@ -0,0 +1,59 @@ +/* + * $Id: multilingual_component_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_multilingual_component_descriptor_h__ +#define __dvb_descriptor_multilingual_component_descriptor_h__ + +#include "descriptor.h" + +class MultilingualComponent +{ + protected: + std::string iso639LanguageCode; + unsigned textDescriptionLength : 8; + std::string text; + + public: + MultilingualComponent(const uint8_t * const buffer); + + std::string getIso639LanguageCode(void) const; + std::string getText(void) const; +}; + +typedef std::vector MultilingualComponentVector; +typedef MultilingualComponentVector::iterator MultilingualComponentIterator; +typedef MultilingualComponentVector::const_iterator MultilingualComponentConstIterator; + +class MultilingualComponentDescriptor : public Descriptor +{ + protected: + unsigned componentTag : 8; + MultilingualComponentVector multilingualComponents; + + public: + MultilingualComponentDescriptor(const uint8_t * const buffer); + ~MultilingualComponentDescriptor(void); + + uint8_t getComponentTag(void) const; + const MultilingualComponentVector *getMultilingualComponents(void) const; +}; + +#endif /* __dvb_descriptor_multilingual_component_descriptor_h__ */ diff --git a/lib/dvb_si/multilingual_network_name_descriptor.cpp b/lib/dvb_si/multilingual_network_name_descriptor.cpp new file mode 100644 index 0000000..8bccc6c --- /dev/null +++ b/lib/dvb_si/multilingual_network_name_descriptor.cpp @@ -0,0 +1,57 @@ +/* + * $Id: multilingual_network_name_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +MultilingualNetworkName::MultilingualNetworkName(const uint8_t * const buffer) +{ + iso639LanguageCode.assign((char *)&buffer[0], 3); + networkNameLength = buffer[3]; + networkName.assign((char *)&buffer[4], networkNameLength); +} + +std::string MultilingualNetworkName::getIso639LanguageCode(void) const +{ + return iso639LanguageCode; +} + +std::string MultilingualNetworkName::getNetworkName(void) const +{ + return networkName; +} + +MultilingualNetworkNameDescriptor::MultilingualNetworkNameDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + for (uint16_t i = 0; i < descriptorLength; i += buffer[i + 5] + 4) + multilingualNetworkNames.push_back(new MultilingualNetworkName(&buffer[i + 2])); +} + +MultilingualNetworkNameDescriptor::~MultilingualNetworkNameDescriptor(void) +{ + for (MultilingualNetworkNameIterator i = multilingualNetworkNames.begin(); i != multilingualNetworkNames.end(); ++i) + delete *i; +} + +const MultilingualNetworkNameVector *MultilingualNetworkNameDescriptor::getMultilingualNetworkNames(void) const +{ + return &multilingualNetworkNames; +} + diff --git a/lib/dvb_si/multilingual_network_name_descriptor.h b/lib/dvb_si/multilingual_network_name_descriptor.h new file mode 100644 index 0000000..b7df57b --- /dev/null +++ b/lib/dvb_si/multilingual_network_name_descriptor.h @@ -0,0 +1,57 @@ +/* + * $Id: multilingual_network_name_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_multilingual_network_name_descriptor_h__ +#define __dvb_descriptor_multilingual_network_name_descriptor_h__ + +#include "descriptor.h" + +class MultilingualNetworkName +{ + protected: + std::string iso639LanguageCode; + unsigned networkNameLength : 8; + std::string networkName; + + public: + MultilingualNetworkName(const uint8_t * const buffer); + + std::string getIso639LanguageCode(void) const; + std::string getNetworkName(void) const; +}; + +typedef std::vector MultilingualNetworkNameVector; +typedef MultilingualNetworkNameVector::iterator MultilingualNetworkNameIterator; +typedef MultilingualNetworkNameVector::const_iterator MultilingualNetworkNameConstIterator; + +class MultilingualNetworkNameDescriptor : public Descriptor +{ + protected: + MultilingualNetworkNameVector multilingualNetworkNames; + + public: + MultilingualNetworkNameDescriptor(const uint8_t * const buffer); + ~MultilingualNetworkNameDescriptor(void); + + const MultilingualNetworkNameVector *getMultilingualNetworkNames(void) const; +}; + +#endif /* __dvb_descriptor_multilingual_network_name_descriptor_h__ */ diff --git a/lib/dvb_si/multilingual_service_name_descriptor.cpp b/lib/dvb_si/multilingual_service_name_descriptor.cpp new file mode 100644 index 0000000..d811aae --- /dev/null +++ b/lib/dvb_si/multilingual_service_name_descriptor.cpp @@ -0,0 +1,68 @@ +/* + * $Id: multilingual_service_name_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +MultilingualServiceName::MultilingualServiceName(const uint8_t * const buffer) +{ + iso639LanguageCode.assign((char *)&buffer[0], 3); + serviceProviderNameLength = buffer[3]; + serviceProviderName.assign((char *)&buffer[4], serviceProviderNameLength); + serviceNameLength = buffer[serviceProviderNameLength + 4]; + serviceName.assign((char *)&buffer[serviceProviderNameLength + 5], serviceNameLength); +} + +std::string MultilingualServiceName::getIso639LanguageCode(void) const +{ + return iso639LanguageCode; +} + +std::string MultilingualServiceName::getServiceProviderName(void) const +{ + return serviceProviderName; +} + +std::string MultilingualServiceName::getServiceName(void) const +{ + return serviceName; +} + +MultilingualServiceNameDescriptor::MultilingualServiceNameDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + MultilingualServiceName *name; + + for (uint16_t i = 0; i < descriptorLength; i += name->serviceProviderNameLength + name->serviceNameLength + 5) { + name = new MultilingualServiceName(&buffer[i + 2]); + multilingualServiceNames.push_back(name); + } +} + +MultilingualServiceNameDescriptor::~MultilingualServiceNameDescriptor(void) +{ + for (MultilingualServiceNameIterator i = multilingualServiceNames.begin(); i != multilingualServiceNames.end(); ++i) + delete *i; +} + +const MultilingualServiceNameVector *MultilingualServiceNameDescriptor::getMultilingualServiceNames(void) const +{ + return &multilingualServiceNames; +} + diff --git a/lib/dvb_si/multilingual_service_name_descriptor.h b/lib/dvb_si/multilingual_service_name_descriptor.h new file mode 100644 index 0000000..5979b5c --- /dev/null +++ b/lib/dvb_si/multilingual_service_name_descriptor.h @@ -0,0 +1,62 @@ +/* + * $Id: multilingual_service_name_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_multilingual_service_name_descriptor_h__ +#define __dvb_descriptor_multilingual_service_name_descriptor_h__ + +#include "descriptor.h" + +class MultilingualServiceName +{ + protected: + std::string iso639LanguageCode; + unsigned serviceProviderNameLength : 8; + std::string serviceProviderName; + unsigned serviceNameLength : 8; + std::string serviceName; + + public: + MultilingualServiceName(const uint8_t * const buffer); + + std::string getIso639LanguageCode(void) const; + std::string getServiceProviderName(void) const; + std::string getServiceName(void) const; + + friend class MultilingualServiceNameDescriptor; +}; + +typedef std::vector MultilingualServiceNameVector; +typedef MultilingualServiceNameVector::iterator MultilingualServiceNameIterator; +typedef MultilingualServiceNameVector::const_iterator MultilingualServiceNameConstIterator; + +class MultilingualServiceNameDescriptor : public Descriptor +{ + protected: + MultilingualServiceNameVector multilingualServiceNames; + + public: + MultilingualServiceNameDescriptor(const uint8_t * const buffer); + ~MultilingualServiceNameDescriptor(void); + + const MultilingualServiceNameVector *getMultilingualServiceNames(void) const; +}; + +#endif /* __dvb_descriptor_multilingual_service_name_descriptor_h__ */ diff --git a/lib/dvb_si/multiplex_buffer_utilization_descriptor.h b/lib/dvb_si/multiplex_buffer_utilization_descriptor.h new file mode 100644 index 0000000..dc7a160 --- /dev/null +++ b/lib/dvb_si/multiplex_buffer_utilization_descriptor.h @@ -0,0 +1,43 @@ +/* + * $Id: multiplex_buffer_utilization_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_multiplex_buffer_utilization_descriptor_h__ +#define __dvb_descriptor_multiplex_buffer_utilization_descriptor_h__ + +#include "descriptor.h" + +class MultiplexBufferUtilizationDescriptor : public Descriptor +{ + protected: + unsigned boundValidFlag : 1; + unsigned ltwOffsetLowerBound : 15; + unsigned reserved : 1; + unsigned ltwOffsetUpperBound : 15; + + public: + MultiplexBufferUtilizationDescriptor(const uint8_t * const buffer); + + uint8_t getBoundValidFlag(void) const; + uint16_t getLtwOffsetLowerBound(void) const; + uint16_t getLtwOffsetUpperBound(void) const; +}; + +#endif /* __dvb_descriptor_multiplex_buffer_utilization_descriptor_h__ */ diff --git a/lib/dvb_si/network_name_descriptor.cpp b/lib/dvb_si/network_name_descriptor.cpp new file mode 100644 index 0000000..3ba3872 --- /dev/null +++ b/lib/dvb_si/network_name_descriptor.cpp @@ -0,0 +1,33 @@ +/* + * $Id: network_name_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +NetworkNameDescriptor::NetworkNameDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + networkName.assign((char *)&buffer[2], descriptorLength); +} + +std::string NetworkNameDescriptor::getNetworkName(void) const +{ + return networkName; +} + diff --git a/lib/dvb_si/network_name_descriptor.h b/lib/dvb_si/network_name_descriptor.h new file mode 100644 index 0000000..694e370 --- /dev/null +++ b/lib/dvb_si/network_name_descriptor.h @@ -0,0 +1,38 @@ +/* + * $Id: network_name_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_network_name_descriptor_h__ +#define __dvb_descriptor_network_name_descriptor_h__ + +#include "descriptor.h" + +class NetworkNameDescriptor : public Descriptor +{ + protected: + std::string networkName; + + public: + NetworkNameDescriptor(const uint8_t * const buffer); + + std::string getNetworkName(void) const; +}; + +#endif /* __dvb_descriptor_network_name_descriptor_h__ */ diff --git a/lib/dvb_si/nit.cpp b/lib/dvb_si/nit.cpp new file mode 100644 index 0000000..dcf90e3 --- /dev/null +++ b/lib/dvb_si/nit.cpp @@ -0,0 +1,70 @@ +/* + * $Id: nit.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +TransportStreamInfo::TransportStreamInfo(const uint8_t * const buffer) +{ + transportStreamId = (buffer[0] << 8) | buffer[1]; + originalNetworkId = (buffer[2] << 8) | buffer[3]; + reserved1 = (buffer[4] >> 8) & 0x0F; + transportDescriptorsLength = ((buffer[4] & 0x0F) << 8) | buffer[5]; + + for (uint16_t i = 6; i < transportDescriptorsLength + 6; i += buffer[i + 1] + 2) + descriptor(&buffer[i]); +} + +uint16_t TransportStreamInfo::getTransportStreamId(void) const +{ + return transportStreamId; +} + +uint16_t TransportStreamInfo::getOriginalNetworkId(void) const +{ + return originalNetworkId; +} + +NetworkInformationTable::NetworkInformationTable(const uint8_t * const buffer) : LongCrcTable(buffer) +{ + reserved4 = (buffer[8] >> 5) & 0x0F; + networkDescriptorsLength = ((buffer[8] & 0x0F) << 8) | buffer[9]; + + for (uint16_t i = 10; i < networkDescriptorsLength + 10; i += buffer[i + 1] + 2) + descriptor(&buffer[i]); + + reserved5 = (buffer[networkDescriptorsLength + 10] >> 4) & 0x0F; + transportStreamLoopLength = ((buffer[networkDescriptorsLength + 10] & 0x0F) << 8) | buffer[networkDescriptorsLength + 11]; + + for (uint16_t i = networkDescriptorsLength + 12; i < sectionLength + 3 - 4; i += ((buffer[i + 4] & 0x0F) | buffer[i + 5]) + 6) + tsInfo.push_back(new TransportStreamInfo(&buffer[i])); +} + +NetworkInformationTable::~NetworkInformationTable(void) +{ + for (TransportStreamInfoIterator i = tsInfo.begin(); i != tsInfo.end(); ++i) + delete *i; +} + +const TransportStreamInfoVector *NetworkInformationTable::getTsInfo(void) const +{ + return &tsInfo; +} + diff --git a/lib/dvb_si/nit.h b/lib/dvb_si/nit.h new file mode 100644 index 0000000..de92a34 --- /dev/null +++ b/lib/dvb_si/nit.h @@ -0,0 +1,71 @@ +/* + * $Id: nit.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_table_nit_h__ +#define __dvb_table_nit_h__ + +#include +#include "long_crc_table.h" + +class TransportStreamInfo : public DescriptorContainer +{ + protected: + unsigned transportStreamId : 16; + unsigned originalNetworkId : 16; + unsigned reserved1 : 4; + unsigned transportDescriptorsLength : 12; + + public: + TransportStreamInfo(const uint8_t * const buffer); + + uint16_t getTransportStreamId(void) const; + uint16_t getOriginalNetworkId(void) const; +}; + +typedef std::vector TransportStreamInfoVector; +typedef TransportStreamInfoVector::iterator TransportStreamInfoIterator; +typedef TransportStreamInfoVector::const_iterator TransportStreamInfoConstIterator; + +class NetworkInformationTable : public LongCrcTable, public DescriptorContainer +{ + protected: + unsigned reserved4 : 3; + unsigned networkDescriptorsLength : 12; + unsigned reserved5 : 4; + unsigned transportStreamLoopLength : 12; + TransportStreamInfoVector tsInfo; + + public: + NetworkInformationTable(const uint8_t * const buffer); + ~NetworkInformationTable(void); + + static const enum PacketId PID = PID_NIT; + static const enum TableId TID = TID_NIT_ACTUAL; + static const uint32_t TIMEOUT = 12000; + + const TransportStreamInfoVector *getTsInfo(void) const; +}; + +typedef std::vector NetworkInformationTableVector; +typedef NetworkInformationTableVector::iterator NetworkInformationTableIterator; +typedef NetworkInformationTableVector::const_iterator NetworkInformationTableConstIterator; + +#endif /* __dvb_table_nit_h__ */ diff --git a/lib/dvb_si/nvod_reference_descriptor.cpp b/lib/dvb_si/nvod_reference_descriptor.cpp new file mode 100644 index 0000000..d742581 --- /dev/null +++ b/lib/dvb_si/nvod_reference_descriptor.cpp @@ -0,0 +1,63 @@ +/* + * $Id: nvod_reference_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + + +NvodReference::NvodReference(const uint8_t * const buffer) +{ + transportStreamId = (buffer[0] << 8) | buffer[1]; + originalNetworkId = (buffer[2] << 8) | buffer[3]; + serviceId = (buffer[4] << 8) | buffer[5]; +} + +uint16_t NvodReference::getTransportStreamId(void) const +{ + return transportStreamId; +} + +uint16_t NvodReference::getOriginalNetworkId(void) const +{ + return originalNetworkId; +} + +uint16_t NvodReference::getServiceId(void) const +{ + return serviceId; +} + +NvodReferenceDescriptor::NvodReferenceDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + for (uint16_t i = 0; i < descriptorLength; i += 6) + nvodReferences.push_back(new NvodReference(&buffer[i + 2])); +} + +NvodReferenceDescriptor::~NvodReferenceDescriptor(void) +{ + for (NvodReferenceIterator i = nvodReferences.begin(); i != nvodReferences.end(); ++i) + delete *i; +} + +const NvodReferenceVector *NvodReferenceDescriptor::getNvodReferences(void) const +{ + return &nvodReferences; +} + diff --git a/lib/dvb_si/nvod_reference_descriptor.h b/lib/dvb_si/nvod_reference_descriptor.h new file mode 100644 index 0000000..4b434c3 --- /dev/null +++ b/lib/dvb_si/nvod_reference_descriptor.h @@ -0,0 +1,58 @@ +/* + * $Id: nvod_reference_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_nvod_reference_descriptor_h__ +#define __dvb_descriptor_nvod_reference_descriptor_h__ + +#include "descriptor.h" + +class NvodReference +{ + protected: + unsigned transportStreamId : 16; + unsigned originalNetworkId : 16; + unsigned serviceId : 16; + + public: + NvodReference(const uint8_t * const buffer); + + uint16_t getTransportStreamId(void) const; + uint16_t getOriginalNetworkId(void) const; + uint16_t getServiceId(void) const; +}; + +typedef std::vector NvodReferenceVector; +typedef NvodReferenceVector::iterator NvodReferenceIterator; +typedef NvodReferenceVector::const_iterator NvodReferenceConstIterator; + +class NvodReferenceDescriptor : public Descriptor +{ + protected: + NvodReferenceVector nvodReferences; + + public: + NvodReferenceDescriptor(const uint8_t * const buffer); + ~NvodReferenceDescriptor(void); + + const NvodReferenceVector* getNvodReferences(void) const; +}; + +#endif /* __dvb_descriptor_nvod_reference_descriptor_h__ */ diff --git a/lib/dvb_si/packet_id.h b/lib/dvb_si/packet_id.h new file mode 100644 index 0000000..3f18f08 --- /dev/null +++ b/lib/dvb_si/packet_id.h @@ -0,0 +1,45 @@ +/* + * $Id: packet_id.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_id_packet_id_h__ +#define __dvb_id_packet_id_h__ + +enum PacketId { + /* ETSI EN 300 468 V1.5.1 (2003-01) */ + PID_PAT = 0x0000, + PID_CAT = 0x0001, + PID_TSDT = 0x0002, + PID_NIT = 0x0010, + PID_BAT = 0x0011, + PID_SDT = 0x0011, + PID_EIT = 0x0012, + PID_RST = 0x0013, + PID_TDT = 0x0014, + PID_TOT = 0x0014, + PID_NS = 0x0015, /* network synchronization */ + PID_IS = 0x001C, /* inband signaling (SIS-12) */ + PID_M = 0x001D, /* measurement (SIS-10) */ + PID_DIT = 0x001E, + PID_SIT = 0x001F, + PID_RESERVED = 0x1FFF +}; + +#endif /* __dvb_id_packet_id_h__ */ diff --git a/lib/dvb_si/parental_rating_descriptor.cpp b/lib/dvb_si/parental_rating_descriptor.cpp new file mode 100644 index 0000000..3926267 --- /dev/null +++ b/lib/dvb_si/parental_rating_descriptor.cpp @@ -0,0 +1,56 @@ +/* + * $Id: parental_rating_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +ParentalRating::ParentalRating(const uint8_t * const buffer) +{ + countryCode.assign((char *)&buffer[0], 3); + rating = buffer[3]; +} + +std::string ParentalRating::getCountryCode(void) const +{ + return countryCode; +} + +uint8_t ParentalRating::getRating(void) const +{ + return rating; +} + +ParentalRatingDescriptor::ParentalRatingDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + for (uint16_t i = 0; i < descriptorLength; i += 4) + parentalRatings.push_back(new ParentalRating(&buffer[i + 2])); +} + +ParentalRatingDescriptor::~ParentalRatingDescriptor(void) +{ + for (ParentalRatingIterator i = parentalRatings.begin(); i != parentalRatings.end(); ++i) + delete *i; +} + +const ParentalRatingVector *ParentalRatingDescriptor::getParentalRatings(void) const +{ + return &parentalRatings; +} + diff --git a/lib/dvb_si/parental_rating_descriptor.h b/lib/dvb_si/parental_rating_descriptor.h new file mode 100644 index 0000000..5528872 --- /dev/null +++ b/lib/dvb_si/parental_rating_descriptor.h @@ -0,0 +1,56 @@ +/* + * $Id: parental_rating_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_parental_rating_descriptor_h__ +#define __dvb_descriptor_parental_rating_descriptor_h__ + +#include "descriptor.h" + +class ParentalRating +{ + protected: + std::string countryCode; + unsigned rating : 8; + + public: + ParentalRating(const uint8_t * const buffer); + + std::string getCountryCode(void) const; + uint8_t getRating(void) const; +}; + +typedef std::vector ParentalRatingVector; +typedef ParentalRatingVector::iterator ParentalRatingIterator; +typedef ParentalRatingVector::const_iterator ParentalRatingConstIterator; + +class ParentalRatingDescriptor : public Descriptor +{ + protected: + ParentalRatingVector parentalRatings; + + public: + ParentalRatingDescriptor(const uint8_t * const buffer); + ~ParentalRatingDescriptor(void); + + const ParentalRatingVector *getParentalRatings(void) const; +}; + +#endif /* __dvb_descriptor_parental_rating_descriptor_h__ */ diff --git a/lib/dvb_si/partial_transport_stream_descriptor.h b/lib/dvb_si/partial_transport_stream_descriptor.h new file mode 100644 index 0000000..dc91226 --- /dev/null +++ b/lib/dvb_si/partial_transport_stream_descriptor.h @@ -0,0 +1,45 @@ +/* + * $Id: partial_transport_stream_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_partial_transport_stream_descriptor_h__ +#define __dvb_descriptor_partial_transport_stream_descriptor_h__ + +#include "descriptor.h" + +class PartialTransportStreamDescriptor : public Descriptor +{ + protected: + unsigned reserved : 2; + unsigned peakRate : 22; + unsigned reserved2 : 2; + unsigned minimumOverallSmootingRate : 22; + unsigned reserved3 : 2; + unsigned maximumOverallSmoothingBuffer : 14; + + public: + PartialTransportStreamDescriptor(const uint8_t * const buffer); + + uint32_t getPeakRate(void) const; + uint32_t getMinimumOverallSmoothingRate(void) const; + uint16_t getMaximumOverallSmoothingBuffer(void) const; +}; + +#endif /* __dvb_descriptor_partial_transport_stream_descriptor_h__ */ diff --git a/lib/dvb_si/pat.cpp b/lib/dvb_si/pat.cpp new file mode 100644 index 0000000..498f67f --- /dev/null +++ b/lib/dvb_si/pat.cpp @@ -0,0 +1,81 @@ +/* + * $Id: pat.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +NetworkAssociation::NetworkAssociation(const uint8_t * const buffer) +{ + programNumber = (buffer[0] << 8) | buffer[1]; + reserved = (buffer[2] >> 5) & 0x07; + networkPid = ((buffer[2] & 0x1F) << 8) | buffer[3]; +} + +uint16_t NetworkAssociation::getNetworkPid(void) const +{ + return networkPid; +} + +ProgramAssociation::ProgramAssociation(const uint8_t * const buffer) +{ + programNumber = (buffer[0] << 8) | buffer[1]; + reserved = (buffer[2] >> 5) & 0x07; + programMapPid = ((buffer[2] & 0x1F) << 8) | buffer[3]; +} + +uint16_t ProgramAssociation::getProgramNumber(void) const +{ + return programNumber; +} + +uint16_t ProgramAssociation::getProgramMapPid(void) const +{ + return programMapPid; +} + +ProgramAssociationTable::ProgramAssociationTable(const uint8_t * const buffer) : LongCrcTable(buffer) +{ + for (uint16_t i = 8; i < sectionLength - 1; i += 4) { + if (((buffer[i] << 8) | buffer[i + 1]) == 0) + networks.push_back(new NetworkAssociation(&buffer[i])); + else + programs.push_back(new ProgramAssociation(&buffer[i])); + } +} + +ProgramAssociationTable::~ProgramAssociationTable(void) +{ + for (NetworkAssociationIterator i = networks.begin(); i != networks.end(); ++i) + delete *i; + + for (ProgramAssociationIterator i = programs.begin(); i != programs.end(); ++i) + delete *i; +} + +const NetworkAssociationVector *ProgramAssociationTable::getNetworks(void) const +{ + return &networks; +} + +const ProgramAssociationVector *ProgramAssociationTable::getPrograms(void) const +{ + return &programs; +} + diff --git a/lib/dvb_si/pat.h b/lib/dvb_si/pat.h new file mode 100644 index 0000000..f17586a --- /dev/null +++ b/lib/dvb_si/pat.h @@ -0,0 +1,84 @@ +/* + * $Id: pat.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_table_pat_h__ +#define __dvb_table_pat_h__ + +#include "long_crc_table.h" + +class NetworkAssociation +{ + protected: + unsigned programNumber : 16; + unsigned reserved : 3; + unsigned networkPid : 13; + + public: + NetworkAssociation(const uint8_t * buffer); + + uint16_t getNetworkPid(void) const; +}; + +typedef std::vector NetworkAssociationVector; +typedef NetworkAssociationVector::iterator NetworkAssociationIterator; +typedef NetworkAssociationVector::const_iterator NetworkAssociationConstIterator; + +class ProgramAssociation +{ + protected: + unsigned programNumber : 16; + unsigned reserved : 3; + unsigned programMapPid : 13; + + public: + ProgramAssociation(const uint8_t * buffer); + + uint16_t getProgramNumber(void) const; + uint16_t getProgramMapPid(void) const; +}; + +typedef std::vector ProgramAssociationVector; +typedef ProgramAssociationVector::iterator ProgramAssociationIterator; +typedef ProgramAssociationVector::const_iterator ProgramAssociationConstIterator; + +class ProgramAssociationTable : public LongCrcTable +{ + protected: + NetworkAssociationVector networks; + ProgramAssociationVector programs; + + public: + ProgramAssociationTable(const uint8_t * const buffer); + ~ProgramAssociationTable(void); + + static const enum PacketId PID = PID_PAT; + static const enum TableId TID = TID_PAT; + static const uint32_t TIMEOUT = 1200; + + const NetworkAssociationVector *getNetworks(void) const; + const ProgramAssociationVector *getPrograms(void) const; +}; + +typedef std::vector ProgramAssociationTableVector; +typedef ProgramAssociationTableVector::iterator ProgramAssociationTableIterator; +typedef ProgramAssociationTableVector::const_iterator ProgramAssociationTableConstIterator; + +#endif /* __dvb_table_pat_h__ */ diff --git a/lib/dvb_si/pdc_descriptor.cpp b/lib/dvb_si/pdc_descriptor.cpp new file mode 100644 index 0000000..8a8c61a --- /dev/null +++ b/lib/dvb_si/pdc_descriptor.cpp @@ -0,0 +1,34 @@ +/* + * $Id: pdc_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +PdcDescriptor::PdcDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + reserved = (buffer[2] >> 4) & 0x0f; + programmeIdentificationLabel = ((buffer[2] & 0x0f) << 16) | buffer[3] | buffer[4]; +} + +uint32_t PdcDescriptor::getProgrammeIdentificationLabel(void) const +{ + return programmeIdentificationLabel; +} + diff --git a/lib/dvb_si/pdc_descriptor.h b/lib/dvb_si/pdc_descriptor.h new file mode 100644 index 0000000..0c8851b --- /dev/null +++ b/lib/dvb_si/pdc_descriptor.h @@ -0,0 +1,39 @@ +/* + * $Id: pdc_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_pdc_descriptor_h__ +#define __dvb_descriptor_pdc_descriptor_h__ + +#include "descriptor.h" + +class PdcDescriptor : public Descriptor +{ + protected: + unsigned reserved : 4; + unsigned programmeIdentificationLabel : 20; + + public: + PdcDescriptor(const uint8_t * const buffer); + + uint32_t getProgrammeIdentificationLabel(void) const; +}; + +#endif /* __dvb_descriptor_pcd_descriptor_h__ */ diff --git a/lib/dvb_si/pmt.cpp b/lib/dvb_si/pmt.cpp new file mode 100644 index 0000000..1c0da43 --- /dev/null +++ b/lib/dvb_si/pmt.cpp @@ -0,0 +1,75 @@ +/* + * $Id: pmt.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +ElementaryStreamInfo::ElementaryStreamInfo(const uint8_t * const buffer) +{ + streamType = buffer[0]; + reserved1 = (buffer[1] >> 5) & 0x07; + elementaryPid = ((buffer[1] & 0x1F) << 8) | buffer[2]; + reserved2 = (buffer[3] >> 4) & 0x0F; + esInfoLength = ((buffer[3] & 0x0F) << 8) | buffer[4]; + + for (uint16_t i = 5; i < esInfoLength + 5; i += buffer[i + 1] + 2) + descriptor(&buffer[i]); +} + +uint8_t ElementaryStreamInfo::getType(void) const +{ + return streamType; +} + +uint16_t ElementaryStreamInfo::getPid(void) const +{ + return elementaryPid; +} + +ProgramMapTable::ProgramMapTable(const uint8_t * const buffer) : LongCrcTable(buffer) +{ + reserved4 = (buffer[8] >> 5) & 0x07; + pcrPid = ((buffer[8] & 0x1F) << 8) | buffer[9]; + reserved5 = (buffer[10] >> 4) & 0x0F; + programInfoLength = ((buffer[10] & 0x0F) << 8) | buffer[11]; + + for (uint16_t i = 12; i < programInfoLength + 12; i += buffer[i + 1] + 2) + descriptor(&buffer[i]); + + for (uint16_t i = programInfoLength + 12; i < sectionLength - 1; i += ((buffer[i + 3] & 0x0F) | buffer[i + 4]) + 5) + esInfo.push_back(new ElementaryStreamInfo(&buffer[i])); +} + +uint16_t ProgramMapTable::getPcrPid(void) const +{ + return pcrPid; +} + +const ElementaryStreamInfoVector *ProgramMapTable::getEsInfo(void) const +{ + return &esInfo; +} + +ProgramMapTable::~ProgramMapTable(void) +{ + for (ElementaryStreamInfoIterator i = esInfo.begin(); i != esInfo.end(); ++i) + delete *i; +} + diff --git a/lib/dvb_si/pmt.h b/lib/dvb_si/pmt.h new file mode 100644 index 0000000..781d7d9 --- /dev/null +++ b/lib/dvb_si/pmt.h @@ -0,0 +1,77 @@ +/* + * $Id: pmt.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_table_pmt_h__ +#define __dvb_table_pmt_h__ + +#include +#include "long_crc_table.h" + +class ElementaryStreamInfo : public DescriptorContainer +{ + protected: + unsigned streamType : 8; + unsigned reserved1 : 3; + unsigned elementaryPid : 13; + unsigned reserved2 : 4; + unsigned esInfoLength : 12; + + public: + ElementaryStreamInfo(const uint8_t * const buffer); + + uint8_t getType(void) const; + uint16_t getPid(void) const; + + friend class CaElementaryStreamInfo; + +}; + +typedef std::vector ElementaryStreamInfoVector; +typedef ElementaryStreamInfoVector::iterator ElementaryStreamInfoIterator; +typedef ElementaryStreamInfoVector::const_iterator ElementaryStreamInfoConstIterator; + +class ProgramMapTable : public LongCrcTable, public DescriptorContainer +{ + protected: + unsigned reserved4 : 3; + unsigned pcrPid : 13; + unsigned reserved5 : 4; + unsigned programInfoLength : 12; + ElementaryStreamInfoVector esInfo; + + public: + ProgramMapTable(const uint8_t * const buffer); + ~ProgramMapTable(void); + + static const enum TableId TID = TID_PMT; + static const uint32_t TIMEOUT = 600; + + uint16_t getPcrPid(void) const; + const ElementaryStreamInfoVector *getEsInfo(void) const; + + friend class CaProgramMapTable; +}; + +typedef std::vector ProgramMapTableVector; +typedef ProgramMapTableVector::iterator ProgramMapTableIterator; +typedef ProgramMapTableVector::const_iterator ProgramMapTableConstIterator; + +#endif /* __dvb_table_pmt_h__ */ diff --git a/lib/dvb_si/private_data_indicator_descriptor.h b/lib/dvb_si/private_data_indicator_descriptor.h new file mode 100644 index 0000000..8e783cd --- /dev/null +++ b/lib/dvb_si/private_data_indicator_descriptor.h @@ -0,0 +1,38 @@ +/* + * $Id: private_data_indicator_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_private_data_indicator_descriptor_h__ +#define __dvb_descriptor_private_data_indicator_descriptor_h__ + +#include "descriptor.h" + +class PrivateDataIndicatorDescriptor : public Descriptor +{ + protected: + unsigned privateDataIndicator : 32; + + public: + PrivateDataIndicatorDescriptor(const uint8_t * const buffer); + + uint32_t getPrivateDataIndicator(void) const; +}; + +#endif /* __dvb_descriptor_private_data_indicator_descriptor_h__ */ diff --git a/lib/dvb_si/private_data_specifier_descriptor.cpp b/lib/dvb_si/private_data_specifier_descriptor.cpp new file mode 100644 index 0000000..dedcb71 --- /dev/null +++ b/lib/dvb_si/private_data_specifier_descriptor.cpp @@ -0,0 +1,33 @@ +/* + * $Id: private_data_specifier_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +PrivateDataSpecifierDescriptor::PrivateDataSpecifierDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + privateDataSpecifier = (buffer[2] << 24) | (buffer[3] << 16) | (buffer[4] << 8) | buffer[5]; +} + +uint32_t PrivateDataSpecifierDescriptor::getPrivateDataSpecifier(void) const +{ + return privateDataSpecifier; +} + diff --git a/lib/dvb_si/private_data_specifier_descriptor.h b/lib/dvb_si/private_data_specifier_descriptor.h new file mode 100644 index 0000000..53b0fb5 --- /dev/null +++ b/lib/dvb_si/private_data_specifier_descriptor.h @@ -0,0 +1,38 @@ +/* + * $Id: private_data_specifier_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_private_data_specifier_descriptor_h__ +#define __dvb_descriptor_private_data_specifier_descriptor_h__ + +#include "descriptor.h" + +class PrivateDataSpecifierDescriptor : public Descriptor +{ + protected: + unsigned privateDataSpecifier : 32; + + public: + PrivateDataSpecifierDescriptor(const uint8_t * const buffer); + + uint32_t getPrivateDataSpecifier(void) const; +}; + +#endif /* __dvb_descriptor_private_data_specifier_descriptor_h__ */ diff --git a/lib/dvb_si/registration_descriptor.h b/lib/dvb_si/registration_descriptor.h new file mode 100644 index 0000000..0f766a1 --- /dev/null +++ b/lib/dvb_si/registration_descriptor.h @@ -0,0 +1,44 @@ +/* + * $Id: registration_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_registration_descriptor_h__ +#define __dvb_descriptor_registration_descriptor_h__ + +#include "descriptor.h" + +typedef std::vector AdditionalIdentificationInfoVector; +typedef AdditionalIdentificationInfoVector::iterator AdditionalIdentificationInfoIterator; +typedef AdditionalIdentificationInfoVector::const_iterator AdditionalIdentificationInfoConstIterator; + +class RegistrationDescriptor : public Descriptor +{ + protected: + unsigned formatIdentifier : 32; + AdditionalIdentificationInfoVector additionalIdentificationInfo; + + private: + RegistrationDescriptor(const uint8_t * const buffer); + + uint32_t getFormatIdentifier(void) const; + const AdditionalIdentificationInfoVector *getAdditionalIdentificationInfo(void) const; +}; + +#endif /* __dvb_descriptor_registration_descriptor_h__ */ diff --git a/lib/dvb_si/satellite_delivery_system_descriptor.cpp b/lib/dvb_si/satellite_delivery_system_descriptor.cpp new file mode 100644 index 0000000..32ff194 --- /dev/null +++ b/lib/dvb_si/satellite_delivery_system_descriptor.cpp @@ -0,0 +1,91 @@ +/* + * $Id: satellite_delivery_system_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +SatelliteDeliverySystemDescriptor::SatelliteDeliverySystemDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + frequency = + ( + ((buffer[2] >> 4) * 10000000) + + ((buffer[2] & 0x0F) * 1000000) + + ((buffer[3] >> 4) * 100000) + + ((buffer[3] & 0x0F) * 10000) + + ((buffer[4] >> 4) * 1000) + + ((buffer[4] & 0x0F) * 100) + + ((buffer[5] >> 4) * 10) + + ((buffer[5] & 0x0F) * 1) + ); + + orbitalPosition = (buffer[6] << 8) | buffer[7]; + westEastFlag = (buffer[8] >> 7) & 0x01; + polarization = (buffer[8] >> 5) & 0x03; + modulation = buffer[8] & 0x1F; + + symbolRate = + ( + ((buffer[9] >> 4) * 1000000) + + ((buffer[9] & 0x0F) * 100000) + + ((buffer[10] >> 4) * 10000) + + ((buffer[10] & 0x0F) * 1000) + + ((buffer[11] >> 4) * 100) + + ((buffer[11] & 0x0F) * 10) + + ((buffer[12] >> 4) * 1) + ); + + fecInner = buffer[12] & 0x0F; +} + +uint32_t SatelliteDeliverySystemDescriptor::getFrequency(void) const +{ + return frequency; +} + +uint16_t SatelliteDeliverySystemDescriptor::getOrbitalPosition(void) const +{ + return orbitalPosition; +} + +uint8_t SatelliteDeliverySystemDescriptor::getWestEastFlag(void) const +{ + return westEastFlag; +} + +uint8_t SatelliteDeliverySystemDescriptor::getPolarization(void) const +{ + return polarization; +} + +uint8_t SatelliteDeliverySystemDescriptor::getModulation(void) const +{ + return modulation; +} + +uint32_t SatelliteDeliverySystemDescriptor::getSymbolRate(void) const +{ + return symbolRate; +} + +uint8_t SatelliteDeliverySystemDescriptor::getFecInner(void) const +{ + return fecInner; +} + diff --git a/lib/dvb_si/satellite_delivery_system_descriptor.h b/lib/dvb_si/satellite_delivery_system_descriptor.h new file mode 100644 index 0000000..b6c2a66 --- /dev/null +++ b/lib/dvb_si/satellite_delivery_system_descriptor.h @@ -0,0 +1,50 @@ +/* + * $Id: satellite_delivery_system_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_satellite_delivery_system_descriptor_h__ +#define __dvb_descriptor_satellite_delivery_system_descriptor_h__ + +#include "descriptor.h" + +class SatelliteDeliverySystemDescriptor : public Descriptor +{ + protected: + unsigned frequency : 32; + unsigned orbitalPosition : 16; + unsigned westEastFlag : 1; + unsigned polarization : 2; + unsigned modulation : 5; + unsigned symbolRate : 28; + unsigned fecInner : 4; + + public: + SatelliteDeliverySystemDescriptor(const uint8_t * const buffer); + + uint32_t getFrequency(void) const; + uint16_t getOrbitalPosition(void) const; + uint8_t getWestEastFlag(void) const; + uint8_t getPolarization(void) const; + uint8_t getModulation(void) const; + uint32_t getSymbolRate(void) const; + uint8_t getFecInner(void) const; +}; + +#endif /* __dvb_descriptor_satellite_delivery_system_descriptor_h__ */ diff --git a/lib/dvb_si/sdt.cpp b/lib/dvb_si/sdt.cpp new file mode 100644 index 0000000..cce8103 --- /dev/null +++ b/lib/dvb_si/sdt.cpp @@ -0,0 +1,87 @@ +/* + * $Id: sdt.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +ServiceDescription::ServiceDescription(const uint8_t * const buffer) +{ + serviceId = (buffer[0] << 8) | buffer[1]; + reserved1 = (buffer[2] >> 2) & 0x3F; + eitScheduleFlag = (buffer[2] >> 1) & 0x01; + eitPresentFollowingFlag = buffer[2] & 0x01; + runningStatus = (buffer[3] >> 5) & 0x07; + freeCaMode = (buffer[3] >> 4) & 0x01; + descriptorsLoopLength = ((buffer[3] & 0x0F) << 8) | buffer[4]; + + for (uint16_t i = 5; i < descriptorsLoopLength + 5; i += buffer[i + 1] + 2) + descriptor(&buffer[i]); +} + +uint16_t ServiceDescription::getServiceId(void) const +{ + return serviceId; +} + +uint8_t ServiceDescription::getEitScheduleFlag(void) const +{ + return eitScheduleFlag; +} + +uint8_t ServiceDescription::getEitPresentFollowingFlag(void) const +{ + return eitPresentFollowingFlag; +} + +uint8_t ServiceDescription::getRunningStatus(void) const +{ + return runningStatus; +} + +uint8_t ServiceDescription::getFreeCaMode(void) const +{ + return freeCaMode; +} + +ServiceDescriptionTable::ServiceDescriptionTable(const uint8_t * const buffer) : LongCrcTable (buffer) +{ + originalNetworkId = (buffer[8] << 8) | buffer[9]; + reserved4 = buffer[10]; + + for (uint16_t i = 11; i < sectionLength - 1; i += ((buffer[i + 3] & 0x0F) | buffer[i + 4]) + 5) + description.push_back(new ServiceDescription(&buffer[i])); +} + +ServiceDescriptionTable::~ServiceDescriptionTable(void) +{ + for (ServiceDescriptionIterator i = description.begin(); i != description.end(); ++i) + delete *i; +} + +uint16_t ServiceDescriptionTable::getOriginalNetworkId(void) const +{ + return originalNetworkId; +} + +const ServiceDescriptionVector *ServiceDescriptionTable::getDescriptions(void) const +{ + return &description; +} + diff --git a/lib/dvb_si/sdt.h b/lib/dvb_si/sdt.h new file mode 100644 index 0000000..e1d511b --- /dev/null +++ b/lib/dvb_si/sdt.h @@ -0,0 +1,77 @@ +/* + * $Id: sdt.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002 by Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_table_sdt_h__ +#define __dvb_table_sdt_h__ + +#include +#include "long_crc_table.h" + +class ServiceDescription : public DescriptorContainer +{ + protected: + unsigned serviceId : 16; + unsigned reserved1 : 6; + unsigned eitScheduleFlag : 1; + unsigned eitPresentFollowingFlag : 1; + unsigned runningStatus : 3; + unsigned freeCaMode : 1; + unsigned descriptorsLoopLength : 12; + + public: + ServiceDescription(const uint8_t * const buffer); + + uint16_t getServiceId(void) const; + uint8_t getEitScheduleFlag(void) const; + uint8_t getEitPresentFollowingFlag(void) const; + uint8_t getRunningStatus(void) const; + uint8_t getFreeCaMode(void) const; +}; + +typedef std::vector ServiceDescriptionVector; +typedef ServiceDescriptionVector::iterator ServiceDescriptionIterator; +typedef ServiceDescriptionVector::const_iterator ServiceDescriptionConstIterator; + +class ServiceDescriptionTable : public LongCrcTable +{ + protected: + unsigned originalNetworkId : 16; + unsigned reserved4 : 8; + ServiceDescriptionVector description; + + public: + ServiceDescriptionTable(const uint8_t * const buffer); + ~ServiceDescriptionTable(void); + + static const enum PacketId PID = PID_SDT; + static const enum TableId TID = TID_SDT_ACTUAL; + static const uint32_t TIMEOUT = 3000; + + uint16_t getOriginalNetworkId(void) const; + uint16_t getTransportStreamId(void) const { return getTableIdExtension(); } + const ServiceDescriptionVector *getDescriptions(void) const; +}; + +typedef std::vector ServiceDescriptionTableVector; +typedef ServiceDescriptionTableVector::iterator ServiceDescriptionTableIterator; +typedef ServiceDescriptionTableVector::const_iterator ServiceDescriptionTableConstIterator; + +#endif /* __dvb_table_sdt_h__ */ diff --git a/lib/dvb_si/service_descriptor.cpp b/lib/dvb_si/service_descriptor.cpp new file mode 100644 index 0000000..a8bffc9 --- /dev/null +++ b/lib/dvb_si/service_descriptor.cpp @@ -0,0 +1,47 @@ +/* + * $Id: service_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +ServiceDescriptor::ServiceDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + serviceType = buffer[2]; + serviceProviderNameLength = buffer[3]; + serviceProviderName.assign((char *)&buffer[4], serviceProviderNameLength); + serviceNameLength = buffer[serviceProviderNameLength + 4]; + serviceName.assign((char *)&buffer[serviceProviderNameLength + 5], serviceNameLength); +} + +uint8_t ServiceDescriptor::getServiceType(void) const +{ + return serviceType; +} + +std::string ServiceDescriptor::getServiceProviderName(void) const +{ + return serviceProviderName; +} + +std::string ServiceDescriptor::getServiceName(void) const +{ + return serviceName; +} + diff --git a/lib/dvb_si/service_descriptor.h b/lib/dvb_si/service_descriptor.h new file mode 100644 index 0000000..38ccc83 --- /dev/null +++ b/lib/dvb_si/service_descriptor.h @@ -0,0 +1,44 @@ +/* + * $Id: service_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_service_descriptor_h__ +#define __dvb_descriptor_service_descriptor_h__ + +#include "descriptor.h" + +class ServiceDescriptor : public Descriptor +{ + protected: + unsigned serviceType : 8; + unsigned serviceProviderNameLength : 8; + std::string serviceProviderName; + unsigned serviceNameLength : 8; + std::string serviceName; + + public: + ServiceDescriptor(const uint8_t * const buffer); + + uint8_t getServiceType(void) const; + std::string getServiceProviderName(void) const; + std::string getServiceName(void) const; +}; + +#endif /* __dvb_descriptor_service_descriptor_h__ */ diff --git a/lib/dvb_si/service_list_descriptor.cpp b/lib/dvb_si/service_list_descriptor.cpp new file mode 100644 index 0000000..0e595cb --- /dev/null +++ b/lib/dvb_si/service_list_descriptor.cpp @@ -0,0 +1,56 @@ +/* + * $Id: service_list_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +ServiceListItem::ServiceListItem(const uint8_t * const buffer) +{ + serviceId = (buffer[0] << 8) | buffer[1]; + serviceType = buffer[2]; +} + +uint16_t ServiceListItem::getServiceId(void) const +{ + return serviceId; +} + +uint8_t ServiceListItem::getServiceType(void) const +{ + return serviceType; +} + +ServiceListDescriptor::ServiceListDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + for (uint16_t i = 0; i < descriptorLength; i += 3) + serviceList.push_back(new ServiceListItem(&buffer[i + 2])); +} + +ServiceListDescriptor::~ServiceListDescriptor(void) +{ + for (ServiceListItemIterator i = serviceList.begin(); i != serviceList.end(); ++i) + delete *i; +} + +const ServiceListItemVector *ServiceListDescriptor::getServiceList(void) const +{ + return &serviceList; +} + diff --git a/lib/dvb_si/service_list_descriptor.h b/lib/dvb_si/service_list_descriptor.h new file mode 100644 index 0000000..bd9c04f --- /dev/null +++ b/lib/dvb_si/service_list_descriptor.h @@ -0,0 +1,56 @@ +/* + * $Id: service_list_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_service_list_descriptor_h__ +#define __dvb_descriptor_service_list_descriptor_h__ + +#include "descriptor.h" + +class ServiceListItem +{ + protected: + unsigned serviceId : 16; + unsigned serviceType : 8; + + public: + ServiceListItem(const uint8_t * const buffer); + + uint16_t getServiceId(void) const; + uint8_t getServiceType(void) const; +}; + +typedef std::vector ServiceListItemVector; +typedef ServiceListItemVector::iterator ServiceListItemIterator; +typedef ServiceListItemVector::const_iterator ServiceListItemConstIterator; + +class ServiceListDescriptor : public Descriptor +{ + protected: + ServiceListItemVector serviceList; + + public: + ServiceListDescriptor(const uint8_t * const buffer); + ~ServiceListDescriptor(void); + + const ServiceListItemVector *getServiceList(void) const; +}; + +#endif /* __dvb_descriptor_service_list_descriptor_h__ */ diff --git a/lib/dvb_si/service_move_descriptor.cpp b/lib/dvb_si/service_move_descriptor.cpp new file mode 100644 index 0000000..817b480 --- /dev/null +++ b/lib/dvb_si/service_move_descriptor.cpp @@ -0,0 +1,45 @@ +/* + * $Id: service_move_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +ServiceMoveDescriptor::ServiceMoveDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + newOriginalNetworkId = (buffer[2] << 8) | buffer[3]; + newTransportStreamId = (buffer[4] << 8) | buffer[5]; + newServiceId = (buffer[6] << 8) | buffer[7]; +} + +uint16_t ServiceMoveDescriptor::getNewOriginalNetworkId(void) const +{ + return newOriginalNetworkId; +} + +uint16_t ServiceMoveDescriptor::getNewTransportStreamId(void) const +{ + return newTransportStreamId; +} + +uint16_t ServiceMoveDescriptor::getNewServiceId(void) const +{ + return newServiceId; +} + diff --git a/lib/dvb_si/service_move_descriptor.h b/lib/dvb_si/service_move_descriptor.h new file mode 100644 index 0000000..55c10c4 --- /dev/null +++ b/lib/dvb_si/service_move_descriptor.h @@ -0,0 +1,42 @@ +/* + * $Id: service_move_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_service_move_descriptor_h__ +#define __dvb_descriptor_service_move_descriptor_h__ + +#include "descriptor.h" + +class ServiceMoveDescriptor : public Descriptor +{ + protected: + unsigned newOriginalNetworkId : 16; + unsigned newTransportStreamId : 16; + unsigned newServiceId : 16; + + public: + ServiceMoveDescriptor(const uint8_t * const buffer); + + uint16_t getNewOriginalNetworkId(void) const; + uint16_t getNewTransportStreamId(void) const; + uint16_t getNewServiceId(void) const; +}; + +#endif /* __dvb_descriptor_service_move_descriptor_h__ */ diff --git a/lib/dvb_si/service_type.h b/lib/dvb_si/service_type.h new file mode 100644 index 0000000..0871af4 --- /dev/null +++ b/lib/dvb_si/service_type.h @@ -0,0 +1,50 @@ +/* + * $Id: service_type.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_id_service_type_h__ +#define __dvb_id_service_type_h__ + +enum ServiceType { + /* 0x00 - 0x10: ETSI EN 300 468 V1.5.1 (2003-01) */ + ST_RESERVED = 0x00, + ST_DIGITAL_TELEVISION_SERVICE = 0x01, + ST_DIGITAL_RADIO_SOUND_SERVICE = 0x02, + ST_TELETEXT_SERVICE = 0x03, + ST_NVOD_REFERENCE_SERVICE = 0x04, + ST_NVOD_TIME_SHIFTED_SERVICE = 0x05, + ST_MOSAIC_SERVICE = 0x06, + ST_PAL_CODED_SIGNAL = 0x07, + ST_SECAM_CODED_SIGNAL = 0x08, + ST_D_D2_MAC = 0x09, + ST_FM_RADIO = 0x0A, + ST_NTSC_CODED_SIGNAL = 0x0B, + ST_DATA_BROADCAST_SERVICE = 0x0C, + ST_COMMON_INTERFACE_RESERVED = 0x0D, + ST_RCS_MAP = 0x0E, + ST_RCS_FLS = 0x0F, + ST_DVB_MHP_SERVICE = 0x10, + /* 0x11 - 0x7F: reserved for future use */ + ST_MULTIFEED = 0x69 + /* 0x80 - 0xFE: user defined */ + /* 0xFF: reserved for future use */ +}; + +#endif /* __dvb_id_service_type_h__ */ diff --git a/lib/dvb_si/short_crc_table.cpp b/lib/dvb_si/short_crc_table.cpp new file mode 100644 index 0000000..bd626ff --- /dev/null +++ b/lib/dvb_si/short_crc_table.cpp @@ -0,0 +1,36 @@ +/* + * $Id: short_crc_table.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +ShortCrcTable::ShortCrcTable(const uint8_t * const buffer) : ShortTable(buffer) +{ + crc32 = (buffer[sectionLength - 1] << 24) | + (buffer[sectionLength + 0] << 16) | + (buffer[sectionLength + 1] << 8) | + (buffer[sectionLength + 2]); +} + +uint32_t ShortCrcTable::getCrc32(void) const +{ + return crc32; +} + diff --git a/lib/dvb_si/short_crc_table.h b/lib/dvb_si/short_crc_table.h new file mode 100644 index 0000000..d9ef6fd --- /dev/null +++ b/lib/dvb_si/short_crc_table.h @@ -0,0 +1,44 @@ +/* + * $Id: short_crc_table.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_table_short_crc_table_h__ +#define __dvb_table_short_crc_table_h__ + +#include "short_table.h" + +class ShortCrcTable : public ShortTable +{ + protected: + unsigned crc32 : 32; + + public: + ShortCrcTable(const uint8_t * const buffer); + + static const uint8_t CRC32 = 1; + + uint32_t getCrc32(void) const; +}; + +typedef std::vector ShortCrcTableVector; +typedef ShortCrcTableVector::iterator ShortCrcTableIterator; +typedef ShortCrcTableVector::const_iterator ShortCrcTableConstIterator; + +#endif /* __dvb_table_short_crc_table_h__ */ diff --git a/lib/dvb_si/short_event_descriptor.cpp b/lib/dvb_si/short_event_descriptor.cpp new file mode 100644 index 0000000..e5fffe8 --- /dev/null +++ b/lib/dvb_si/short_event_descriptor.cpp @@ -0,0 +1,47 @@ +/* + * $Id: short_event_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +ShortEventDescriptor::ShortEventDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + iso639LanguageCode.assign((char *)&buffer[2], 3); + eventNameLength = buffer[5]; + eventName.assign((char *)&buffer[6], eventNameLength); + textLength = buffer[6 + eventNameLength]; + text.assign((char *)&buffer[7 + eventNameLength], textLength); +} + +std::string ShortEventDescriptor::getIso639LanguageCode(void) const +{ + return iso639LanguageCode; +} + +std::string ShortEventDescriptor::getEventName(void) const +{ + return eventName; +} + +std::string ShortEventDescriptor::getText(void) const +{ + return text; +} + diff --git a/lib/dvb_si/short_event_descriptor.h b/lib/dvb_si/short_event_descriptor.h new file mode 100644 index 0000000..e421cde --- /dev/null +++ b/lib/dvb_si/short_event_descriptor.h @@ -0,0 +1,44 @@ +/* + * $Id: short_event_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_short_event_descriptor_h__ +#define __dvb_descriptor_short_event_descriptor_h__ + +#include "descriptor.h" + +class ShortEventDescriptor : public Descriptor +{ + protected: + std::string iso639LanguageCode; + unsigned eventNameLength : 8; + std::string eventName; + unsigned textLength : 8; + std::string text; + + public: + ShortEventDescriptor(const uint8_t * const buffer); + + std::string getIso639LanguageCode(void) const; + std::string getEventName(void) const; + std::string getText(void) const; +}; + +#endif /* __dvb_descriptor_short_event_descriptor_h__ */ diff --git a/lib/dvb_si/short_smoothing_buffer_descriptor.h b/lib/dvb_si/short_smoothing_buffer_descriptor.h new file mode 100644 index 0000000..66c0802 --- /dev/null +++ b/lib/dvb_si/short_smoothing_buffer_descriptor.h @@ -0,0 +1,41 @@ +/* + * $Id: short_smoothing_buffer_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_short_smoothing_buffer_descriptor_h__ +#define __dvb_descriptor_short_smoothing_buffer_descriptor_h__ + +#include "descriptor.h" + +class ShortSmoothingBufferDescriptor : public Descriptor +{ + protected: + unsigned sbSize : 2; + unsigned sbLeakRate : 6; + std::vector reserved; + + public: + ShortSmoothingBufferDescriptor(const uint8_t * const buffer); + + uint8_t getSbSize(void) const; + uint8_t getSbLeakRate(void) const; +}; + +#endif /* __dvb_descriptor_short_smoothing_buffer_descriptor_h__ */ diff --git a/lib/dvb_si/short_table.cpp b/lib/dvb_si/short_table.cpp new file mode 100644 index 0000000..b162fe9 --- /dev/null +++ b/lib/dvb_si/short_table.cpp @@ -0,0 +1,47 @@ +/* + * $Id: short_table.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +ShortTable::ShortTable(const uint8_t * const buffer) +{ + tableId = buffer[0]; + sectionSyntaxIndicator = (buffer[1] >> 7) & 0x01; + reserved1 = (buffer[1] >> 6) & 0x01; + reserved2 = (buffer[1] >> 4) & 0x03; + sectionLength = ((buffer[1] & 0x0F) << 8) | buffer[2]; +} + +uint8_t ShortTable::getTableId(void) const +{ + return tableId; +} + +uint8_t ShortTable::getSectionSyntaxIndicator(void) const +{ + return sectionSyntaxIndicator; +} + +uint16_t ShortTable::getSectionLength(void) const +{ + return sectionLength; +} + diff --git a/lib/dvb_si/short_table.h b/lib/dvb_si/short_table.h new file mode 100644 index 0000000..743891f --- /dev/null +++ b/lib/dvb_si/short_table.h @@ -0,0 +1,58 @@ +/* + * $Id: short_table.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_table_short_table_h__ +#define __dvb_table_short_table_h__ + +#include +#include +#include +#include + +class ShortTable +{ + protected: + unsigned tableId : 8; + unsigned sectionSyntaxIndicator : 1; + unsigned reserved1 : 1; + unsigned reserved2 : 2; + unsigned sectionLength : 12; + + public: + ShortTable(const uint8_t * const buffer); + + static const uint8_t CRC32 = 0; + static const uint16_t LENGTH = 1024; + static const enum PacketId PID = PID_RESERVED; + static const uint8_t SYNTAX = 0; + static const enum TableId TID = TID_RESERVED; + static const uint32_t TIMEOUT = 0; + + uint8_t getTableId(void) const; + uint8_t getSectionSyntaxIndicator(void) const; + uint16_t getSectionLength(void) const; +}; + +typedef std::vector ShortTableVector; +typedef ShortTableVector::iterator ShortTableIterator; +typedef ShortTableVector::const_iterator ShortTableConstIterator; + +#endif /* __dvb_table_short_table_h__ */ diff --git a/lib/dvb_si/smoothing_buffer_descriptor.h b/lib/dvb_si/smoothing_buffer_descriptor.h new file mode 100644 index 0000000..68ccdde --- /dev/null +++ b/lib/dvb_si/smoothing_buffer_descriptor.h @@ -0,0 +1,42 @@ +/* + * $Id: smoothing_buffer_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_smoothing_buffer_descriptor_h__ +#define __dvb_descriptor_smoothing_buffer_descriptor_h__ + +#include "descriptor.h" + +class SmoothingBufferDescriptor : public Descriptor +{ + protected: + unsigned reserved : 2; + unsigned sbLeakRate : 22; + unsigned reserved2 : 2; + unsigned sbSize : 22; + + public: + SmoothingBufferDescriptor(const uint8_t * const buffer); + + uint32_t getSbLeakRate(void) const; + uint32_t getSbSize(void) const; +}; + +#endif /* __dvb_descriptor_smoothing_buffer_descriptor_h__ */ diff --git a/lib/dvb_si/std_descriptor.h b/lib/dvb_si/std_descriptor.h new file mode 100644 index 0000000..a216b04 --- /dev/null +++ b/lib/dvb_si/std_descriptor.h @@ -0,0 +1,39 @@ +/* + * $Id: std_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_std_descriptor_h__ +#define __dvb_descriptor_std_descriptor_h__ + +#include "descriptor.h" + +class StdDescriptor : public Descriptor +{ + protected: + unsigned reserved : 7; + unsigned leakValidFlag : 1; + + public: + StdDescriptor(const uint8_t * const buffer); + + uint8_t getLeakValidFlag(void) const; +}; + +#endif /* __dvb_descriptor_std_descriptor_h__ */ diff --git a/lib/dvb_si/stream_identifier_descriptor.cpp b/lib/dvb_si/stream_identifier_descriptor.cpp new file mode 100644 index 0000000..5595699 --- /dev/null +++ b/lib/dvb_si/stream_identifier_descriptor.cpp @@ -0,0 +1,33 @@ +/* + * $Id: stream_identifier_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +StreamIdentifierDescriptor::StreamIdentifierDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + componentTag = buffer[2]; +} + +uint8_t StreamIdentifierDescriptor::getComponentTag(void) const +{ + return componentTag; +} + diff --git a/lib/dvb_si/stream_identifier_descriptor.h b/lib/dvb_si/stream_identifier_descriptor.h new file mode 100644 index 0000000..4a37b4e --- /dev/null +++ b/lib/dvb_si/stream_identifier_descriptor.h @@ -0,0 +1,38 @@ +/* + * $Id: stream_identifier_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_stream_identifier_descriptor_h__ +#define __dvb_descriptor_stream_identifier_descriptor_h__ + +#include "descriptor.h" + +class StreamIdentifierDescriptor : public Descriptor +{ + protected: + unsigned componentTag : 8; + + public: + StreamIdentifierDescriptor(const uint8_t * const buffer); + + uint8_t getComponentTag(void) const; +}; + +#endif /* __dvb_descriptor_stream_identifier_descriptor_h__ */ diff --git a/lib/dvb_si/stream_type.h b/lib/dvb_si/stream_type.h new file mode 100644 index 0000000..d9f68dd --- /dev/null +++ b/lib/dvb_si/stream_type.h @@ -0,0 +1,51 @@ +/* + * $Id: stream_type.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_id_stream_type_h__ +#define __dvb_id_stream_type_h__ + +enum StreamType { + STT_RESERVED = 0x00, + STT_MPEG1_VIDEO = 0x01, + STT_MPEG2_VIDEO = 0x02, + STT_MPEG1_AUDIO = 0x03, + STT_MPEG2_AUDIO = 0x04, + STT_MPEG2_SECTIONS = 0x05, + STT_MPEG2_PES = 0x06, + STT_MHEG = 0x07, + STT_DSM_CC = 0x08, + STT_TREC_H_222_1 = 0x09, + STT_13818_6_A = 0x0A, + STT_13818_6_B = 0x0B, + STT_13818_6_C = 0x0C, + STT_13818_6_D = 0x0D, + STT_AUXILIARY = 0x0E, + STT_ADTS_AUDIO = 0x0F, + STT_MPEG4_VIDEO = 0x10, + STT_MPEG4_AUDIO = 0x11, + STT_MPEG4_PES = 0x12, + STT_MPEG4_SECTIONS = 0x13, + STT_SYNC_DOWNLOAD_PROT = 0x14 + /* 0x15 - 0x7F: ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved */ + /* 0x80 - 0xFF: User Private */ +}; + +#endif /* __dvb_id_stream_type_h__ */ diff --git a/lib/dvb_si/stuffing_descriptor.cpp b/lib/dvb_si/stuffing_descriptor.cpp new file mode 100644 index 0000000..f672fbf --- /dev/null +++ b/lib/dvb_si/stuffing_descriptor.cpp @@ -0,0 +1,29 @@ +/* + * $Id: stuffing_descriptor.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +StuffingDescriptor::StuffingDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + for (uint16_t i = 0; i < descriptorLength; ++i) + stuffingByte.push_back(buffer[i + 2]); +} + diff --git a/lib/dvb_si/stuffing_descriptor.h b/lib/dvb_si/stuffing_descriptor.h new file mode 100644 index 0000000..31486c2 --- /dev/null +++ b/lib/dvb_si/stuffing_descriptor.h @@ -0,0 +1,36 @@ +/* + * $Id: stuffing_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_stuffing_descriptor_h__ +#define __dvb_descriptor_stuffing_descriptor_h__ + +#include "descriptor.h" + +class StuffingDescriptor : public Descriptor +{ + protected: + std::vector stuffingByte; + + public: + StuffingDescriptor(const uint8_t * const buffer); +}; + +#endif /* __dvb_descriptor_stuffing_descriptor_h__ */ diff --git a/lib/dvb_si/subtitling_descriptor.cpp b/lib/dvb_si/subtitling_descriptor.cpp new file mode 100644 index 0000000..c2bdfb9 --- /dev/null +++ b/lib/dvb_si/subtitling_descriptor.cpp @@ -0,0 +1,68 @@ +/* + * $Id: subtitling_descriptor.cpp,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +Subtitling::Subtitling(const uint8_t * const buffer) +{ + iso639LanguageCode.assign((char *)&buffer[0], 3); + subtitlingType = buffer[3]; + compositionPageId = (buffer[4] << 8) | buffer[5]; + ancillaryPageId = (buffer[6] << 8) | buffer[7]; +} + +std::string Subtitling::getIso639LanguageCode(void) const +{ + return iso639LanguageCode; +} + +uint8_t Subtitling::getSubtitlingType(void) const +{ + return subtitlingType; +} + +uint16_t Subtitling::getCompositionPageId(void) const +{ + return compositionPageId; +} + +uint16_t Subtitling::getAncillaryPageId(void) const +{ + return ancillaryPageId; +} + +SubtitlingDescriptor::SubtitlingDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + for (uint16_t i = 0; i < descriptorLength; i += 8) + subtitlings.push_back(new Subtitling(&buffer[i + 2])); +} + +SubtitlingDescriptor::~SubtitlingDescriptor(void) +{ + for (SubtitlingIterator i = subtitlings.begin(); i != subtitlings.end(); ++i) + delete *i; +} + +const SubtitlingVector *SubtitlingDescriptor::getSubtitlings(void) const +{ + return &subtitlings; +} + diff --git a/lib/dvb_si/subtitling_descriptor.h b/lib/dvb_si/subtitling_descriptor.h new file mode 100644 index 0000000..5232dbd --- /dev/null +++ b/lib/dvb_si/subtitling_descriptor.h @@ -0,0 +1,60 @@ +/* + * $Id: subtitling_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_subtitling_descriptor_h__ +#define __dvb_descriptor_subtitling_descriptor_h__ + +#include "descriptor.h" + +class Subtitling +{ + protected: + std::string iso639LanguageCode; + unsigned subtitlingType : 8; + unsigned compositionPageId : 16; + unsigned ancillaryPageId : 16; + + public: + Subtitling(const uint8_t * const buffer); + + std::string getIso639LanguageCode(void) const; + uint8_t getSubtitlingType(void) const; + uint16_t getCompositionPageId(void) const; + uint16_t getAncillaryPageId(void) const; +}; + +typedef std::vector SubtitlingVector; +typedef SubtitlingVector::iterator SubtitlingIterator; +typedef SubtitlingVector::const_iterator SubtitlingConstIterator; + +class SubtitlingDescriptor : public Descriptor +{ + protected: + SubtitlingVector subtitlings; + + public: + SubtitlingDescriptor(const uint8_t * const buffer); + ~SubtitlingDescriptor(void); + + const SubtitlingVector *getSubtitlings(void) const; +}; + +#endif /* __dvb_descriptor_subtitling_descriptor_h__ */ diff --git a/lib/dvb_si/system_clock_descriptor.h b/lib/dvb_si/system_clock_descriptor.h new file mode 100644 index 0000000..2216cef --- /dev/null +++ b/lib/dvb_si/system_clock_descriptor.h @@ -0,0 +1,44 @@ +/* + * $Id: system_clock_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_system_clock_descriptor_h__ +#define __dvb_descriptor_system_clock_descriptor_h__ + +#include "descriptor.h" + +class SystemClockDescriptor : public Descriptor +{ + protected: + unsigned externalClockReferenceIndicator : 1; + unsigned reserved : 1; + unsigned clockAccuracyInteger : 6; + unsigned clockAccuracyExponent : 3; + unsigned reserved2 : 5; + + public: + SystemClockDescriptor(const uint8_t * const buffer); + + uint8_t getExternalClockReferenceIndicator(void) const; + uint8_t getClockAccuracyInteger(void) const; + uint8_t getClockAccuracyExponent(void) const; +}; + +#endif /* __dvb_descriptor_system_clock_descriptor_h__ */ diff --git a/lib/dvb_si/table_id.h b/lib/dvb_si/table_id.h new file mode 100644 index 0000000..39e005c --- /dev/null +++ b/lib/dvb_si/table_id.h @@ -0,0 +1,123 @@ +/* + * $Id: table_id.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_id_table_id_h__ +#define __dvb_id_table_id_h__ + +enum TableId { + /* ISO/IEC 13818-1, ITU T-REC H.222.0 */ + TID_PAT = 0x00, /* program_association_section */ + TID_CAT = 0x01, /* conditional_access_section */ + TID_PMT = 0x02, /* TS_program_map_section */ + TID_TSDT = 0x03, /* TS_description_section */ + TID_SDT = 0x04, /* ISO_IEC_14496_scene_description_section */ + TID_ODT = 0x05, /* ISO_IEC_14496_object_descriptor_section */ + + /* 0x06 - 0x09: ITU-T Rec. H.222.0 | ISO/IEC 13818-1 reserved */ + + /* 0x0A - 0x0D: ISO/IEC 13818-6 */ + TID_DSMCC_MULTIPROTOCOL = 0x0A, /* Multiprotocol */ + TID_DSMCC_MSG_HEADER = 0x0B, /* DSM-CC Messages Header (U-N) */ + TID_DSMCC_DESCR_LOOP = 0x0C, /* DSM-CC Descriptors Loop */ + TID_DSMCC_TBD = 0x0D, /* TBD */ + + /* 0x0E - 0x37: ITU-T Rec. H.222.0 | ISO/IEC 13818-1 reserved */ + + /* 0x38 - 0x3F: Defined in ISO/IEC 13818-6 */ + TID_DSMCC_DL_MESSAGE = 0x3B, /* DSM-CC Download Message */ + TID_DSMCC_DL_DATA = 0x3C, /* DSM-CC Download Data */ + TID_DSMCC_DL_EVENT = 0x3D, /* DSM-CC Download Event */ + + /* 0x40 - 0x7F: ETSI EN 300 468 V1.5.1 (2003-01) */ + TID_NIT_ACTUAL = 0x40, /* network_information_section - actual_network */ + TID_NIT_OTHER = 0x41, /* network_information_section - other_network */ + TID_SDT_ACTUAL = 0x42, /* service_description_section - actual_transport_stream */ + TID_SDT_OTHER = 0x46, /* service_description_section - other_transport_stream */ + TID_BAT = 0x4A, /* bouquet_association_section */ + TID_EIT_ACTUAL = 0x4E, /* event_information_section - actual_transport_stream, present/following */ + TID_EIT_OTHER = 0x4F, /* event_information_section - other_transport_stream, present/following */ + TID_EIT_ACTUAL_SCHED_0 = 0x50, /* event_information_section - actual_transport_stream, schedule */ + TID_EIT_ACTUAL_SCHED_1 = 0x51, /* event_information_section - actual_transport_stream, schedule */ + TID_EIT_ACTUAL_SCHED_2 = 0x52, /* event_information_section - actual_transport_stream, schedule */ + TID_EIT_ACTUAL_SCHED_3 = 0x53, /* event_information_section - actual_transport_stream, schedule */ + TID_EIT_ACTUAL_SCHED_4 = 0x54, /* event_information_section - actual_transport_stream, schedule */ + TID_EIT_ACTUAL_SCHED_5 = 0x55, /* event_information_section - actual_transport_stream, schedule */ + TID_EIT_ACTUAL_SCHED_6 = 0x56, /* event_information_section - actual_transport_stream, schedule */ + TID_EIT_ACTUAL_SCHED_7 = 0x57, /* event_information_section - actual_transport_stream, schedule */ + TID_EIT_ACTUAL_SCHED_8 = 0x58, /* event_information_section - actual_transport_stream, schedule */ + TID_EIT_ACTUAL_SCHED_9 = 0x59, /* event_information_section - actual_transport_stream, schedule */ + TID_EIT_ACTUAL_SCHED_A = 0x5A, /* event_information_section - actual_transport_stream, schedule */ + TID_EIT_ACTUAL_SCHED_B = 0x5B, /* event_information_section - actual_transport_stream, schedule */ + TID_EIT_ACTUAL_SCHED_C = 0x5C, /* event_information_section - actual_transport_stream, schedule */ + TID_EIT_ACTUAL_SCHED_D = 0x5D, /* event_information_section - actual_transport_stream, schedule */ + TID_EIT_ACTUAL_SCHED_E = 0x5E, /* event_information_section - actual_transport_stream, schedule */ + TID_EIT_ACTUAL_SCHED_F = 0x5F, /* event_information_section - actual_transport_stream, schedule */ + TID_EIT_OTHER_SCHED_0 = 0x60, /* event_information_section - other_transport_stream, schedule */ + TID_EIT_OTHER_SCHED_1 = 0x61, /* event_information_section - other_transport_stream, schedule */ + TID_EIT_OTHER_SCHED_2 = 0x62, /* event_information_section - other_transport_stream, schedule */ + TID_EIT_OTHER_SCHED_3 = 0x63, /* event_information_section - other_transport_stream, schedule */ + TID_EIT_OTHER_SCHED_4 = 0x64, /* event_information_section - other_transport_stream, schedule */ + TID_EIT_OTHER_SCHED_5 = 0x65, /* event_information_section - other_transport_stream, schedule */ + TID_EIT_OTHER_SCHED_6 = 0x66, /* event_information_section - other_transport_stream, schedule */ + TID_EIT_OTHER_SCHED_7 = 0x67, /* event_information_section - other_transport_stream, schedule */ + TID_EIT_OTHER_SCHED_8 = 0x68, /* event_information_section - other_transport_stream, schedule */ + TID_EIT_OTHER_SCHED_9 = 0x69, /* event_information_section - other_transport_stream, schedule */ + TID_EIT_OTHER_SCHED_A = 0x6A, /* event_information_section - other_transport_stream, schedule */ + TID_EIT_OTHER_SCHED_B = 0x6B, /* event_information_section - other_transport_stream, schedule */ + TID_EIT_OTHER_SCHED_C = 0x6C, /* event_information_section - other_transport_stream, schedule */ + TID_EIT_OTHER_SCHED_D = 0x6D, /* event_information_section - other_transport_stream, schedule */ + TID_EIT_OTHER_SCHED_E = 0x6E, /* event_information_section - other_transport_stream, schedule */ + TID_EIT_OTHER_SCHED_F = 0x6F, /* event_information_section - other_transport_stream, schedule */ + TID_TDT = 0x70, /* time_date_section */ + TID_RST = 0x71, /* running_status_section */ + TID_ST = 0x72, /* stuffing_section */ + TID_TOT = 0x73, /* time_offset_section */ + TID_AIT = 0x74, /* application_information_section */ + TID_DIT = 0x7E, /* discontinuity_information_section */ + TID_SIT = 0x7F, /* selection_information_section */ + + /* 0x80 - 0x8F: ETSI ETR 289 ed.1 (1996-10) */ + TID_CAMT_ECM_0 = 0x80, + TID_CAMT_ECM_1 = 0x81, + TID_CAMT_PRIVATE_0 = 0x82, + TID_CAMT_PRIVATE_1 = 0x83, + TID_CAMT_PRIVATE_2 = 0x84, + TID_CAMT_PRIVATE_3 = 0x85, + TID_CAMT_PRIVATE_4 = 0x86, + TID_CAMT_PRIVATE_5 = 0x87, + TID_CAMT_PRIVATE_6 = 0x88, + TID_CAMT_PRIVATE_7 = 0x89, + TID_CAMT_PRIVATE_8 = 0x8A, + TID_CAMT_PRIVATE_9 = 0x8B, + TID_CAMT_PRIVATE_A = 0x8C, + TID_CAMT_PRIVATE_B = 0x8D, + TID_CAMT_PRIVATE_C = 0x8E, + TID_CAMT_PRIVATE_D = 0x8F, + + /* 0x90 - 0xFE: PRIVATE */ + TID_TOC = 0x91, + TID_HIT = 0x92, + + /* 0xFF: ISO RESERVED */ + TID_RESERVED = 0xFF +}; + +#endif /* __dvb_id_table_id_h__ */ diff --git a/lib/dvb_si/target_background_grid_descriptor.cpp b/lib/dvb_si/target_background_grid_descriptor.cpp new file mode 100644 index 0000000..921157f --- /dev/null +++ b/lib/dvb_si/target_background_grid_descriptor.cpp @@ -0,0 +1,45 @@ +/* + * $Id: target_background_grid_descriptor.cpp,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +TargetBackgroundGridDescriptor::TargetBackgroundGridDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + horizontalSize = ((buffer[2] << 8) | (buffer[3] & 0xF3)) >> 2; + verticalSize = (((buffer[3] & 0x03) << 16) | (buffer[4] << 8) | (buffer[5] & 0xF0)) >> 4; + aspectRatioInformation = buffer[5] & 0x0F; +} + +uint16_t TargetBackgroundGridDescriptor::getHorizontalSize(void) const +{ + return horizontalSize; +} + +uint16_t TargetBackgroundGridDescriptor::getVerticalSize(void) const +{ + return verticalSize; +} + +uint8_t TargetBackgroundGridDescriptor::getAspectRatioInformation(void) const +{ + return aspectRatioInformation; +} + diff --git a/lib/dvb_si/target_background_grid_descriptor.h b/lib/dvb_si/target_background_grid_descriptor.h new file mode 100644 index 0000000..38ce518 --- /dev/null +++ b/lib/dvb_si/target_background_grid_descriptor.h @@ -0,0 +1,42 @@ +/* + * $Id: target_background_grid_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_target_background_grid_descriptor_h__ +#define __dvb_descriptor_target_background_grid_descriptor_h__ + +#include "descriptor.h" + +class TargetBackgroundGridDescriptor : public Descriptor +{ + protected: + unsigned horizontalSize : 14; + unsigned verticalSize : 14; + unsigned aspectRatioInformation : 4; + + public: + TargetBackgroundGridDescriptor(const uint8_t * const buffer); + + uint16_t getHorizontalSize(void) const; + uint16_t getVerticalSize(void) const; + uint8_t getAspectRatioInformation(void) const; +}; + +#endif /* __dvb_descriptor_target_background_grid_descriptor_h__ */ diff --git a/lib/dvb_si/tdt.cpp b/lib/dvb_si/tdt.cpp new file mode 100644 index 0000000..dcbd3e3 --- /dev/null +++ b/lib/dvb_si/tdt.cpp @@ -0,0 +1,39 @@ +/* + * $Id: tdt.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +TimeAndDateTable::TimeAndDateTable(const uint8_t * const buffer) : ShortTable(buffer) +{ + utcTimeMjd = (buffer[3] << 8) | buffer[4]; + utcTimeBcd = (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; +} + +uint16_t TimeAndDateTable::getUtcTimeMjd(void) const +{ + return utcTimeMjd; +} + +uint32_t TimeAndDateTable::getUtcTimeBcd(void) const +{ + return utcTimeBcd; +} + diff --git a/lib/dvb_si/tdt.h b/lib/dvb_si/tdt.h new file mode 100644 index 0000000..ef4a0ab --- /dev/null +++ b/lib/dvb_si/tdt.h @@ -0,0 +1,48 @@ +/* + * $Id: tdt.h,v 1.1 2003-10-17 15:36:39 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_table_tdt_h__ +#define __dvb_table_tdt_h__ + +#include "short_table.h" + +class TimeAndDateTable : public ShortTable +{ + protected: + unsigned utcTimeMjd : 16; + unsigned utcTimeBcd : 24; + + public: + TimeAndDateTable(const uint8_t * const buffer); + + static const enum PacketId PID = PID_TDT; + static const enum TableId TID = TID_TDT; + static const uint32_t TIMEOUT = 36000; + + uint16_t getUtcTimeMjd(void) const; + uint32_t getUtcTimeBcd(void) const; +}; + +typedef std::vector TimeAndDateTableVector; +typedef TimeAndDateTableVector::iterator TimeAndDateTableIterator; +typedef TimeAndDateTableVector::const_iterator TimeAndDateTableConstIterator; + +#endif /* __dvb_table_tdt_h__ */ diff --git a/lib/dvb_si/telephone_descriptor.cpp b/lib/dvb_si/telephone_descriptor.cpp new file mode 100644 index 0000000..3849215 --- /dev/null +++ b/lib/dvb_si/telephone_descriptor.cpp @@ -0,0 +1,83 @@ +/* + * $Id: telephone_descriptor.cpp,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +TelephoneDescriptor::TelephoneDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + reserved = (buffer[2] >> 6) & 0x03; + foreignAvailability = (buffer[2] >> 5) & 0x01; + connectionType = buffer[2] & 0x1f; + reserved2 = (buffer[3] >> 7) & 0x01; + countryPrefixLength = (buffer[3] >> 5) & 0x03; + internationalAreaCodeLength = (buffer[3] >> 2) & 0x07; + operatorCodeLength = buffer[3] & 0x03; + reserved3 = (buffer[4] >> 7) & 0x01; + nationalAreaCodeLength = (buffer[4] >> 4) & 0x07; + coreNumberLength = buffer[4] & 0x0f; + + uint16_t offset = 5; + countryPrefix.assign((char *)&buffer[offset], countryPrefixLength); + offset += countryPrefixLength; + internationalAreaCode.assign((char *)&buffer[offset], internationalAreaCodeLength); + offset += internationalAreaCodeLength; + operatorCode.assign((char *)&buffer[offset], operatorCodeLength); + offset += operatorCodeLength; + nationalAreaCode.assign((char *)&buffer[offset], nationalAreaCodeLength); + offset += nationalAreaCodeLength; + coreNumber.assign((char *)&buffer[offset], coreNumberLength); +} + +uint8_t TelephoneDescriptor::getForeignAvailability(void) const +{ + return foreignAvailability; +} + +uint8_t TelephoneDescriptor::getConnectionType(void) const +{ + return connectionType; +} + +std::string TelephoneDescriptor::getCountryPrefix(void) const +{ + return countryPrefix; +} + +std::string TelephoneDescriptor::getInternationalAreaCode(void) const +{ + return internationalAreaCode; +} + +std::string TelephoneDescriptor::getOperatorCode(void) const +{ + return operatorCode; +} + +std::string TelephoneDescriptor::getNationalAreaCode(void) const +{ + return nationalAreaCode; +} + +std::string TelephoneDescriptor::getCoreNumber(void) const +{ + return coreNumber; +} + diff --git a/lib/dvb_si/telephone_descriptor.h b/lib/dvb_si/telephone_descriptor.h new file mode 100644 index 0000000..c64bc2c --- /dev/null +++ b/lib/dvb_si/telephone_descriptor.h @@ -0,0 +1,58 @@ +/* + * $Id: telephone_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_telephone_descriptor_h__ +#define __dvb_descriptor_telephone_descriptor_h__ + +#include "descriptor.h" + +class TelephoneDescriptor : public Descriptor +{ + protected: + unsigned reserved : 2; + unsigned foreignAvailability : 1; + unsigned connectionType : 5; + unsigned reserved2 : 1; + unsigned countryPrefixLength : 2; + unsigned internationalAreaCodeLength : 3; + unsigned operatorCodeLength : 2; + unsigned reserved3 : 1; + unsigned nationalAreaCodeLength : 3; + unsigned coreNumberLength : 4; + std::string countryPrefix; + std::string internationalAreaCode; + std::string operatorCode; + std::string nationalAreaCode; + std::string coreNumber; + + public: + TelephoneDescriptor(const uint8_t * const buffer); + + uint8_t getForeignAvailability(void) const; + uint8_t getConnectionType(void) const; + std::string getCountryPrefix(void) const; + std::string getInternationalAreaCode(void) const; + std::string getOperatorCode(void) const; + std::string getNationalAreaCode(void) const; + std::string getCoreNumber(void) const; +}; + +#endif /* __dvb_descriptor_telephone_descriptor_h__ */ diff --git a/lib/dvb_si/teletext_descriptor.cpp b/lib/dvb_si/teletext_descriptor.cpp new file mode 100644 index 0000000..dcbb499 --- /dev/null +++ b/lib/dvb_si/teletext_descriptor.cpp @@ -0,0 +1,27 @@ +/* + * $Id: teletext_descriptor.cpp,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +TeletextDescriptor::TeletextDescriptor(const uint8_t * const buffer) : VbiTeletextDescriptor(buffer) +{ +} + diff --git a/lib/dvb_si/teletext_descriptor.h b/lib/dvb_si/teletext_descriptor.h new file mode 100644 index 0000000..d95d5ad --- /dev/null +++ b/lib/dvb_si/teletext_descriptor.h @@ -0,0 +1,33 @@ +/* + * $Id: teletext_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_teletext_descriptor_h__ +#define __dvb_descriptor_teletext_descriptor_h__ + +#include "vbi_teletext_descriptor.h" + +class TeletextDescriptor : public VbiTeletextDescriptor +{ + public: + TeletextDescriptor(const uint8_t * const buffer); +}; + +#endif /* __dvb_descriptor_teletext_descriptor_h__ */ diff --git a/lib/dvb_si/terrestrial_delivery_system_descriptor.cpp b/lib/dvb_si/terrestrial_delivery_system_descriptor.cpp new file mode 100644 index 0000000..bddc9d7 --- /dev/null +++ b/lib/dvb_si/terrestrial_delivery_system_descriptor.cpp @@ -0,0 +1,83 @@ +/* + * $Id: terrestrial_delivery_system_descriptor.cpp,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +TerrestrialDeliverySystemDescriptor::TerrestrialDeliverySystemDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + centreFrequency = (buffer[2] << 24) | (buffer[3] << 16) | (buffer[4] << 8) | buffer[5]; + bandwidth = (buffer[6] >> 5) & 0x07; + reserved = buffer[6] & 0x1f; + constellation = (buffer[7] >> 6) & 0x03; + hierarchyInformation = (buffer[7] >> 3) & 0x07; + codeRateHpStream = buffer[7] & 0x07; + codeRateLpStream = (buffer[8] >> 5) & 0x07; + guardInterval = (buffer[8] >> 3) & 0x03; + transmissionMode = (buffer[8] >> 1) & 0x03; + otherFrequencyFlag = buffer[8] & 0x01; + reserved2 = (buffer[9] << 24) | (buffer[10] << 16) | (buffer[11] << 8) | buffer[12]; +} + +uint32_t TerrestrialDeliverySystemDescriptor::getCentreFrequency(void) const +{ + return centreFrequency; +} + +uint8_t TerrestrialDeliverySystemDescriptor::getBandwidth(void) const +{ + return bandwidth; +} + +uint8_t TerrestrialDeliverySystemDescriptor::getConstellation(void) const +{ + return constellation; +} + +uint8_t TerrestrialDeliverySystemDescriptor::getHierarchyInformation(void) const +{ + return hierarchyInformation; +} + +uint8_t TerrestrialDeliverySystemDescriptor::getCodeRateHpStream(void) const +{ + return codeRateHpStream; +} + +uint8_t TerrestrialDeliverySystemDescriptor::getCodeRateLpStream(void) const +{ + return codeRateLpStream; +} + +uint8_t TerrestrialDeliverySystemDescriptor::getGuardInterval(void) const +{ + return guardInterval; +} + +uint8_t TerrestrialDeliverySystemDescriptor::getTransmissionMode(void) const +{ + return transmissionMode; +} + +uint8_t TerrestrialDeliverySystemDescriptor::getOtherFrequencyFlag(void) const +{ + return otherFrequencyFlag; +} + diff --git a/lib/dvb_si/terrestrial_delivery_system_descriptor.h b/lib/dvb_si/terrestrial_delivery_system_descriptor.h new file mode 100644 index 0000000..9bbe817 --- /dev/null +++ b/lib/dvb_si/terrestrial_delivery_system_descriptor.h @@ -0,0 +1,56 @@ +/* + * $Id: terrestrial_delivery_system_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_terrestrial_delivery_system_descriptor_h__ +#define __dvb_descriptor_terrestrial_delivery_system_descriptor_h__ + +#include "descriptor.h" + +class TerrestrialDeliverySystemDescriptor : public Descriptor +{ + protected: + unsigned centreFrequency : 32; + unsigned bandwidth : 3; + unsigned reserved : 5; + unsigned constellation : 2; + unsigned hierarchyInformation : 3; + unsigned codeRateHpStream : 3; + unsigned codeRateLpStream : 3; + unsigned guardInterval : 2; + unsigned transmissionMode : 2; + unsigned otherFrequencyFlag : 1; + unsigned reserved2 : 32; + + public: + TerrestrialDeliverySystemDescriptor(const uint8_t * const buffer); + + uint32_t getCentreFrequency(void) const; + uint8_t getBandwidth(void) const; + uint8_t getConstellation(void) const; + uint8_t getHierarchyInformation(void) const; + uint8_t getCodeRateHpStream(void) const; + uint8_t getCodeRateLpStream(void) const; + uint8_t getGuardInterval(void) const; + uint8_t getTransmissionMode(void) const; + uint8_t getOtherFrequencyFlag(void) const; +}; + +#endif /* __dvb_descriptor_terrestrial_delivery_system_descriptor_h__ */ diff --git a/lib/dvb_si/time_shifted_event_descriptor.h b/lib/dvb_si/time_shifted_event_descriptor.h new file mode 100644 index 0000000..bd9a1b6 --- /dev/null +++ b/lib/dvb_si/time_shifted_event_descriptor.h @@ -0,0 +1,40 @@ +/* + * $Id: time_shifted_event_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_time_shifted_event_descriptor_h__ +#define __dvb_descriptor_time_shifted_event_descriptor_h__ + +#include "descriptor.h" + +class TimeShiftedEventDescriptor : public Descriptor +{ + protected: + unsigned referenceServiceId : 16; + unsigned referenceEventId : 16; + + public: + TimeShiftedEventDescriptor(const uint8_t * const buffer); + + uint16_t getReferenceServiceId(void) const; + uint16_t getReferenceEventId(void) const; +}; + +#endif /* __dvb_descriptor_time_shifted_event_descriptor_h__ */ diff --git a/lib/dvb_si/time_shifted_service_descriptor.cpp b/lib/dvb_si/time_shifted_service_descriptor.cpp new file mode 100644 index 0000000..0d77946 --- /dev/null +++ b/lib/dvb_si/time_shifted_service_descriptor.cpp @@ -0,0 +1,33 @@ +/* + * $Id: time_shifted_service_descriptor.cpp,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +TimeShiftedServiceDescriptor::TimeShiftedServiceDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + referenceServiceId = (buffer[2] << 8) | buffer[3]; +} + +uint16_t TimeShiftedServiceDescriptor::getReferenceServiceId(void) const +{ + return referenceServiceId; +} + diff --git a/lib/dvb_si/time_shifted_service_descriptor.h b/lib/dvb_si/time_shifted_service_descriptor.h new file mode 100644 index 0000000..57ba72b --- /dev/null +++ b/lib/dvb_si/time_shifted_service_descriptor.h @@ -0,0 +1,38 @@ +/* + * $Id: time_shifted_service_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_time_shifted_service_descriptor_h__ +#define __dvb_descriptor_time_shifted_service_descriptor_h__ + +#include "descriptor.h" + +class TimeShiftedServiceDescriptor : public Descriptor +{ + protected: + unsigned referenceServiceId : 16; + + public: + TimeShiftedServiceDescriptor(const uint8_t * const buffer); + + uint16_t getReferenceServiceId(void) const; +}; + +#endif /* __dvb_descriptor_time_shifted_service_descriptor_h__ */ diff --git a/lib/dvb_si/tot.cpp b/lib/dvb_si/tot.cpp new file mode 100644 index 0000000..16171a5 --- /dev/null +++ b/lib/dvb_si/tot.cpp @@ -0,0 +1,44 @@ +/* + * $Id: tot.cpp,v 1.1 2003-10-17 15:36:37 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +TimeOffsetTable::TimeOffsetTable(const uint8_t * const buffer) : ShortCrcTable(buffer) +{ + utcTimeMjd = (buffer[3] << 8) | buffer[4]; + utcTimeBcd = (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; + reserved = (buffer[8] >> 4) & 0x0f; + descriptorsLoopLength = ((buffer[8] & 0x0f) << 8) | buffer[9]; + + for (uint16_t i = 0; i < descriptorsLoopLength; i += buffer[i + 11] + 2) + descriptor(&buffer[i + 10]); +} + +uint16_t TimeOffsetTable::getUtcTimeMjd(void) const +{ + return utcTimeMjd; +} + +uint32_t TimeOffsetTable::getUtcTimeBcd(void) const +{ + return utcTimeBcd; +} + diff --git a/lib/dvb_si/tot.h b/lib/dvb_si/tot.h new file mode 100644 index 0000000..ce43488 --- /dev/null +++ b/lib/dvb_si/tot.h @@ -0,0 +1,51 @@ +/* + * $Id: tot.h,v 1.1 2003-10-17 15:36:39 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_table_tot_h__ +#define __dvb_table_tot_h__ + +#include +#include "short_crc_table.h" + +class TimeOffsetTable : public ShortCrcTable, public DescriptorContainer +{ + protected: + unsigned utcTimeMjd : 16; + unsigned utcTimeBcd : 24; + unsigned reserved : 4; + unsigned descriptorsLoopLength : 12; + + public: + TimeOffsetTable(const uint8_t * const buffer); + + static const enum PacketId PID = PID_TOT; + static const enum TableId TID = TID_TOT; + static const uint32_t TIMEOUT = 36000; + + uint16_t getUtcTimeMjd(void) const; + uint32_t getUtcTimeBcd(void) const; +}; + +typedef std::vector TimeOffsetTableVector; +typedef TimeOffsetTableVector::iterator TimeOffsetTableIterator; +typedef TimeOffsetTableVector::const_iterator TimeOffsetTableConstIterator; + +#endif /* __dvb_table_tot_h__ */ diff --git a/lib/dvb_si/transport_stream_descriptor.h b/lib/dvb_si/transport_stream_descriptor.h new file mode 100644 index 0000000..0c223e2 --- /dev/null +++ b/lib/dvb_si/transport_stream_descriptor.h @@ -0,0 +1,38 @@ +/* + * $Id: transport_stream_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_transport_stream_descriptor_h__ +#define __dvb_descriptor_transport_stream_descriptor_h__ + +#include "descriptor.h" + +class TransportStreamDescriptor : public Descriptor +{ + protected: + std::string bytes; + + public: + TransportStreamDescriptor(const uint8_t * const buffer); + + std::string getBytes(void) const; +}; + +#endif /* __dvb_descriptor_transport_stream_descriptor_h__ */ diff --git a/lib/dvb_si/url_descriptor.h b/lib/dvb_si/url_descriptor.h new file mode 100644 index 0000000..23db23a --- /dev/null +++ b/lib/dvb_si/url_descriptor.h @@ -0,0 +1,34 @@ +/* + * $Id: url_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_url_descriptor_h__ +#define __dvb_descriptor_url_descriptor_h__ + +#include "descriptor.h" + +/* 0x90 */ +class UrlDescriptor : public Descriptor +{ + public: + UrlDescriptor(const uint8_t * const buffer); +}; + +#endif /* __dvb_descriptor_url_descriptor_h__ */ diff --git a/lib/dvb_si/vbi_data_descriptor.cpp b/lib/dvb_si/vbi_data_descriptor.cpp new file mode 100644 index 0000000..8791155 --- /dev/null +++ b/lib/dvb_si/vbi_data_descriptor.cpp @@ -0,0 +1,98 @@ +/* + * $Id: vbi_data_descriptor.cpp,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +VbiDataLine::VbiDataLine(const uint8_t * const buffer) +{ + reserved = (buffer[0] >> 6) & 0x03; + fieldParity = (buffer[0] >> 5) & 0x01; + lineOffset = buffer[0] & 0x1F; +} + +uint8_t VbiDataLine::getFieldParity(void) const +{ + return fieldParity; +} + +uint8_t VbiDataLine::getLineOffset(void) const +{ + return lineOffset; +} + +VbiDataService::VbiDataService(const uint8_t * const buffer) +{ + uint16_t i; + + dataServiceId = buffer[0]; + dataServiceDescriptorLength = buffer[1]; + + switch (dataServiceId) { + case 0x01: + case 0x02: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + for (i = 0; i < dataServiceDescriptorLength; ++i); + vbiDataLines.push_back(new VbiDataLine(&buffer[i + 2])); + break; + + default: + for (i = 0; i < dataServiceDescriptorLength; ++i) + reserved.push_back(buffer[i + 2]); + break; + } +} + +VbiDataService::~VbiDataService(void) +{ + for (VbiDataLineIterator i = vbiDataLines.begin(); i != vbiDataLines.end(); ++i) + delete *i; +} + +uint8_t VbiDataService::getDataServiceId(void) const +{ + return dataServiceId; +} + +const VbiDataLineVector *VbiDataService::getVbiDataLines(void) const +{ + return &vbiDataLines; +} + +VbiDataDescriptor::VbiDataDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + for (uint16_t i = 0; i < descriptorLength; i += buffer[i + 3] + 2) + vbiDataServices.push_back(new VbiDataService(&buffer[i + 2])); +} + +VbiDataDescriptor::~VbiDataDescriptor(void) +{ + for (VbiDataServiceIterator i = vbiDataServices.begin(); i != vbiDataServices.end(); ++i) + delete *i; +} + +const VbiDataServiceVector *VbiDataDescriptor::getVbiDataServices(void) const +{ + return &vbiDataServices; +} + diff --git a/lib/dvb_si/vbi_data_descriptor.h b/lib/dvb_si/vbi_data_descriptor.h new file mode 100644 index 0000000..aa26b91 --- /dev/null +++ b/lib/dvb_si/vbi_data_descriptor.h @@ -0,0 +1,77 @@ +/* + * $Id: vbi_data_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_vbi_data_descriptor_h__ +#define __dvb_descriptor_vbi_data_descriptor_h__ + +#include "descriptor.h" + +class VbiDataLine +{ + protected: + unsigned reserved : 2; + unsigned fieldParity : 1; + unsigned lineOffset : 5; + + public: + VbiDataLine(const uint8_t * const buffer); + + uint8_t getFieldParity(void) const; + uint8_t getLineOffset(void) const; +}; + +typedef std::vector VbiDataLineVector; +typedef VbiDataLineVector::iterator VbiDataLineIterator; +typedef VbiDataLineVector::const_iterator VbiDataLineConstIterator; + +class VbiDataService +{ + protected: + unsigned dataServiceId : 8; + unsigned dataServiceDescriptorLength : 8; + VbiDataLineVector vbiDataLines; + std::vector reserved; + + public: + VbiDataService(const uint8_t * const buffer); + ~VbiDataService(void); + + uint8_t getDataServiceId(void) const; + const VbiDataLineVector *getVbiDataLines(void) const; +}; + +typedef std::vector VbiDataServiceVector; +typedef VbiDataServiceVector::iterator VbiDataServiceIterator; +typedef VbiDataServiceVector::const_iterator VbiDataServiceConstIterator; + +class VbiDataDescriptor : public Descriptor +{ + protected: + VbiDataServiceVector vbiDataServices; + + public: + VbiDataDescriptor(const uint8_t * const buffer); + ~VbiDataDescriptor(void); + + const VbiDataServiceVector *getVbiDataServices(void) const; +}; + +#endif /* __dvb_descriptor_vbi_data_descriptor_h__ */ diff --git a/lib/dvb_si/vbi_teletext_descriptor.cpp b/lib/dvb_si/vbi_teletext_descriptor.cpp new file mode 100644 index 0000000..bf7c0ab --- /dev/null +++ b/lib/dvb_si/vbi_teletext_descriptor.cpp @@ -0,0 +1,68 @@ +/* + * $Id: vbi_teletext_descriptor.cpp,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +VbiTeletext::VbiTeletext(const uint8_t * const buffer) +{ + iso639LanguageCode.assign((char *)&buffer[0], 3); + teletextType = (buffer[3] >> 3) & 0x1F; + teletextMagazineNumber = buffer[3] & 0x07; + teletextPageNumber = buffer[4]; +} + +std::string VbiTeletext::getIso639LanguageCode(void) const +{ + return iso639LanguageCode; +} + +uint8_t VbiTeletext::getTeletextType(void) const +{ + return teletextType; +} + +uint8_t VbiTeletext::getTeletextMagazineNumber(void) const +{ + return teletextMagazineNumber; +} + +uint8_t VbiTeletext::getTeletextPageNumber(void) const +{ + return teletextPageNumber; +} + +VbiTeletextDescriptor::VbiTeletextDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + for (uint16_t i = 0; i < descriptorLength; i += 5) + vbiTeletexts.push_back(new VbiTeletext(&buffer[i + 2])); +} + +VbiTeletextDescriptor::~VbiTeletextDescriptor(void) +{ + for (VbiTeletextIterator i = vbiTeletexts.begin(); i != vbiTeletexts.end(); ++i) + delete *i; +} + +const VbiTeletextVector *VbiTeletextDescriptor::getVbiTeletexts(void) const +{ + return &vbiTeletexts; +} + diff --git a/lib/dvb_si/vbi_teletext_descriptor.h b/lib/dvb_si/vbi_teletext_descriptor.h new file mode 100644 index 0000000..084d248 --- /dev/null +++ b/lib/dvb_si/vbi_teletext_descriptor.h @@ -0,0 +1,60 @@ +/* + * $Id: vbi_teletext_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_vbi_teletext_descriptor_h__ +#define __dvb_descriptor_vbi_teletext_descriptor_h__ + +#include "descriptor.h" + +class VbiTeletext +{ + protected: + std::string iso639LanguageCode; + unsigned teletextType : 5; + unsigned teletextMagazineNumber : 3; + unsigned teletextPageNumber : 8; + + public: + VbiTeletext(const uint8_t * const buffer); + + std::string getIso639LanguageCode(void) const; + uint8_t getTeletextType(void) const; + uint8_t getTeletextMagazineNumber(void) const; + uint8_t getTeletextPageNumber(void) const; +}; + +typedef std::vector VbiTeletextVector; +typedef VbiTeletextVector::iterator VbiTeletextIterator; +typedef VbiTeletextVector::const_iterator VbiTeletextConstIterator; + +class VbiTeletextDescriptor : public Descriptor +{ + protected: + VbiTeletextVector vbiTeletexts; + + public: + VbiTeletextDescriptor(const uint8_t * const buffer); + ~VbiTeletextDescriptor(void); + + const VbiTeletextVector *getVbiTeletexts(void) const; +}; + +#endif /* __dvb_descriptor_vbi_teletext_descriptor_h__ */ diff --git a/lib/dvb_si/video_stream_descriptor.cpp b/lib/dvb_si/video_stream_descriptor.cpp new file mode 100644 index 0000000..103026d --- /dev/null +++ b/lib/dvb_si/video_stream_descriptor.cpp @@ -0,0 +1,73 @@ +/* + * $Id: video_stream_descriptor.cpp,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +VideoStreamDescriptor::VideoStreamDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + multipleFrameRateFlag = (buffer[2] >> 7) & 0x01; + frameRateCode = (buffer[2] >> 3) & 0x0F; + mpeg1OnlyFlag = (buffer[2] >> 2) & 0x01; + constrainedParameterFlag = (buffer[2] >> 1) & 0x01; + + if (!mpeg1OnlyFlag) { + profileAndLevelIndication = buffer[3]; + chromaFormat = (buffer[4] >> 6) & 0x03; + frameRateExtensionFlag = (buffer[4] >> 5) & 0x01; + reserved = buffer[4] & 0x1F; + } +} + +uint8_t VideoStreamDescriptor::getMultipleFrameRateFlag(void) const +{ + return multipleFrameRateFlag; +} + +uint8_t VideoStreamDescriptor::getFrameRateCode(void) const +{ + return frameRateCode; +} + +uint8_t VideoStreamDescriptor::getMpeg1OnlyFlag(void) const +{ + return mpeg1OnlyFlag; +} + +uint8_t VideoStreamDescriptor::getConstrainedParameterFlag(void) const +{ + return constrainedParameterFlag; +} + +uint8_t VideoStreamDescriptor::getProfileAndLevelIndication(void) const +{ + return profileAndLevelIndication; +} + +uint8_t VideoStreamDescriptor::getChromaFormat(void) const +{ + return chromaFormat; +} + +uint8_t VideoStreamDescriptor::getFrameRateExtensionFlag(void) const +{ + return frameRateExtensionFlag; +} + diff --git a/lib/dvb_si/video_stream_descriptor.h b/lib/dvb_si/video_stream_descriptor.h new file mode 100644 index 0000000..2652acc --- /dev/null +++ b/lib/dvb_si/video_stream_descriptor.h @@ -0,0 +1,53 @@ +/* + * $Id: video_stream_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_video_stream_descriptor_h__ +#define __dvb_descriptor_video_stream_descriptor_h__ + +#include "descriptor.h" + +class VideoStreamDescriptor : public Descriptor +{ + protected: + unsigned multipleFrameRateFlag : 1; + unsigned frameRateCode : 4; + unsigned mpeg1OnlyFlag : 1; + unsigned constrainedParameterFlag : 1; + unsigned stillPictureFlag : 1; + unsigned profileAndLevelIndication : 8; + unsigned chromaFormat : 2; + unsigned frameRateExtensionFlag : 1; + unsigned reserved : 5; + + public: + VideoStreamDescriptor(const uint8_t * const buffer); + + uint8_t getMultipleFrameRateFlag(void) const; + uint8_t getFrameRateCode(void) const; + uint8_t getMpeg1OnlyFlag(void) const; + uint8_t getConstrainedParameterFlag(void) const; + uint8_t getStillPictureFlag(void) const; + uint8_t getProfileAndLevelIndication(void) const; + uint8_t getChromaFormat(void) const; + uint8_t getFrameRateExtensionFlag(void) const; +}; + +#endif /* __dvb_descriptor_video_stream_descriptor_h__ */ diff --git a/lib/dvb_si/video_window_descriptor.cpp b/lib/dvb_si/video_window_descriptor.cpp new file mode 100644 index 0000000..d8e7d69 --- /dev/null +++ b/lib/dvb_si/video_window_descriptor.cpp @@ -0,0 +1,45 @@ +/* + * $Id: video_window_descriptor.cpp,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +VideoWindowDescriptor::VideoWindowDescriptor(const uint8_t * const buffer) : Descriptor(buffer) +{ + horizontalOffset = ((buffer[2] << 8) | (buffer[3] & 0xF3)) >> 2; + verticalOffset = (((buffer[3] & 0x03) << 16) | (buffer[4] << 8) | (buffer[5] & 0xF0)) >> 4; + windowPriority = buffer[5] & 0x0F; +} + +uint16_t VideoWindowDescriptor::getHorizontalOffset(void) const +{ + return horizontalOffset; +} + +uint16_t VideoWindowDescriptor::getVerticalOffset(void) const +{ + return verticalOffset; +} + +uint8_t VideoWindowDescriptor::getWindowPriority(void) const +{ + return windowPriority; +} + diff --git a/lib/dvb_si/video_window_descriptor.h b/lib/dvb_si/video_window_descriptor.h new file mode 100644 index 0000000..1558f78 --- /dev/null +++ b/lib/dvb_si/video_window_descriptor.h @@ -0,0 +1,42 @@ +/* + * $Id: video_window_descriptor.h,v 1.1 2003-10-17 15:36:38 tmbinc Exp $ + * + * (C) 2002-2003 Andreas Oberritter + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __dvb_descriptor_video_window_descriptor_h__ +#define __dvb_descriptor_video_window_descriptor_h__ + +#include "descriptor.h" + +class VideoWindowDescriptor : public Descriptor +{ + protected: + unsigned horizontalOffset : 14; + unsigned verticalOffset : 14; + unsigned windowPriority : 4; + + public: + VideoWindowDescriptor(const uint8_t * const buffer); + + uint16_t getHorizontalOffset(void) const; + uint16_t getVerticalOffset(void) const; + uint8_t getWindowPriority(void) const; +}; + +#endif /* __dvb_descriptor_video_window_descriptor_h__ */ diff --git a/lib/gdi/.cvsignore b/lib/gdi/.cvsignore new file mode 100644 index 0000000..316b06f --- /dev/null +++ b/lib/gdi/.cvsignore @@ -0,0 +1,7 @@ +*.moc.* +Makefile +Makefile.in +.deps +.libs +*.lo +*.la diff --git a/lib/gdi/Makefile.am b/lib/gdi/Makefile.am new file mode 100644 index 0000000..eff43b0 --- /dev/null +++ b/lib/gdi/Makefile.am @@ -0,0 +1,8 @@ +INCLUDES = \ + -I$(top_srcdir)/include + +noinst_LIBRARIES = libenigma_gdi.a + +libenigma_gdi_a_SOURCES = \ + epng.cpp erect.cpp fb.cpp font.cpp font_arabic.cpp gfbdc.cpp \ + glcddc.cpp gpixmap.cpp grc.cpp lcd.cpp diff --git a/lib/gdi/epng.cpp b/lib/gdi/epng.cpp new file mode 100644 index 0000000..d476ec3 --- /dev/null +++ b/lib/gdi/epng.cpp @@ -0,0 +1,187 @@ +#include +#include +#include +#include + +gImage *loadPNG(const char *filename) +{ + __u8 header[8]; + FILE *fp=fopen(filename, "rb"); + + gImage *res=0; + + if (!fp) + { +// eDebug("couldn't open %s", filename ); + return 0; + } + if (!fread(header, 8, 1, fp)) + { + eDebug("couldn't read"); + fclose(fp); + return 0; + } + if (png_sig_cmp(header, 0, 8)) + { + fclose(fp); + return 0; + } + png_structp png_ptr=png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if (!png_ptr) + { + eDebug("no pngptr"); + fclose(fp); + return 0; + } + png_infop info_ptr=png_create_info_struct(png_ptr); + if (!info_ptr) + { + eDebug("no info ptr"); + png_destroy_read_struct(&png_ptr, (png_infopp)0, (png_infopp)0); + fclose(fp); + return 0; + } + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) + { + eDebug("no end"); + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + fclose(fp); + return 0; + } + if (setjmp(png_ptr->jmpbuf)) + { + eDebug("das war wohl nix"); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + fclose(fp); + if (res) + delete res; + return 0; + } + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, 8); + png_set_invert_alpha(png_ptr); + png_read_info(png_ptr, info_ptr); + + png_uint_32 width, height; + int bit_depth; + int color_type; + + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); + +// eDebug("%s: %dx%dx%d png, %d", filename, (int)width, (int)height, (int)bit_depth, color_type); + + if (color_type != 6) + { + res=new gImage(eSize(width, height), bit_depth); + + png_bytep *rowptr=new png_bytep[height]; + + for (unsigned int i=0; idata))+i*res->stride; + png_read_rows(png_ptr, rowptr, 0, height); + + delete rowptr; + + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)) + { + png_color *palette; + int num_palette; + png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); + if (num_palette) + res->clut.data=new gRGB[num_palette]; + else + res->clut.data=0; + res->clut.colors=num_palette; + + for (int i=0; iclut.data[i].a=0; + res->clut.data[i].r=palette[i].red; + res->clut.data[i].g=palette[i].green; + res->clut.data[i].b=palette[i].blue; + } + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + { + png_byte *trans; + png_get_tRNS(png_ptr, info_ptr, &trans, &num_palette, 0); + for (int i=0; iclut.data[i].a=255-trans[i]; + } + } else + { + res->clut.data=0; + res->clut.colors=0; + } + png_read_end(png_ptr, end_info); + } else + res=0; + + png_destroy_read_struct(&png_ptr, &info_ptr,&end_info); + fclose(fp); + return res; +} + +int savePNG(const char *filename, gPixmap *pixmap) +{ + FILE *fp=fopen(filename, "wb"); + if (!fp) + return -1; + png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if (!png_ptr) + { + eDebug("write png, couldnt allocate write struct"); + fclose(fp); + unlink(filename); + return -2; + } + png_infop info_ptr=png_create_info_struct(png_ptr); + if (!info_ptr) + { + eDebug("info"); + png_destroy_write_struct(&png_ptr, 0); + fclose(fp); + unlink(filename); + return -3; + } + if (setjmp(png_ptr->jmpbuf)) + { + eDebug("error :/"); + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fp); + unlink(filename); + return -4; + } + png_init_io(png_ptr, fp); + png_set_filter(png_ptr, 0, PNG_FILTER_NONE|PNG_FILTER_SUB|PNG_FILTER_PAETH); + png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); + + png_set_IHDR(png_ptr, info_ptr, pixmap->x, pixmap->y, pixmap->bpp, + pixmap->clut.data ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_GRAY, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + if (pixmap->clut.data) + { + png_color palette[pixmap->clut.colors]; + png_byte trans[pixmap->clut.colors]; + for (int i=0; iclut.colors; ++i) + { + palette[i].red=pixmap->clut.data[i].r; + palette[i].green=pixmap->clut.data[i].g; + palette[i].blue=pixmap->clut.data[i].b; + trans[i]=255-pixmap->clut.data[i].a; + } + png_set_PLTE(png_ptr, info_ptr, palette, pixmap->clut.colors); + png_set_tRNS(png_ptr, info_ptr, trans, pixmap->clut.colors, 0); + } + png_write_info(png_ptr, info_ptr); + png_set_packing(png_ptr); + png_byte *row_pointers[pixmap->y]; + for (int i=0; iy; ++i) + row_pointers[i]=((png_byte*)pixmap->data)+i*pixmap->stride; + png_write_image(png_ptr, row_pointers); + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fp); + eDebug("wrote png ! fine !"); + return 0; +} diff --git a/lib/gdi/epng.h b/lib/gdi/epng.h new file mode 100644 index 0000000..650c251 --- /dev/null +++ b/lib/gdi/epng.h @@ -0,0 +1,9 @@ +#ifndef __png_h +#define __png_h + +#include "grc.h" + +gImage *loadPNG(const char *filename); +int savePNG(const char *filename, gPixmap *pixmap); + +#endif diff --git a/lib/gdi/epoint.h b/lib/gdi/epoint.h new file mode 100644 index 0000000..fc5f983 --- /dev/null +++ b/lib/gdi/epoint.h @@ -0,0 +1,172 @@ +#ifndef EPOINT_H +#define EPOINT_H + +#include + +#ifndef ABS +#define ABS(x) ( x>0 ? x : -x ) +#endif + +class ePoint +{ +public: + ePoint(); + ePoint( int xpos, int ypos ); + + bool isNull() const; + + int x() const; + int y() const; + void setX( int x ); + void setY( int y ); + + int manhattanLength() const; + + int &rx(); + int &ry(); + + ePoint &operator+=( const ePoint &p ); + ePoint &operator-=( const ePoint &p ); + ePoint &operator*=( int c ); + ePoint &operator*=( double c ); + ePoint &operator/=( int c ); + ePoint &operator/=( double c ); + + friend inline bool operator==( const ePoint &, const ePoint & ); + friend inline bool operator!=( const ePoint &, const ePoint & ); + friend inline ePoint operator+( const ePoint &, const ePoint & ); + friend inline ePoint operator-( const ePoint &, const ePoint & ); + friend inline ePoint operator*( const ePoint &, int ); + friend inline ePoint operator*( int, const ePoint & ); + friend inline ePoint operator*( const ePoint &, double ); + friend inline ePoint operator*( double, const ePoint & ); + friend inline ePoint operator-( const ePoint & ); + friend inline ePoint operator/( const ePoint &, int ); + friend inline ePoint operator/( const ePoint &, double ); +private: + int xp; + int yp; +}; + + +inline int ePoint::manhattanLength() const +{ + return ABS(x())+ABS(y()); +} + + +/***************************************************************************** + ePoint stream functions + *****************************************************************************/ +namespace std +{ + inline ostream &operator<<( ostream & s, const ePoint & p ) + { + s << p.x() << p.y(); + return s; + } + + inline istream &operator>>( istream & s, ePoint & p ) + { + s >> p.rx() >> p.ry(); + return s; + } +} + + +/***************************************************************************** + ePoint inline functions + *****************************************************************************/ + +inline ePoint::ePoint() +{ xp=0; yp=0; } + +inline ePoint::ePoint( int xpos, int ypos ) +{ xp=(int)xpos; yp=(int)ypos; } + +inline bool ePoint::isNull() const +{ return xp == 0 && yp == 0; } + +inline int ePoint::x() const +{ return xp; } + +inline int ePoint::y() const +{ return yp; } + +inline void ePoint::setX( int x ) +{ xp = (int)x; } + +inline void ePoint::setY( int y ) +{ yp = (int)y; } + +inline int &ePoint::rx() +{ return xp; } + +inline int &ePoint::ry() +{ return yp; } + +inline ePoint &ePoint::operator+=( const ePoint &p ) +{ xp+=p.xp; yp+=p.yp; return *this; } + +inline ePoint &ePoint::operator-=( const ePoint &p ) +{ xp-=p.xp; yp-=p.yp; return *this; } + +inline ePoint &ePoint::operator*=( int c ) +{ xp*=(int)c; yp*=(int)c; return *this; } + +inline ePoint &ePoint::operator*=( double c ) +{ xp=(int)(xp*c); yp=(int)(yp*c); return *this; } + +inline bool operator==( const ePoint &p1, const ePoint &p2 ) +{ return p1.xp == p2.xp && p1.yp == p2.yp; } + +inline bool operator!=( const ePoint &p1, const ePoint &p2 ) +{ return p1.xp != p2.xp || p1.yp != p2.yp; } + +inline ePoint operator+( const ePoint &p1, const ePoint &p2 ) +{ return ePoint(p1.xp+p2.xp, p1.yp+p2.yp); } + +inline ePoint operator-( const ePoint &p1, const ePoint &p2 ) +{ return ePoint(p1.xp-p2.xp, p1.yp-p2.yp); } + +inline ePoint operator*( const ePoint &p, int c ) +{ return ePoint(p.xp*c, p.yp*c); } + +inline ePoint operator*( int c, const ePoint &p ) +{ return ePoint(p.xp*c, p.yp*c); } + +inline ePoint operator*( const ePoint &p, double c ) +{ return ePoint((int)(p.xp*c), (int)(p.yp*c)); } + +inline ePoint operator*( double c, const ePoint &p ) +{ return ePoint((int)(p.xp*c), (int)(p.yp*c)); } + +inline ePoint operator-( const ePoint &p ) +{ return ePoint(-p.xp, -p.yp); } + +inline ePoint &ePoint::operator/=( int c ) +{ + xp/=(int)c; + yp/=(int)c; + return *this; +} + +inline ePoint &ePoint::operator/=( double c ) +{ + xp=(int)(xp/c); + yp=(int)(yp/c); + return *this; +} + +inline ePoint operator/( const ePoint &p, int c ) +{ + return ePoint(p.xp/c, p.yp/c); +} + +inline ePoint operator/( const ePoint &p, double c ) +{ + return ePoint((int)(p.xp/c), (int)(p.yp/c)); +} + + +#endif // EPOINT_H diff --git a/lib/gdi/erect.cpp b/lib/gdi/erect.cpp new file mode 100644 index 0000000..b72e5d0 --- /dev/null +++ b/lib/gdi/erect.cpp @@ -0,0 +1,204 @@ +#include +#include + +/***************************************************************************** + eRect member functions + *****************************************************************************/ + +eRect::eRect( const ePoint &topLeft, const ePoint &bottomRight ) +{ + x1 = topLeft.x(); + y1 = topLeft.y(); + x2 = bottomRight.x(); + y2 = bottomRight.y(); +} + +eRect eRect::normalize() const +{ + eRect r; + if ( x2 < x1 ) { // swap bad x values + r.x1 = x2; + r.x2 = x1; + } else { + r.x1 = x1; + r.x2 = x2; + } + if ( y2 < y1 ) { // swap bad y values + r.y1 = y2; + r.y2 = y1; + } else { + r.y1 = y1; + r.y2 = y2; + } + return r; +} + +void eRect::rect( int *x, int *y, int *w, int *h ) const +{ + *x = x1; + *y = y1; + *w = x2-x1; + *h = y2-y1; +} + +void eRect::coords( int *xp1, int *yp1, int *xp2, int *yp2 ) const +{ + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + +void eRect::moveTopLeft( const ePoint &p ) +{ + x2 += (p.x() - x1); + y2 += (p.y() - y1); + x1 = p.x(); + y1 = p.y(); +} + +void eRect::moveBottomRight( const ePoint &p ) +{ + x1 += (p.x() - x2); + y1 += (p.y() - y2); + x2 = p.x(); + y2 = p.y(); +} + +void eRect::moveTopRight( const ePoint &p ) +{ + x1 += (p.x() - x2); + y2 += (p.y() - y1); + x2 = p.x(); + y1 = p.y(); +} + +void eRect::moveBottomLeft( const ePoint &p ) +{ + x2 += (p.x() - x1); + y1 += (p.y() - y2); + x1 = p.x(); + y2 = p.y(); +} + +void eRect::moveCenter( const ePoint &p ) +{ + int w = x2 - x1; + int h = y2 - y1; + x1 = (p.x() - w/2); + y1 = (p.y() - h/2); + x2 = x1 + w; + y2 = y1 + h; +} + +void eRect::setRect( int x, int y, int w, int h ) +{ + x1 = x; + y1 = y; + x2 = (x+w); + y2 = (y+h); +} + +void eRect::setCoords( int xp1, int yp1, int xp2, int yp2 ) +{ + x1 = xp1; + y1 = yp1; + x2 = xp2; + y2 = yp2; +} + +void eRect::setWidth( int w ) +{ + x2 = x1 + w; +} + +void eRect::setHeight( int h ) +{ + y2 = y1 + h; +} + +void eRect::setSize( const eSize &s ) +{ + x2 = s.width() +x1; + y2 = s.height()+y1; +} + +bool eRect::contains( const ePoint &p) const +{ + return p.x() >= x1 && p.x() < x2 && + p.y() >= y1 && p.y() < y2; +} + +bool eRect::contains( const eRect &r) const +{ + return r.x1 >= x1 && + r.x2 <= x2 && + r.y1 >= y1 && + r.y2 <= y2; +} + +eRect& eRect::operator|=(const eRect &r) +{ + *this = *this | r; + return *this; +} + +eRect& eRect::operator&=(const eRect &r) +{ + *this = *this & r; + return *this; +} + +eRect eRect::operator|(const eRect &r) const +{ + if ( isValid() ) { + if ( r.isValid() ) { + eRect tmp; + tmp.setLeft( MIN( x1, r.x1 ) ); + tmp.setRight( MAX( x2, r.x2 ) ); + tmp.setTop( MIN( y1, r.y1 ) ); + tmp.setBottom( MAX( y2, r.y2 ) ); + return tmp; + } else { + return *this; + } + } else { + return r; + } +} + +eRect eRect::unite( const eRect &r ) const +{ + return *this | r; +} + +eRect eRect::operator&( const eRect &r ) const +{ + eRect tmp; + tmp.x1 = MAX( x1, r.x1 ); + tmp.x2 = MIN( x2, r.x2 ); + tmp.y1 = MAX( y1, r.y1 ); + tmp.y2 = MIN( y2, r.y2 ); + return tmp; +} + +eRect eRect::intersect( const eRect &r ) const +{ + return *this & r; +} + +bool eRect::intersects( const eRect &r ) const +{ + return ( MAX( x1, r.x1 ) < MIN( x2, r.x2 ) && + MAX( y1, r.y1 ) < MIN( y2, r.y2 ) ); +} + +bool operator==( const eRect &r1, const eRect &r2 ) +{ + return r1.x1==r2.x1 && r1.x2==r2.x2 && r1.y1==r2.y1 && r1.y2==r2.y2; +} + +bool operator!=( const eRect &r1, const eRect &r2 ) +{ + return r1.x1!=r2.x1 || r1.x2!=r2.x2 || r1.y1!=r2.y1 || r1.y2!=r2.y2; +} diff --git a/lib/gdi/erect.h b/lib/gdi/erect.h new file mode 100644 index 0000000..c41d831 --- /dev/null +++ b/lib/gdi/erect.h @@ -0,0 +1,229 @@ +#ifndef ERECT_H +#define ERECT_H + +#include +#include + + +// x2 = x1 + width (AND NOT, NEVER, NEVER EVER +1 or -1 !!!!) + +class eRect // rectangle class +{ +public: + eRect() { x1 = y1 = x2 = y2 = 0; } + eRect( const ePoint &topleft, const ePoint &bottomright ); + + // we use this contructor very often... do it inline... + eRect( const ePoint &topleft, const eSize &size ) + { + x1 = topleft.x(); + y1 = topleft.y(); + x2 = (x1+size.width()); + y2 = (y1+size.height()); + } + + eRect( int left, int top, int width, int height ); + + bool isNull() const; + bool isEmpty() const; + bool isValid() const; + eRect normalize() const; + + int left() const; + int top() const; + int right() const; + int bottom() const; + int &rLeft(); + int &rTop(); + int &rRight(); + int &rBottom(); + + int x() const; + int y() const; + void setLeft( int pos ); + void setTop( int pos ); + void setRight( int pos ); + void setBottom( int pos ); + void setX( int x ); + void setY( int y ); + + ePoint topLeft() const; + ePoint bottomRight() const; + ePoint topRight() const; + ePoint bottomLeft() const; + ePoint center() const; + + void rect( int *x, int *y, int *w, int *h ) const; + void coords( int *x1, int *y1, int *x2, int *y2 ) const; + + void moveTopLeft( const ePoint &p ); + void moveBottomRight( const ePoint &p ); + void moveTopRight( const ePoint &p ); + void moveBottomLeft( const ePoint &p ); + void moveCenter( const ePoint &p ); + + void moveBy( int dx, int dy ) + { + x1 += dx; + y1 += dy; + x2 += dx; + y2 += dy; + } + + void setRect( int x, int y, int w, int h ); + void setCoords( int x1, int y1, int x2, int y2 ); + + eSize size() const; + int width() const; + int height() const; + void setWidth( int w ); + void setHeight( int h ); + void setSize( const eSize &s ); + + eRect operator|(const eRect &r) const; + eRect operator&(const eRect &r) const; + eRect& operator|=(const eRect &r); + eRect& operator&=(const eRect &r); + + bool contains( const ePoint &p) const; + bool contains( int x, int y) const; + bool contains( const eRect &r) const; + eRect unite( const eRect &r ) const; + eRect intersect( const eRect &r ) const; + bool intersects( const eRect &r ) const; + + friend bool operator==( const eRect &, const eRect & ); + friend bool operator!=( const eRect &, const eRect & ); + +private: + int x1; + int y1; + int x2; + int y2; +}; + +bool operator==( const eRect &, const eRect & ); +bool operator!=( const eRect &, const eRect & ); + + +/***************************************************************************** + eRect stream functions + *****************************************************************************/ +namespace std +{ + inline ostream &operator<<( ostream & s, const eRect & r ) + { + s << r.left() << r.top() + << r.right() << r.bottom(); + + return s; + } + + inline istream &operator>>( istream & s, eRect & r ) + { + int x1, y1, x2, y2; + s >> x1 >> y1 >> x2 >> y2; + r.setCoords( x1, y1, x2, y2 ); + return s; + } +} + +/***************************************************************************** + eRect inline member functions + *****************************************************************************/ + +inline eRect::eRect( int left, int top, int width, int height ) +{ + x1 = left; + y1 = top; + x2 = left+width; + y2 = top+height; +} + +inline bool eRect::isNull() const +{ return x2 == x1 && y2 == y1; } + +inline bool eRect::isEmpty() const +{ return x1 >= x2 || y1 >= y2; } + +inline bool eRect::isValid() const +{ return x1 <= x2 && y1 <= y2; } + +inline int eRect::left() const +{ return x1; } + +inline int eRect::top() const +{ return y1; } + +inline int eRect::right() const +{ return x2; } + +inline int eRect::bottom() const +{ return y2; } + +inline int &eRect::rLeft() +{ return x1; } + +inline int & eRect::rTop() +{ return y1; } + +inline int & eRect::rRight() +{ return x2; } + +inline int & eRect::rBottom() +{ return y2; } + +inline int eRect::x() const +{ return x1; } + +inline int eRect::y() const +{ return y1; } + +inline void eRect::setLeft( int pos ) +{ x1 = pos; } + +inline void eRect::setTop( int pos ) +{ y1 = pos; } + +inline void eRect::setRight( int pos ) +{ x2 = pos; } + +inline void eRect::setBottom( int pos ) +{ y2 = pos; } + +inline void eRect::setX( int x ) +{ x1 = x; } + +inline void eRect::setY( int y ) +{ y1 = y; } + +inline ePoint eRect::topLeft() const +{ return ePoint(x1, y1); } + +inline ePoint eRect::bottomRight() const +{ return ePoint(x2, y2); } + +inline ePoint eRect::topRight() const +{ return ePoint(x2, y1); } + +inline ePoint eRect::bottomLeft() const +{ return ePoint(x1, y2); } + +inline ePoint eRect::center() const +{ return ePoint((x1+x2)/2, (y1+y2)/2); } + +inline int eRect::width() const +{ return x2 - x1; } + +inline int eRect::height() const +{ return y2 - y1; } + +inline eSize eRect::size() const +{ return eSize(x2-x1, y2-y1); } + +inline bool eRect::contains( int x, int y) const +{ + return x >= x1 && x < x2 && y >= y1 && y < y2; +} + +#endif // eRect_H diff --git a/lib/gdi/esize.h b/lib/gdi/esize.h new file mode 100644 index 0000000..d4bd4af --- /dev/null +++ b/lib/gdi/esize.h @@ -0,0 +1,187 @@ +#ifndef ESIZE_H +#define ESIZE_H + +#include + +#define MIN(a,b) (a < b ? a : b) +#define MAX(a,b) (a > b ? a : b) + +class eSize +{ +public: + eSize(); + eSize( int w, int h ); + + bool isNull() const; + bool isEmpty() const; + bool isValid() const; + + int width() const; + int height() const; + void setWidth( int w ); + void setHeight( int h ); + void transpose(); + + eSize expandedTo( const eSize & ) const; + eSize boundedTo( const eSize & ) const; + + int &rwidth(); + int &rheight(); + + eSize &operator+=( const eSize & ); + eSize &operator-=( const eSize & ); + eSize &operator*=( int c ); + eSize &operator*=( double c ); + eSize &operator/=( int c ); + eSize &operator/=( double c ); + + friend inline bool operator==( const eSize &, const eSize & ); + friend inline bool operator!=( const eSize &, const eSize & ); + friend inline eSize operator+( const eSize &, const eSize & ); + friend inline eSize operator-( const eSize &, const eSize & ); + friend inline eSize operator*( const eSize &, int ); + friend inline eSize operator*( int, const eSize & ); + friend inline eSize operator*( const eSize &, double ); + friend inline eSize operator*( double, const eSize & ); + friend inline eSize operator/( const eSize &, int ); + friend inline eSize operator/( const eSize &, double ); + +private: + int wd; + int ht; +}; + + +/***************************************************************************** + eSize stream functions + *****************************************************************************/ + +namespace std +{ + inline ostream &operator<<( ostream &s, const eSize &sz ) + { + s << sz.width() << sz.height(); + return s; + } + + inline istream &operator>>( istream &s, eSize &sz ) + { + s >> sz.rwidth() >> sz.rheight(); + return s; + } +} + + +/***************************************************************************** + eSize inline functions + *****************************************************************************/ + +inline eSize::eSize() +{ wd = ht = -1; } + +inline eSize::eSize( int w, int h ) +{ wd=w; ht=h; } + +inline bool eSize::isNull() const +{ return wd==0 && ht==0; } + +inline bool eSize::isEmpty() const +{ return wd<1 || ht<1; } + +inline bool eSize::isValid() const +{ return wd>=0 && ht>=0; } + +inline int eSize::width() const +{ return wd; } + +inline int eSize::height() const +{ return ht; } + +inline void eSize::setWidth( int w ) +{ wd=w; } + +inline void eSize::setHeight( int h ) +{ ht=h; } + +inline int &eSize::rwidth() +{ return wd; } + +inline int &eSize::rheight() +{ return ht; } + +inline eSize &eSize::operator+=( const eSize &s ) +{ wd+=s.wd; ht+=s.ht; return *this; } + +inline eSize &eSize::operator-=( const eSize &s ) +{ wd-=s.wd; ht-=s.ht; return *this; } + +inline eSize &eSize::operator*=( int c ) +{ wd*=c; ht*=c; return *this; } + +inline eSize &eSize::operator*=( double c ) +{ wd=(int)(wd*c); ht=(int)(ht*c); return *this; } + +inline bool operator==( const eSize &s1, const eSize &s2 ) +{ return s1.wd == s2.wd && s1.ht == s2.ht; } + +inline bool operator!=( const eSize &s1, const eSize &s2 ) +{ return s1.wd != s2.wd || s1.ht != s2.ht; } + +inline eSize operator+( const eSize & s1, const eSize & s2 ) +{ return eSize(s1.wd+s2.wd, s1.ht+s2.ht); } + +inline eSize operator-( const eSize &s1, const eSize &s2 ) +{ return eSize(s1.wd-s2.wd, s1.ht-s2.ht); } + +inline eSize operator*( const eSize &s, int c ) +{ return eSize(s.wd*c, s.ht*c); } + +inline eSize operator*( int c, const eSize &s ) +{ return eSize(s.wd*c, s.ht*c); } + +inline eSize operator*( const eSize &s, double c ) +{ return eSize((int)(s.wd*c), (int)(s.ht*c)); } + +inline eSize operator*( double c, const eSize &s ) +{ return eSize((int)(s.wd*c), (int)(s.ht*c)); } + +inline eSize &eSize::operator/=( int c ) +{ + wd/=c; ht/=c; + return *this; +} + +inline eSize &eSize::operator/=( double c ) +{ + wd=(int)(wd/c); ht=(int)(ht/c); + return *this; +} + +inline eSize operator/( const eSize &s, int c ) +{ + return eSize(s.wd/c, s.ht/c); +} + +inline eSize operator/( const eSize &s, double c ) +{ + return eSize((int)(s.wd/c), (int)(s.ht/c)); +} + +inline eSize eSize::expandedTo( const eSize & otherSize ) const +{ + return eSize( MAX(wd,otherSize.wd), MAX(ht,otherSize.ht) ); +} + +inline eSize eSize::boundedTo( const eSize & otherSize ) const +{ + return eSize( MIN(wd,otherSize.wd), MIN(ht,otherSize.ht) ); +} + +inline void eSize::transpose() +{ + int tmp = wd; + wd = ht; + ht = tmp; +} + +#endif // ESIZE_H diff --git a/lib/gdi/fb.cpp b/lib/gdi/fb.cpp new file mode 100644 index 0000000..96ca431 --- /dev/null +++ b/lib/gdi/fb.cpp @@ -0,0 +1,186 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +fbClass *fbClass::instance; + +fbClass *fbClass::getInstance() +{ + return instance; +} + +fbClass::fbClass(const char *fb) +{ + instance=this; + locked=0; + available=0; + cmap.start=0; + cmap.len=256; + cmap.red=red; + cmap.green=green; + cmap.blue=blue; + cmap.transp=trans; + + int state=0; + eConfig::getInstance()->getKey("/ezap/osd/showConsoleOnFB", state); + + fd=open(fb, O_RDWR); + if (fd<0) + { + perror(fb); + goto nolfb; + } + if (ioctl(fd, FBIOGET_VSCREENINFO, &screeninfo)<0) + { + perror("FBIOGET_VSCREENINFO"); + goto nolfb; + } + + memcpy(&oldscreen, &screeninfo, sizeof(screeninfo)); + + fb_fix_screeninfo fix; + if (ioctl(fd, FBIOGET_FSCREENINFO, &fix)<0) + { + perror("FBIOGET_FSCREENINFO"); + goto nolfb; + } + + available=fix.smem_len; + eDebug("%dk video mem", available/1024); + lfb=(unsigned char*)mmap(0, available, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0); + if (!lfb) + { + perror("mmap"); + goto nolfb; + } + + showConsole(state); + return; +nolfb: + lfb=0; + printf("framebuffer not available.\n"); + return; +} + +int fbClass::showConsole(int state) +{ + int fd=open("/dev/vc/0", O_RDWR); + if(fd>=0) + { + if(ioctl(fd, KDSETMODE, state?KD_TEXT:KD_GRAPHICS)<0) + { + eDebug("setting /dev/vc/0 status failed."); + } + close(fd); + } + return 0; +} + +int fbClass::SetMode(unsigned int nxRes, unsigned int nyRes, unsigned int nbpp) +{ + screeninfo.xres_virtual=screeninfo.xres=nxRes; + screeninfo.yres_virtual=screeninfo.yres=nyRes; + screeninfo.xoffset=screeninfo.yoffset=0; + screeninfo.bits_per_pixel=nbpp; + if (ioctl(fd, FBIOPUT_VSCREENINFO, &screeninfo)<0) + { + perror("FBIOPUT_VSCREENINFO"); + printf("fb failed\n"); + return -1; + } + if ((screeninfo.xres!=nxRes) && (screeninfo.yres!=nyRes) && (screeninfo.bits_per_pixel!=nbpp)) + { + eDebug("SetMode failed: wanted: %dx%dx%d, got %dx%dx%d", + nxRes, nyRes, nbpp, + screeninfo.xres, screeninfo.yres, screeninfo.bits_per_pixel); + } + xRes=screeninfo.xres; + yRes=screeninfo.yres; + bpp=screeninfo.bits_per_pixel; + fb_fix_screeninfo fix; + if (ioctl(fd, FBIOGET_FSCREENINFO, &fix)<0) + { + perror("FBIOGET_FSCREENINFO"); + printf("fb failed\n"); + } + stride=fix.line_length; + memset(lfb, 0, stride*yRes); + return 0; +} + +fbClass::~fbClass() +{ + if (available) + ioctl(fd, FBIOPUT_VSCREENINFO, &oldscreen); + if (lfb) + munmap(lfb, available); +} + +int fbClass::PutCMAP() +{ + return ioctl(fd, FBIOPUTCMAP, &cmap); +} + +void fbClass::Box(int x, int y, int width, int height, int color, int backcolor) +{ + if (width<=2) + return; + int offset=y*stride+x/2; + int first=0xF0|((color&0xF0)>>4); + int last= 0xF0|((backcolor&0xF0)>>4); + color=(color&0xF)*0x11; + int halfwidth=width/2; + for (int ay=y; ay<(y+height); ay++) + { + lfb[offset]=first; + memset(lfb+offset+1, color, halfwidth-2); + lfb[offset+halfwidth-1]=last; + offset+=stride; + } +} + +void fbClass::NBox(int x, int y, int width, int height, int color) +{ + int offset=y*stride+x/2; + int halfwidth=width/2; + for (int ay=y; ay<(y+height); ay++) + { + memset(lfb+offset, color, halfwidth); + offset+=stride; + } +} + +void fbClass::VLine(int x, int y, int sy, int color) +{ + int offset=y*stride+x/2; + while (sy--) + { + lfb[offset]=color; + offset+=stride; + } +} + +int fbClass::lock() +{ + if (locked) + return -1; + locked=1; + return fd; +} + +void fbClass::unlock() +{ + if (!locked) + return; + locked=0; + SetMode(xRes, yRes, bpp); + PutCMAP(); +} diff --git a/lib/gdi/fb.h b/lib/gdi/fb.h new file mode 100644 index 0000000..d0ad6fe --- /dev/null +++ b/lib/gdi/fb.h @@ -0,0 +1,43 @@ +#ifndef __FB_H +#define __FB_H + +#include +#include + +class fbClass +{ + int fd; + unsigned int xRes, yRes, stride, bpp; + int available; + struct fb_var_screeninfo screeninfo, oldscreen; + fb_cmap cmap; + __u16 red[256], green[256], blue[256], trans[256]; + static fbClass *instance; + + int locked; +public: + unsigned char *lfb; + int showConsole(int state); + int SetMode(unsigned int xRes, unsigned int yRes, unsigned int bpp); + int Available() { return available; } + unsigned int Stride() { return stride; } + fb_cmap *CMAP() { return &cmap; } + + fbClass(const char *fb="/dev/fb/0"); + ~fbClass(); + + static fbClass *getInstance(); + + // low level gfx stuff + int PutCMAP(); + + // gfx stuff (colors are 8bit!) + void Box(int x, int y, int width, int height, int color, int backcolor=0); + void NBox(int x, int y, int width, int height, int color); + void VLine(int x, int y, int sy, int color); + + int lock(); + void unlock(); +}; + +#endif diff --git a/lib/gdi/font.cpp b/lib/gdi/font.cpp new file mode 100644 index 0000000..e7a0074 --- /dev/null +++ b/lib/gdi/font.cpp @@ -0,0 +1,855 @@ +#include + +#include +#include +#include +#include +#include +#include + +// use this for init Freetype... +#include +#include FT_FREETYPE_H + +#include +#include +#include +#include +#include +#include + +//#define HAVE_FRIBIDI +// until we have it in the cdk + +#ifdef HAVE_FRIBIDI +#include +#endif + +#include + +fontRenderClass *fontRenderClass::instance; + +static pthread_mutex_t ftlock=PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; +static pthread_mutex_t refcntlck=PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; + +static FTC_Font cache_current_font=0; + +struct fntColorCacheKey +{ + gRGB start, end; + fntColorCacheKey(const gRGB &start, const gRGB &end) + : start(start), end(end) + { + } + bool operator <(const fntColorCacheKey &c) const + { + if (start < c.start) + return 1; + else if (start == c.start) + return end < c.end; + return 0; + } +}; + +std::map colorcache; + +static gLookup &getColor(const gPalette &pal, const gRGB &start, const gRGB &end) +{ + fntColorCacheKey key(start, end); + std::map::iterator i=colorcache.find(key); + if (i != colorcache.end()) + return i->second; + gLookup &n=colorcache.insert(std::pair(key,gLookup())).first->second; + eDebug("[FONT] creating new font color cache entry %02x%02x%02x%02x .. %02x%02x%02x%02x", start.a, start.r, start.g, start.b, + end.a, end.r, end.g, end.b); + n.build(16, pal, start, end); +/* for (int i=0; i<16; i++) + eDebugNoNewLine("%02x|%02x%02x%02x%02x ", (int)n.lookup[i], pal.data[n.lookup[i]].a, pal.data[n.lookup[i]].r, pal.data[n.lookup[i]].g, pal.data[n.lookup[i]].b); + eDebug("");*/ + return n; +} + +fontRenderClass *fontRenderClass::getInstance() +{ + return instance; +} + +FT_Error myFTC_Face_Requester(FTC_FaceID face_id, + FT_Library library, + FT_Pointer request_data, + FT_Face* aface) +{ + return ((fontRenderClass*)request_data)->FTC_Face_Requester(face_id, aface); +} + + +FT_Error fontRenderClass::FTC_Face_Requester(FTC_FaceID face_id, FT_Face* aface) +{ + fontListEntry *font=(fontListEntry *)face_id; + if (!font) + return -1; + +// eDebug("[FONT] FTC_Face_Requester (%s)", font->face.c_str()); + + int error; + if ((error=FT_New_Face(library, font->filename.c_str(), 0, aface))) + { + eDebug(" failed: %s", strerror(error)); + return error; + } + FT_Select_Charmap(*aface, ft_encoding_unicode); + return 0; +} + +FTC_FaceID fontRenderClass::getFaceID(const eString &face) +{ + for (fontListEntry *f=font; f; f=f->next) + { + if (f->face == face) + return (FTC_FaceID)f; + } + return 0; +} + +FT_Error fontRenderClass::getGlyphBitmap(FTC_Image_Desc *font, FT_ULong glyph_index, FTC_SBit *sbit) +{ + FT_Error res=FTC_SBit_Cache_Lookup(sbitsCache, font, glyph_index, sbit); + return res; +} + +eString fontRenderClass::AddFont(const eString &filename, const eString &name, int scale) +{ + eDebugNoNewLine("[FONT] adding font %s...", filename.c_str()); + fflush(stdout); + int error; + fontListEntry *n=new fontListEntry; + + n->scale=scale; + FT_Face face; + singleLock s(ftlock); + + if ((error=FT_New_Face(library, filename.c_str(), 0, &face))) + eFatal(" failed: %s", strerror(error)); + + n->filename=filename; + n->face=name; + FT_Done_Face(face); + + n->next=font; + eDebug("OK (%s)", n->face.c_str()); + font=n; + + return n->face; +} + +fontRenderClass::fontListEntry::~fontListEntry() +{ +} + +fontRenderClass::fontRenderClass(): fb(fbClass::getInstance()) +{ + instance=this; + eDebug("[FONT] initializing lib..."); + { + if (FT_Init_FreeType(&library)) + { + eDebug("[FONT] initializing failed."); + return; + } + } + eDebug("[FONT] loading fonts..."); + fflush(stdout); + font=0; + + int maxbytes=4*1024*1024; + eDebug("[FONT] Intializing font cache, using max. %dMB...", maxbytes/1024/1024); + fflush(stdout); + { + if (FTC_Manager_New(library, 8, 8, maxbytes, myFTC_Face_Requester, this, &cacheManager)) + { + eDebug("[FONT] initializing font cache failed!"); + return; + } + if (!cacheManager) + { + eDebug("[FONT] initializing font cache manager error."); + return; + } + if (FTC_SBit_Cache_New(cacheManager, &sbitsCache)) + { + eDebug("[FONT] initializing font cache sbit failed!"); + return; + } + if (FTC_Image_Cache_New(cacheManager, &imageCache)) + { + eDebug("[FONT] initializing font cache imagecache failed!"); + } + } + return; +} + +float fontRenderClass::getLineHeight(const gFont& font) +{ + if (!instance) + return 0; + Font *fnt = getFont( font.family.c_str(), font.pointSize); + if (!fnt) + return 0; + singleLock s(ftlock); + FT_Face current_face; + if (FTC_Manager_Lookup_Size(cacheManager, &fnt->font.font, ¤t_face, &fnt->size)<0) + { + eDebug("FTC_Manager_Lookup_Size failed!"); + return 0; + } + int linegap=current_face->size->metrics.height-(current_face->size->metrics.ascender+current_face->size->metrics.descender); + float height=(current_face->size->metrics.ascender+current_face->size->metrics.descender+linegap/2.0)/64; + delete fnt; + return height; +} + + +fontRenderClass::~fontRenderClass() +{ + singleLock s(ftlock); +// auskommentiert weil freetype und enigma die kritische masse des suckens ueberschreiten. +// FTC_Manager_Done(cacheManager); +// FT_Done_FreeType(library); +} + +Font *fontRenderClass::getFont(const eString &face, int size, int tabwidth) +{ + FTC_FaceID id=getFaceID(face); + if (!id) + return 0; + return new Font(this, id, size * ((fontListEntry*)id)->scale / 100, tabwidth); +} + +Font::Font(fontRenderClass *render, FTC_FaceID faceid, int isize, int tw): tabwidth(tw) +{ + renderer=render; + font.font.face_id=faceid; + font.font.pix_width = isize; + font.font.pix_height = isize; + font.image_type = ftc_image_grays; + height=isize; + if (tabwidth==-1) + tabwidth=8*isize; + ref=0; +// font.image_type |= ftc_image_flag_autohinted; +} + +FT_Error Font::getGlyphBitmap(FT_ULong glyph_index, FTC_SBit *sbit) +{ + return renderer->getGlyphBitmap(&font, glyph_index, sbit); +} + +Font::~Font() +{ +} + +void Font::lock() +{ + ref++; +} + +void Font::unlock() +{ + ref--; + if (!ref) + delete this; +} + +int eTextPara::appendGlyph(Font *current_font, FT_Face current_face, FT_UInt glyphIndex, int flags, int rflags) +{ + FTC_SBit glyph; + if (current_font->getGlyphBitmap(glyphIndex, &glyph)) + return 1; + + int nx=cursor.x(); + + nx+=glyph->xadvance; + + if ( + (rflags&RS_WRAP) && + (nx >= area.right()) + ) + { + int cnt = 0; + glyphString::iterator i(glyphs.end()); + --i; + while (i != glyphs.begin()) + { + if (i->flags&(GS_ISSPACE|GS_ISFIRST)) + break; + cnt++; + --i; + } + if (i != glyphs.begin() && ((i->flags&(GS_ISSPACE|GS_ISFIRST))==GS_ISSPACE) && (++i != glyphs.end())) // skip space + { + int linelength=cursor.x()-i->x; + i->flags|=GS_ISFIRST; + ePoint offset=ePoint(i->x, i->y); + newLine(rflags); + offset-=cursor; + while (i != glyphs.end()) // rearrange them into the next line + { + i->x-=offset.x(); + i->y-=offset.y(); + i->bbox.moveBy(-offset.x(), -offset.y()); + ++i; + } + cursor+=ePoint(linelength, 0); // put the cursor after that line + } else + { + if (cnt) + { + newLine(rflags); + flags|=GS_ISFIRST; + } + } + } + + int xadvance=glyph->xadvance, kern=0; + + if (previous && use_kerning) + { + FT_Vector delta; + FT_Get_Kerning(current_face, previous, glyphIndex, ft_kerning_default, &delta); + kern=delta.x>>6; + } + + pGlyph ng; + + ng.bbox.setLeft( (flags&GS_ISFIRST|glyphs.empty()?cursor.x():cursor.x()-1) + glyph->left ); + ng.bbox.setTop( cursor.y() - glyph->top ); + ng.bbox.setWidth( glyph->width ); + ng.bbox.setHeight( glyph->height ); + + xadvance+=kern; + + ng.x=cursor.x()+kern; + + ng.y=cursor.y(); + ng.w=xadvance; + ng.font=current_font; + ng.font->lock(); + ng.glyph_index=glyphIndex; + ng.flags=flags; + glyphs.push_back(ng); + + cursor+=ePoint(xadvance, 0); + previous=glyphIndex; + return 0; +} + +void eTextPara::calc_bbox() +{ + boundBox.setLeft( 32000 ); + boundBox.setTop( 32000 ); + boundBox.setRight( -32000 ); // for each glyph image, compute its bounding box, translate it, + boundBox.setBottom( -32000 ); + // and grow the string bbox + + for ( glyphString::iterator i(glyphs.begin()); i != glyphs.end(); ++i) + { + if ( i->bbox.left() < boundBox.left() ) + boundBox.setLeft( i->bbox.left() ); + if ( i->bbox.top() < boundBox.top() ) + boundBox.setTop( i->bbox.top() ); + if ( i->bbox.right() > boundBox.right() ) + boundBox.setRight( i->bbox.right() ); + if ( i->bbox.bottom() > boundBox.bottom() ) + boundBox.setBottom( i->bbox.bottom() ); + } +// eDebug("boundBox left = %i, top = %i, right = %i, bottom = %i", boundBox.left(), boundBox.top(), boundBox.right(), boundBox.bottom() ); + bboxValid=1; +} + +void eTextPara::newLine(int flags) +{ + if (maximum.width()size->metrics.height-(current_face->size->metrics.ascender+current_face->size->metrics.descender); + cursor+=ePoint(0, (current_face->size->metrics.ascender+current_face->size->metrics.descender+linegap*1/2)>>6); + if (maximum.height()=0) + eFatal("verdammt man der war noch gelockt :/\n"); +} + +void eTextPara::destroy() +{ + singleLock s(refcntlck); + + if (!refcnt--) + delete this; +} + +eTextPara *eTextPara::grab() +{ + singleLock s(refcntlck); + + refcnt++; + return this; +} + +void eTextPara::setFont(const gFont &font) +{ + if (refcnt) + eFatal("mod. after lock"); + Font *fnt=fontRenderClass::getInstance()->getFont(font.family.c_str(), font.pointSize); + if (!fnt) + eWarning("FONT '%s' MISSING!", font.family.c_str()); + setFont(fnt, + fontRenderClass::getInstance()->getFont(replacement_facename.c_str(), font.pointSize)); +} + +eString eTextPara::replacement_facename; + +void eTextPara::setFont(Font *fnt, Font *replacement) +{ + if (refcnt) + eFatal("mod. after lock"); + if (!fnt) + return; + if (current_font && !current_font->ref) + delete current_font; + current_font=fnt; + replacement_font=replacement; + singleLock s(ftlock); + + // we ask for replacment_font first becauseof the cache + if (replacement_font) + { + if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, + &replacement_font->font.font, &replacement_face, + &replacement_font->size)<0) + { + eDebug("FTC_Manager_Lookup_Size failed!"); + return; + } + } + if (current_font) + { + if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, ¤t_font->font.font, ¤t_face, ¤t_font->size)<0) + { + eDebug("FTC_Manager_Lookup_Size failed!"); + return; + } + } + cache_current_font=¤t_font->font.font; + previous=0; + use_kerning=FT_HAS_KERNING(current_face); +} + +void +shape (std::vector &string, const std::vector &text); + +int eTextPara::renderString(const eString &string, int rflags) +{ + singleLock s(ftlock); + + if (refcnt) + eFatal("mod. after lock"); + + if (!current_font) + return -1; + + if (cursor.y()==-1) + { + cursor=ePoint(area.x(), area.y()+(current_face->size->metrics.ascender>>6)); + left=cursor.x(); + } + + if (¤t_font->font.font != cache_current_font) + { + if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, ¤t_font->font.font, ¤t_face, ¤t_font->size)<0) + { + eDebug("FTC_Manager_Lookup_Size failed!"); + return -1; + } + cache_current_font=¤t_font->font.font; + } + + std::vector uc_string, uc_visual; + uc_string.reserve(string.length()); + + std::string::const_iterator p(string.begin()); + + while(p != string.end()) + { + unsigned int unicode=*p++; + + if (unicode & 0x80) // we have (hopefully) UTF8 here, and we assume that the encoding is VALID + { + if ((unicode & 0xE0)==0xC0) // two bytes + { + unicode&=0x1F; + unicode<<=6; + if (p != string.end()) + unicode|=(*p++)&0x3F; + } else if ((unicode & 0xF0)==0xE0) // three bytes + { + unicode&=0x0F; + unicode<<=6; + if (p != string.end()) + unicode|=(*p++)&0x3F; + unicode<<=6; + if (p != string.end()) + unicode|=(*p++)&0x3F; + } else if ((unicode & 0xF8)==0xF0) // four bytes + { + unicode&=0x07; + unicode<<=6; + if (p != string.end()) + unicode|=(*p++)&0x3F; + unicode<<=6; + if (p != string.end()) + unicode|=(*p++)&0x3F; + unicode<<=6; + if (p != string.end()) + unicode|=(*p++)&0x3F; + } + } + uc_string.push_back(unicode); + } + + std::vector uc_shape; + + // character -> glyph conversion + shape(uc_shape, uc_string); + + // now do the usual logical->visual reordering +#ifdef HAVE_FRIBIDI + FriBidiCharType dir=FRIBIDI_TYPE_ON; + { + int size=uc_shape.size(); + uc_visual.resize(size); + // gaaanz lahm, aber anders geht das leider nicht, sorry. + FriBidiChar array[size], target[size]; + std::copy(uc_shape.begin(), uc_shape.end(), array); + fribidi_log2vis(array, size, &dir, target, 0, 0, 0); + uc_visual.assign(target, target+size); + } +#else + uc_visual=uc_shape; +#endif + + glyphs.reserve(uc_visual.size()); + + for (std::vector::const_iterator i(uc_visual.begin()); + i != uc_visual.end(); ++i) + { + int isprintable=1; + int flags=0; + if (!(rflags&RS_DIRECT)) + { + switch (*i) + { + case '\\': + { + unsigned long c = *(i+1); + switch (c) + { + case 'n': + i++; + goto newline; + case 't': + i++; + goto tab; + case 'r': + i++; + goto nprint; + default: + ; + } + break; + } + case '\t': +tab: isprintable=0; + cursor+=ePoint(current_font->tabwidth, 0); + cursor-=ePoint(cursor.x()%current_font->tabwidth, 0); + break; + case 0x8A: + case 0xE08A: + case '\n': +newline:isprintable=0; + newLine(rflags); + flags|=GS_ISFIRST; + break; + case '\r': + case 0x86: case 0xE086: + case 0x87: case 0xE087: +nprint: isprintable=0; + break; + case ' ': + flags|=GS_ISSPACE; + default: + break; + } + } + if (isprintable) + { + FT_UInt index; + + index=(rflags&RS_DIRECT)? *i : FT_Get_Char_Index(current_face, *i); + + if (!index) + { + if (replacement_face) + index=(rflags&RS_DIRECT)? *i : FT_Get_Char_Index(replacement_face, *i); + + if (!index) + eDebug("unicode %d ('%c') not present", *i, *i); + else + appendGlyph(replacement_font, replacement_face, index, flags, rflags); + } else + appendGlyph(current_font, current_face, index, flags, rflags); + } + } + bboxValid=false; + calc_bbox(); +#ifdef HAVE_FRIBIDI + if (dir & FRIBIDI_MASK_RTL) + realign(dirRight); +#endif + return 0; +} + +void eTextPara::blit(gPixmapDC &dc, const ePoint &offset, const gRGB &background, const gRGB &foreground) +{ + singleLock s(ftlock); + + if (!current_font) + return; + + if (¤t_font->font.font != cache_current_font) + { + if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, ¤t_font->font.font, ¤t_face, ¤t_font->size)<0) + { + eDebug("FTC_Manager_Lookup_Size failed!"); + return; + } + cache_current_font=¤t_font->font.font; + } + + ePtr target; + dc.getPixmap(target); + + register int opcode; + gColor *lookup8=0; + __u32 lookup32[16]; + + if (target->bpp == 8) + { + if (target->clut.data) + { + lookup8=getColor(target->clut, background, foreground).lookup; + opcode=0; + } else + opcode=1; + } else if (target->bpp == 32) + { + opcode=3; + if (target->clut.data) + { + lookup8=getColor(target->clut, background, foreground).lookup; + for (int i=0; i<16; ++i) + lookup32[i]=((target->clut.data[lookup8[i]].a<<24)| + (target->clut.data[lookup8[i]].r<<16)| + (target->clut.data[lookup8[i]].g<<8)| + (target->clut.data[lookup8[i]].b))^0xFF000000; + } else + { + for (int i=0; i<16; ++i) + lookup32[i]=(0x010101*i)|0xFF000000; + } + } else + { + eWarning("can't render to %dbpp", target->bpp); + return; + } + + eRect clip(0, 0, target->x, target->y); + clip&=dc.getClip(); + + int buffer_stride=target->stride; + + for (glyphString::iterator i(glyphs.begin()); i != glyphs.end(); ++i) + { + static FTC_SBit glyph_bitmap; + if (fontRenderClass::instance->getGlyphBitmap(&i->font->font, i->glyph_index, &glyph_bitmap)) + continue; + int rx=i->x+glyph_bitmap->left + offset.x(); + int ry=i->y-glyph_bitmap->top + offset.y(); + __u8 *d=(__u8*)(target->data)+buffer_stride*ry+rx*target->bypp; + __u8 *s=glyph_bitmap->buffer; + register int sx=glyph_bitmap->width; + int sy=glyph_bitmap->height; + if ((sy+ry) >= clip.bottom()) + sy=clip.bottom()-ry; + if ((sx+rx) >= clip.right()) + sx=clip.right()-rx; + if (rx < clip.left()) + { + int diff=clip.left()-rx; + s+=diff; + sx-=diff; + rx+=diff; + d+=diff*target->bypp; + } + if (ry < clip.top()) + { + int diff=clip.top()-ry; + s+=diff*glyph_bitmap->pitch; + sy-=diff; + ry+=diff; + d+=diff*buffer_stride; + } + if (sx>0) + for (int ay=0; ay>4; + if(b) + *td++=lookup8[b]; + else + td++; + } + } else if (opcode == 1) // 8bit direct + { + register __u8 *td=d; + register int ax; + for (ax=0; ax>4; + if(b) + *td++=lookup32[b]; + else + td++; + } + } + s+=glyph_bitmap->pitch-sx; + d+=buffer_stride; + } + } +} + +void eTextPara::realign(int dir) // der code hier ist ein wenig merkwuerdig. +{ + glyphString::iterator begin(glyphs.begin()), c(glyphs.begin()), end(glyphs.begin()), last; + if (dir==dirLeft) + return; + while (c != glyphs.end()) + { + int linelength=0; + int numspaces=0, num=0; + begin=end; + + ASSERT( end != glyphs.end()); + + // zeilenende suchen + do { + last=end; + ++end; + } while ((end != glyphs.end()) && (!(end->flags&GS_ISFIRST))); + // end zeigt jetzt auf begin der naechsten zeile + + for (c=begin; c!=end; ++c) + { + // space am zeilenende skippen + if ((c==last) && (c->flags&GS_ISSPACE)) + continue; + + if (c->flags&GS_ISSPACE) + numspaces++; + linelength+=c->w; + num++; + } + if (!num) // line mit nur einem space + continue; + + switch (dir) + { + case dirRight: + case dirCenter: + { + int offset=area.width()-linelength; + if (dir==dirCenter) + offset/=2; + offset+=area.left(); + while (begin != end) + { + begin->bbox.moveBy(offset-begin->x,0); + begin->x=offset; + offset+=begin->w; + ++begin; + } + break; + } + case dirBlock: + { + if (end == glyphs.end()) // letzte zeile linksbuendig lassen + continue; + int spacemode; + if (numspaces) + spacemode=1; + else + spacemode=0; + if ((!spacemode) && (num<2)) + break; + int off=(area.width()-linelength)*256/(spacemode?numspaces:(num-1)); + int curoff=0; + while (begin != end) + { + int doadd=0; + if ((!spacemode) || (begin->flags&GS_ISSPACE)) + doadd=1; + begin->x+=curoff>>8; + begin->bbox.moveBy(curoff>>8,0); + if (doadd) + curoff+=off; + ++begin; + } + break; + } + } + } + bboxValid=false; + calc_bbox(); +} + +void eTextPara::clear() +{ + singleLock s(ftlock); + + for (glyphString::iterator i(glyphs.begin()); i!=glyphs.end(); ++i) + i->font->unlock(); + + glyphs.clear(); +} + +eAutoInitP0 init_fontRenderClass(eAutoInitNumbers::graphic-1, "Font Render Class"); diff --git a/lib/gdi/font.cpp-new b/lib/gdi/font.cpp-new new file mode 100644 index 0000000..e9aa543 --- /dev/null +++ b/lib/gdi/font.cpp-new @@ -0,0 +1,787 @@ +#include + +#include +#include +#include +#include +#include +#include + +// use this for init Freetype... +#include +#include FT_FREETYPE_H + +#include +#include +#include +#include +#include + +#include + +fontRenderClass *fontRenderClass::instance; +static eLock ftlock; +static FTC_Font cache_current_font=0; + +struct fntColorCacheKey +{ + gRGB start, end; + fntColorCacheKey(const gRGB &start, const gRGB &end) + : start(start), end(end) + { + } + bool operator <(const fntColorCacheKey &c) const + { + if (start < c.start) + return 1; + else if (start == c.start) + return end < c.end; + return 0; + } +}; + +std::map colorcache; + +static gLookup &getColor(const gPalette &pal, const gRGB &start, const gRGB &end) +{ + fntColorCacheKey key(start, end); + std::map::iterator i=colorcache.find(key); + if (i != colorcache.end()) + return i->second; + gLookup &n=colorcache.insert(std::pair(key,gLookup())).first->second; + eDebug("[FONT] creating new font color cache entry %02x%02x%02x%02x .. %02x%02x%02x%02x", start.a, start.r, start.g, start.b, + end.a, end.r, end.g, end.b); + n.build(16, pal, start, end); +/* for (int i=0; i<16; i++) + eDebugNoNewLine("%02x|%02x%02x%02x%02x ", (int)n.lookup[i], pal.data[n.lookup[i]].a, pal.data[n.lookup[i]].r, pal.data[n.lookup[i]].g, pal.data[n.lookup[i]].b); + eDebug("");*/ + return n; +} + +fontRenderClass *fontRenderClass::getInstance() +{ + return instance; +} + +FT_Error myFTC_Face_Requester(FTC_FaceID face_id, + FT_Library library, + FT_Pointer request_data, + FT_Face* aface) +{ + return ((fontRenderClass*)request_data)->FTC_Face_Requester(face_id, aface); +} + + +FT_Error fontRenderClass::FTC_Face_Requester(FTC_FaceID face_id, FT_Face* aface) +{ + fontListEntry *font=(fontListEntry *)face_id; + if (!font) + return -1; + + eDebug("[FONT] FTC_Face_Requester (%s)", font->face); + + int error; + if ((error=FT_New_Face(library, font->filename, 0, aface))) + { + eDebug(" failed: %s", strerror(error)); + return error; + } + FT_Select_Charmap(*aface, ft_encoding_unicode); + return 0; +} + +FTC_FaceID fontRenderClass::getFaceID(const char *face) +{ + for (fontListEntry *f=font; f; f=f->next) + { + if (!strcmp(f->face, face)) + return (FTC_FaceID)f; + } + return 0; +} + +FT_Error fontRenderClass::getGlyphBitmap(FTC_ImageTypeRec *font, FT_ULong glyph_index, FTC_SBit *sbit) +{ + FT_Error res=FTC_SBitCache_Lookup(sbitsCache, font, glyph_index, sbit, 0); + eDebug("%x", sizeof(**sbit)); + return res; +} + +const char* fontRenderClass::AddFont(const char *filename) +{ + eDebugNoNewLine("[FONT] adding font %s...", filename); + fflush(stdout); + int error; + fontListEntry *n=new fontListEntry; + + FT_Face face; + eLocker lock(ftlock); + + if ((error=FT_New_Face(library, filename, 0, &face))) + eFatal(" failed: %s", strerror(error)); + + strcpy(n->filename=new char[strlen(filename)+1], filename); + strcpy(n->face=new char[strlen(face->family_name)+strlen(face->style_name)+2], face->family_name); + if (face->style_name[0]!=' ') + strcat(n->face, " "); + strcat(n->face, face->style_name); + FT_Done_Face(face); + + n->next=font; + eDebug("OK (%s)", n->face); + font=n; + + return n->face; +} + +fontRenderClass::fontListEntry::~fontListEntry() +{ + delete[] filename; + delete[] face; +} + +fontRenderClass::fontRenderClass(): fb(fbClass::getInstance()) +{ + instance=this; + eDebug("[FONT] initializing lib..."); + { + if (FT_Init_FreeType(&library)) + { + eDebug("[FONT] initializing failed."); + return; + } + } + eDebug("[FONT] loading fonts..."); + fflush(stdout); + font=0; + + int maxbytes=4*1024*1024; + eDebug("[FONT] Intializing font cache, using max. %dMB...", maxbytes/1024/1024); + fflush(stdout); + { + if (FTC_Manager_New(library, 8, 8, maxbytes, myFTC_Face_Requester, this, &cacheManager)) + { + eDebug("[FONT] initializing font cache failed!"); + return; + } + if (!cacheManager) + { + eDebug("[FONT] initializing font cache manager error."); + return; + } + if (FTC_SBitCache_New(cacheManager, &sbitsCache)) + { + eDebug("[FONT] initializing font cache sbit failed!"); + return; + } +/* if (FTC_ImageCache_New(cacheManager, &imageCache)) + { + eDebug("[FONT] initializing font cache imagecache failed!"); + } */ + } + return; +} + +fontRenderClass::~fontRenderClass() +{ + ftlock.lock(); +// auskommentiert weil freetype und enigma die kritische masse des suckens ueberschreiten. +// FTC_Manager_Done(cacheManager); +// FT_Done_FreeType(library); +} + +Font *fontRenderClass::getFont(const char *face, int size, int tabwidth) +{ + FTC_FaceID id=getFaceID(face); + if (!id) + eDebug("face %s does not exist!", face); + if (!id) + return 0; + return new Font(this, id, size, tabwidth); +} + +Font::Font(fontRenderClass *render, FTC_FaceID faceid, int isize, int tw): tabwidth(tw) +{ + renderer=render; + font.font.face_id=faceid; + font.font.pix_width = isize; + font.font.pix_height = isize; + font.flags = FT_LOAD_DEFAULT; + height=isize; + if (tabwidth==-1) + tabwidth=8*isize; + ref=0; +} + +FT_Error Font::getGlyphBitmap(FT_ULong glyph_index, FTC_SBit *sbit) +{ + return renderer->getGlyphBitmap(&font, glyph_index, sbit); +} + +Font::~Font() +{ +} + +void Font::lock() +{ + ref++; +} + +void Font::unlock() +{ + ref--; + if (!ref) + delete this; +} + +int eTextPara::appendGlyph(FT_UInt glyphIndex, int flags, int rflags) +{ + FTC_SBit glyph; + if (current_font->getGlyphBitmap(glyphIndex, &glyph)) + return 1; + + int nx=cursor.x(); + + if (! (rflags & RS_RTL)) + nx+=glyph->xadvance; + else + { + eDebug("RTL: glyph->xadvance: %d", glyph->xadvance); + nx-=glyph->xadvance; + } + + if ( + (rflags&RS_WRAP) && + ( + (!(rflags & RS_RTL)) + ? + (nx >= area.right()) : + (nx < area.left()) + ) + ) + { + int cnt = 0; + glyphString::iterator i(glyphs.end()); + --i; + while (i != glyphs.begin()) + { + if (i->flags&(GS_ISSPACE|GS_ISFIRST)) + break; + cnt++; + --i; + } + if (i != glyphs.begin() && ((i->flags&(GS_ISSPACE|GS_ISFIRST))==GS_ISSPACE) && (++i != glyphs.end())) // skip space + { + int linelength=cursor.x()-i->x; + // RTL: linelength is negative + i->flags|=GS_ISFIRST; + ePoint offset=ePoint(i->x, i->y); + newLine(rflags); + offset-=cursor; + while (i != glyphs.end()) // rearrange them into the next line + { + i->x-=offset.x(); + i->y-=offset.y(); + i->bbox->moveBy(-offset.x(), -offset.y()); + ++i; + } + cursor+=ePoint(linelength, 0); // put the cursor after that line + } else + { + if (cnt) + { + newLine(rflags); + flags|=GS_ISFIRST; + } + } + } + + int xadvance=glyph->xadvance, kern=0; + + if (previous && use_kerning) + { + FT_Vector delta; + FT_Get_Kerning(current_face, previous, glyphIndex, ft_kerning_default, &delta); + kern=delta.x>>6; + } + + eRect* bbox = new eRect(); + bbox->setLeft( (flags&GS_ISFIRST|glyphs.empty()?cursor.x():cursor.x()-1) + glyph->left ); + bbox->setTop( cursor.y() - glyph->top ); + bbox->setWidth( glyph->width ); + bbox->setHeight( glyph->height ); + + pGlyph ng; + + xadvance+=kern; + + if (!(rflags & RS_RTL)) + ng.x=cursor.x()+kern; + else + ng.x=cursor.x()-xadvance; + + ng.y=cursor.y(); + ng.w=xadvance; + ng.font=current_font; + ng.font->lock(); + ng.glyph_index=glyphIndex; + ng.flags=flags; + ng.bbox=bbox; + glyphs.push_back(ng); + + if (!(rflags & RS_RTL)) + cursor+=ePoint(xadvance, 0); + else + cursor-=ePoint(xadvance, 0); + previous=glyphIndex; + return 0; +} + +void eTextPara::calc_bbox() +{ + boundBox.setLeft( 32000 ); + boundBox.setTop( 32000 ); + boundBox.setRight( -32000 ); // for each glyph image, compute its bounding box, translate it, + boundBox.setBottom( -32000 ); + // and grow the string bbox + + for ( glyphString::iterator i(glyphs.begin()); i != glyphs.end(); ++i) + { + if ( i->bbox->left() < boundBox.left() ) + boundBox.setLeft( i->bbox->left() ); + if ( i->bbox->top() < boundBox.top() ) + boundBox.setTop( i->bbox->top() ); + if ( i->bbox->right() > boundBox.right() ) + boundBox.setRight( i->bbox->right() ); + if ( i->bbox->bottom() > boundBox.bottom() ) + boundBox.setBottom( i->bbox->bottom() ); + } +// eDebug("boundBox left = %i, top = %i, right = %i, bottom = %i", boundBox.left(), boundBox.top(), boundBox.right(), boundBox.bottom() ); + bboxValid=1; +} + +void eTextPara::newLine(int flags) +{ + if (!(flags & RS_RTL)) + { + if (maximum.width()size->metrics.height-(current_face->size->metrics.ascender+current_face->size->metrics.descender); + cursor+=ePoint(0, (current_face->size->metrics.ascender+current_face->size->metrics.descender+linegap*1/2)>>6); + if (maximum.height()=0) + eFatal("verdammt man der war noch gelockt :/\n"); +} + +void eTextPara::destroy() +{ + eLocker lock(refcntlck); + + if (!refcnt--) + delete this; +} + +eTextPara *eTextPara::grab() +{ + eLocker lock(refcntlck); + + refcnt++; + return this; +} + +void eTextPara::setFont(const gFont &font) +{ + if (refcnt) + eFatal("mod. after lock"); + setFont(fontRenderClass::getInstance()->getFont(font.family.c_str(), font.pointSize)); +} + +void eTextPara::setFont(Font *fnt) +{ + if (refcnt) + eFatal("mod. after lock"); + if (!fnt) + return; + if (current_font && !current_font->ref) + delete current_font; + current_font=fnt; + eLocker lock(ftlock); + + if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, ¤t_font->font.font, ¤t_face, ¤t_font->size)<0) + { + eDebug("FTC_Manager_Lookup_Size failed!"); + return; + } + cache_current_font=¤t_font->font.font; + previous=0; + use_kerning=FT_HAS_KERNING(current_face); +} + +int eTextPara::renderString(const eString &string, int rflags) +{ + eLocker lock(ftlock); + + if (refcnt) + eFatal("mod. after lock"); + + if (!current_font) + return -1; + + if (cursor.y()==-1) + { + if (!(rflags & RS_RTL)) + { + cursor=ePoint(area.x(), area.y()+(current_face->size->metrics.ascender>>6)); + } else + { + cursor=ePoint(area.right(), area.y()+(current_face->size->metrics.ascender>>6)); + } + left=cursor.x(); + } + + glyphs.reserve(glyphs.size()+string.length()); + + if (¤t_font->font.font != cache_current_font) + { + if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, ¤t_font->font.font, ¤t_face, ¤t_font->size)<0) + { + eDebug("FTC_Manager_Lookup_Size failed!"); + return -1; + } + cache_current_font=¤t_font->font.font; + } + + std::string::const_iterator p(string.begin()); + + while(p != string.end()) + { + int isprintable=1; + int flags=0; + + unsigned int unicode=*p++; + + if (unicode & 0x80) // we have (hopefully) UTF8 here, and we assume that the encoding is VALID + { + if ((unicode & 0xE0)==0xC0) // two bytes + { + unicode&=0x1F; + unicode<<=6; + if (p != string.end()) + unicode|=(*p++)&0x3F; + } else if ((unicode & 0xF0)==0xE0) // three bytes + { + unicode&=0x0F; + unicode<<=6; + if (p != string.end()) + unicode|=(*p++)&0x3F; + unicode<<=6; + if (p != string.end()) + unicode|=(*p++)&0x3F; + } else if ((unicode & 0xF8)==0xF0) // four bytes + { + unicode&=0x07; + unicode<<=6; + if (p != string.end()) + unicode|=(*p++)&0x3F; + unicode<<=6; + if (p != string.end()) + unicode|=(*p++)&0x3F; + unicode<<=6; + if (p != string.end()) + unicode|=(*p++)&0x3F; + } + } + + if (!(rflags&RS_DIRECT)) + { + switch (unicode) + { + case '\t': + isprintable=0; + if (!(rflags & RS_RTL)) + { + cursor+=ePoint(current_font->tabwidth, 0); + cursor-=ePoint(cursor.x()%current_font->tabwidth, 0); + } else + { + // does this work? + cursor-=ePoint(current_font->tabwidth, 0); + cursor+=ePoint(cursor.x()%current_font->tabwidth, 0); + } + break; + case 0x8A: + case 0xE08A: + case '\n': + isprintable=0; + newLine(rflags); + flags|=GS_ISFIRST; + break; + case '\r': + case 0x86: case 0xE086: + case 0x87: case 0xE087: + isprintable=0; + break; + case ' ': + flags|=GS_ISSPACE; + default: + break; + } + } + if (isprintable) + { + FT_UInt index; + + index=(rflags&RS_DIRECT)? unicode : FT_Get_Char_Index(current_face, unicode); + + if (!index) + eDebug("unicode %d ('%c') not present", unicode, unicode); + else + appendGlyph(index, flags, rflags); + } + } + bboxValid=false; + calc_bbox(); + return 0; +} + +void eTextPara::blit(gPixmapDC &dc, const ePoint &offset, const gRGB &background, const gRGB &foreground) +{ + eLocker lock(ftlock); + + if (¤t_font->font.font != cache_current_font) + { + if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, ¤t_font->font.font, ¤t_face, ¤t_font->size)<0) + { + eDebug("FTC_Manager_Lookup_Size failed!"); + return; + } + cache_current_font=¤t_font->font.font; + } + + gPixmap &target=dc.getPixmap(); + + register int opcode; + gColor *lookup8=0; + __u32 lookup32[16]; + + if (target.bpp == 8) + { + if (target.clut.data) + { + lookup8=getColor(target.clut, background, foreground).lookup; + opcode=0; + } else + opcode=1; + } else if (target.bpp == 32) + { + opcode=3; + if (target.clut.data) + { + lookup8=getColor(target.clut, background, foreground).lookup; + for (int i=0; i<16; ++i) + lookup32[i]=((target.clut.data[lookup8[i]].a<<24)| + (target.clut.data[lookup8[i]].r<<16)| + (target.clut.data[lookup8[i]].g<<8)| + (target.clut.data[lookup8[i]].b))^0xFF000000; + } else + { + for (int i=0; i<16; ++i) + lookup32[i]=(0x010101*i)|0xFF000000; + } + } else + { + eWarning("can't render to %dbpp", target.bpp); + return; + } + + eRect clip(0, 0, target.x, target.y); + clip&=dc.getClip(); + + int buffer_stride=target.stride; + + for (glyphString::iterator i(glyphs.begin()); i != glyphs.end(); ++i) + { + FTC_SBit glyph_bitmap; + memset(&glyph_bitmap, 0, sizeof(glyph_bitmap)); + if (fontRenderClass::instance->getGlyphBitmap(&i->font->font, i->glyph_index, &glyph_bitmap)) + continue; + if (!glyph_bitmap->buffer) + eFatal("you suck."); + int rx=i->x+glyph_bitmap->left + offset.x(); + int ry=i->y-glyph_bitmap->top + offset.y(); + __u8 *d=(__u8*)(target.data)+buffer_stride*ry+rx*target.bypp; + __u8 *s=glyph_bitmap->buffer; + register int sx=glyph_bitmap->width; + int sy=glyph_bitmap->height; + if ((sy+ry) >= clip.bottom()) + sy=clip.bottom()-ry; + if ((sx+rx) >= clip.right()) + sx=clip.right()-rx; + if (rx < clip.left()) + { + int diff=clip.left()-rx; + s+=diff; + sx-=diff; + rx+=diff; + d+=diff*target.bypp; + } + if (ry < clip.top()) + { + int diff=clip.top()-ry; + s+=diff*glyph_bitmap->pitch; + sy-=diff; + ry+=diff; + d+=diff*buffer_stride; + } + if (sx>0) + for (int ay=0; ay>4; + if(b) + *td++=lookup8[b]; + else + td++; + } + } else if (opcode == 1) // 8bit direct + { + register __u8 *td=d; + register int ax; + for (ax=0; ax>4; + if(b) + *td++=lookup32[b]; + else + td++; + } + } + s+=glyph_bitmap->pitch-sx; + d+=buffer_stride; + } + } +} + +void eTextPara::realign(int dir) // der code hier ist ein wenig merkwuerdig. +{ + glyphString::iterator begin(glyphs.begin()), c(glyphs.begin()), end(glyphs.begin()), last; + if (dir==dirLeft) + return; + while (c != glyphs.end()) + { + int linelength=0; + int numspaces=0, num=0; + begin=end; + + // zeilenende suchen + do { + last=end; + ++end; + } while ((end != glyphs.end()) && (!(end->flags&GS_ISFIRST))); + // end zeigt jetzt auf begin der naechsten zeile + + for (c=begin; c!=end; ++c) + { + // space am zeilenende skippen + if ((c==last) && (c->flags&GS_ISSPACE)) + continue; + + if (c->flags&GS_ISSPACE) + numspaces++; + linelength+=c->w;; + num++; + } + if (!num) // line mit nur einem space + continue; + + switch (dir) + { + case dirRight: + case dirCenter: + { + int offset=area.width()-linelength; + if (dir==dirCenter) + offset/=2; + while (begin != end) + { + begin->x+=offset; + begin->bbox->moveBy(offset,0); + ++begin; + } + break; + } + case dirBlock: + { + if (end == glyphs.end()) // letzte zeile linksbuendig lassen + continue; + int spacemode; + if (numspaces) + spacemode=1; + else + spacemode=0; + if ((!spacemode) && (num<2)) + break; + int off=(area.width()-linelength)*256/(spacemode?numspaces:(num-1)); + int curoff=0; + while (begin != end) + { + int doadd=0; + if ((!spacemode) || (begin->flags&GS_ISSPACE)) + doadd=1; + begin->x+=curoff>>8; + begin->bbox->moveBy(curoff>>8,0); + if (doadd) + curoff+=off; + ++begin; + } + break; + } + } + } + bboxValid=false; +} + +void eTextPara::clear() +{ + eLocker lock(ftlock); + + for (glyphString::iterator i(glyphs.begin()); i!=glyphs.end(); ++i) + { + i->font->unlock(); + delete i->bbox; + } + glyphs.clear(); +} + +eAutoInitP0 init_fontRenderClass(1, "Font Render Class"); diff --git a/lib/gdi/font.h b/lib/gdi/font.h new file mode 100644 index 0000000..ac55c88 --- /dev/null +++ b/lib/gdi/font.h @@ -0,0 +1,163 @@ +#ifndef __FONT_H +#define __FONT_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +class FontRenderClass; +class Font; +class gPixmapDC; +class gFont; +class gRGB; + +class fontRenderClass +{ + friend class Font; + friend class eTextPara; + fbClass *fb; + struct fontListEntry + { + eString filename, face; + int scale; // 100 is 1:1 + fontListEntry *next; + ~fontListEntry(); + } *font; + + FT_Library library; + FTC_Manager cacheManager; /* the cache manager */ + FTC_Image_Cache imageCache; /* the glyph image cache */ + FTC_SBit_Cache sbitsCache; /* the glyph small bitmaps cache */ + + FTC_FaceID getFaceID(const eString &face); + FT_Error getGlyphBitmap(FTC_Image_Desc *font, FT_ULong glyph_index, FTC_SBit *sbit); + static fontRenderClass *instance; +public: + float getLineHeight(const gFont& font); + eString AddFont(const eString &filename, const eString &name, int scale); + static fontRenderClass *getInstance(); + FT_Error FTC_Face_Requester(FTC_FaceID face_id, + FT_Face* aface); + Font *getFont(const eString &face, int size, int tabwidth=-1); + fontRenderClass(); + ~fontRenderClass(); +}; + +#define RS_WRAP 1 +#define RS_DOT 2 +#define RS_DIRECT 4 +#define RS_FADE 8 + +#define GS_ISSPACE 1 +#define GS_ISFIRST 2 +#define GS_USED 4 + +struct pGlyph +{ + int x, y, w; + Font *font; + FT_ULong glyph_index; + int flags; + eRect bbox; +}; + +typedef std::vector glyphString; + +class Font; +class eLCD; + +class eTextPara +{ + Font *current_font, *replacement_font; + FT_Face current_face, replacement_face; + int use_kerning; + int previous; + static eString replacement_facename; + + eRect area; + ePoint cursor; + eSize maximum; + int left; + glyphString glyphs; + int refcnt; + + int appendGlyph(Font *current_font, FT_Face current_face, FT_UInt glyphIndex, int flags, int rflags); + void newLine(int flags); + void setFont(Font *font, Font *replacement_font); + eRect boundBox; + void calc_bbox(); + int bboxValid; +public: + eTextPara(eRect area, ePoint start=ePoint(-1, -1)) + : current_font(0), replacement_font(0), current_face(0), replacement_face(0), + area(area), cursor(start), maximum(0, 0), left(start.x()), refcnt(0), bboxValid(0) + { + } + ~eTextPara(); + + static void setReplacementFont(eString font) { replacement_facename=font; } + + void destroy(); + eTextPara *grab(); + + void setFont(const gFont &font); + int renderString(const eString &string, int flags=0); + + void clear(); + + void blit(gPixmapDC &dc, const ePoint &offset, const gRGB &background, const gRGB &foreground); + + enum + { + dirLeft, dirRight, dirCenter, dirBlock + }; + + void realign(int dir); + + const eRect & getBoundBox() + { + if (!bboxValid) + calc_bbox(); + + return boundBox; + } + + const eRect& getGlyphBBox(int num) const + { + return glyphs[num].bbox; + } +}; + +class Font +{ +public: + FTC_Image_Desc font; + fontRenderClass *renderer; + int ref; + FT_Error getGlyphBitmap(FT_ULong glyph_index, FTC_SBit *sbit); + FT_Face face; + FT_Size size; + + int tabwidth; + int height; + Font(fontRenderClass *render, FTC_FaceID faceid, int isize, int tabwidth); + ~Font(); + + void lock(); + void unlock(); // deletes if ref==0 +}; + +extern fontRenderClass *font; + +#endif diff --git a/lib/gdi/font_arabic.cpp b/lib/gdi/font_arabic.cpp new file mode 100644 index 0000000..573f9fa --- /dev/null +++ b/lib/gdi/font_arabic.cpp @@ -0,0 +1,266 @@ +/* + * This was taken from pango. + * Modified for enigma by Felix Domke . + * I removed support for vowels and ligatures. Sorry. + * + * Original header: + * + * This is part of Pango - Arabic shaping module + * + * (C) 2000 Karl Koehler + * (C) 2001 Roozbeh Pournader + * + */ + +#include +#include + +typedef struct +{ + unsigned long basechar; + int count; + unsigned long charshape[4]; +} shapestruct; + +typedef struct +{ + unsigned long basechar; + char numshapes; +} charstruct; + +static void +charstruct_init (charstruct * s) +{ + s->basechar = 0; + s->numshapes = 1; +} + +#define connects_to_left(a) ((a).numshapes > 2) + +/* The Unicode order is always 'isolated, final, initial, medial'. */ + +/* *INDENT-OFF* */ +static shapestruct chartable[] = { + {0x0621, 1, {0xFE80}}, /* HAMZA */ + {0x0622, 2, {0xFE81, 0xFE82}}, /* ALEF WITH MADDA ABOVE */ + {0x0623, 2, {0xFE83, 0xFE84}}, /* ALEF WITH HAMZA ABOVE */ + {0x0624, 2, {0xFE85, 0xFE86}}, /* WAW WITH HAMZA ABOVE */ + {0x0625, 2, {0xFE87, 0xFE88}}, /* ALEF WITH HAMZA BELOW */ + {0x0626, 4, {0xFE89, 0xFE8A, 0xFE8B, 0xFE8C}}, /* YEH WITH HAMZA ABOVE */ + {0x0627, 2, {0xFE8D, 0xFE8E}}, /* ALEF */ + {0x0628, 4, {0xFE8F, 0xFE90, 0xFE91, 0xFE92}}, /* BEH */ + {0x0629, 2, {0xFE93, 0xFE94}}, /* TEH MARBUTA */ + {0x062A, 4, {0xFE95, 0xFE96, 0xFE97, 0xFE98}}, /* TEH */ + {0x062B, 4, {0xFE99, 0xFE9A, 0xFE9B, 0xFE9C}}, /* THEH */ + {0x062C, 4, {0xFE9D, 0xFE9E, 0xFE9F, 0xFEA0}}, /* JEEM */ + {0x062D, 4, {0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4}}, /* HAH */ + {0x062E, 4, {0xFEA5, 0xFEA6, 0xFEA7, 0xFEA8}}, /* KHAH */ + {0x062F, 2, {0xFEA9, 0xFEAA}}, /* DAL */ + {0x0630, 2, {0xFEAB, 0xFEAC}}, /* THAL */ + {0x0631, 2, {0xFEAD, 0xFEAE}}, /* REH */ + {0x0632, 2, {0xFEAF, 0xFEB0}}, /* ZAIN */ + {0x0633, 4, {0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4}}, /* SEEN */ + {0x0634, 4, {0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8}}, /* SHEEN */ + {0x0635, 4, {0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC}}, /* SAD */ + {0x0636, 4, {0xFEBD, 0xFEBE, 0xFEBF, 0xFEC0}}, /* DAD */ + {0x0637, 4, {0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4}}, /* TAH */ + {0x0638, 4, {0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8}}, /* ZAH */ + {0x0639, 4, {0xFEC9, 0xFECA, 0xFECB, 0xFECC}}, /* AIN */ + {0x063A, 4, {0xFECD, 0xFECE, 0xFECF, 0xFED0}}, /* GHAIN */ + {0x0640, 4, {0x0640, 0x0640, 0x0640, 0x0640}}, /* TATWEEL */ + {0x0641, 4, {0xFED1, 0xFED2, 0xFED3, 0xFED4}}, /* FEH */ + {0x0642, 4, {0xFED5, 0xFED6, 0xFED7, 0xFED8}}, /* QAF */ + {0x0643, 4, {0xFED9, 0xFEDA, 0xFEDB, 0xFEDC}}, /* KAF */ + {0x0644, 4, {0xFEDD, 0xFEDE, 0xFEDF, 0xFEE0}}, /* LAM */ + {0x0645, 4, {0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4}}, /* MEEM */ + {0x0646, 4, {0xFEE5, 0xFEE6, 0xFEE7, 0xFEE8}}, /* NOON */ + {0x0647, 4, {0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC}}, /* HEH */ + {0x0648, 2, {0xFEED, 0xFEEE}}, /* WAW */ + {0x0649, 4, {0xFEEF, 0xFEF0, 0xFBE8, 0xFBE9}}, /* ALEF MAKSURA */ + {0x064A, 4, {0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4}}, /* YEH */ + {0x0671, 2, {0xFB50, 0xFB51}}, /* ALEF WASLA */ + {0x0679, 4, {0xFB66, 0xFB67, 0xFB68, 0xFB69}}, /* TTEH */ + {0x067A, 4, {0xFB5E, 0xFB5F, 0xFB60, 0xFB61}}, /* TTEHEH */ + {0x067B, 4, {0xFB52, 0xFB53, 0xFB54, 0xFB55}}, /* BEEH */ + {0x067E, 4, {0xFB56, 0xFB57, 0xFB58, 0xFB59}}, /* PEH */ + {0x067F, 4, {0xFB62, 0xFB63, 0xFB64, 0xFB65}}, /* TEHEH */ + {0x0680, 4, {0xFB5A, 0xFB5B, 0xFB5C, 0xFB5D}}, /* BEHEH */ + {0x0683, 4, {0xFB76, 0xFB77, 0xFB78, 0xFB79}}, /* NYEH */ + {0x0684, 4, {0xFB72, 0xFB73, 0xFB74, 0xFB75}}, /* DYEH */ + {0x0686, 4, {0xFB7A, 0xFB7B, 0xFB7C, 0xFB7D}}, /* TCHEH */ + {0x0687, 4, {0xFB7E, 0xFB7F, 0xFB80, 0xFB81}}, /* TCHEHEH */ + {0x0688, 2, {0xFB88, 0xFB89}}, /* DDAL */ + {0x068C, 2, {0xFB84, 0xFB85}}, /* DAHAL */ + {0x068D, 2, {0xFB82, 0xFB83}}, /* DDAHAL */ + {0x068E, 2, {0xFB86, 0xFB87}}, /* DUL */ + {0x0691, 2, {0xFB8C, 0xFB8D}}, /* RREH */ + {0x0698, 2, {0xFB8A, 0xFB8B}}, /* JEH */ + {0x06A4, 4, {0xFB6A, 0xFB6B, 0xFB6C, 0xFB6D}}, /* VEH */ + {0x06A6, 4, {0xFB6E, 0xFB6F, 0xFB70, 0xFB71}}, /* PEHEH */ + {0x06A9, 4, {0xFB8E, 0xFB8F, 0xFB90, 0xFB91}}, /* KEHEH */ + {0x06AD, 4, {0xFBD3, 0xFBD4, 0xFBD5, 0xFBD6}}, /* NG */ + {0x06AF, 4, {0xFB92, 0xFB93, 0xFB94, 0xFB95}}, /* GAF */ + {0x06B1, 4, {0xFB9A, 0xFB9B, 0xFB9C, 0xFB9D}}, /* NGOEH */ + {0x06B3, 4, {0xFB96, 0xFB97, 0xFB98, 0xFB99}}, /* GUEH */ + {0x06BB, 4, {0xFBA0, 0xFBA1, 0xFBA2, 0xFBA3}}, /* RNOON */ + {0x06BE, 4, {0xFBAA, 0xFBAB, 0xFBAC, 0xFBAD}}, /* HEH DOACHASHMEE */ + {0x06C0, 2, {0xFBA4, 0xFBA5}}, /* HEH WITH YEH ABOVE */ + {0x06C1, 4, {0xFBA6, 0xFBA7, 0xFBA8, 0xFBA9}}, /* HEH GOAL */ + {0x06C5, 2, {0xFBE0, 0xFBE1}}, /* KIRGHIZ OE */ + {0x06C6, 2, {0xFBD9, 0xFBDA}}, /* OE */ + {0x06C7, 2, {0xFBD7, 0xFBD8}}, /* U */ + {0x06C8, 2, {0xFBDB, 0xFBDC}}, /* YU */ + {0x06C9, 2, {0xFBE2, 0xFBE3}}, /* KIRGHIZ YU */ + {0x06CB, 2, {0xFBDE, 0xFBDF}}, /* VE */ + {0x06CC, 4, {0xFBFC, 0xFBFD, 0xFBFE, 0xFBFF}}, /* FARSI YEH */ + {0x06D0, 4, {0xFBE4, 0xFBE5, 0xFBE6, 0xFBE7}}, /* E */ + {0x06D2, 2, {0xFBAE, 0xFBAF}}, /* YEH BARREE */ + {0x06D3, 2, {0xFBB0, 0xFBB1}}, /* YEH BARREE WITH HAMZA ABOVE */ +}; + +#define ALEF 0x0627 +#define ALEFHAMZA 0x0623 +#define ALEFHAMZABELOW 0x0625 +#define ALEFMADDA 0x0622 +#define LAM 0x0644 +#define HAMZA 0x0621 +#define TATWEEL 0x0640 +#define ZWJ 0x200D + +#define HAMZAABOVE 0x0654 +#define HAMZABELOW 0x0655 + +#define WAWHAMZA 0x0624 +#define YEHHAMZA 0x0626 +#define WAW 0x0648 +#define ALEFMAKSURA 0x0649 +#define YEH 0x064A +#define FARSIYEH 0x06CC + +#define SHADDA 0x0651 +#define KASRA 0x0650 +#define FATHA 0x064E +#define DAMMA 0x064F +#define MADDA 0x0653 + +#define LAM_ALEF 0xFEFB +#define LAM_ALEFHAMZA 0xFEF7 +#define LAM_ALEFHAMZABELOW 0xFEF9 +#define LAM_ALEFMADDA 0xFEF5 + +static short +shapecount (unsigned long s) +{ + int l, r, m; + if ((s >= 0x0621) && (s <= 0x06D3)) + { + l = 0; + r = sizeof (chartable) / sizeof (shapestruct); + while (l <= r) + { + m = (l + r) / 2; + if (s == chartable[m].basechar) + { + return chartable[m].count; + } + else if (s < chartable[m].basechar) + { + r = m - 1; + } + else + { + l = m + 1; + } + } + } + else if (s == ZWJ) + { + return 4; + } + return 1; +} + +static unsigned long +charshape (unsigned long s, int which) +/* which 0=isolated 1=final 2=initial 3=medial */ +{ + int l, r, m; + if ((s >= 0x0621) && (s <= 0x06D3)) + { + l = 0; + r = sizeof (chartable) / sizeof (shapestruct); + while (l <= r) + { + m = (l + r) / 2; + if (s == chartable[m].basechar) + { + return chartable[m].charshape[which]; + } + else if (s < chartable[m].basechar) + { + r = m - 1; + } + else + { + l = m + 1; + } + } + } + else if ((s >= 0xFEF5) && (s <= 0xFEFB)) + { /* Lam+Alef */ + return s + which; + } + + return s; +} + +void +shape (std::vector &string, const std::vector &text) +{ + string.reserve(text.size()); + + charstruct oldchar, curchar; + int which; + unsigned long nextletter; + + charstruct_init (&oldchar); + charstruct_init (&curchar); + + for (std::vector::const_iterator i(text.begin()); + i != text.end(); ++i) + { + nextletter = *i; + int nc = shapecount (nextletter); + + if (nc == 1) + which = 0; /* final or isolated */ + else + which = 2; /* medial or initial */ + if (connects_to_left (oldchar)) + which++; + which = which % (curchar.numshapes); + curchar.basechar = charshape (curchar.basechar, which); + /* get rid of oldchar */ + if (oldchar.basechar) + string.push_back(oldchar.basechar); + oldchar = curchar; /* new values in oldchar */ + + /* init new curchar */ + charstruct_init (&curchar); + curchar.basechar = nextletter; + curchar.numshapes = nc; + } + + /* Handle last char */ + if (connects_to_left (oldchar)) + which = 1; + else + which = 0; + which = which % (curchar.numshapes); + curchar.basechar = charshape (curchar.basechar, which); + + if (oldchar.basechar != 0) + string.push_back(oldchar.basechar); + if (curchar.basechar != 0) + string.push_back(curchar.basechar); +} diff --git a/lib/gdi/gfbdc.cpp b/lib/gdi/gfbdc.cpp new file mode 100644 index 0000000..83f681d --- /dev/null +++ b/lib/gdi/gfbdc.cpp @@ -0,0 +1,166 @@ +#include + +#include +#include +#include + +gFBDC *gFBDC::instance; + +gFBDC::gFBDC() +{ + instance=this; + fb=new fbClass; + + if (!fb->Available()) + eFatal("no framebuffer available"); + + fb->SetMode(720, 576, 8); + for (int y=0; y<576; y++) // make whole screen transparent + memset(fb->lfb+y*fb->Stride(), 0x00, fb->Stride()); + + pixmap=new gPixmap(); + pixmap->x=720; + pixmap->y=576; + pixmap->bpp=8; + pixmap->bypp=1; + pixmap->stride=fb->Stride(); + pixmap->data=fb->lfb; + + pixmap->clut.colors=256; + pixmap->clut.data=new gRGB[pixmap->clut.colors]; + memset(pixmap->clut.data, 0, sizeof(*pixmap->clut.data)*pixmap->clut.colors); + reloadSettings(); +} + +gFBDC::~gFBDC() +{ + delete pixmap; + delete fb; + instance=0; +} + +void gFBDC::calcRamp() +{ +#if 0 + float fgamma=gamma ? gamma : 1; + fgamma/=10.0; + fgamma=1/log(fgamma); + for (int i=0; i<256; i++) + { + float raw=i/255.0; // IIH, float. + float corr=pow(raw, fgamma) * 256.0; + + int d=corr * (float)(256-brightness) / 256 + brightness; + if (d < 0) + d=0; + if (d > 255) + d=255; + ramp[i]=d; + + rampalpha[i]=i*alpha/256; + } +#endif + for (int i=0; i<256; i++) + { + int d; + d=i; + d=(d-128)*(gamma+64)/(128+64)+128; + d+=brightness-128; // brightness correction + if (d<0) + d=0; + if (d>255) + d=255; + ramp[i]=d; + +/* if ( eDVB::getInstance()->getmID == 1 ) + rampalpha[i]=i*alpha/65535; + else*/ + rampalpha[i]=i*alpha/256; + } + + rampalpha[255]=255; // transparent BLEIBT bitte so. +} + +void gFBDC::setPalette() +{ + if (!pixmap->clut.data) + return; + + for (int i=0; i<256; ++i) + { + fb->CMAP()->red[i]=ramp[pixmap->clut.data[i].r]<<8; + fb->CMAP()->green[i]=ramp[pixmap->clut.data[i].g]<<8; + fb->CMAP()->blue[i]=ramp[pixmap->clut.data[i].b]<<8; + fb->CMAP()->transp[i]=rampalpha[pixmap->clut.data[i].a]<<8; + if (!fb->CMAP()->red[i]) + fb->CMAP()->red[i]=0x100; + } + fb->PutCMAP(); +} + +void gFBDC::exec(gOpcode *o) +{ + switch (o->opcode) + { + case gOpcode::setPalette: + { + gPixmapDC::exec(o); + setPalette(); + break; + } + default: + gPixmapDC::exec(o); + break; + } +} + +gFBDC *gFBDC::getInstance() +{ + return instance; +} + +void gFBDC::setAlpha(int a) +{ + alpha=a; + + calcRamp(); + setPalette(); +} + +void gFBDC::setBrightness(int b) +{ + brightness=b; + + calcRamp(); + setPalette(); +} + +void gFBDC::setGamma(int g) +{ + gamma=g; + + calcRamp(); + setPalette(); +} + +void gFBDC::saveSettings() +{ + eConfig::getInstance()->setKey("/ezap/osd/alpha", alpha); + eConfig::getInstance()->setKey("/ezap/osd/gamma", gamma); + eConfig::getInstance()->setKey("/ezap/osd/brightness", brightness); +} + +void gFBDC::reloadSettings() +{ + if (eConfig::getInstance()->getKey("/ezap/osd/alpha", alpha)) + alpha=255; + if (eConfig::getInstance()->getKey("/ezap/osd/gamma", gamma)) + gamma=128; + if (eConfig::getInstance()->getKey("/ezap/osd/brightness", brightness)) + brightness=128; + + calcRamp(); + setPalette(); +} + +eAutoInitP0 init_gFBDC(eAutoInitNumbers::graphic+1, "GFBDC"); diff --git a/lib/gdi/gfbdc.h b/lib/gdi/gfbdc.h new file mode 100644 index 0000000..f975fb5 --- /dev/null +++ b/lib/gdi/gfbdc.h @@ -0,0 +1,35 @@ +#ifndef __gfbdc_h +#define __gfbdc_h + +#include "fb.h" +#include "gpixmap.h" +#include "grc.h" + +class gFBDC: public gPixmapDC +{ + fbClass *fb; + static gFBDC *instance; + void exec(gOpcode *opcode); + unsigned char ramp[256], rampalpha[256]; // RGB ramp 0..255 + int brightness, gamma, alpha; + void calcRamp(); + void setPalette(); +public: + void reloadSettings(); + void setAlpha(int alpha); + void setBrightness(int brightness); + void setGamma(int gamma); + + int getAlpha() { return alpha; } + int getBrightness() { return brightness; } + int getGamma() { return gamma; } + + void saveSettings(); + + gFBDC(); + ~gFBDC(); + static gFBDC *getInstance(); +}; + + +#endif diff --git a/lib/gdi/glcddc.cpp b/lib/gdi/glcddc.cpp new file mode 100644 index 0000000..3895df9 --- /dev/null +++ b/lib/gdi/glcddc.cpp @@ -0,0 +1,56 @@ +#ifndef DISABLE_LCD + +#include +#include + +gLCDDC *gLCDDC::instance; + +gLCDDC::gLCDDC(eLCD *lcd): lcd(lcd) +{ + instance=this; + + update=1; + + pixmap=new gPixmap(); + pixmap->x=lcd->size().width(); + pixmap->y=lcd->size().height(); + pixmap->bpp=8; + pixmap->bypp=1; + pixmap->stride=lcd->stride(); + pixmap->data=lcd->buffer(); + + pixmap->clut.colors=256; + pixmap->clut.data=0; +} + +gLCDDC::~gLCDDC() +{ + delete pixmap; + instance=0; +} + +void gLCDDC::exec(gOpcode *o) +{ + switch (o->opcode) + { + case gOpcode::flush: + case gOpcode::end: + if (update) + lcd->update(); + default: + gPixmapDC::exec(o); + break; + } +} + +gLCDDC *gLCDDC::getInstance() +{ + return instance; +} + +void gLCDDC::setUpdate(int u) +{ + update=u; +} + +#endif //DISABLE_LCD diff --git a/lib/gdi/glcddc.h b/lib/gdi/glcddc.h new file mode 100644 index 0000000..9342e5e --- /dev/null +++ b/lib/gdi/glcddc.h @@ -0,0 +1,26 @@ +#ifndef DISABLE_LCD + +#ifndef __glcddc_h +#define __glcddc_h + +#include "grc.h" + +class eLCD; + +class gLCDDC: public gPixmapDC +{ + eLCD *lcd; + static gLCDDC *instance; + int update; + void exec(gOpcode *opcode); +public: + gLCDDC(eLCD *lcd); + ~gLCDDC(); + void setUpdate(int update); + static gLCDDC *getInstance(); +}; + + +#endif + +#endif //DISABLE_LCD diff --git a/lib/gdi/gpixmap.cpp b/lib/gdi/gpixmap.cpp new file mode 100644 index 0000000..c089051 --- /dev/null +++ b/lib/gdi/gpixmap.cpp @@ -0,0 +1,295 @@ +#include + +gLookup::gLookup() +{ + size=0; + lookup=0; +} + +gLookup::gLookup(int size, const gPalette &pal, const gRGB &start, const gRGB &end) +{ + size=0; + lookup=0; + build(size, pal, start, end); +} + +void gLookup::build(int _size, const gPalette &pal, const gRGB &start, const gRGB &end) +{ + if (lookup) + { + delete lookup; + lookup=0; + size=0; + } + size=_size; + if (!size) + return; + lookup=new gColor[size]; + + for (int i=0; i= 0) goto AFTERNEGX; dX=-dX; // ndet und beheben kann. das liegt nicht zuletzt an den komment +fbXincr=-1; AFTERNEGX: fbYincr=stride; if ( (dY=By // aren. und ausserdem, je kuerzer der code, desto weniger k +-Ay) >= 0) goto AFTERNEGY; fbYincr=-stride; dY=-dY;AFTERNEGY: // ann daran falsch sein. erwaehnte ich schon, da +fbXYincr = fbXincr+fbYincr; if (dY > dX) goto YisIndependent; dPr = dY+ // s dieser tolle code wahnsinnig schnell +dY; P = -dX; dPru = P+P; dY = dX>>1; XLOOP: *AfbAddr=color; *BfbAddr=color; if ((P+=dPr) > 0) // ist? bye, tmbinc +goto RightAndUp; AfbAddr+=fbXincr; BfbAddr-=fbXincr; if ((dY=dY-1) > 0) goto XLOOP; *AfbAddr=color; if ((dX & 1) +== 0) return; *BfbAddr=color; return; RightAndUp: AfbAddr+=fbXYincr; BfbAddr-=fbXYincr; P+=dPru; if ((dY=dY-1) > +0) goto XLOOP; *AfbAddr=color; if ((dX & 1) == 0) return; *BfbAddr=color; return; YisIndependent: dPr = dX+dX; P += -dY; dPru = P+P; dX = dY>>1; YLOOP: *AfbAddr=color; *BfbAddr=color; if ((P+=dPr) > 0) goto RightAndUp2; AfbAddr ++=fbYincr; BfbAddr-=fbYincr; if ((dX=dX-1) > 0) goto YLOOP; *AfbAddr=color; if ((dY & 1) == 0) return; *BfbAddr= +color;return; RightAndUp2: AfbAddr+=fbXYincr; BfbAddr-=fbXYincr; P+=dPru; if ((dX=dX-1) > 0) goto YLOOP; *AfbAddr +=color; if((dY & 1) == 0) return; *BfbAddr=color; return; // nun ist der tolle code leider zu ende. tut mir leid. +} + +gColor gPalette::findColor(const gRGB &rgb) const +{ + int difference=1<<30, best_choice=0; + for (int t=0; t=difference) + continue; + td=(signed)(rgb.g-data[t].g); td*=td; td*=(255-data[t].a); + ttd+=td; + if (ttd>=difference) + continue; + td=(signed)(rgb.b-data[t].b); td*=td; td*=(255-data[t].a); + ttd+=td; + if (ttd>=difference) + continue; + td=(signed)(rgb.a-data[t].a); td*=td; td*=255; + ttd+=td; + if (ttd>=difference) + continue; + difference=ttd; + best_choice=t; + } + return best_choice; +} + +gPixmap::gPixmap() +{ +} + +gPixmap::~gPixmap() +{ +} + +gImage::gImage(eSize size, int _bpp) +{ + x=size.width(); + y=size.height(); + bpp=_bpp; + switch (bpp) + { + case 8: + bypp=1; + break; + case 15: + case 16: + bypp=2; + break; + case 24: // never use 24bit mode + case 32: + bypp=4; + break; + default: + bypp=(bpp+7)/8; + } + stride=x*bypp; + if (bpp==8) + { + clut.colors=256; + clut.data=new gRGB[clut.colors]; + } else + { + clut.colors=0; + clut.data=0; + } + data=new char[x*y*bypp]; +} + +gImage::~gImage() +{ + delete[] clut.data; + delete[] (char*)data; +} diff --git a/lib/gdi/gpixmap.h b/lib/gdi/gpixmap.h new file mode 100644 index 0000000..f68a574 --- /dev/null +++ b/lib/gdi/gpixmap.h @@ -0,0 +1,142 @@ +#ifndef __gpixmap_h +#define __gpixmap_h + +#include +#include +#include +#include +#include + +#include + +struct gColor +{ + int color; + gColor(int color): color(color) + { + } + gColor(): color(0) + { + } + operator int() const { return color; } + bool operator==(const gColor &o) const { return o.color==color; } +}; + +struct gRGB +{ + int b, g, r, a; + gRGB(int r, int g, int b, int a=0): b(b), g(g), r(r), a(a) + { + } + gRGB(unsigned long val): b(val&0xFF), g((val>>8)&0xFF), r((val>>16)&0xFF), a((val>>24)&0xFF) // ARGB + { + } + gRGB() + { + } + bool operator < (const gRGB &c) const + { + if (b < c.b) + return 1; + if (b == c.b) + { + if (g < c.g) + return 1; + if (g == c.g) + { + if (r < c.r) + return 1; + if (r == c.r) + return a < c.a; + } + } + return 0; + } + bool operator==(const gRGB &c) const + { + return (b == c.b) && (g == c.g) && (r == c.r) && (a == c.a); + } +}; + +struct gPalette +{ + int start, colors; + gRGB *data; + gColor findColor(const gRGB &rgb) const; +}; + +struct gLookup +{ + int size; + gColor *lookup; + gLookup(int size, const gPalette &pal, const gRGB &start, const gRGB &end); + gLookup(); + void build(int size, const gPalette &pal, const gRGB &start, const gRGB &end); +}; + +/** + * \brief A softreference to a font. + * + * The font is specified by a name and a size. + * \c gFont is part of the \ref gdi. + */ +struct gFont +{ + eString family; + int pointSize; + + /** + * \brief Constructs a font with the given name and size. + * \param family The name of the font, for example "NimbusSansL-Regular Sans L Regular". + * \param pointSize the size of the font in PIXELS. + */ + gFont(const eString &family, int pointSize): + family(family), pointSize(pointSize) + { + } + + enum + { + tRegular, tFixed + }; + + gFont(int type, int pointSize); + + gFont() + :pointSize(0) + { + } +}; + +struct gPixmap: public iObject +{ +DECLARE_REF; +public: + int x, y, bpp, bypp, stride; + void *data; + + gPalette clut; + + eSize getSize() const { return eSize(x, y); } + + void fill(const eRect &area, const gColor &color); + + enum + { + blitAlphaTest=1 + }; + void blit(const gPixmap &src, ePoint pos, const eRect &clip=eRect(), int flags=0); + + void mergePalette(const gPixmap &target); + void line(ePoint start, ePoint end, gColor color); + gPixmap(); + virtual ~gPixmap(); +}; + +struct gImage: gPixmap +{ + gImage(eSize size, int bpp); + ~gImage(); +}; + +#endif diff --git a/lib/gdi/grc.cpp b/lib/gdi/grc.cpp new file mode 100644 index 0000000..3bd0078 --- /dev/null +++ b/lib/gdi/grc.cpp @@ -0,0 +1,348 @@ +// for debugging use: +// #define SYNC_PAINT +#include +#ifndef SYNC_PAINT +#include +#endif + +#include +#include +#include +#include + +#define MAXSIZE 1024 + +#ifndef SYNC_PAINT +void *gRC::thread_wrapper(void *ptr) +{ + nice(3); + return ((gRC*)ptr)->thread(); +} +#endif + +gRC *gRC::instance=0; + +gRC::gRC(): queuelock(MAXSIZE), queue(2048) +{ + ASSERT(!instance); + instance=this; + queuelock.lock(MAXSIZE); +#ifndef SYNC_PAINT + eDebug(pthread_create(&the_thread, 0, thread_wrapper, this)?"RC thread couldn't be created":"RC thread createted successfully"); +#endif +} + +gRC::~gRC() +{ + gOpcode o; + o.dc=0; + o.opcode=gOpcode::shutdown; + submit(o); + instance=0; +} + +void *gRC::thread() +{ +#ifndef SYNC_PAINT + while (1) +#else + while (queue.size()) +#endif + { + queuelock.lock(1); + gOpcode& o(queue.current()); + if (o.opcode==gOpcode::shutdown) + break; + o.dc->exec(&o); + queue.dequeue(); + } +#ifndef SYNC_PAINT + pthread_exit(0); +#endif + return 0; +} + +gRC &gRC::getInstance() +{ + return *instance; +} + +static int gPainter_instances; + +gPainter::gPainter(gDC &dc, eRect rect): dc(dc), rc(gRC::getInstance()), foregroundColor(0), backgroundColor(0) +{ + if (rect.isNull()) + rect=eRect(ePoint(0, 0), dc.getSize()); +// ASSERT(!gPainter_instances); + gPainter_instances++; + begin(rect); +} + +gPainter::~gPainter() +{ + end(); + gPainter_instances--; +} + +void gPainter::begin(const eRect &rect) +{ + gOpcode o; + dc.lock(); + o.dc=&dc; + o.opcode=gOpcode::begin; + o.parm.begin=new gOpcode::para::pbegin(rect); +// cliparea=std::stack >(); + cliparea=std::stack(); + cliparea.push(rect); + setLogicalZero(cliparea.top().topLeft()); + rc.submit(o); +} + +void gPainter::setBackgroundColor(const gColor &color) +{ + backgroundColor=color; +} + +void gPainter::setForegroundColor(const gColor &color) +{ + foregroundColor=color; +} + +void gPainter::setFont(const gFont &mfont) +{ + font=mfont; +} + +void gPainter::renderText(const eRect &pos, const std::string &string, int flags) +{ + eRect area=pos; + area.moveBy(logicalZero.x(), logicalZero.y()); + + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::renderText; + o.parm.renderText=new gOpcode::para::prenderText(font, area, string, dc.getRGB(foregroundColor), dc.getRGB(backgroundColor)); + o.flags=flags; + rc.submit(o); +} + +void gPainter::renderPara(eTextPara ¶, ePoint offset) +{ + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::renderPara; + o.parm.renderPara=new gOpcode::para::prenderPara(logicalZero+offset, para.grab(), dc.getRGB(foregroundColor), dc.getRGB(backgroundColor)); + rc.submit(o); +} + +void gPainter::fill(const eRect &area) +{ + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::fill; + eRect a=area; + a.moveBy(logicalZero.x(), logicalZero.y()); + a&=cliparea.top(); + + o.parm.fill=new gOpcode::para::pfill(a, foregroundColor); + rc.submit(o); +} + +void gPainter::clear() +{ + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::fill; + o.parm.fill=new gOpcode::para::pfill(cliparea.top(), backgroundColor); + rc.submit(o); +} + +void gPainter::setPalette(gRGB *colors, int start, int len) +{ + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::setPalette; + gPalette *p=new gPalette; + + p->data=new gRGB[len]; + memcpy(p->data, colors, len*sizeof(gRGB)); + p->start=start; + p->colors=len; + o.parm.setPalette=new gOpcode::para::psetPalette(p); + rc.submit(o); +} + +void gPainter::mergePalette(gPixmap *target) +{ + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::mergePalette; + o.parm.mergePalette=new gOpcode::para::pmergePalette(target); + rc.submit(o); +} + +void gPainter::line(ePoint start, ePoint end) +{ + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::line; + o.parm.line=new gOpcode::para::pline(start+logicalZero, end+logicalZero, foregroundColor); + rc.submit(o); +} + +void gPainter::setLogicalZero(ePoint rel) +{ + logicalZero=rel; +} + +void gPainter::moveLogicalZero(ePoint rel) +{ + logicalZero+=rel; +} + +void gPainter::resetLogicalZero() +{ + logicalZero.setX(0); + logicalZero.setY(0); +} + +void gPainter::clip(eRect clip) +{ + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::clip; + clip.moveBy(logicalZero.x(), logicalZero.y()); + cliparea.push(cliparea.top()&clip); + o.parm.clip=new gOpcode::para::pclip(cliparea.top()); + + rc.submit(o); +} + +void gPainter::clippop() +{ + ASSERT (cliparea.size()>1); + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::clip; + cliparea.pop(); + o.parm.clip=new gOpcode::para::pclip(cliparea.top()); + rc.submit(o); +} + +void gPainter::flush() +{ + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::flush; + rc.submit(o); +} + +void gPainter::end() +{ + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::end; + rc.submit(o); +} + +gDC::~gDC() +{ +} + +gPixmapDC::gPixmapDC(): pixmap(0) +{ +} + +gPixmapDC::gPixmapDC(gPixmap *pixmap): pixmap(pixmap) +{ +} + +gPixmapDC::~gPixmapDC() +{ + dclock.lock(); +} + +void gPixmapDC::exec(gOpcode *o) +{ + switch(o->opcode) + { + case gOpcode::begin: + clip=o->parm.begin->area; + delete o->parm.begin; + break; + case gOpcode::renderText: + { + eTextPara *para=new eTextPara(o->parm.renderText->area); + para->setFont(o->parm.renderText->font); + para->renderString(o->parm.renderText->text, o->flags); + para->blit(*this, ePoint(0, 0), o->parm.renderText->backgroundColor, o->parm.renderText->foregroundColor); + para->destroy(); + delete o->parm.renderText; + break; + } + case gOpcode::renderPara: + { + o->parm.renderPara->textpara->blit(*this, o->parm.renderPara->offset, o->parm.renderPara->backgroundColor, o->parm.renderPara->foregroundColor); + o->parm.renderPara->textpara->destroy(); + delete o->parm.renderPara; + break; + } + case gOpcode::fill: + pixmap->fill(o->parm.fill->area, o->parm.fill->color); + delete o->parm.fill; + break; + case gOpcode::blit: + { + if (o->parm.blit->clip.isNull()) + o->parm.blit->clip=clip; + else + o->parm.blit->clip&=clip; + pixmap->blit(*o->parm.blit->pixmap, o->parm.blit->position, o->parm.blit->clip, o->flags); + delete o->parm.blit; + break; + } + case gOpcode::setPalette: + if (o->parm.setPalette->palette->start>pixmap->clut.colors) + o->parm.setPalette->palette->start=pixmap->clut.colors; + if (o->parm.setPalette->palette->colors>(pixmap->clut.colors-o->parm.setPalette->palette->start)) + o->parm.setPalette->palette->colors=pixmap->clut.colors-o->parm.setPalette->palette->start; + if (o->parm.setPalette->palette->colors) + memcpy(pixmap->clut.data+o->parm.setPalette->palette->start, o->parm.setPalette->palette->data, o->parm.setPalette->palette->colors*sizeof(gRGB)); + delete[] o->parm.setPalette->palette->data; + delete o->parm.setPalette->palette; + delete o->parm.setPalette; + break; + case gOpcode::mergePalette: + pixmap->mergePalette(*o->parm.blit->pixmap); + delete o->parm.blit; + break; + case gOpcode::line: + pixmap->line(o->parm.line->start, o->parm.line->end, o->parm.line->color); + delete o->parm.line; + break; + case gOpcode::clip: + clip=o->parm.clip->clip; + delete o->parm.clip; + break; + case gOpcode::end: + unlock(); + case gOpcode::flush: + break; + default: + eFatal("illegal opcode %d. expect memory leak!", o->opcode); + } +} + +gRGB gPixmapDC::getRGB(gColor col) +{ + if ((!pixmap) || (!pixmap->clut.data)) + return gRGB(col, col, col); + if (col<0) + { + eFatal("bla transp"); + return gRGB(0, 0, 0, 0xFF); + } + return pixmap->clut.data[col]; +} + +eAutoInitP0 init_grc(eAutoInitNumbers::graphic, "gRC"); diff --git a/lib/gdi/grc.h b/lib/gdi/grc.h new file mode 100644 index 0000000..53cd4a8 --- /dev/null +++ b/lib/gdi/grc.h @@ -0,0 +1,245 @@ +#ifndef __grc_h +#define __grc_h + +/* + gPainter ist die high-level version. die highlevel daten werden zu low level opcodes ueber + die gRC-queue geschickt und landen beim gDC der hardwarespezifisch ist, meist aber auf einen + gPixmap aufsetzt (und damit unbeschleunigt ist). +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include + + +class eTextPara; + +class gDC; +struct gOpcode +{ + enum Opcode + { + begin, + + renderText, + renderPara, + + fill, + blit, + + setPalette, + mergePalette, + + line, + + clip, + + flush, + end, + + shutdown + } opcode; + + union para + { + struct pbegin + { + eRect area; + pbegin(const eRect &area): area(area) { } + } *begin; + + struct pfill + { + eRect area; + gColor color; + pfill(const eRect &area, gColor color): area(area), color(color) { } + } *fill; + + struct prenderText + { + gFont font; + eRect area; + eString text; + gRGB foregroundColor, backgroundColor; + prenderText(const gFont &font, const eRect &area, const eString &text, const gRGB &foregroundColor, const gRGB &backgroundColor): + font(font), area(area), text(text), foregroundColor(foregroundColor), backgroundColor(backgroundColor) { } + } *renderText; + + struct prenderPara + { + ePoint offset; + eTextPara *textpara; + gRGB foregroundColor, backgroundColor; + prenderPara(const ePoint &offset, eTextPara *textpara, const gRGB &foregroundColor, const gRGB &backgroundColor) + : offset(offset), textpara(textpara), foregroundColor(foregroundColor), backgroundColor(backgroundColor) { } + } *renderPara; + + struct psetPalette + { + gPalette *palette; + psetPalette(gPalette *palette): palette(palette) { } + } *setPalette; + + struct pblit + { + ePtr pixmap; + ePoint position; + eRect clip; + pblit(gPixmap *pixmap, const ePoint &position, const eRect &clip) + : pixmap(pixmap), position(position), clip(clip) { } + } *blit; + + struct pmergePalette + { + ePtr target; + pmergePalette(gPixmap *target): target(target) { } + } *mergePalette; + + struct pline + { + ePoint start, end; + gColor color; + pline(const ePoint &start, const ePoint &end, gColor color): start(start), end(end), color(color) { } + } *line; + + struct pclip + { + eRect clip; + pclip(const eRect &clip): clip(clip) { } + } *clip; + } parm; + + int flags; + + gDC *dc; +}; + +class gRC +{ + static gRC *instance; + + static void *thread_wrapper(void *ptr); + pthread_t the_thread; + void *thread(); + + eLock queuelock; + + queueRingBuffer queue; +public: + gRC(); + virtual ~gRC(); + + void submit(const gOpcode &o) + { + static int collected=0; + queue.enqueue(o); + collected++; + if (o.opcode==gOpcode::end) + { + queuelock.unlock(collected); +#ifdef SYNC_PAINT + thread(); +#endif + collected=0; + } + } + + static gRC &getInstance(); +}; + +class gPainter +{ + gDC &dc; + gRC &rc; + friend class gRC; + + gOpcode *beginptr; + /* paint states */ +// std::stack > cliparea; + std::stack cliparea; + gFont font; + gColor foregroundColor, backgroundColor; + ePoint logicalZero; + void begin(const eRect &rect); + void end(); +public: + gPainter(gDC &dc, eRect rect=eRect()); + virtual ~gPainter(); + + void setBackgroundColor(const gColor &color); + void setForegroundColor(const gColor &color); + + void setFont(const gFont &font); + void renderText(const eRect &position, const std::string &string, int flags=0); + void renderPara(eTextPara ¶, ePoint offset=ePoint(0, 0)); + + void fill(const eRect &area); + + void clear(); + + void gPainter::blit(gPixmap *pixmap, ePoint pos, eRect clip=eRect(), int flags=0) + { + gOpcode o; + o.dc=&dc; + o.opcode=gOpcode::blit; + pos+=logicalZero; + clip.moveBy(logicalZero.x(), logicalZero.y()); + o.parm.blit=new gOpcode::para::pblit(pixmap, pos, clip); + o.flags=flags; + rc.submit(o); + } + + void setPalette(gRGB *colors, int start=0, int len=256); + void mergePalette(gPixmap *target); + + void line(ePoint start, ePoint end); + + void setLogicalZero(ePoint abs); + void moveLogicalZero(ePoint rel); + void resetLogicalZero(); + + void clip(eRect clip); + void clippop(); + + void flush(); +}; + +class gDC +{ +protected: + eLock dclock; +public: + virtual void exec(gOpcode *opcode)=0; + virtual RESULT getPixmap(ePtr &)=0; + virtual eSize getSize()=0; + virtual const eRect &getClip()=0; + virtual gRGB getRGB(gColor col)=0; + virtual ~gDC(); + void lock() { dclock.lock(1); } + void unlock() { dclock.unlock(1); } +}; + +class gPixmapDC: public gDC +{ +protected: + ePtr pixmap; + eRect clip; + + void exec(gOpcode *opcode); + gPixmapDC(); +public: + gPixmapDC(gPixmap *pixmap); + virtual ~gPixmapDC(); + RESULT getPixmap(ePtr &ptr) { ptr = pixmap; return 0; } + gRGB getRGB(gColor col); + const eRect &getClip() { return clip; } + virtual eSize getSize() { return eSize(pixmap->x, pixmap->y); } +}; + +#endif diff --git a/lib/gdi/lcd.cpp b/lib/gdi/lcd.cpp new file mode 100644 index 0000000..844ad08 --- /dev/null +++ b/lib/gdi/lcd.cpp @@ -0,0 +1,210 @@ +#ifndef DISABLE_LCD + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +eDBoxLCD *eDBoxLCD::instance; + +eLCD::eLCD(eSize size): res(size) +{ + locked=0; + _buffer=new unsigned char[res.height()*res.width()]; + _stride=res.width(); +} + +eLCD::~eLCD() +{ + delete [] _buffer; +} + +int eLCD::lock() +{ + if (locked) + return -1; + + locked=1; + return lcdfd; +} + +void eLCD::unlock() +{ + read( lcdfd, NULL, 0); + if ( errno == 9 ) + { + eDebug("reopen lcd"); + lcdfd=open("/dev/dbox/lcd0", O_RDWR); // reopen device + } + else + eDebug("do not reopen lcd.. errno = %d", errno); + + locked=0; +} + +/* void eLCD::line(ePoint start, ePoint dst, int color) +{ +int Ax=start.x(), // dieser code rult ganz ganz doll weil er ganz ganz fast ist und auch sehr gut dokumentiert is +Ay=start.y(), Bx=dst.x(), // t. es handelt sich immerhin um den weltbekannten bresenham algorithmus der nicht nur +By=dst.y(); int dX, dY, fbXincr, // sehr schnell ist sondern auch sehr gut dokumentiert und getestet wurde. nicht +fbYincr, fbXYincr, dPr, dPru, P; __u8 // nur auf dem LCD der dbox, sondern auch ueberall anders. und auch auf der +*AfbAddr = &buffer()[Ay*stride()+Ax]; __u8 // dbox mit LCD soll das teil nun tun, und ich denke das tut es. ausse +*BfbAddr = &buffer()[By*stride()+Bx]; fbXincr= // rdem hat dieser algo den vorteil dass man fehler sehr leicht fi +1; if ( (dX=Bx-Ax) >= 0) goto AFTERNEGX; dX=-dX; // ndet und beheben kann. das liegt nicht zuletzt an den komment +fbXincr=-1; AFTERNEGX: fbYincr=stride(); if ( (dY=By // aren. und ausserdem, je kuerzer der code, desto weniger k +-Ay) >= 0) goto AFTERNEGY; fbYincr=-stride(); dY=-dY;AFTERNEGY: // ann daran falsch sein. erwaehnte ich schon, da +fbXYincr = fbXincr+fbYincr; if (dY > dX) goto YisIndependent; dPr = dY+ // s dieser tolle code wahnsinnig schnell +dY; P = -dX; dPru = P+P; dY = dX>>1; XLOOP: *AfbAddr=color; *BfbAddr=color; if ((P+=dPr) > 0) // ist? bye, tmbinc +goto RightAndUp; AfbAddr+=fbXincr; BfbAddr-=fbXincr; if ((dY=dY-1) > 0) goto XLOOP; *AfbAddr=color; if ((dX & 1) +== 0) return; *BfbAddr=color; return; RightAndUp: AfbAddr+=fbXYincr; BfbAddr-=fbXYincr; P+=dPru; if ((dY=dY-1) > +0) goto XLOOP; *AfbAddr=color; if ((dX & 1) == 0) return; *BfbAddr=color; return; YisIndependent: dPr = dX+dX; P += -dY; dPru = P+P; dX = dY>>1; YLOOP: *AfbAddr=color; *BfbAddr=color; if ((P+=dPr) > 0) goto RightAndUp2; AfbAddr ++=fbYincr; BfbAddr-=fbYincr; if ((dX=dX-1) > 0) goto YLOOP; *AfbAddr=color; if ((dY & 1) == 0) return; *BfbAddr= +color;return; RightAndUp2: AfbAddr+=fbXYincr; BfbAddr-=fbXYincr; P+=dPru; if ((dX=dX-1) > 0) goto YLOOP; *AfbAddr +=color; if((dY & 1) == 0) return; *BfbAddr=color; return; // nun ist der tolle code leider zu ende. tut mir leid. +} */ + +eDBoxLCD::eDBoxLCD(): eLCD(eSize(128, 64)) +{ +#ifndef NO_LCD + lcdfd=open("/dev/dbox/lcd0", O_RDWR); +#else + lcdfd=-1; +#endif + instance=this; + + if (lcdfd<0) + eDebug("couldn't open LCD - load lcd.o!"); + else + { + int i=LCD_MODE_BIN; + ioctl(lcdfd, LCD_IOCTL_ASC_MODE, &i); + int lcdbrightness=0, lcdcontrast=0; + + if( eConfig::getInstance()->getKey("/ezap/lcd/brightness", lcdbrightness) ) + { + lcdbrightness=130; + eConfig::getInstance()->setKey("/ezap/lcd/brightness", lcdbrightness); + } + if( eConfig::getInstance()->getKey("/ezap/lcd/contrast", lcdcontrast) ) + { + lcdcontrast=32; + eConfig::getInstance()->setKey("/ezap/lcd/contrast", lcdcontrast); + } + setLCDParameter(lcdbrightness, lcdcontrast); + int tmp; + if( eConfig::getInstance()->getKey("/ezap/lcd/inverted", tmp ) ) + { + inverted=0; + eConfig::getInstance()->setKey("/ezap/lcd/inverted", (int) 0 ); + } + else + inverted=(unsigned char)tmp; + } +} + +void eDBoxLCD::setInverted(unsigned char inv) +{ + inverted=inv; + update(); +} + +int eDBoxLCD::setLCDParameter(int brightness, int contrast) +{ + int fp; + if((fp=open("/dev/dbox/fp0", O_RDWR))<=0) + { + eDebug("[LCD] can't open /dev/dbox/fp0"); + return(-1); + } + + if(ioctl(lcdfd, LCD_IOCTL_SRV, &contrast)) + { + eDebug("[LCD] can't set lcd contrast"); + } + + if(ioctl(fp, FP_IOCTL_LCD_DIMM, &brightness)) + { + eDebug("[LCD] can't set lcd brightness"); + } + eDebug("[LCD] set brightness %d, contrast %d", brightness, contrast); + return(0); +} + +int eDBoxLCD::switchLCD(int state) +{ + int lcdbrightness, lcdcontrast, lcdstandby=0; + + eConfig::getInstance()->getKey("/ezap/lcd/contrast", lcdcontrast); + + if(state==0) + { + eConfig::getInstance()->getKey("/ezap/lcd/standby", lcdstandby); + setLCDParameter(lcdstandby, lcdcontrast); + } + else + { + eConfig::getInstance()->getKey("/ezap/lcd/brightness", lcdbrightness); + setLCDParameter(lcdbrightness, lcdcontrast); + + } + return(0); +} + +eDBoxLCD::~eDBoxLCD() +{ + if (lcdfd>0) + close(lcdfd); +} + +eDBoxLCD *eDBoxLCD::getInstance() +{ + return instance; +} + +void eDBoxLCD::update() +{ + if (!locked) + { + unsigned char raw[120*8]; + int x, y, yy; + for (y=0; y<8; y++) + { + for (x=0; x<120; x++) + { + int pix=0; + for (yy=0; yy<8; yy++) + { + pix|=(_buffer[(y*8+yy)*128+x]>=108)<0) + write(lcdfd, raw, 120*8); + } +} + +class eDBoxLCDHardware +{ + eDBoxLCD lcd; + gLCDDC lcddc; +public: + eDBoxLCDHardware(): lcddc(&lcd) + { + } +}; + +eAutoInitP0 init_eDBoxLCDHardware(eAutoInitNumbers::lowlevel, "d-Box LCD Hardware"); + +#endif //DISABLE_LCD diff --git a/lib/gdi/lcd.h b/lib/gdi/lcd.h new file mode 100644 index 0000000..567d064 --- /dev/null +++ b/lib/gdi/lcd.h @@ -0,0 +1,53 @@ +#ifndef DISABLE_LCD + +#ifndef __lcd_h +#define __lcd_h + +#include +#include +#include + +#define LCD_CONTRAST_MIN 0 +#define LCD_CONTRAST_MAX 63 +#define LCD_BRIGHTNESS_MIN 0 +#define LCD_BRIGHTNESS_MAX 255 + +class eLCD +{ +protected: + eSize res; + unsigned char *_buffer; + int lcdfd; + int _stride; + int locked; +public: + int lock(); + void unlock(); + + eLCD(eSize size); + virtual ~eLCD(); + + __u8 *buffer() { return (__u8*)_buffer; } + int stride() { return _stride; } + eSize size() { return res; } + + virtual void update()=0; +}; + +class eDBoxLCD: public eLCD +{ + static eDBoxLCD *instance; + unsigned char inverted; +public: + static eDBoxLCD *getInstance(); + int switchLCD(int state); + int setLCDParameter(int brightness, int contrast); + void setInverted( unsigned char ); + eDBoxLCD(); + ~eDBoxLCD(); + void update(); +}; + +#endif + +#endif //DISABLE_LCD diff --git a/lib/gui/.cvsignore b/lib/gui/.cvsignore new file mode 100644 index 0000000..316b06f --- /dev/null +++ b/lib/gui/.cvsignore @@ -0,0 +1,7 @@ +*.moc.* +Makefile +Makefile.in +.deps +.libs +*.lo +*.la diff --git a/lib/gui/Makefile.am b/lib/gui/Makefile.am new file mode 100644 index 0000000..634f85b --- /dev/null +++ b/lib/gui/Makefile.am @@ -0,0 +1,13 @@ +INCLUDES = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/src + +noinst_LIBRARIES = libenigma_gui.a + +libenigma_gui_a_SOURCES = \ + ebutton.cpp echeckbox.cpp elabel.cpp emessage.cpp \ + enumber.cpp eprogress.cpp eskin.cpp listbox.cpp eskin_register.cpp \ + ewidget.cpp ewindow.cpp multipage.cpp actions.cpp guiactions.cpp \ + decoration.cpp statusbar.cpp combobox.cpp numberactions.cpp slider.cpp \ + textinput.cpp testpicture.cpp + diff --git a/lib/gui/actions.cpp b/lib/gui/actions.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/actions.h b/lib/gui/actions.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/combobox.cpp b/lib/gui/combobox.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/combobox.h b/lib/gui/combobox.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/decoration.cpp b/lib/gui/decoration.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/decoration.h b/lib/gui/decoration.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/ebutton.cpp b/lib/gui/ebutton.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/ebutton.h b/lib/gui/ebutton.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/echeckbox.cpp b/lib/gui/echeckbox.cpp new file mode 100644 index 0000000..c41721c --- /dev/null +++ b/lib/gui/echeckbox.cpp @@ -0,0 +1,138 @@ +#include + +#include +#include +#include +#include + +eCheckbox::eCheckbox(eWidget *parent, int checked, int takefocus, bool swapTxtPixmap, const char *deco) + :eButton(parent, 0, takefocus, deco), swapTxtPixmap(swapTxtPixmap) +{ + align=eTextPara::dirLeft; + ischecked = -1; + setCheck(checked); + CONNECT(selected, eCheckbox::sel); +} + +eCheckbox::~eCheckbox() +{ +} + +void eCheckbox::sel() +{ + setCheck(ischecked?0:1); + /*emit*/ checked(ischecked); +} + +void eCheckbox::gotFocus() +{ +#ifndef DISABLE_LCD + if (parent && parent->LCDElement) + { + LCDTmp = new eLabel(parent->LCDElement); + LCDTmp->hide(); + eSize s = parent->LCDElement->getSize(); + LCDTmp->move(ePoint(0,0)); + LCDTmp->resize(eSize(s.width(), s.height())); + ((eLabel*)LCDTmp)->setFlags(RS_WRAP); + ePtr pm; + eSkin::getActive()->queryImage(pm, ischecked?"eCheckboxLCD.checked":"eCheckboxLCD.unchecked"); + LCDTmp->setPixmap(pm); + ((eLabel*)LCDTmp)->pixmap_position=ePoint(0, (size.height()-15)/2); + ((eLabel*)LCDTmp)->text_position=ePoint(21, 0); + LCDTmp->setText(text); + LCDTmp->show(); + } +#endif + setForegroundColor(focusF, false); + setBackgroundColor(focusB); +// invalidate(); +} + +void eCheckbox::lostFocus() +{ +#ifndef DISABLE_LCD + if (LCDTmp) + { + delete LCDTmp; + LCDTmp = 0; + } +#endif + eButton::lostFocus(); +} + + +void eCheckbox::setCheck(int c) +{ + if (ischecked != -1 && ischecked == c) + return; + + ischecked=c; + + ePtr pixmap; + eSkin::getActive()->queryImage(pixmap, ischecked?"eCheckbox.checked":"eCheckbox.unchecked"); + setPixmap(pixmap); +#ifndef DISABLE_LCD + eSkin::getActive()->queryImage(pixmap, ischecked?"eCheckboxLCD.checked":"eCheckboxLCD.unchecked"); + if (LCDTmp) + LCDTmp->setPixmap(pixmap); +#endif +} + +int eCheckbox::setProperty(const eString &prop, const eString &value) +{ + if (prop=="swaptxtpixmap") + { + swapTxtPixmap = (value != "off"); + event( eWidgetEvent::changedSize ); + } + else + return eButton::setProperty(prop, value); + return 0; +} + +int eCheckbox::eventHandler(const eWidgetEvent &event) +{ + switch (event.type) + { + case eWidgetEvent::changedSize: + if (swapTxtPixmap) + { + text_position=ePoint(0,0); + eLabel::invalidate(); + validate(); + pixmap_position=ePoint( para->getBoundBox().right()+5, (size.height()-pixmap->y) / 2 ); + } + else + { + pixmap_position=ePoint(0, (size.height()-pixmap->y)/2); + text_position=ePoint((int)(pixmap->x*1.25), 0); + } + //return eButton::eventHandler(event); // changed Size must seen by eLabel... + break; + + default: + return eButton::eventHandler(event); + } + return 1; +} + +static eWidget *create_eCheckbox(eWidget *parent) +{ + return new eCheckbox(parent); +} + +class eCheckboxSkinInit +{ +public: + eCheckboxSkinInit() + { + eSkin::addWidgetCreator("eCheckbox", create_eCheckbox); + } + ~eCheckboxSkinInit() + { + eSkin::removeWidgetCreator("eCheckbox", create_eCheckbox); + } +}; + +eAutoInitP0 init_eCheckboxSkinInit(eAutoInitNumbers::guiobject, "eCheckbox"); diff --git a/lib/gui/echeckbox.h b/lib/gui/echeckbox.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/elabel.cpp b/lib/gui/elabel.cpp new file mode 100644 index 0000000..ae04eb4 --- /dev/null +++ b/lib/gui/elabel.cpp @@ -0,0 +1,253 @@ +#include + +#include +#include +#include +#include +#include +#include + +eLabel::eLabel(eWidget *parent, int flags, int takefocus, const char *deco ): + eDecoWidget(parent, takefocus, deco), blitFlags(0), flags(flags), + para(0), align( eTextPara::dirLeft ), shortcutPixmap(0) +{ +} + +eLabel::~eLabel() +{ + if (para) + { + para->destroy(); + para=0; + } +} + +void eLabel::setPixmapPosition( const ePoint &p ) +{ + pixmap_position = p; + invalidate(); +} + +void eLabel::validate( const eSize* s ) +{ + if (!para) + { + if (s) + para=new eTextPara( eRect(text_position.x(), text_position.y(), s->width() - text_position.x(), s->height() - text_position.y())); + else + para=new eTextPara( eRect(text_position.x(), text_position.y(), size.width() - text_position.x(), size.height() - text_position.y())); + + para->setFont(font); + para->renderString(text, flags); + para->realign(align); + } +} + +void eLabel::invalidate() +{ + if (para) + { + para->destroy(); + para=0; + } + if (isVisible()) + eDecoWidget::invalidate(); // we must redraw... +} + +void eLabel::setFlags(int flag) +{ + flags|=flag; + if (flag) + invalidate(); +} + +void eLabel::setBlitFlags( int flags ) +{ + blitFlags |= flags; +} + +void eLabel::removeFlags(int flag) +{ + flags &= ~flag; + if (flag) + invalidate(); +} + +void eLabel::setAlign(int align) +{ + this->align = align; + invalidate(); +} + +void eLabel::redrawWidget(gPainter *target, const eRect &rc) +{ +/* eDebug("decoStr = %s, text=%s, name=%s, %p left = %d, top = %d, width=%d, height = %d", strDeco?strDeco.c_str():"no", text?text.c_str():"no" , name?name.c_str():"no", this, this->getPosition().x(), this->getPosition().y(), this->getSize().width(), this->getSize().height() ); + eDebug("renderContext left = %d, top = %d, width = %d, height = %d", rc.left(), rc.top(), rc.width(), rc.height() );*/ + + target->clip( rc ); + eRect area=eRect(ePoint(0, 0), ePoint(width(), height())); +/* eDebug("area left = %d, top = %d, width = %d, height = %d", + area.left(), area.top(), + area.width(), area.height() );*/ + + if (deco_selected && have_focus) + { + deco_selected.drawDecoration(target, ePoint(width(), height())); + area=crect_selected; + } else if (deco) + { + deco.drawDecoration(target, ePoint(width(), height())); + area=crect; + } +/* eDebug("area left = %d, top = %d, width = %d, height = %d", + area.left(), area.top(), + area.width(), area.height() );*/ + + if (shortcutPixmap) + { + //area.setWidth(area.width()-area.height()); + area.setX(area.height()); + } + + if (text.length()) + { + if ( area.size().height() < size.height() || + area.size().width() < size.width() ) + { + // then deco is drawed + eSize s=area.size(); + validate( &s ); + } else + validate(); + + if (flags & flagVCenter) + yOffs = ( (area.height() - para->getBoundBox().height() ) / 2 + 0) - para->getBoundBox().top(); + else + yOffs = 0; + + eWidget *w; + if ((blitFlags & BF_ALPHATEST) && (transparentBackgroundColor >= 0)) + { + w=this; + target->setBackgroundColor(transparentBackgroundColor); + } else + { + w=getNonTransparentBackground(); + target->setBackgroundColor(w->getBackgroundColor()); + } + target->setFont(font); + target->renderPara(*para, ePoint( area.left(), area.top()+yOffs) ); + } + if (pixmap) + { +// eDebug("blit pixmap area left=%d, top=%d, right=%d, bottom=%d", rc.left(), rc.top(), rc.right(), rc.bottom() ); +// eDebug("pixmap_pos x = %d, y = %d, xsize=%d, ysize=%d", pixmap_position.x(), pixmap_position.y(), pixmap->x, pixmap->y ); + target->blit(pixmap, shortcutPixmap?pixmap_position+ePoint( area.left(), 0):pixmap_position, area, (blitFlags & BF_ALPHATEST) ? gPixmap::blitAlphaTest : 0); + } + if (shortcutPixmap) + target->blit(shortcutPixmap, + ePoint((area.height()-shortcutPixmap->x)/2, area.top()+(area.height()-shortcutPixmap->y)/2), + eRect(), + gPixmap::blitAlphaTest); + target->clippop(); +} + +int eLabel::eventHandler(const eWidgetEvent &event) +{ + switch (event.type) + { + case eWidgetEvent::changedFont: + case eWidgetEvent::changedText: + if (para) + { + para->destroy(); + para=0; + } + if ( have_focus && deco_selected ) + eDecoWidget::invalidate( crect_selected ); + else if ( deco ) + eDecoWidget::invalidate( crect ); + else + eDecoWidget::invalidate(); + break; + + case eWidgetEvent::changedSize: + invalidate(); + break; + + default: + return eDecoWidget::eventHandler(event); + break; + } + return 1; +} + +eSize eLabel::getExtend() +{ + validate(); + return eSize(para->getBoundBox().width()+(shortcutPixmap?shortcutPixmap->x*2:0), para->getBoundBox().height()); +} + +ePoint eLabel::getLeftTop() +{ + validate(); + return ePoint(para->getBoundBox().left(), para->getBoundBox().top()); +} + +int eLabel::setProperty(const eString &prop, const eString &value) +{ + if (prop=="wrap" && value == "on") + setFlags(RS_WRAP); + else if (prop=="alphatest" && value == "on") + { + transparentBackgroundColor=getBackgroundColor(); + setBackgroundColor(-1); + blitFlags |= BF_ALPHATEST; + } else if (prop=="align") + { + if (value=="left") + setAlign(eTextPara::dirLeft); + else if (value=="center") + setAlign(eTextPara::dirCenter); + else if (value=="right") + setAlign(eTextPara::dirRight); + else if (value=="block") + setAlign(eTextPara::dirBlock); + else + setAlign(eTextPara::dirLeft); + } + else if (prop=="vcenter") + setFlags( flagVCenter ); + else if (prop == "shortcut") + { + setShortcutPixmap(value); + return eWidget::setProperty(prop, value); + } else + return eDecoWidget::setProperty(prop, value); + return 0; +} + +void eLabel::setShortcutPixmap(const eString &shortcut) +{ + eSkin::getActive()->queryImage(shortcutPixmap, "shortcut." + shortcut); +} + +static eWidget *create_eLabel(eWidget *parent) +{ + return new eLabel(parent); +} + +class eLabelSkinInit +{ +public: + eLabelSkinInit() + { + eSkin::addWidgetCreator("eLabel", create_eLabel); + } + ~eLabelSkinInit() + { + eSkin::removeWidgetCreator("eLabel", create_eLabel); + } +}; + +eAutoInitP0 init_eLabelSkinInit(eAutoInitNumbers::guiobject, "eLabel"); diff --git a/lib/gui/elabel.h b/lib/gui/elabel.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/emessage.cpp b/lib/gui/emessage.cpp new file mode 100644 index 0000000..388eb14 --- /dev/null +++ b/lib/gui/emessage.cpp @@ -0,0 +1,163 @@ +#include + +#include +#include +#include +#include +#include + +eMessageBox::eMessageBox(eString message, eString caption, int flags, int def): eWindow(0), icon(0) +{ + setText(caption); + int fontsize=eSkin::getActive()->queryValue("fontsize", 20); + int posx = eSkin::getActive()->queryValue("eMessageBox.pos.x", 100); + int posy = eSkin::getActive()->queryValue("eMessageBox.pos.y", 70); + move(ePoint(posx, posy)); + resize(eSize(450, 430)); + + if ( flags > 15 ) // we have to draw an icon + { + ePtr pm; + switch ( flags & ~15 ) + { + case iconInfo: + eSkin::getActive()->queryImage(pm, "icon_info" ); + break; + case iconQuestion: + eSkin::getActive()->queryImage(pm, "icon_question" ); + break; + case iconWarning: + eSkin::getActive()->queryImage(pm, "icon_warning" ); + break; + case iconError: + eSkin::getActive()->queryImage(pm, "icon_error" ); + break; + } + if (pm) + { + icon = new eLabel(this); + icon->setPixmap( pm ); + icon->pixmap_position=ePoint(0,0); + icon->resize( eSize(pm->x, pm->y) ); + icon->setBlitFlags( BF_ALPHATEST ); + } + } + + text=new eLabel(this); + text->setText(message); + text->resize( eSize( clientrect.width(), clientrect.height() )); + text->setFlags( RS_WRAP|eLabel::flagVCenter ); + eSize txtSize=text->getExtend(); + txtSize+=eSize(8,4); // the given Size of the Text is okay... but the renderer sucks... + text->resize(txtSize); + + // here the two labels ( icon, text) has the correct size.. now we calc the border + + eSize ext; + + if ( icon ) + { + if ( icon->getSize().height() > text->getSize().height() ) + { + eDebug("icon is higher"); + eSize s = icon->getSize(); + icon->move( ePoint( 20, 20 ) ); + text->move( ePoint( 20 + s.width() + 20, icon->getPosition().y() + s.height() / 2 - txtSize.height() / 2 ) ); + ext.setHeight( icon->getPosition().y() + icon->getSize().height() + 20 ); + } + else + { + eDebug("text is higher"); + text->move( ePoint( 20 + icon->getSize().width() + 20 , 20 ) ); + icon->move( ePoint( 20, text->getPosition().y() + text->getSize().height() / 2 - icon->getSize().height() / 2 ) ); + ext.setHeight( text->getPosition().y() + text->getSize().height() + 20 ); + } + ext.setWidth( text->getPosition().x() + text->getSize().width() + 20 ); + } + else + { + text->move( ePoint(20, 20) ); + ext.setWidth( text->getPosition().x() + text->getSize().width()+20 ); + ext.setHeight( text->getPosition().y() + text->getSize().height() + 20 ); + } + + if (ext.width()<150) + ext.setWidth(150); + + int xpos=20; + + if ( flags & 15) + { + for (int i=btOK; iresize(eSize(size.width(), fontsize+4)); + const char *t="", *shortcut=""; + switch (i) + { + case btOK: t=_("OK"); shortcut="green"; CONNECT(b->selected, eMessageBox::pressedOK); break; + case btCancel: t=_("Cancel"); shortcut="red"; CONNECT(b->selected, eMessageBox::pressedCancel); break; + case btYes: t=_("Yes"); shortcut="green"; CONNECT(b->selected, eMessageBox::pressedYes); break; + case btNo: t=_("No"); shortcut="red"; CONNECT(b->selected, eMessageBox::pressedNo); break; + } + b->setProperty("shortcut", shortcut); + b->setText(t); + eSize bSize=b->getExtend(); + bSize.setWidth( bSize.width() * 2 ); + bSize.setHeight( fontsize + 4 + 10 ); + b->resize(bSize); + b->move( ePoint( xpos, ext.height() ) ); + + b->loadDeco(); + + if (def == i) + setFocus(b); + + xpos += bSize.width()+20; + if ( xpos+20 > ext.width() ) + cresize( eSize( xpos+20, ext.height() + bSize.height() + 20 ) ); + else + cresize( eSize( ext.width(), ext.height() + bSize.height() + 20 ) ); + } + } + else + cresize( ext ); + zOrderRaise(); +} + +eMessageBox::~eMessageBox() +{ +} + +void eMessageBox::pressedOK() +{ + if ( in_loop ) + close(btOK); + else + hide(); +} + +void eMessageBox::pressedCancel() +{ + if ( in_loop ) + close(btCancel); + else + hide(); +} + +void eMessageBox::pressedYes() +{ + if ( in_loop ) + close(btYes); + else + hide(); +} + +void eMessageBox::pressedNo() +{ + if ( in_loop ) + close(btNo); + else + hide(); +} diff --git a/lib/gui/emessage.h b/lib/gui/emessage.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/enumber.cpp b/lib/gui/enumber.cpp new file mode 100644 index 0000000..c03ec8f --- /dev/null +++ b/lib/gui/enumber.cpp @@ -0,0 +1,447 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +void eNumber::unpack(__u32 l, int *t) +{ + for (int i=0; i<4; i++) + *t++=(l>>((3-i)*8))&0xFF; +} + +void eNumber::pack(__u32 &l, int *t) +{ + l=0; + for (int i=0; i<4; i++) + l|=(*t++)<<((3-i)*8); +} + +eRect eNumber::getNumberRect(int n) +{ + if (deco_selected && have_focus) + return eRect( deco_selected.borderLeft + n * space_selected, deco_selected.borderTop, dspace, crect_selected.height() ); + else if (deco) + return eRect( deco.borderLeft + n * dspace, deco.borderTop, dspace, crect.height() ); + else + return eRect( n * dspace, 0, dspace, height() ); +} + +void eNumber::redrawNumber(gPainter *p, int n, const eRect &area) +{ + eRect pos = getNumberRect(n); + + if (!area.contains(pos) ) + return; + + p->setForegroundColor((have_focus && n==active)?cursorB:normalB); + p->fill(pos); + p->setFont(font); + + eString t; + if (flags & flagFillWithZeros || ( (flags & flagFixedNum) && n )) + { + eString s = "%0"+eString().setNum(maxdigits)+(base==10?"d":"X"); + const char* p = s.c_str(); + char* tmp = new char[10]; + strcpy( tmp, p ); + t.sprintf(tmp, number[n]); + delete [] tmp; + } + else + { + if (flags&flagHideInput) + t="*"; + else if (base==10) + t.sprintf("%d", number[n]); + else if (base==0x10) + t.sprintf("%X", number[n]); + } + + if (!n && flags & flagPosNeg && neg) + t="-"+t; + + if (n && (flags & flagTime)) + t=":"+t; + + else if (n && ( (flags & flagDrawPoints) || (flags & flagFixedNum)) ) + t="."+t; + + p->setForegroundColor((have_focus && n==active)?cursorF:normalF); + p->setBackgroundColor((have_focus && n==active)?cursorB:normalB); + + p->clip( pos ); + if (!n && len==2 && ((flags & flagFixedNum) || (flags & flagTime)) ) // first element... + { + eTextPara *para = new eTextPara( pos ); + para->setFont( font ); + para->renderString( t ); + para->realign( eTextPara::dirRight ); + p->renderPara( *para ); + para->destroy(); + } + else + p->renderText(pos, t); + + p->clippop(); +} + +double eNumber::getFixedNum() +{ + if (flags & flagFixedNum) + { + if (flags&flagPosNeg && neg) + { + double d = -((double)number[0]+(double)number[1]/1000); + eDebug("getFixedNum %lf", d); + return d; + } + else + { + float d = (double)number[0]+(double)number[1]/1000; + eDebug("getFixedNum %lf", d); + return d; + } + } + else + return 0; +} + +void eNumber::setFixedNum(double d) +{ + eDebug("setFixedNum %lf", d); + if (flags & flagPosNeg) + neg=d<0; + else + neg=0; + + d=fabs(d); + + if (flags & flagFixedNum) + { + number[0]=(int)d; + number[1]=(int)round(( ( d - number[0] ) * 1000) ); + } + else + eDebug("eNumber bug... the Number %s is not a fixed Point number", name.c_str()); +} + +void eNumber::redrawWidget(gPainter *p, const eRect &area) +{ + for (int i=0; ieventHandler(event); +#endif + switch (event.type) + { + case eWidgetEvent::changedSize: + if (deco) + dspace = (crect.width()) / len; + else + dspace = (size.width()) / len; + if (deco_selected) + space_selected = (crect_selected.width()) / len; + break; + case eWidgetEvent::evtAction: + if ( len > 1 && event.action == &i_cursorActions->left) + { + int oldac=active; + active--; + invalidate(getNumberRect(oldac)); + if (active<0) + active=len-1; + if (active!=oldac) + invalidate(getNumberRect(active)); + digit=0; + } else if ( len > 1 && (event.action == &i_cursorActions->right) || (event.action == &i_cursorActions->ok)) + { + int oldac=active; + active++; + invalidate(getNumberRect(oldac)); + if (active>=len) + { + if (event.action == &i_cursorActions->ok) + /*emit*/ selected(number); + active=0; + } + if (active!=oldac) + invalidate(getNumberRect(active)); + digit=0; + } else + break; + return 1; + default: + break; + } + return eDecoWidget::eventHandler(event); +} + +// isactive is the digit (always in the first field ) +// that ist active after get the first focus ! + +eNumber::eNumber(eWidget *parent, int _len, int _min, int _max, int _maxdigits, int *init, int isactive, eWidget* descr, int grabfocus, const char *deco) + :eDecoWidget(parent, grabfocus, deco ), + active(0), + cursorB(eSkin::getActive()->queryScheme("global.selected.background")), + cursorF(eSkin::getActive()->queryScheme("global.selected.foreground")), + normalB(eSkin::getActive()->queryScheme("global.normal.background")), + normalF(eSkin::getActive()->queryScheme("global.normal.foreground")), + have_focus(0), digit(isactive), isactive(isactive), flags(0), descr(descr), tmpDescr(0), + neg(false) +{ + setNumberOfFields(_len); + setLimits(_min, _max); + setMaximumDigits(_maxdigits); + setBase(10); + for (int i=0; init && imap); +} + +eNumber::~eNumber() +{ +} + +int eNumber::keyDown(int key) +{ +#ifndef DISABLE_LCD + if (LCDTmp) + ((eNumber*) LCDTmp)->keyDown(key); +#endif + switch (key) + { + case eRCInput::RC_0 ... eRCInput::RC_9: + { + int nn=(digit!=0)?number[active]*10:0; + nn+=key-eRCInput::RC_0; + if (flags & flagTime) + { + if ( active ) + max = 59; + else + max = 23; + } + else if (flags & flagFixedNum) + { + if (active) + max=999; + else + max=oldmax; + } + if (nn>=min && nn<=max) + { + number[active]=nn; + invalidate(getNumberRect(active)); + digit++; + if ((digit>=maxdigits) || (nn==0)) + { + active++; + invalidate(getNumberRect(active-1)); + digit=0; + /*emit*/ numberChanged(); + if (active>=len) + { + /*emit*/ selected(number); + active=0; + } + else + invalidate(getNumberRect(active)); + } + } + break; + + break; + } + case eRCInput::RC_PLUS: + if (flags & flagPosNeg && neg ) + { + neg=false; + invalidate(getNumberRect(0)); + } + break; + + case eRCInput::RC_MINUS: + if (flags & flagPosNeg && !neg ) + { + neg=true; + invalidate(getNumberRect(0)); + } + default: + return 0; + } + return 1; +} + +void eNumber::gotFocus() +{ + have_focus++; + digit=isactive; + + if (deco && deco_selected) + invalidate(); + else + invalidate(getNumberRect(active)); + +#ifndef DISABLE_LCD + if (parent && parent->LCDElement) // detect if LCD Avail + { + LCDTmp = new eNumber(parent->LCDElement, len, min, max, maxdigits, &(number[0]), isactive, 0, 0); + LCDTmp->hide(); + ((eNumber*)LCDTmp)->setFlags(flags); + eSize s = parent->LCDElement->getSize(); + + if (descr) + { + LCDTmp->move(ePoint(0,s.height()/2)); + LCDTmp->resize(eSize(s.width(), s.height()/2)); + tmpDescr = new eLabel(parent->LCDElement); + tmpDescr->hide(); + tmpDescr->move(ePoint(0,0)); + tmpDescr->resize(eSize(s.width(), s.height()/2)); + tmpDescr->setText(descr->getText()); + tmpDescr->show(); + } + else + { + LCDTmp->resize(s); + LCDTmp->move(ePoint(0,0)); + } + ((eNumber*)LCDTmp)->digit=digit; + ((eNumber*)LCDTmp)->active=active; + ((eNumber*)LCDTmp)->normalF=255; + ((eNumber*)LCDTmp)->normalB=0; + ((eNumber*)LCDTmp)->cursorF=0; + ((eNumber*)LCDTmp)->cursorB=255; + ((eNumber*)LCDTmp)->have_focus=1; + LCDTmp->show(); + } + #endif //DISABLE_LCD +} + +void eNumber::lostFocus() +{ +#ifndef DISABLE_LCD + if (LCDTmp) + { + delete LCDTmp; + LCDTmp=0; + if (tmpDescr) + { + delete tmpDescr; + tmpDescr=0; + } + } +#endif + have_focus--; + + if (deco && deco_selected) + invalidate(); + else + invalidate(getNumberRect(active)); + isactive=0; +} + +void eNumber::setNumber(int f, int n) +{ + if (flags & flagPosNeg) + { + if(!f && n<0) + neg=true; + else + neg=false; + } + else + neg=false; + + if ((f>=0) && (f 16) + n=16; + maxdigits=n; + if (digit >= maxdigits) + digit=0; +} + +void eNumber::setFlags(int _flags) +{ + if (flags&flagFixedNum) + len=2; + + flags=_flags; +} + +void eNumber::setBase(int _base) +{ + base=_base; +} + +void eNumber::setNumber(int n) +{ + if ( flags&flagPosNeg ) + neg = n < 0; + else + neg=0; + + if( len == 1 ) + number[0]=abs(n); + else + for (int i=len-1; i>=0; --i) + { + number[i]=n%base; + n/=base; + } + invalidateNum(); +} + +int eNumber::getNumber() +{ + int n=0; + for (int i=0; i +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +std::map< eString,tWidgetCreator > eSkin::widget_creator; + +eSkin *eSkin::active; + +eNamedColor *eSkin::searchColor(const eString &name) +{ + for (std::list::iterator i(colors.begin()); i != colors.end(); ++i) + { + if (!i->name.compare(name)) + return &*i; + } + return 0; +} + +void eSkin::clear() +{ +} + +void eSkin::addWidgetCreator(const eString &name, tWidgetCreator creator) +{ + widget_creator[name] = creator; // add this tWidgetCreator to map... if exist.. overwrite +} + +void eSkin::removeWidgetCreator(const eString &name, tWidgetCreator creator) +{ + widget_creator.erase(name); +} + +int eSkin::parseColor(const eString &name, const char* color, gRGB &col) +{ + if (color[0]=='#') + { + unsigned long vcol=0; + if (sscanf(color+1, "%lx", &vcol)!=1) + { + eDebug("invalid color named \"%s\" (value: %s)", name.c_str(), color+1); + return -1; + } + col.r=(vcol>>16)&0xFF; + col.g=(vcol>>8)&0xFF; + col.b=vcol&0xFF; + col.a=(vcol>>24)&0xFF; + } else + { + eNamedColor *n=searchColor(color); + if (!n) + { + eDebug("invalid color named \"%s\" (alias to: \"%s\")", name.c_str(), color); + return -1; + } + col=n->value; + } + return 0; +} + +int eSkin::parseColors(XMLTreeNode *xcolors) +{ + XMLTreeNode *node; + + std::list::iterator newcolors=colors.end(); + + for (node=xcolors->GetChild(); node; node=node->GetNext()) + { + if (strcmp(node->GetType(), "color")) + { + eDebug("junk found in colorsection (%s)", node->GetType()); + continue; + } + + const char *name=node->GetAttributeValue("name"), *color=node->GetAttributeValue("color"), *end=node->GetAttributeValue("end"); + + if (!color || !name) + { + eDebug("no color/name specified"); + continue; + } + + eNamedColor col; + col.name=name; + + const char *size=node->GetAttributeValue("size"); + + if (size) + col.size=atoi(size); + else + col.size=0; + + if (!col.size) + col.size=1; + + if ((col.size>1) && (!end)) + { + eDebug("no end specified in \"%s\" but is gradient", name); + continue; + } + + if (parseColor(name, color, col.value)) + continue; + + if (end && parseColor(name, end, col.end)) + continue; + + colors.push_back(col); + if (newcolors == colors.end()) + --newcolors; + } + + for (std::list::iterator i(newcolors); i != colors.end(); ++i) + { + eNamedColor &col=*i; + int d; + for (d=0; dmaxcolors) || colorused[d+s]) + break; + if (s==col.size) + break; + } + if (d==maxcolors) + continue; + col.index=gColor(d); + for (int s=0; sGetChild(); node; node=node->GetNext()) + { + if (strcmp(node->GetType(), "map")) + { + eDebug("illegal scheme entry found: %s", node->GetType()); + continue; + } + char *name=node->GetAttributeValue("name"), *color=node->GetAttributeValue("color"); + if (!name || !color) + { + eDebug("no name or color specified in colorscheme"); + continue; + } + eString base=color; + int offset=0, p; + if ((p=base.find('+'))!=-1) + { + offset=atoi(base.mid(p).c_str()); + base=base.left(p); + } + eNamedColor *n=searchColor(base); + if (!n) + { + eDebug("illegal color \"%s\" specified", base.c_str()); + continue; + } + scheme[name] = gColor(n->index+offset); + } + return 0; +} + +int eSkin::parseFontAlias(XMLTreeNode *xscheme) +{ + XMLTreeNode *node; + for (node=xscheme->GetChild(); node; node=node->GetNext()) + { + if (strcmp(node->GetType(), "map")) + { + eDebug("illegal fontalias entry found: %s", node->GetType()); + continue; + } + char *font=node->GetAttributeValue("font"), + *name=node->GetAttributeValue("name"), + *size=node->GetAttributeValue("size"); + + if (!name || !font || !size) + { + eDebug("no name, alias or size spezified in fontaliase"); + continue; + } + + std::map::iterator it = fontAlias.find(name); + if (it != fontAlias.end()) + continue; + + std::map::iterator i = fonts.find(font); + if (i == fonts.end()) + { + eDebug("font %s not found, skip make alias %s", font, name); + continue; + } + fontAlias[name]=gFont(i->second, atoi(size)); + } + return 0; +} + +int eSkin::parseImages(XMLTreeNode *inode) +{ + char *abasepath=inode->GetAttributeValue("basepath"); + if (!abasepath) + abasepath=""; + eString basepath=eString("/enigma/pictures/"); + if (abasepath[0] == '/') // allow absolute paths + basepath=""; + basepath+=abasepath; + if (basepath[basepath.length()-1]!='/') + basepath+="/"; + + for (XMLTreeNode *node=inode->GetChild(); node; node=node->GetNext()) + { + if (strcmp(node->GetType(), "img")) + { + eDebug("illegal image entry found: %s", node->GetType()); + continue; + } + const char *name=node->GetAttributeValue("name"); + if (!name) + { + eDebug("illegal entry: no name"); + continue; + } + const char *src=node->GetAttributeValue("src"); + if (!src) + { + eDebug("image/img=\"%s\" no src given", name); + continue; + } + std::map >::iterator it = images.find(name); + if (it != images.end()) + { +// eDebug("Image with name %s already loaded, skip %s", name, src); + continue; + } + ePtr image=0; + eString filename=basepath + eString(src); + if (abasepath[0] != '/') + { + // search first in CONFIGDIR + image=loadPNG((eString(CONFIGDIR)+filename).c_str()); + if (!image) + image=loadPNG((eString(DATADIR)+filename).c_str()); + } + else // abs path + image=loadPNG(filename.c_str()); + + if (!image) + { + eDebug("image/img=\"%s\" - %s: file not found", name, filename.c_str()); + continue; + } + + if (paldummy && !node->GetAttributeValue("nomerge")) + { + gPixmapDC mydc(image); + gPainter p(mydc); + p.mergePalette(paldummy); + } + images[name] = image; + } + return 0; +} + +int eSkin::parseImageAlias(XMLTreeNode *xvalues) +{ + for (XMLTreeNode *node=xvalues->GetChild(); node; node=node->GetNext()) + { + if (strcmp(node->GetType(), "map")) + { + eDebug("illegal values entry %s", node->GetType()); + continue; + } + const char *name=node->GetAttributeValue("name"), + *img=node->GetAttributeValue("img"); + if (!name || !img) + { + eDebug("map entry has no name or img"); + continue; + } + std::map::iterator it = imageAlias.find(name); + if (it != imageAlias.end()) + { + eDebug("imagealias %s does exist, skip make alias for image %s", name, img); + continue; + } + std::map >::iterator i = images.find(img); + if (i == images.end()) + { + eDebug("image %s not found, skip make alias %s", img , name); + continue; + } + imageAlias[name]=img; + } + return 0; +} + +int eSkin::parseFonts(XMLTreeNode *xfonts) +{ + const char *abasepath=xfonts->GetAttributeValue("basepath"); + eString basepath=abasepath?abasepath:FONTDIR; + + if (basepath.length()) + if (basepath[basepath.length()-1]!='/') + basepath+="/"; + + for (XMLTreeNode *node=xfonts->GetChild(); node; node=node->GetNext()) + { + if (strcmp(node->GetType(), "font")) + { + eDebug("illegal fonts entry %s", node->GetType()); + continue; + } + const char *file=node->GetAttributeValue("file"); + if (!file) + { + eDebug("fonts entry has no file"); + continue; + } + const char *name=node->GetAttributeValue("name"); + if (!name) + { + eDebug("fonts entry has no name use filename %s as name", file); + name = file; + } + std::map::iterator it = fonts.find(name); + const char *ascale=node->GetAttributeValue("scale"); + int scale=0; + if (ascale) + scale=atoi(ascale); + if (!scale) + scale=100; + if (it != fonts.end()) + { + eDebug("Font with name %s already loaded, skip %s", name, file); + continue; + } + fonts[name]=fontRenderClass::getInstance()->AddFont(basepath+eString(file), name, scale); + if (node->GetAttributeValue("replacement")) + eTextPara::setReplacementFont(name); + } + return 0; +} + +int eSkin::parseValues(XMLTreeNode *xvalues) +{ + for (XMLTreeNode *node=xvalues->GetChild(); node; node=node->GetNext()) + { + if (strcmp(node->GetType(), "value")) + { + eDebug("illegal values entry %s", node->GetType()); + continue; + } + const char *name=node->GetAttributeValue("name"); + if (!name) + { + eDebug("values entry has no name"); + continue; + } + const char *value=node->GetAttributeValue("value"); + if (!value) + { + eDebug("values entry has no value"); + continue; + } + std::map::iterator it = values.find(name); + if (it != values.end()) + { + eDebug("value %s does exist, skip make value %s=%i", name, value); + continue; + } + values[name]=atoi(value); + } + return 0; +} + +gDC *eSkin::getDCbyName(const char *name) +{ + gPixmapDC *dc=0; + if (!strcmp(name, "fb")) + dc=gFBDC::getInstance(); +#ifndef DISABLE_LCD + else if (!strcmp(name, "lcd")) + dc=gLCDDC::getInstance(); +#endif + return dc; +} + +int eSkin::build(eWidget *widget, XMLTreeNode *node) +{ +// eDebug("building a %s", node->GetType()); +/* if (widget->getType() != node->GetType()) + return -1;*/ + + for (XMLAttribute *attrib=node->GetAttributes(); attrib; attrib=attrib->GetNext()) + { +// eDebug("setting %s := %s", attrib->GetName(), attrib->GetValue()); + if (widget->setProperty(attrib->GetName(), attrib->GetValue())) + { + eDebug("failed"); + return -1; + } + } + for (XMLTreeNode *c=node->GetChild(); c; c=c->GetNext()) + { + eWidget *w=0; + + const char *name=c->GetAttributeValue("name"); + + if (name) + w=widget->search(name); + + if (!w) + { + std::map< eString, tWidgetCreator >::iterator it = widget_creator.find(c->GetType()); + + if ( it == widget_creator.end() ) + { + eWarning("widget class %s does not exist", c->GetType()); + return -ENOENT; + } + w = (it->second)(widget); + } + if (!w) + { + // eDebug("failed."); + return -EINVAL; + } + w->zOrderRaise(); + int err; + if ((err=build(w, c))) + { + return err; + } + } + return 0; +} + +eSkin::eSkin() +{ + maxcolors=256; + + palette=new gRGB[maxcolors]; + + memset(palette, 0, sizeof(gRGB)*maxcolors); + paldummy=new gImage(eSize(1, 1), 8); + paldummy->clut.data=palette; + paldummy->clut.colors=maxcolors; + + colorused=new int[maxcolors]; + memset(colorused, 0, maxcolors*sizeof(int)); +} + +eSkin::~eSkin() +{ + if (active==this) + active=0; + + clear(); + + delete colorused; + + for (std::map >::iterator it(images.begin()); it != images.end(); it++) + delete it->second; + + if (paldummy) + delete paldummy; +} + +int eSkin::load(const char *filename) +{ + eDebug("loading skin: %s", filename); + FILE *in=fopen(filename, "rt"); + if (!in) + return -1; + + parsers.push_front(new XMLTreeParser("ISO-8859-1")); + XMLTreeParser &parser=*parsers.first(); + char buf[2048]; + + int done; + do + { + unsigned int len=fread(buf, 1, sizeof(buf), in); + done=lenGetType(), "eskin")) + { + eDebug("not an eskin"); + return -1; + } + + return 0; +} + +void eSkin::parseSkins() +{ + for (ePtrList::reverse_iterator it(parsers); it != parsers.rend(); it++) + { + XMLTreeNode *node=it->RootNode(); + + for (node=node->GetChild(); node; node=node->GetNext()) + if (!strcmp(node->GetType(), "colors")) + parseColors(node); + } + + for (ePtrList::reverse_iterator it(parsers); it != parsers.rend(); it++) + { + XMLTreeNode *node=it->RootNode(); + + for (node=node->GetChild(); node; node=node->GetNext()) + if (!strcmp(node->GetType(), "colorscheme")) + parseScheme(node); + } + + for (ePtrList::iterator it(parsers); it != parsers.end(); it++) + { + XMLTreeNode *node=it->RootNode(); + + for (node=node->GetChild(); node; node=node->GetNext()) + if (!strcmp(node->GetType(), "fonts")) + parseFonts(node); + } + + for (ePtrList::iterator it(parsers); it != parsers.end(); it++) + { + XMLTreeNode *node=it->RootNode(); + + for (node=node->GetChild(); node; node=node->GetNext()) + if (!strcmp(node->GetType(), "fontalias")) + parseFontAlias(node); + } + + for (ePtrList::iterator it(parsers); it != parsers.end(); it++) + { + XMLTreeNode *node=it->RootNode(); + + for (node=node->GetChild(); node; node=node->GetNext()) + if (!strcmp(node->GetType(), "images")) + parseImages(node); + + } + + for (ePtrList::iterator it(parsers); it != parsers.end(); it++) + { + XMLTreeNode *node=it->RootNode(); + + for (node=node->GetChild(); node; node=node->GetNext()) + if (!strcmp(node->GetType(), "imagealias")) + parseImageAlias(node); + + } + + for (ePtrList::iterator it(parsers); it != parsers.end(); it++) + { + XMLTreeNode *node=it->RootNode(); + + for (node=node->GetChild(); node; node=node->GetNext()) + if (!strcmp(node->GetType(), "values")) + parseValues(node); + } +} + + +int eSkin::build(eWidget *widget, const char *name) +{ + for (parserList::iterator i(parsers.begin()); i!=parsers.end(); ++i) + { + XMLTreeNode *node=i->RootNode(); + node=node->GetChild(); + while (node) + { + if (!strcmp(node->GetType(), "object")) + { + const char *oname=node->GetAttributeValue("name"); + if (!std::strcmp(name, oname)) + { + node=node->GetChild(); + return build(widget, node); + } + } + node=node->GetNext(); + } + } + eDebug("didn't found it"); + return -ENOENT; +} + +void eSkin::setPalette(gPixmapDC *pal) +{ + if (palette) + { + gPainter p(*pal); + p.setPalette(palette, 0, 256); + } +} + +eSkin *eSkin::getActive() +{ + if (!active) + eFatal("no active skin"); + return active; +} + +void eSkin::makeActive() +{ + active=this; +} + +gColor eSkin::queryScheme(const eString& name) const +{ + eString base=name; + int offset=0, p; + if ((p=base.find('+'))!=-1) + { + offset=atoi(base.mid(p).c_str()); + base=base.left(p); + } + + std::map::const_iterator it = scheme.find(base); + + if (it != scheme.end()) + return it->second + offset; + +// eWarning("%s does not exist", name.c_str()); + + return gColor(0); +} + +RESULT eSkin::queryImage(ePtr &ptr, const eString& name) const +{ + eString img; + + std::map::const_iterator i = imageAlias.find(name); + + if (i != imageAlias.end()) + img = i->second; + else + img = name; + + std::map >::const_iterator it = images.find(img); + + if (it != images.end()) + ptr = it->second; + + return 0; +} + +int eSkin::queryValue(const eString& name, int d) const +{ + std::map::const_iterator it = values.find(name); + + if (it != values.end()) + return it->second; + + return d; +} + +gColor eSkin::queryColor(const eString& name) +{ + char *end; + + int numcol=strtol(name.c_str(), &end, 10); + + if (!*end) + return gColor(numcol); + + eString base=name; + int offset=0, p; + if ((p=base.find('+'))!=-1) + { + offset=atoi(base.mid(p).c_str()); + base=base.left(p); + } + + eNamedColor *col=searchColor(base); + + if (!col) + { + return queryScheme(name); + } else + return col->index + offset; +} + +gFont eSkin::queryFont(const eString& name) +{ + std::map::iterator it = fontAlias.find(name); // check if name is a font alias + + if ( it != fontAlias.end() ) // font alias found + return it->second; + + eString family; + int size=0; + + unsigned int sem = name.rfind(';'); // check if exist ';' in name + if (sem != eString::npos) // then exist + { + family=name.left(sem); + size = atoi( name.mid(sem+1).c_str() ); + if (size<=0) + size=16; + } + + std::map::iterator i = fonts.find(family); // check if family is a font name + if ( i != fonts.end() ) // font exist + return gFont(i->second, size); + + for (i = fonts.begin() ; i != fonts.end(); i++) // as last check if family name is a complete font Face + if ( i->second == family) + return gFont(i->second, size); + + eFatal("Font %s does not exist", name.c_str() ); // halt Programm now... Font does not exist + + return gFont(); +} diff --git a/lib/gui/eskin.h b/lib/gui/eskin.h new file mode 100644 index 0000000..0a6bd35 --- /dev/null +++ b/lib/gui/eskin.h @@ -0,0 +1,87 @@ +#ifndef __eskin_h +#define __eskin_h + +#include +#include +#include + +#include +#include +#include + +class eWidget; +class gPixmap; +typedef eWidget *(*tWidgetCreator)(eWidget *parent); + +struct eNamedColor +{ + eString name; + gRGB value, end; + int index; + int size; +}; + +class eSkin +{ + typedef ePtrList parserList; + parserList parsers; + void clear(); + + int parseColor(const eString& name, const char *color, gRGB &col); + int parseColors(XMLTreeNode *colors); + int parseScheme(XMLTreeNode *scheme); + int parseImages(XMLTreeNode *images); + int parseImageAlias(XMLTreeNode *images); + int parseValues(XMLTreeNode *values); + int parseFonts(XMLTreeNode *fonts); + int parseFontAlias(XMLTreeNode *fonts); + + gDC *getDCbyName(const char *name); + + gRGB *palette; + int maxcolors; + gImage *paldummy; + int *colorused; + + static std::map< eString, tWidgetCreator > widget_creator; + int build(eWidget *widget, XMLTreeNode *rootwidget); + + std::list colors; + std::map scheme; + std::map > images; + std::map values; + std::map fonts; + std::map fontAlias; + std::map imageAlias; + + eNamedColor *searchColor(const eString &name); + + static eSkin *active; +public: + eSkin(); + ~eSkin(); + + static void addWidgetCreator(const eString &name, tWidgetCreator creator); + static void removeWidgetCreator(const eString &name, tWidgetCreator creator); + + int load(const char *filename); + void parseSkins(); + + int build(eWidget *widget, const char *name); + void setPalette(gPixmapDC *pal); + + gColor queryColor(const eString &name); + gColor queryScheme(const eString &name) const; + RESULT queryImage(ePtr &pixmap, const eString &name) const; + int queryValue(const eString &name, int d) const; + gFont queryFont(const eString &name); + + void makeActive(); + + static eSkin *getActive(); +}; + +#define ASSIGN(v, t, n) \ + v =(t*)search(n); if (! v ) { eWarning("skin has undefined element: %s", n); v=new t(this); } + +#endif diff --git a/lib/gui/eskin_register.cpp b/lib/gui/eskin_register.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/eskin_register.h b/lib/gui/eskin_register.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/ewidget.cpp b/lib/gui/ewidget.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/ewidget.h b/lib/gui/ewidget.h new file mode 100644 index 0000000..3a1199a --- /dev/null +++ b/lib/gui/ewidget.h @@ -0,0 +1,498 @@ +#ifndef __ewidget_h +#define __ewidget_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class eWidgetEvent +{ +public: + enum eventType + { + evtKey, + willShow, willHide, + execBegin, execDone, + gotFocus, lostFocus, + + changedText, changedFont, changedForegroundColor, changedBackgroundColor, + changedSize, changedPosition, changedPixmap, childChangedHelpText, + + evtAction, evtShortcut + } type; + union + { + int parameter; + const eAction *action; + const eRCKey *key; + }; + eWidgetEvent(eventType type, int parameter=0): type(type), parameter(parameter) { } + eWidgetEvent(eventType type, const eAction *action): type(type), action(action) { } + eWidgetEvent(eventType type, const eRCKey &key): type(type), key(&key) { } + + /** + * \brief Event should be delivered to the focused widget. + * + * \return true if the event should be delivered to the focused widget instead of the widget itself. + */ + int toFocus() const + { + switch (type) + { + case evtKey: + return 1; + default: + return 0; + } + } +}; + +/** \brief The main widget class. All widgets inherit this class. + * eWidget handles focus management. + */ +class eWidget: public Object +{ + enum + { + /// Widget was shown with show() or implicit show() + stateShow=1, + /// Widget is visible on screen. Implies stateShow. + stateVisible=2 + }; + +public: + /** + * \brief Exits a (model) widget. + * + * Quit the local event loop, thus returning the control to the function which called \a exec. + * \sa eWidget::accept + * \sa eWidget::reject + */ + void close(int result); + + /** + * \brief Closes with a returncode of 0 (success). + * + * Synonym to \a close(0);. Useful to use as a slot. + * \sa eWidget::close + */ + void accept(); + + /** + * \brief Closes with a returncode of -1 (failure). + * + * Synonym to \a close(-1);. Useful to use as a slot. + * \sa eWidget::close + */ + void reject(); + /** + * \brief Signal is send, when the focus Changed + * + * used from a existing statusbar. + * \sa eWidget::focusChanged + */ + Signal1 focusChanged; + static Signal2< void, ePtrList*, int > showHelp; +protected: + ePtrList actionHelpList; + int helpID; + ePtrList childlist; + static eWidget *root; + eWidget *parent; + eString name; + eString helptext; + ePoint position; + ePoint absPosition; + eSize size; + eRect clientrect; + eRect clientclip; + + eAction *shortcut; + eWidget *shortcutFocusWidget; + + ePtrList _focusList; + + ePtrList actionListener; + eWidget *focus, *TLW; + + /// old top-level focus + eWidget *oldTLfocus; + int takefocus; + int state; + + gDC *target; + + inline eWidget *getTLW() // pseudoTLW !! + { + return TLW ? TLW : (TLW = (parent && parent->parent) ? parent->getTLW() : this ); + } + int result, in_loop, have_focus, just_showing; + void takeFocus(); + void releaseFocus(); + + void _willShow(); + void _willHide(); + + virtual void willShow(); + virtual void willHide(); + + virtual void setPalette(); + + void willShowChildren(); + void willHideChildren(); + + /** + * \brief Hi priority event filter. + * + * This event filter is called before the event is delivered via \a event. + * \return 1 if the event should NOT be forwarded. + */ + virtual int eventFilter(const eWidgetEvent &event); + + /** + * \brief Handles an event. + * + * If re-implemented in a widget-sub-class, \c eWidget::event should be called whenever the event is + * not processed by the widget. + * \return 1 if the event was processed, 0 if ignored. it might be forwarded to other widgets then. + */ + + virtual int keyDown(int rc); + virtual int keyUp(int rc); + + virtual void gotFocus(); + virtual void lostFocus(); + + virtual void recalcClientRect(); + void recalcClip(); + void checkFocus(); + + typedef ePtrList actionMapList; + + void findAction(eActionPrioritySet &prio, const eRCKey &key, eWidget *context); + void addActionMap(eActionMap *map); + void removeActionMap(eActionMap *map); + actionMapList actionmaps; + static actionMapList globalActions; + + // generic properties + gFont font; + eString text; + gColor backgroundColor, foregroundColor; + + ePtr pixmap; + + eString descr; + +public: + virtual int eventHandler(const eWidgetEvent &event); + static void addGlobalActionMap(eActionMap *map); + static void removeGlobalActionMap(eActionMap *map); + inline eWidget *getNonTransparentBackground() + { + if (backgroundColor >= 0) + return this; + return parent?parent->getNonTransparentBackground():this; + } + +#ifndef DISABLE_LCD + eWidget *LCDTitle; + eWidget *LCDElement; + eWidget *LCDTmp; +#endif + + void recalcAbsolutePosition(); + + inline const ePoint &getAbsolutePosition() const + { + return absPosition; + } + + inline ePoint getRelativePosition(eWidget *e) const + { + ePoint pos=position; + if (this != e) + for (eWidget *a=parent; a && (a != e); a=a->parent) + pos+=a->clientrect.topLeft(); + return pos; + } + + virtual void redrawWidget(gPainter *target, const eRect &area); + + virtual void eraseBackground(gPainter *target, const eRect &area); + + /** + * \brief Constructs a new eWidget. + * \param parent The parent widget. The widget gets automatically removed when the parent gets removed. + * \param takefocus Specifies if the widget should be appended to the focus list of the TLW, i.e. if it can + receive keys. + */ + eWidget(eWidget *parent=0, int takefocus=0); + + /** + * \brief Destructs an eWidget and all its childs. + * + * hide() is called when the widget is shown. The set ePixmap is \e not + * freed. If the widget acquired focus, it will be removed from the focuslist. + * \sa eWidget::setPixmap + */ + virtual ~eWidget(); + + /** + * \brief Returns a pointer to the focus list. + * + * The focus list is the list of childs which have the \c takefocus flag set. + * This list is only maintained for TLWs. + */ + ePtrList *focusList() { return &_focusList; } + + /** + * \brief Resizes the widget. + * + * Sets the size of the widget to the given size. The event \c changedSize event will be generated. + * \param size The new size, relative to the position. + */ + void resize(const eSize& size); + + /** + * \brief Resizes clientrect (and the widget). + * + * Sets the clientrect of the widget to the given size. The real size of the widget will be set to met + * these requirement. The event \c changedSize event will be generated. + * \param size The new size of the clientrect, relative to the position. + */ + void cresize(const eSize& size); + + /** + * \brief Moves the widget. + * + * Set the new position of the widget to the given position. The \c changedPosition event will be generated. + * \param position The new position, relative to the parent's \c clientrect. + */ + void move(const ePoint& position); + + /** + * \brief Moves the clientrect (and the widget). + * + * Set the new position of the clientrect to the given position. The \c changedPosition event will be generated. + * \param position The new position, relative to the parent's \c clientrect. + */ + void cmove(const ePoint& position); + + /** + * \brief Returns the current size. + * + * \return Current size of the widget, relative to the position. + */ + const eSize& getSize() const { return size; } + + /** + * \brief Returns the current position. + * + * \return Current position, relative to the parent's \c clientrect. + */ + const ePoint& getPosition() const { return position; } + + /** + * \brief Returns the size of the clientrect. + * + * \return The usable size for the childwidgets. + */ + eSize getClientSize() const { return clientrect.size(); } + + /** + * \brief Returns the clientrect. + * + * \return The area usable for the childwidgets. + */ + const eRect& getClientRect() const { return clientrect; } + + /** + * \brief Recursive redraw of a widget. + * + * All client windows get repaint too, but no widgets above. Unless you have a good reason, you shouldn't + * use this function and use \c invalidate(). + * \param area The area which should be repaint. The default is to repaint the whole widget. + * \sa eWidget::invalidate + */ + void redraw(eRect area=eRect()); + + /** + * \brief Recursive (complete) redraw of a widget. + * + * Redraws the widget including background. This is the function to use if you want to manually redraw something! + * \param area The area which should be repaint. The default is to repaint the whole widget. + * \param force Forces a parent-invalidate even on non-visible widgets. Shouldn't be used outside eWidget. + * \sa eWidget::redraw + */ + void invalidate(eRect area=eRect(), int force=0); + + /** + * \brief Enters modal message loop. + * + * A new event loop will be launched. The function returns when \a close is called. + * \return The argument of \a close. + * \sa eWidget::close + */ + int exec(); + + /** + * \brief Visually clears the widget. + * + * Clears the widget. This is done on \a hide(). + * \sa eWidget::hide + */ + void clear(); + + /** + * \brief Delivers a widget-event. + * + * Internally calles \a eventFilter, then \a eventHandler() (in some cases of the focused widget) + * \param event The event to deliver. + */ + int event(const eWidgetEvent &event); + + /** + * \brief Shows the widget. + * + * If necessary, the widget will be linked into the TLW's active focus list. The widget will + * visually appear. + * \sa eWidget::hide + */ + void show(); + + /** + * \brief Hides the widget. + * + * The widget will be removed from the screen. All childs will be hidden too. + * \sa eWidget::show + */ + void hide(); + + /** + * \brief Returns if the widget is vissible. + * + * \return If the widget and all parents are visible, \c true is returned, else false. + */ + int isVisible() { return (state&stateVisible) && ( (!parent) || parent->isVisible() ); } + + /** + * \brief Possible focus directions. + */ + enum focusDirection + { + focusDirNext, focusDirPrev, focusDirN, focusDirE, focusDirS, focusDirW + }; + + /** + * \brief changes the focused widget. + * + * Focuses the next or previous widget of the \c focuslist. An \c gotFocus and \c lostFocus event will be + * generated. + * \param dir The direction, \c focusDirection. + */ + void focusNext(int dir=0); + + /** + * \brief Gives focus to a widget. + * + * Set the focus to the specified widget. The \c focuslist is updated, too. + * An \c gotFocus and \c lostFocus event will be generated. + * \param newfocus The new widget to focus. + */ + void setFocus(eWidget *newfocus); + + /** + * \brief Sets the widget font. + * + * The font is used for example by the \c eLabel. + * \sa eLabel + * \param font The new font used by widget-specific drawing code. + */ + void setFont(const gFont &font); + + /** + * \brief Sets the widget caption or text. + * + * \param label The text to assign to the widget. + */ + void setText(const eString &label); + + const eString& getText() const { return text; } + void setBackgroundColor(const gColor& color, bool inv=true); + void setForegroundColor(const gColor& color, bool inv=true); + void setPixmap(gPixmap *pmap); + void setTarget(gDC *target); + gDC *getTarget() { return target; } + +#ifndef DISABLE_LCD + void setLCD(eWidget *lcdtitle, eWidget *lcdelement); +#endif + + void setName(const char *name); + const eString& getName() const { return name; } + eWidget*& getParent() { return parent; } + const gFont& getFont() const { return font; } + + const gColor& getBackgroundColor() const { return backgroundColor; } + const gColor& getForegroundColor() const { return foregroundColor; } + + int width() { return getSize().width(); } + int height() { return getSize().height(); } + + gPainter *getPainter(eRect area); + + const eString& getHelpText() const { return helptext; } + + void setHelpText( const eString&); + /** + * \brief Sets a property. + * + * A property is a value/data pair which is used for serializing widgets (like in skinfiles). + * These properties are available to all \c "eWidget"-based classes. + * \arg \c position, the position of the widget, relative to the parent's childarea. Consider using csize for TLWs. + * Positions are specified in a "x:y" manner. + * \arg \c cposition, the position of the widget's clientrect (upper left). + * This is useful for specifing a position independant of a decoration which might be + * different sized. The real position will be calculated to match the requested position. + * \arg \c size, the size of the widget. Consider using csize for TLWs. Sizes are specified in a "width:height" manner. + * \arg \c csize, the size of the clientrect. The real size will be calculated to match the requested size. + * \arg \c text, the text/caption of the widget. + * \arg \c font, the primary font used in the widget. + * \arg \c name, the name of the widget for referring them. + * \arg \c pixmap, an already loaded, named pixmap to be used as the widget's pixmap. + * \arg \c foregroundColor, a named color, which will be used for the widget's foreground color. + * \arg \c backgroundColor + * \param prop The property to be set. + * \param value The value to be set. + */ + virtual int setProperty(const eString &prop, const eString &value); + + eWidget *search(const eString &name); + + eWidget* getFocus() { return focus; } + + void makeRoot(); + + void zOrderLower(); + void zOrderRaise(); + + /** + * \brief sets the shortcut (generate evtShortcut) + */ + void setShortcut(const eString &shortcut); + void setShortcutFocus(eWidget *focus); + + void addActionToHelpList(eAction *action); + void clearHelpList(); + void setHelpID(int fHelpID); +}; + +#endif diff --git a/lib/gui/ewindow.cpp b/lib/gui/ewindow.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/ewindow.h b/lib/gui/ewindow.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/guiactions.cpp b/lib/gui/guiactions.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/guiactions.h b/lib/gui/guiactions.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/listbox.cpp b/lib/gui/listbox.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/listbox.h b/lib/gui/listbox.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/multipage.cpp b/lib/gui/multipage.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/multipage.h b/lib/gui/multipage.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/numberactions.cpp b/lib/gui/numberactions.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/numberactions.h b/lib/gui/numberactions.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/slider.cpp b/lib/gui/slider.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/slider.h b/lib/gui/slider.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/statusbar.cpp b/lib/gui/statusbar.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/statusbar.h b/lib/gui/statusbar.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/testpicture.cpp b/lib/gui/testpicture.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/testpicture.h b/lib/gui/testpicture.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/textinput.cpp b/lib/gui/textinput.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/gui/textinput.h b/lib/gui/textinput.h new file mode 100644 index 0000000..e69de29 diff --git a/lib/network/Makefile.am b/lib/network/Makefile.am new file mode 100644 index 0000000..cfdc79b --- /dev/null +++ b/lib/network/Makefile.am @@ -0,0 +1,7 @@ +INCLUDES = \ + -I$(top_srcdir)/include + +noinst_LIBRARIES = libenigma_network.a + +libenigma_network_a_SOURCES = \ + http_dyn.cpp http_file.cpp httpd.cpp serversocket.cpp socket.cpp xmlrpc.cpp diff --git a/lib/network/Makefile.in b/lib/network/Makefile.in new file mode 100644 index 0000000..e69de29 diff --git a/lib/network/http_dyn.cpp b/lib/network/http_dyn.cpp new file mode 100644 index 0000000..ea47019 --- /dev/null +++ b/lib/network/http_dyn.cpp @@ -0,0 +1,66 @@ +#include + +eHTTPDyn::eHTTPDyn(eHTTPConnection *c, eString result): eHTTPDataSource(c), result(result) +{ + wptr=0; + char buffer[10]; + snprintf(buffer, 10, "%d", size=result.length()); + c->local_header["Content-Length"]=std::string(buffer); + if (c->code == -1) + { + c->code=200; + c->code_descr="OK"; + } +} + +eHTTPDyn::~eHTTPDyn() +{ +} + +int eHTTPDyn::doWrite(int hm) +{ + int tw=size-wptr; + if (tw>hm) + tw=hm; + if (tw<=0) + return -1; + connection->writeBlock(result.c_str()+wptr, tw); + wptr+=tw; + return (size > wptr) ? 1 : -1; +} + +eHTTPDynPathResolver::eHTTPDynPathResolver() +{ + dyn.setAutoDelete(true); +} + +void eHTTPDynPathResolver::addDyn(eString request, eString path, eString (*function)(eString, eString, eString, eHTTPConnection*)) +{ + dyn.push_back(new eHTTPDynEntry(request, path, function)); +} + +eHTTPDataSource *eHTTPDynPathResolver::getDataSource(eString request, eString path, eHTTPConnection *conn) +{ + eString p, opt; + if (path.find('?')!=eString::npos) + { + p=path.left(path.find('?')); + opt=path.mid(path.find('?')+1); + } else + { + p=path; + opt=""; + } + for (ePtrList::iterator i(dyn); i != dyn.end(); ++i) + if ((i->path==p) && (i->request==request)) + { + conn->code=-1; + eString s=i->function(request, path, opt, conn); + + if (s) + return new eHTTPDyn(conn, s); + + return new eHTTPError(conn, 500); + } + return 0; +} diff --git a/lib/network/http_dyn.h b/lib/network/http_dyn.h new file mode 100644 index 0000000..6fb8b9b --- /dev/null +++ b/lib/network/http_dyn.h @@ -0,0 +1,34 @@ +#ifndef __http_dyn_h_ +#define __http_dyn_h_ +#include +#include + +class eHTTPDyn: public eHTTPDataSource +{ + eString result; + int wptr, size; +public: + eHTTPDyn(eHTTPConnection *c, eString result); + ~eHTTPDyn(); + int doWrite(int); +}; + +class eHTTPDynPathResolver: public eHTTPPathResolver +{ + struct eHTTPDynEntry + { + eString request, path; + eString (*function)(eString request, eString path, eString opt, eHTTPConnection *content); + + eHTTPDynEntry(eString request, eString path, eString (*function)(eString, eString, eString, eHTTPConnection *)): request(request), path(path), function(function) + { + } + }; + ePtrList dyn; +public: + void addDyn(eString request, eString path, eString (*function)(eString, eString, eString, eHTTPConnection *conn)); + eHTTPDynPathResolver(); + eHTTPDataSource *getDataSource(eString request, eString path, eHTTPConnection *conn); +}; + +#endif diff --git a/lib/network/http_file.cpp b/lib/network/http_file.cpp new file mode 100644 index 0000000..8991856 --- /dev/null +++ b/lib/network/http_file.cpp @@ -0,0 +1,228 @@ +#include +#include +#include +#include +#include +#include +#include + +eHTTPFile::eHTTPFile(eHTTPConnection *c, int _fd, int method, const char *mime): eHTTPDataSource(c), method(method) +{ + fd=_fd; + if (method == methodGET) + { + c->local_header["Content-Type"]=std::string(mime); + size=lseek(fd, 0, SEEK_END); + char asize[10]; + snprintf(asize, 10, "%d", size); + lseek(fd, 0, SEEK_SET); + c->local_header["Content-Length"]=std::string(asize); + } + connection->code_descr="OK"; + connection->code=200; +} + +int eHTTPFile::doWrite(int bytes) +{ + if (method == methodGET) + { + char buff[bytes]; + if (!size) + return -1; + int len=bytes; + if (len>size) + len=size; + len=read(fd, buff, len); + if (len<=0) + return -1; + size-=connection->writeBlock(buff, len); + if (!size) + return -1; + else + return 1; + } else + return -1; +} + +void eHTTPFile::haveData(void *data, int len) +{ + if (method != methodPUT) + return; + ::write(fd, data, len); +} + +eHTTPFile::~eHTTPFile() +{ + close(fd); +} + +eHTTPFilePathResolver::eHTTPFilePathResolver() +{ + translate.setAutoDelete(true); +} + + +static char _base64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + +static int unbase64(eString &dst, const eString string) +{ + dst=""; + char c[4]; + int pos=0; + unsigned int i=0; + + while (1) + { + if (i == string.size()) + break; + char *ch=strchr(_base64, string[i++]); + if (!ch) + { + i++; + continue; + } + c[pos++]=ch-_base64; + if (pos == 4) + { + char d[3]; + d[0]=c[0]<<2; + d[0]|=c[1]>>4; + d[1]=c[1]<<4; + d[1]|=c[2]>>2; + d[2]=c[2]<<6; + d[2]|=c[3]; + + dst+=d[0]; + if (c[2] != 64) + dst+=d[1]; + if (c[3] != 64) + dst+=d[2]; + pos=0; + } + } + return pos; +} + +int CheckUnixPassword(const char *user, const char *pass) +{ + passwd *pwd=getpwnam(user); + if (!pwd) + return -1; + char *cpwd=pwd->pw_passwd; + if (pwd && (!strcmp(pwd->pw_passwd, "x"))) + { + spwd *sp=getspnam(user); + if (!sp) // no shadow password defined. + return -1; + cpwd=sp->sp_pwdp; + } + if (!cpwd) + return -1; + if ((*cpwd=='!')||(*cpwd=='*')) // disabled user + return -2; + char *cres=crypt(pass, cpwd); + return !!strcmp(cres, cpwd); +} + +static int checkAuth(const eString cauth) +{ + eString auth; + if (cauth.left(6) != "Basic ") + return -1; + if (unbase64(auth, cauth.mid(6))) + return -1; + eString username=auth.left(auth.find(":")); + eString password=auth.mid(auth.find(":")+1); + if (CheckUnixPassword(username.c_str(), password.c_str())) + return -1; + return 0; +} + +eHTTPDataSource *eHTTPFilePathResolver::getDataSource(eString request, eString path, eHTTPConnection *conn) +{ + int method; + eDebug("request = %s, path = %s", request.c_str(), path.c_str()); + if (request == "GET") + method=eHTTPFile::methodGET; + else if (request == "PUT") + method=eHTTPFile::methodPUT; + else + return new eHTTPError(conn, 405); // method not allowed + if (path.find("../")!=eString::npos) // evil hax0r + return new eHTTPError(conn, 403); + if (path[0] != '/') // prepend '/' + path.insert(0,"/"); + if (path[path.length()-1]=='/') + path+="index.html"; + + eHTTPDataSource *data=0; + for (ePtrList::iterator i(translate); i != translate.end(); ++i) + { + if (i->root==path.left(i->root.length())) + { + eString newpath=i->path+path.mid(i->root.length()); + if (newpath.find('?')) + newpath=newpath.left(newpath.find('?')); + eDebug("translated %s to %s", path.c_str(), newpath.c_str()); + + if (i->authorized & ((method==eHTTPFile::methodGET)?1:2)) + { + std::map::iterator i=conn->remote_header.find("Authorization"); + if ((i == conn->remote_header.end()) || checkAuth(i->second)) + { + conn->local_header["WWW-Authenticate"]="Basic realm=\"dreambox\""; + return new eHTTPError(conn, 401); // auth req'ed + } + } + + int fd=open(newpath.c_str(), (method==eHTTPFile::methodGET)?O_RDONLY:(O_WRONLY|O_CREAT|O_TRUNC), 0644); + + if (fd==-1) + { + switch (errno) + { + case ENOENT: + data=new eHTTPError(conn, 404); + break; + case EACCES: + data=new eHTTPError(conn, 403); + break; + default: + data=new eHTTPError(conn, 403); // k.a. + break; + } + break; + } + + eString ext=path.mid(path.rfind('.')); + const char *mime="text/unknown"; + if ((ext==".html") || (ext==".htm")) + mime="text/html"; + else if ((ext==".jpeg") || (ext==".jpg")) + mime="image/jpeg"; + else if (ext==".gif") + mime="image/gif"; + else if (ext==".css") + mime="text/css"; + else if (ext==".png") + mime="image/png"; + else if (ext==".xml") + mime="text/xml"; + else if (ext==".xsl") + mime="text/xsl"; + + data=new eHTTPFile(conn, fd, method, mime); + break; + } + } + return data; +} + +void eHTTPFilePathResolver::addTranslation(eString path, eString root, int authorized) +{ + if (path[path.length()-1]!='/') + path+='/'; + if (root[root.length()-1]!='/') + root+='/'; + translate.push_back(new eHTTPFilePath(path, root, authorized)); +} diff --git a/lib/network/http_file.h b/lib/network/http_file.h new file mode 100644 index 0000000..88bffc6 --- /dev/null +++ b/lib/network/http_file.h @@ -0,0 +1,37 @@ +#ifndef __http_file_h +#define __http_file_h + +#include "httpd.h" + +class eHTTPFile: public eHTTPDataSource +{ + int fd, size; + const char *mime; + int method; +public: + enum { methodGET, methodPUT }; + eHTTPFile(eHTTPConnection *c, int fd, int method, const char *mime); + ~eHTTPFile(); + int doWrite(int); + void haveData(void *data, int len); +}; + +class eHTTPFilePathResolver: public eHTTPPathResolver +{ + struct eHTTPFilePath + { + eString path; + eString root; + int authorized; // must be authorized (1 means read, 2 write) + eHTTPFilePath(eString path, eString root, int authorized): path(path), root(root), authorized(authorized) + { + } + }; + ePtrList translate; +public: + eHTTPFilePathResolver(); + eHTTPDataSource *getDataSource(eString request, eString path, eHTTPConnection *conn); + void addTranslation(eString path, eString root, int auth); +}; + +#endif diff --git a/lib/network/httpd.cpp b/lib/network/httpd.cpp new file mode 100644 index 0000000..b8ed1a9 --- /dev/null +++ b/lib/network/httpd.cpp @@ -0,0 +1,631 @@ +// #define DEBUG_HTTPD +#include + +#include +#include +#include +#include +#include + +eHTTPDataSource::eHTTPDataSource(eHTTPConnection *c): connection(c) +{ +} + +eHTTPDataSource::~eHTTPDataSource() +{ +} + +void eHTTPDataSource::haveData(void *data, int len) +{ +} + +int eHTTPDataSource::doWrite(int) +{ + return 0; +} + +eHTTPError::eHTTPError(eHTTPConnection *c, int errcode): eHTTPDataSource(c), errcode(errcode) +{ + eString error="unknown error"; + switch (errcode) + { + case 400: error="Bad Request"; break; + case 401: error="Unauthorized"; break; + case 403: error="Forbidden"; break; + case 404: error="Not found"; break; + case 405: error="Method not allowed"; break; + case 500: error="Internal server error"; break; + } + connection->code_descr=error; + connection->code=errcode; + + connection->local_header["Content-Type"]=std::string("text/html"); +} + +int eHTTPError::doWrite(int w) +{ + eString html; + html+="Error "+eString().setNum(connection->code)+""+ + "

Error "+eString().setNum(errcode)+": "+connection->code_descr+"

\n"; + connection->writeBlock(html.c_str(), html.length()); + return -1; +} + +eHTTPConnection::eHTTPConnection(int socket, int issocket, eHTTPD *parent, int persistent): eSocket(socket, issocket, parent->ml), parent(parent), persistent(persistent) +{ +#ifdef DEBUG_HTTPD + eDebug("eHTTPConnection"); +#endif + CONNECT(this->readyRead_ , eHTTPConnection::readData); + CONNECT(this->bytesWritten_ , eHTTPConnection::bytesWritten); + CONNECT(this->error_ , eHTTPConnection::gotError); + CONNECT(this->connectionClosed_ , eHTTPConnection::destruct); + CONNECT(this->hangup , eHTTPConnection::gotHangup); + + buffersize=128*1024; + localstate=stateWait; + remotestate=stateRequest; + data=0; +} + +void eHTTPConnection::destruct() +{ + gotHangup(); + delete this; +} + +eHTTPConnection::eHTTPConnection(eMainloop *ml): eSocket(ml), parent(0), persistent(0) +{ + CONNECT(this->readyRead_ , eHTTPConnection::readData); + CONNECT(this->bytesWritten_ , eHTTPConnection::bytesWritten); + CONNECT(this->error_ , eHTTPConnection::gotError); + CONNECT(this->connected_ , eHTTPConnection::hostConnected); + CONNECT(this->connectionClosed_ , eHTTPConnection::destruct); + + localstate=stateWait; + remotestate=stateWait; + + buffersize=64*1024; + data=0; +} + +void eHTTPConnection::hostConnected() +{ + processLocalState(); +} + +void eHTTPConnection::start() +{ + if (localstate==stateWait) + { + localstate=stateRequest; + processLocalState(); + } +} + +void eHTTPConnection::gotHangup() +{ + if (data && remotestate == stateData) + data->haveData(0, 0); + if (data) + { + delete data; + data=0; + } + transferDone(0); + + localstate=stateWait; + remotestate=stateRequest; + + remote_header.clear(); + local_header.clear(); +} + +eHTTPConnection *eHTTPConnection::doRequest(const char *uri, eMainloop *ml, int *error) +{ + if (error) + *error=0; + + char *defaultproto="http"; + std::string proto, host, path; + int port=80; + + int state=0; // 0 proto, 1 host, 2 port 3 path + + while (*uri) + { + switch (state) + { + case 0: + if (!strncmp(uri, "://", 3)) + { + state=1; + uri+=3; + } else if ((*uri=='/') || (*uri==':')) + { + host=proto; + state=1; + proto=defaultproto; + } else + proto.push_back(*uri++); + break; + case 1: + if (*uri=='/') + state=3; + else if (*uri==':') + { + state=2; + port=0; + uri++; + } else + host.push_back(*uri++); + break; + case 2: + if (*uri=='/') + state=3; + else + { + if (!isdigit(*uri)) + { + port=-1; + state=3; + } else + { + port*=10; + port+=*uri++-'0'; + } + } + break; + case 3: + path.push_back(*uri++); + } + } + + if (state==0) + { + path=proto; + proto=defaultproto; + } + +#ifdef DEBUG_HTTPD + eDebug("proto: '%s', host '%s', path '%s', port '%d'", proto.c_str(), host.c_str(), path.c_str(), port); +#endif + + if (!host.size()) + { + eDebug("no host given"); + if (error) + *error=ENOENT; + return 0; + } + + if (strcmp(proto.c_str(), "http")) + { + eDebug("invalid protocol (%s)", proto.c_str()); + if (error) + *error=EINVAL; + return 0; + } + + if (port == -1) + { + eDebug("invalid port"); + if (error) + *error=EINVAL; + return 0; + } + + if (!path.size()) + path="/"; + + eHTTPConnection *c=new eHTTPConnection(ml); + c->request="GET"; + c->requestpath=path.c_str(); + c->httpversion="HTTP/1.0"; + c->local_header["Host"]=host; + if ((*error=c->connectToHost(host, port))) // already deleted by error + return 0; + return c; +} + +void eHTTPConnection::readData() +{ + processRemoteState(); +} + +void eHTTPConnection::bytesWritten(int) +{ + processLocalState(); +} + +int eHTTPConnection::processLocalState() +{ + switch (state()) + { + case Connection: + break; + default: + return 0; + } + int done=0; + while (!done) + { +#ifdef DEBUG_HTTPD + eDebug("processing local state %d", localstate); +#endif + switch (localstate) + { + case stateWait: +#ifdef DEBUG_HTTPD + eDebug("local wait"); +#endif + done=1; + break; + case stateRequest: + { +#ifdef DEBUG_HTTPD + eDebug("local request"); +#endif + eString req=request+" "+requestpath+" "+httpversion+"\r\n"; + writeBlock(req.c_str(), req.length()); + localstate=stateHeader; + remotestate=stateResponse; + break; + } + case stateResponse: + { +#ifdef DEBUG_HTTPD + eDebug("local Response"); +#endif + writeString( (httpversion + " " + eString().setNum(code)+" " + code_descr + "\r\n").c_str() ); + localstate=stateHeader; + local_header["Connection"]="close"; + break; + } + case stateHeader: +#ifdef DEBUG_HTTPD + eDebug("local header"); +#endif + for (std::map::iterator cur=local_header.begin(); cur!=local_header.end(); ++cur) + { + writeString(cur->first.c_str()); + writeString(": "); + writeString(cur->second.c_str()); + writeString("\r\n"); + } + writeString("\r\n"); + if (request=="HEAD") + localstate=stateDone; + else + localstate=stateData; + break; + case stateData: +#ifdef DEBUG_HTTPD + eDebug("local data"); +#endif + if (data) + { + int btw=buffersize-bytesToWrite(); + if (btw>0) + { + if (data->doWrite(btw)<0) + { + localstate=stateDone; + } else + done=1; + } else + done=1; + } else + done=1; // wait for remote response + break; + case stateDone: +#if 0 + // move to stateClose + if (remote_header.find("Connection") != remote_header.end()) + { + eString &connection=remote_header["Connection"]; + if (connection == "keep-alive") + localstate=stateWait; + else + localstate=stateClose; + } +#endif +#ifdef DEBUG_HTTPD + eDebug("locate state done"); +#endif + if (!persistent) + localstate=stateClose; + else + localstate=stateWait; + break; + case stateClose: +#ifdef DEBUG_HTTPD + eDebug("closedown"); +#endif + if (persistent) + { + if (data) + delete data; + data=0; + localstate=stateWait; + } else + close(); // bye, bye, remote + return 1; + } + } +#ifdef DEBUG_HTTPD + eDebug("end local"); +#endif + return 0; +} + +int eHTTPConnection::processRemoteState() +{ + int abort=0, done=0; +#ifdef DEBUG_HTTPD + eDebug("%d bytes avail", bytesAvailable()); +#endif + while (((!done) || bytesAvailable()) && !abort) + { + switch (remotestate) + { + case stateWait: + { + int i=0; +#ifdef DEBUG_HTTPD + eDebug("remote stateWait"); +#endif + char buffer[1024]; + while (bytesAvailable()) { + i=readBlock(buffer, 1024); + } + done=1; + break; + } + case stateRequest: + { +#ifdef DEBUG_HTTPD + eDebug("stateRequest"); +#endif + eString line; + if (!getLine(line)) + { + done=1; + abort=1; + break; + } + + int del[2]; + del[0]=line.find(" "); + del[1]=line.find(" ", del[0]+1); + if (del[0]==-1) + { + if (data) + delete data; + eDebug("request buggy"); + httpversion="HTTP/1.0"; + data=new eHTTPError(this, 400); + done=0; + localstate=stateResponse; + remotestate=stateDone; + if (processLocalState()) + return -1; + break; + } + request=line.left(del[0]); + requestpath=line.mid(del[0]+1, (del[1]==-1)?-1:(del[1]-del[0]-1)); + if (del[1]!=-1) + { + is09=0; + httpversion=line.mid(del[1]+1); + } else + is09=1; + + if (is09 || (httpversion.left(7) != "HTTP/1.") || httpversion.size()!=8) + { + remotestate=stateData; + done=0; + httpversion="HTTP/1.0"; + content_length_remaining=content_length_remaining=0; + data=new eHTTPError(this, 400); // bad request - not supporting version 0.9 yet + } else + remotestate=stateHeader; + break; + } + case stateResponse: + { +#ifdef DEBUG_HTTPD + eDebug("state response.."); +#endif + eString line; + if (!getLine(line)) + { + done=1; + abort=1; + break; + } +#ifdef DEBUG_HTTPD + eDebug("line: %s", line.c_str()); +#endif + int del[2]; + del[0]=line.find(" "); + del[1]=line.find(" ", del[0]+1); + if (del[0]==-1) + code=-1; + else + { + httpversion=line.left(del[0]); + code=atoi(line.mid(del[0]+1, (del[1]==-1)?-1:(del[1]-del[0]-1)).c_str()); + if (del[1] != -1) + code_descr=line.mid(del[1]+1); + else + code_descr=""; + } + + remotestate=stateHeader; + break; + } + case stateHeader: + { +#ifdef DEBUG_HTTPD + eDebug("remote stateHeader"); +#endif + eString line; + if (!getLine(line)) + { + done=1; + abort=1; + break; + } + if (!line.length()) + { + content_length=0; + content_length_remaining=-1; + if (remote_header.count("Content-Length")) + { + content_length=atoi(remote_header["Content-Length"].c_str()); + content_length_remaining=content_length; + } + + if (parent) + { + for (ePtrList::iterator i(parent->resolver); i != parent->resolver.end(); ++i) + { + if ((data=i->getDataSource(request, requestpath, this))) + break; + } + localstate=stateResponse; // can be overridden by dataSource + } else + data=createDataSource(this); + + if (!data) + { + if (data) + delete data; + data=new eHTTPError(this, 404); + } + + if (content_length || // if content-length is set, we have content + remote_header.count("Content-Type") || // content-type - the same + (localstate != stateResponse)) // however, if we are NOT in response-state, so we are NOT server, there's ALWAYS more data to come. (exception: http/1.1 persistent) + remotestate=stateData; + else + { + data->haveData(0, 0); + remotestate=stateDone; + } + if (processLocalState()) + return -1; + } else + { + int del=line.find(":"); + eString name=line.left(del), value=line.mid(del+1); + if (value[0]==' ') + value=value.mid(1); + remote_header[std::string(name)]=std::string(value); + } + done=1; + break; + } + case stateData: + { +#ifdef DEBUG_HTTPD + eDebug("remote stateData"); +#endif + ASSERT(data); + char buffer[16284]; + int len; + while (bytesAvailable()) + { + int tr=sizeof(buffer); + if (content_length_remaining != -1) + if (tr>content_length_remaining) + tr=content_length_remaining; + len=readBlock(buffer, tr); + data->haveData(buffer, len); + if (content_length_remaining != -1) + content_length_remaining-=len; + if (!content_length_remaining) + { + data->haveData(0, 0); + remotestate=stateDone; + break; + } + } + done=1; + if (processLocalState()) + return -1; + break; + } + case stateDone: + remotestate=stateClose; + break; + case stateClose: +// if (!persistent) + remotestate=stateWait; +// else +// remotestate=stateRequest; + abort=1; + break; + default: + eDebug("HTTP: invalid state %d", remotestate); + done=1; + } + } +#ifdef DEBUG_HTTPD + eDebug("end remote"); +#endif + return 0; +} + +void eHTTPConnection::writeString(const char *data) +{ + writeBlock(data, strlen(data)); +} + +int eHTTPConnection::getLine(eString &line) +{ + if (!canReadLine()) + return 0; + + line = readLine(); + line.erase(line.length()-1); + + if (line[(line.length()-1)] == '\r') + line.erase(line.length()-1); + + return 1; +} + +void eHTTPConnection::gotError(int err) +{ + if (data) + { + delete data; + data=0; + } + transferDone(err); + delete this; +} + +eHTTPD::eHTTPD(int port, eMainloop *ml): eServerSocket(port, ml), ml(ml) +{ + if (!ok()) + eDebug("[NET] httpd server FAILED on port %d", port); + else + eDebug("[NET] httpd server started on port %d", port); + resolver.setAutoDelete(true); +} + +eHTTPConnection::~eHTTPConnection() +{ + if ((!persistent) && (state()!=Idle)) + eWarning("~eHTTPConnection, status still %d", state()); + if (data) + delete data; +} + +void eHTTPD::newConnection(int socket) +{ + new eHTTPConnection(socket, 1, this); +} diff --git a/lib/network/httpd.h b/lib/network/httpd.h new file mode 100644 index 0000000..791d49c --- /dev/null +++ b/lib/network/httpd.h @@ -0,0 +1,123 @@ +#ifndef __httpd_h +#define __httpd_h + +#include +#include + +#include +#include +#include +#include +#include +#include + +class eHTTPConnection; +class eHTTPDataSource; +class eHTTPD; + +class eHTTPPathResolver +{ +public: + virtual ~eHTTPPathResolver() {}; + virtual eHTTPDataSource *getDataSource(eString request, eString path, eHTTPConnection *conn)=0; +}; + +class eHTTPDataSource +{ +protected: + eHTTPConnection *connection; +public: + eHTTPDataSource(eHTTPConnection *c); + virtual ~eHTTPDataSource(); + virtual void haveData(void *data, int len); + virtual int doWrite(int bytes); // number of written bytes, -1 for "no more" +}; + +class eHTTPError: public eHTTPDataSource +{ + int errcode; +public: + eHTTPError(eHTTPConnection *c, int errcode); + ~eHTTPError() { } + void haveData(); + int doWrite(int bytes); +}; + +class eHTTPConnection: public eSocket +{ + void doError(int error); + + int getLine(eString &line); + + int processLocalState(); + int processRemoteState(); + void writeString(const char *data); + + eHTTPDataSource *data; + eHTTPD *parent; + + int buffersize; +private: + void readData(); + void gotError(int); + void bytesWritten(int); + void hostConnected(); + void destruct(); +public: + Signal1 transferDone; + Signal1 createDataSource; + enum + { + /* + + < GET / HTTP/1.0 + < If-modified-since: bla + < + < Data + > 200 OK HTTP/1.0 + > Content-Type: text/html + > + > Data + */ + + stateWait, stateRequest, stateResponse, stateHeader, stateData, stateDone, stateClose + }; + int localstate, remotestate; + int persistent; + + eHTTPConnection(int socket, int issocket, eHTTPD *parent, int persistent=0); + eHTTPConnection(eMainloop *ml); // ready to do "connectToHost" + static eHTTPConnection *doRequest(const char *uri, eMainloop *ml, int *error=0); + void start(); + void gotHangup(); + ~eHTTPConnection(); + + // stateRequest + eString request, requestpath, httpversion; + int is09; + + // stateResponse + + int code; + eString code_descr; + + std::map remote_header, local_header; + + // stateData + int content_length, content_length_remaining; +}; + +class eHTTPD: public eServerSocket +{ + friend class eHTTPConnection; + ePtrList resolver; + eMainloop *ml; +public: + eHTTPD(int port, eMainloop *ml); + void newConnection(int socket); + + void addResolver(eHTTPPathResolver *r) { resolver.push_back(r); } + void removeResolver(eHTTPPathResolver *r) { resolver.remove(r); } +}; + +#endif diff --git a/lib/network/serversocket.cpp b/lib/network/serversocket.cpp new file mode 100644 index 0000000..fe2737e --- /dev/null +++ b/lib/network/serversocket.cpp @@ -0,0 +1,55 @@ +#include + +bool eServerSocket::ok() +{ + return okflag; +} + +void eServerSocket::notifier(int) +{ + int clientfd, clientlen; + struct sockaddr_in client_addr; + + eDebug("[SERVERSOCKET] incoming connection!"); + + clientlen=sizeof(client_addr); + clientfd=accept(getDescriptor(), + (struct sockaddr *) &client_addr, + (socklen_t*)&clientlen); + if(clientfd<0) + eDebug("[SERVERSOCKET] error on accept()"); + + newConnection(clientfd); +} + +eServerSocket::eServerSocket(int port, eMainloop *ml): eSocket(ml) +{ + struct sockaddr_in serv_addr; + + serv_addr.sin_family=AF_INET; + serv_addr.sin_addr.s_addr=INADDR_ANY; + serv_addr.sin_port=htons(port); + + okflag=1; + int val=1; + + setsockopt(getDescriptor(), SOL_SOCKET, SO_REUSEADDR, &val, 4); + + if(bind(getDescriptor(), + (struct sockaddr *) &serv_addr, + sizeof(serv_addr))<0) + { + eDebug("[SERVERSOCKET] ERROR on bind() (%m)"); + okflag=0; + } + listen(getDescriptor(), 0); + + rsn->setRequested(eSocketNotifier::Read); +} + +eServerSocket::~eServerSocket() +{ +#if 0 + eDebug("[SERVERSOCKET] destructed"); +#endif +} diff --git a/lib/network/serversocket.h b/lib/network/serversocket.h new file mode 100644 index 0000000..6bd59f1 --- /dev/null +++ b/lib/network/serversocket.h @@ -0,0 +1,18 @@ +#ifndef __serversocket_h +#define __serversocket_h + +#include "socket.h" + +class eServerSocket: public eSocket +{ + void notifier(int handle); + int okflag; +protected: + virtual void newConnection(int socket)=0; +public: + eServerSocket(int port, eMainloop *ml); + virtual ~eServerSocket(); + bool ok(); +}; + +#endif /* __serversocket_h */ diff --git a/lib/network/serversocket.lo b/lib/network/serversocket.lo new file mode 100644 index 0000000000000000000000000000000000000000..8039034b8382536bc6c6c5410dc750dac8766e1a GIT binary patch literal 191574 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 z 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 KZlr4?k^ciq=qh{w literal 0 HcmV?d00001 diff --git a/lib/network/socket.cpp b/lib/network/socket.cpp new file mode 100644 index 0000000..9de0ca0 --- /dev/null +++ b/lib/network/socket.cpp @@ -0,0 +1,294 @@ +#include +#include +#include +#include + +#include + +void eSocket::close() +{ + if (writebuffer.empty()) + { + int wasconnected=(mystate==Connection) || (mystate==Closing); + delete rsn; + rsn=0; + ::close(socketdesc); + socketdesc=-1; + mystate=Idle; + if (wasconnected) + connectionClosed_(); + } else + { + mystate=Closing; + rsn->setRequested(rsn->getRequested()|eSocketNotifier::Write); + } +} + +void eSocket::enableRead() +{ + if (rsn) + rsn->setRequested(rsn->getRequested()|eSocketNotifier::Read); +} + +void eSocket::disableRead() +{ + if (rsn) + rsn->setRequested(rsn->getRequested()&~eSocketNotifier::Read); +} + +void eSocket::inject(const char *data, int len) +{ + readbuffer.write(data, len); + if (mystate == Connection) + readyRead_(); +} + +eString eSocket::readLine() +{ + int size=readbuffer.searchchr('\n'); + if (size == -1) + return eString(); + size++; // ich will auch das \n + char buffer[size+1]; + buffer[size]=0; + readbuffer.read(buffer, size); + return eString(buffer); +} + +bool eSocket::canReadLine() +{ + return readbuffer.searchchr('\n') != -1; +} + +int eSocket::bytesAvailable() +{ + return readbuffer.size(); +} + +int eSocket::readBlock(char *data, unsigned int maxlen) +{ + return readbuffer.read(data, maxlen); +} + +int eSocket::bytesToWrite() +{ + return writebuffer.size(); +} + +int eSocket::state() +{ + return mystate; +} + +int eSocket::setSocket(int s, int iss, eMainloop *ml) +{ + socketdesc=s; + issocket=iss; + fcntl(socketdesc, F_SETFL, O_NONBLOCK); + last_break = 0xFFFFFFFF; + + if (rsn) + delete rsn; + rsn=new eSocketNotifier(ml, getDescriptor(), + eSocketNotifier::Read|eSocketNotifier::Hungup); + CONNECT(rsn->activated, eSocket::notifier); + return 0; +} + +void eSocket::notifier(int what) +{ + if ((what & eSocketNotifier::Read) && (mystate == Connection)) + { + int bytesavail=256; + if (issocket) + if (ioctl(getDescriptor(), FIONREAD, &bytesavail)<0) + eDebug("FIONREAD failed.\n"); + + { + if (issocket) + { + if (!bytesavail) // does the REMOTE END has closed the connection? (no Hungup here!) + { + writebuffer.clear(); + close(); + return; + } + } else // when operating on terminals, check for break + { + // where is this struct defined? + struct async_icount { + unsigned long cts, dsr, rng, dcd, tx, rx; + unsigned long frame, parity, overrun, brk; + unsigned long buf_overrun; + } icount; + + if (!ioctl(getDescriptor(), TIOCGICOUNT, &icount)) + { + if (last_break == 0xFFFFFFFF) + last_break = icount.brk; + else if (last_break != icount.brk) + { + last_break = icount.brk; + readbuffer.fromfile(getDescriptor(), bytesavail); + readbuffer.clear(); + writebuffer.clear(); + rsn->setRequested(rsn->getRequested()&~eSocketNotifier::Write); + write(getDescriptor(), "BREAK!", 6); + hangup(); + return; + } + } + } + int r; + if ((r=readbuffer.fromfile(getDescriptor(), bytesavail)) != bytesavail) + if (issocket) + eDebug("fromfile failed!"); + readyRead_(); + } + } else if (what & eSocketNotifier::Write) + { + if ((mystate == Connection) || (mystate == Closing)) + { + if (!writebuffer.empty()) + { + bytesWritten_(writebuffer.tofile(getDescriptor(), 65536)); + if (writebuffer.empty()) + { + rsn->setRequested(rsn->getRequested()&~eSocketNotifier::Write); + if (mystate == Closing) + { + close(); // warning, we might get destroyed after close. + return; + } + } + } else + eDebug("got ready to write, but nothin in buffer. strange."); + if (mystate == Closing) + close(); + } else if (mystate == Connecting) + { + mystate=Connection; + rsn->setRequested(rsn->getRequested()&~eSocketNotifier::Write); + + int res; + socklen_t size=sizeof(res); + ::getsockopt(getDescriptor(), SOL_SOCKET, SO_ERROR, &res, &size); + if (!res) + connected_(); + else + { + close(); + error_(res); + } + } + } else if (what & eSocketNotifier::Hungup) + { + if (mystate == Connection) + { + writebuffer.clear(); + close(); + } else if (mystate == Connecting) + { + int res; + socklen_t size=sizeof(res); + ::getsockopt(getDescriptor(), SOL_SOCKET, SO_ERROR, &res, &size); + close(); + error_(res); + } + } +} + +int eSocket::writeBlock(const char *data, unsigned int len) +{ + int w=len; + if (issocket && writebuffer.empty()) + { + int tw=::send(getDescriptor(), data, len, MSG_NOSIGNAL); + if ((tw < 0) && (errno != EWOULDBLOCK)) + eDebug("write: %m"); + + if (tw < 0) + tw = 0; + data+=tw; + len-=tw; + } + if (len) + writebuffer.write(data, len); + + if (!writebuffer.empty()) + rsn->setRequested(rsn->getRequested()|eSocketNotifier::Write); + return w; +} + +int eSocket::getDescriptor() +{ + return socketdesc; +} + +int eSocket::connectToHost(eString hostname, int port) +{ + struct hostent *server; + int res; + + if(!socketdesc){ + error_(errno); + return(-1); + } + server=gethostbyname(hostname.c_str()); + if(server==NULL) + { + eDebug("can't resolve %s", hostname.c_str()); + error_(errno); + return(-2); + } + bzero( (char*)&serv_addr, sizeof(serv_addr)); + serv_addr.sin_family=AF_INET; + bcopy( (char*)server->h_addr, + (char*)&serv_addr.sin_addr.s_addr, + server->h_length); + serv_addr.sin_port=htons(port); + res=::connect(socketdesc, (const sockaddr*)&serv_addr, sizeof(serv_addr)); + if ((res < 0) && (errno != EINPROGRESS)) + { + eDebug("can't connect to host: %s", hostname.c_str()); + close(); + error_(errno); + return(-3); + } + if (res < 0) // EINPROGRESS + { + rsn->setRequested(rsn->getRequested()|eSocketNotifier::Write); + mystate=Connecting; + } else + { + mystate=Connection; + connected_(); + } + return(0); +} + +eSocket::eSocket(eMainloop *ml): readbuffer(32768), writebuffer(32768), rsn(0) +{ + int s=socket(AF_INET, SOCK_STREAM, 0); +#if 0 + eDebug("[SOCKET]: initalized socket %d", socketdesc); +#endif + mystate=Idle; + setSocket(s, 1, ml); +} + +eSocket::eSocket(int socket, int issocket, eMainloop *ml): readbuffer(32768), writebuffer(32768), rsn(0) +{ + setSocket(socket, issocket, ml); + mystate=Connection; +} + +eSocket::~eSocket() +{ + if (rsn) + delete rsn; + if(socketdesc>=0) + { + ::close(socketdesc); + } +} diff --git a/lib/network/socket.h b/lib/network/socket.h new file mode 100644 index 0000000..edf9b90 --- /dev/null +++ b/lib/network/socket.h @@ -0,0 +1,63 @@ +#ifndef __socket_h +#define __socket_h + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +class eSocket: public Object +{ + int mystate; + int issocket; + unsigned int last_break; +private: + int socketdesc; + eIOBuffer readbuffer; + eIOBuffer writebuffer; + int writebusy; + sockaddr_in serv_addr; +protected: + eSocketNotifier *rsn; + virtual void notifier(int); +public: + eSocket(eMainloop *ml); + eSocket(int socket, int issocket, eMainloop *ml); + ~eSocket(); + int connectToHost(eString hostname, int port); + int getDescriptor(); + int writeBlock(const char *data, unsigned int len); + int setSocket(int socketfd, int issocket, eMainloop *ml); + int bytesToWrite(); + int readBlock(char *data, unsigned int maxlen); + int bytesAvailable(); + bool canReadLine(); + eString readLine(); + void close(); + // flow control: start/stop data delivery into read buffer. + void enableRead(); + void disableRead(); + + void inject(const char *data, int len); + + enum State { Idle, HostLookup, Connecting, + Listening, Connection, Closing }; + int state(); + + Signal0 connectionClosed_; + Signal0 connected_; + Signal0 readyRead_; + Signal0 hangup; + Signal1 bytesWritten_; + Signal1 error_; +}; + +#endif /* __socket_h */ diff --git a/lib/network/socket.lo b/lib/network/socket.lo new file mode 100644 index 0000000000000000000000000000000000000000..1d8094693be75a2216233886e229806dd6b287a3 GIT binary patch literal 224253 zcmc%y2XvLi7d{Hld(H_7B!Lh@2x;VygtSBG5FkK+&_P=0Ef6|LF98w~2vxdRklsX8 z6zmELB4AhS6%?ch0{*~`qTJ`%GpC{7`tEPiFR>^6WBu$~*5%NxQD?Et{o0 z|5+*mX)nPxZG%#!N;Q-csj7~0-RHc0>Bs^VRJ6>tr>G>RFsnetu3eyFJEES;u2gdr zZ@o|zmVK+TOSdV9D_*HA$L(7s$9^tM+^($V+m$ML<#I&MLX}%uqC(bhQ`u5C4(0N1 z-(IU}4)CKEI92Q+^i`Z{-;?E3QFoQ91k}l4`7XC_pXXn;Yx2*Q9#Pp%l?qr_Lpe$> zsF3`N%CYu@3PITyiF-k!%663(d|{=_!7o$R+Hoqx-S&T#$tzRYzGZ;^I?8=foNC*H zegmM3sC7=|D!OFb(|orITM&5rdRD2bP}C~8u&5}euw>Vj!jdnm60pl#~6 zhYR{*U8F;#?Jdy}%x4`@L0R9bFzR{J5fw(>+9IVE3`hC0bA@Fql%r&seb1p4x33ou zA5=)4mK`p2Kt~5k=a;IcZK3b{9;r1E3AN5DQgxj}^mRn&(Wr>BQ@CoX*2z{{+sRy5tI>ulAv&@^O%u(I$f0ohh zc>M0a%9Iu;huf~kW(Q~^=p$h5S(V2$ZFTr8YzIGHyGsSFErTy?4^qSr`5d%c(N_(` zzslfS)OmBK3Tt)-e=QY6eZx=R%*t0{U-(~0)*1L@?#G2$ud2$lKlNXFxHO1Wp@V z-97*<0CsLTr4&94TIq?~*N^N_NhPnRM^Jb0!7%C@s8 zgMPxY&Z>y@JG8zrR>E8%O2GzUD3fcj?=Cs_mzH&#>X9{E>j^TgY<@(=(q=5jScQ61 z5Tin}w?lu1j(pdmv50HKRmeI^k1aQkb%4L&f?+Cq!FUzYXI$Wm>(YS_@x}!maV+w0 z&cfRXZVc!*f_(hFgz+VD66x#EQCRDp|NLRoLX z)OWWHtduVb?ImQDXdcqbvQ>_QZR9&fmt-N1xaDnKsowq-r?5HO(0%Its z$euv?LVz=hbLhrIYV(jb?dk@y#6+jGPzI&#s6VSn}$Q1qs45A{@f@%EXb zLm7o7Z`6+{+L%~abR>ggWpMK?D%Mp)bf3+5pd!DIAYW~=(h}UwWQ$d z!je?@3i^Vtq|#S7zBo?Y^&#_%Y}cLFcDlW}v{;2m*->9(Jmx5OUqy4^v!y7X_U0eO z$^T3#NleWuIrl}6l7Z0?Xlo?Pm6j>xa;RYF=@jIb@d4fkjAM}1GiUs#J}~ZC=3n~Q zl&JM_P;`+h{cKh}_?L5E*#gLmG3hI7=_167mZeFhBX9q*>?Ng&c2|G0be{6cqQ~E< z<}WK3#*Bi$gt`5-X@asnm91P0U}MIn(h1;iR&}M{A{Fw?V3o;u!aAf;ZzX3`E$W88 zS6ZZU*Tw2s#h3y4^I5K}EpWi*u4pwK{*qnxmr~2V1kP5se}S%CMc=;lWa%c2(QT7b z$mzxfS^q{`5ha(bgq-cSHI4)W&d~8;9`vD+A`SWr~jaKYfxx=n+X1G-g=-$f547Orcn@`@d83)dH@ z{+_w#RB3kUtHPDMI~l%RPOnKscG&y>hwKt-IiIXO zs$xrbD|H0p66-XHJw=R~5^uVy_q++$^{`vi4AAugGMlDp?gU4`zbfD#TuKX->sX zRT_q5halz#C?=IE$AnUW?lPJklxeYo&XiA5_MqV4mT0*`mF91=`()Dmz|s5VxL(R`2=B~>(UGHDF~jm5-rtzbFg zO#e3(9cNGn+UQvMnG8fxs&*`tl2xHTs%E+iDOJa9an_-o>iQDUA!+rrA9x^;R=<(x zxB;39V)G5fCnT3@g<^_J6wW_J|pRE3qBSA)zd`tFVmd6HW|I?6s$+b$n)us>eF5J@fD9g+4m0Hv7LT?2(+>L8|h_u zvf=;_C~xZneL#6lHpqu6{x#T_AU?xhhj<#KHZ2)YJ$s}NU3*BIr?>lz#<{y&<;4pkDMj4=Ti3Ob~Z z4F*-Iac(@c#rR-LsR;q99?}z0)MKQ(*+ji6m920q2f=9)i`bBu%trb^tSJo*Won$K zq*Bx3EL};d>F8a}U@4_$5{29Tv)HuG65(tOws|l!g{Ghcs5u6ZWah@9CBji^UZ&E* z6|t(@B|C|{#gNV;#C(F)bxMLQqAs<9F-x+M1(qo(tuJJb2qjl$`7{g)ku5@r4iIIr z{H0qgF)12(sk=TrobeczV&?CI#g_D*|-?w@EHm$$nN8sysC zZ?BD<)0P;7T;H1ZOw9{H?$74-Iyx7I+;hY1IPtStw!{eJTBh0SXuf2gTnLtcm8hP7a1hIn1WecOcwn&lphCubFQ{FE>a??cKWhWeg6&)gF9ZGD~ zVcHf8iLE-6_;aP2olmfj1cC<(?4y8ght~h%&+MZ$R?1lMSNj;0K5a{cjFxSLeXPz= z#>ea0$LSnp>@&s=52M{EzoDN=p&Ak zI%h-favjG9I%h|2z$(Xw$nDf4(ZO-gxUdTj;doSpaO|gWj^`+xV+Hu5{_v+$I4@0v zdufst1Sz)mvq^Yxh(#S1;X4iizQY3i0JzzuUgaF007k2a9G`-0;9q{q@tMvM{>!%= zaAfibzaZ7|h0YQFkBuBSLL-m%#xt|8bS?lr1>_iEzRzW60yZRi*>x68@`zUoHhZg?q|5PNUy++i4(fhP>rI)DS-xh^GbOLGa(2=uam^FHPLVaoQ$sUalpPmTM_U&jD$xFyL%F=TPq|AX z)g?+5_!6bM1gV1hE4A(Zh=5T*mv}K?3~Y73*8k;U92pf5(I`i^F9Q&OE1cBGvvL9u zfH|!&hTWQ-mR;KZ%*pp(B3TqUF3t9__%J0Qua#mKrR3Q6pPrx*Af6@|F?-?*1 zxj(c-0q+OQ)YAr|@{aKVv$3` zAvau4u#Ia6lSSutDc&?_W9vY}PqyCuniNR;5CUwRY~?9Iy@D z6&mf#3fP4<;Tc?TROoCZS^7IFtQN%O_fnFO`zT3;5#T@D&Yw<6yfiIIg+jP*;93e* zVVe9d=dY&NB+3W%hYMA}1T8M)<2d3y%jq-AMJp9HVZB@7BnY-3O*k%X9hPw-zZJe` zcZabk_-KtZUg4^A`VYvCO-wpAp|{}XrZ=y{{>x3#jQ~<*(pPYK*6ZP^r-`kBD0n!E zVhFJH;J(BXw3ffY)6uE;c}ORv4vh&amkS$I?NzQF8-XPwQ09iCTnFq@H#}LcBXrvd z$ss?LL-CLvk>I|g-6jsLY-7>Zt(v0Q&$!ym{lE5|e4F}gCA^Fs&1TMP-uHbt`3HMU`kZBM< zSdzhoLM{~Ya*`!k{z)Nh1VJ1=6f68i-GTxh1@boU2RsHlW2Hq4NK}g^Mp!)6Jz^6#j7#H0i_%xQITo-|NsaxRl+RVfl`jEkx1mKnS zYAi>IvC@-*@hR|UHSnhsqn8Gyv7xpx>;p6MlnsGaKrEJOV*}ZaI25wZ)qz$p$|5>C zklp)Apatm61|eIwB+$YtOJXOoxjzJ26?HZY*>mp)S{OOxMj-prmOu-7VKxfc@f8BO zJ!3W&*~b^=(9guzg%|I&^*;}^6j>yjXGtjDs>_4`|9>`X25ok5h z!Ms1Sxq()e&JIR4@rghSV}q#jk$rPupp^~339u!Ogk37lH3m+@WE+O#Vk`_Q?WAGC zVrbw>_ktfV#h*?CdudP)4g7}qTU-8CdKRV&Z42QmVXe5nha>O=Bp<(8;kWV}YGLOb z8-9o0XXg%}eH%vr{ycfpDhIPwy5$+0*snRjsB=0f0E{r$#SGd8;^?}|Fef{E11=u+ z05)-LgMuc&jsN~OXd)@v#p^`{-Hn|hw&dJ|hfOjv7t}>&i^vwXOJshOGKVbz|M9l| zbjs|dp+}sK?$?{}uv%0=SS^YkwjXH1Y5`5k&=mCPY&?0{pAf{pm#Gr3sob+YFj#1>Da zkTZcl{6~XTodmJAV5-u9SgSN8)+*(|Z#vnZPOM&Qsy=)J zRgXh7CW#fQFN=u{{sLJ0&kx34l51;vKwx_CSLAD~Q{D?c1@T_gtMG(T!KZcb;HvE1 zkYH?oSdXi+%RPe6=z2Ubnc9$3JHt#A`~Aqs*z6-L?QpDxxDp`&KO$6Q`~m#37=Jot z^wLDe(K-59ph_-fs*+2YBC0^9D!Gs;VmA`KLV~|X|1Vz+zNGz_GM)b;_y>)KliK)A z!B=#SE6vqYg0HbI*5Pce$RHgx0-0wtqL*lmh$BQAaUb|q^xiqbO(#+>4P%T|xjKyY zOv4dTL>v(%h))1jLs$G>jJU=AZO0O|x+(;E{Wsn9t)R-w*7qY!!h#F%g3)5B| zsFw>>lMK$=Fl5!?g0t!v;;brb^s3q5AD-_|Cr&R-G}`N+MBB47m!<cBE(MUoH$$pB_diT)R$U*Y^ z@}b9&yoKbC8KK9Oiekj72GtN*RJ!yYRhOuu8gmt0trW-ci~Y%1@OVkGI*-~EdIDvW zwWQTvF_N~(K{Ks&`xRw>6_r=w^jb4O&I_X;YgCAk z14;hpJBiRyi6AEmSr!plVOmeslBuU^$<$L6pC9OCwdz8yF67=MSBLtd&MWoMyMUwul;1&=b07bwv@sy~rO^(XS`p;ShHAg{g;Lv(p;C?j7` zOei9s8~LlJLocI{LB90MP{hHgM2);$vrvp^h%N{Iy#|L~)jUS*>WhpVdDqyy#{0C$ z@gn7@z8U<8<^FWa;ic&TTs@qCtA`8V!vI`89Dt)6;DG0HMCc6wUVJX}XAOe`xMyJK zZ{+Kvo#l!S8n2pH3KFny8b<%7WmSpgG2LbXTG|Ml?q; z;=gxQY>WPLhE>ef#wD6A-&br$zK&|q?)}fU3LxrZzi2u3i3IyfkgAXgRQpmIJ$(@r1zxyBK)D;9o;3_S7(X zd0{v{tk~PY0PoM3ig|hl;9fE21Ug_`3Hgyt=+1u@P&WnCY48XC>Q5&qFHM7rnM~cp zOr~yPZUa=zWPplYsMPkzkctD)ah>rM2kMS#vggAq4)TIJVXHV;=Ljm6>pp@yjB|uo zJs-r%d=M-1LF_c}w?_&n51pXAGz}`&K~S;s3@VoKKh_b0V2wmr--u9aJSND#*`d}1 zXENQK+nG_)%V&gZjJ%<)o~PgM0YL+!f|pS z9EU5hXx5)jP+pqe{sr41?M_5fvLy0TH64^bH(FI}9 zgpF2;R|dxEP837!Im1=Cn@^o5G zm=zAQbZHjGr}uGujE1aZLm2n}vW!7CbxIib|I7wCQL}BsFe|F2Qa1*MS@5758-YIl zx1cbKM+t{3g>i>p<2l{b8;irZqp^iS)0k%CKhU06Ib5)Vf_OIRtACEQDi6E;xd zgd*@=xPyTOnTJk^y)^A(2_jsAgz1~rt60`aOq`~dv_ZSnXIV4C z-6wPO=PI+qc`_%KM|SU=a0_EWOplgo_B6mYBJ|aqBLHfa5LnH{1Xgn#`2Y0qrxTc$ zW`O-JzDVJA}dBn+KU?i^8oPSjsKc2bs&)gj+kcwt0}b&>n8>V&^!>1f0Z1 zM#eM0AyXi-Bo3jBiQ_0^VmI*ZdLiebQ${ZhZBs5e_1C|NlObcxwU99>5kT%w4YwXe z*W>4fTNvqlu0nvsb@pibik2Mf9k)3oP+H^skXTbN9uqJ*yJ*>(-@cqHBR#|~*0B5+IET2uUvQp>xoOR4ARj>->ku6UV zs7~dZP@rH)`yNL0(i18KpkLwLrlABI3yfj(1MqJdV zcu75oJE;e8C)!=@%_IWr0!<{`OpkDRd2&{D2Ln?9)HIKkNNmIIz+gBEmg_7NI zj*r~wn-R^C>#HxpJn&FNE6i;;tjz-N{sR#?;K7%(k&AB@(OUCz$U7L(M)PuUn@Dwx zXsdZ0$vYmAt9f0Sdq1L`&h=pKi-`6*mj|8I`Wx!xRNR4$jTBX8MsxvgSg-u5HSEHVKeF;HiK`I zC%QivF-Y_Bw9N)1N8kq51lsi(!^%ho)Viqc{NCG52ehb2~y_K1gSS+f|PkMLE1fd9{yEM z)i}UdeZDGIIBECjK@_#4YE9(!Xve+xXw^jIp3(|>s-S9;&Sj&uG@g4A@OW4-?PYPM zwATnY?M(tsV;uP9D}Ooxduh-U%5B8|+aO6lsjpU^%nf20s5Oe%M zm1neu%F`s7K9%J3sY31#vQDObETumdGUxxak7KaxMeyv|CbpWjI~GqK##gf*CJjO( zd+BU7Ymd%y{;%Jpn)Qgza{li#wVJh;_SEx#-jHh6qdLp^|M|PCaVbGAAJz_CS&d5x zw#@m+Rl%sh2D7qI$k3kuVKfTT>sQwQwyn|XWXq^L9@|sr3yh1m|moitG+u3 zdj;mWP}}YdnyYhM{tW#+Xr9h-ZBe&%P?64sA-ByIRIGD+g!M#&p!qr%h1`;QK_xoJ zZD+5if=YER0l5z!2`bYMl#`IFcTdnFz{cV@m2lrPI=BW;$^2Pl&BU{9WNxbz{GPWU zg+Dpfm!vh2DGRDhSy0spfCe(TpsEAGto>m@YtZej@x?q$)flv;xjM@kqdS09Pa zfx}$almt)?RF7`0R3~glSSAOQO=qIp;7z3zO({)RYSqH%ws`)&MpG;W((0mUz}sf3 z*+_cgo#=MB^D|JFa*))y3(msT*{+%tL{h!HXlOCflnNv1En9R)Sm!HUDuJXv1EV`B zb=M!7l%oLa+^y)&N_`%oNe4jrCoH-PE;M8EBJ|Fv_Vk!&^i|iW$)Z-;{Tz+yII5X0 zL|wjqEE@CiXPC8FjkCW?6*NUGP$wEoKIbV-NyxzUn;Owjqhlxhw*D~VY1f00 z(MLb^<>;rr9R1Xv4L-bt;m|{;r+H~O)n;RKvAWGTBI-6{x<1c;>NcaJ0p5|jteAnM zo;3aad4dex?%(w626lRO{qMm?Vd>9JXMbLr9(?uX?A*P+4 zyRDdc)v+o#2;v-Q_MI1EiUzKP8#?dJKZ6Bwvq=LFAO=u8lDWoAr0G)e8pd zqVL$SKx!z{Vndl08*m?#cF>O0b+t)Cs|*zs08kP61!|+_25Mz& zZyV`Tphn{ewb5ikZPeQf)kF7z3Yvj>k`B=DBw;t2NvJ0QwJ{>$rHfX~dIPo5Tn+U; zg9K3TH%UN!z$5|nL6Zd34NS6Cu6wvRa6QF_MXQY?2(@vvK>f!H)kF7z3Yv!6XfB~P znoFpS69Bc*Tta;b(XPK0v&lehoUEZ@V2IKHb&E*?>Q<8k)NLjSsN0!jt9Pq)5NeZb1GU;PHY`w^$nilFIX-BT=Y{H_`#=RvYqjwQ)N11o zsMRLi|2O^sP_ruG`P)z{=5YhH$vh2ppFsksPnaa2K53HB>VA_1)B{YiRmMGPb;T_< zEKswmQma{V*{})U12LoBL-&CSnugj$_Q_3TpPa?}e@(bg&O*zz>sT?*7^qpbG}LDe z57bPLsz8064v_Vj4^%hZ7wW49YL;9M z%#zE2S?>aB7B2@j9i`MWnO4jpH`EU`)RzqsKz+p|q1D4C39TM6NkDy-Nocic3j>wM zWKBnjR+~;B)TUDjwP_zOR1e(;Drj1(Ss&5?vOc5(G@VVT9|CH#Fua~z&5Ai@pf)Yi zP>&lVfO^6t0rhp01k^W75>Ve{lC54cP*-$h!veKgB%wBoCDdj(MRPlVhwcLvG!3dOPYe=3 z{nR9()z3^4P(L?GK>dPAwB5X=f!gaT8y2X|CsM1;rx9xNzFw#vx(`&)v{svaBv3yh z)aHEuq1i`(nq3*o-`A~}uMO1Z#Tx1+KB>^MTr=KHTE0(j^?P(jmBn->#m^I}5HP6X8E#e|CS6W7>^IcK0|r)sF@4H7`T zV3L4((If%&dy@pzOH8s=-uq(&7@viJEwb<=&J@`-@JOC{9oR7vyv zD?1fXTZ~a^xn;#%aYOxBLxl;&K>_ueNdoGRCJCtjGf6C|eAeqN{^x(`&)3{*J@%9fL$7IO&oV?b>gj@55PE9PedwZ(i5^%sK#P=7T^ zK>f`m0ToMPkp)oyV3JUOFi^j}$%X}L%P2x^8BeG!ZCAb_jsQJ^@Y6}2SZUFlX0OUmjt=#vY zKf1-@qJf-P0?4UJ06Dz>Y}DS8o)LI@Gv#ddO7&U1g+h4UjW;{r_``eOJprJu8^F^BKs`5rUkrdvIO=E{D*)QoB!IT51kg6b3&2D7 z0RWoTK%1upz|#cKHj@CJCV(wCZeb+E<{Fj+p@9L=$N*?N(*OucVQ~#WUV~{Xufeok zljl~5(ZW1++ zn@$bnR`de!(0u@arZv!3UU_LNue{{eC4grM0Qj#gVgU#SNAj|cB#pA7)|Z;b+UGHZLFPKCu_&=fS{K2>87(!FU|D1Qu7lbvFHQH^UT(p(mw48m&%fF$ z_W2w4BjwNFa2WMra)hY4#3-!FPS_PbV}l%|Meo!R_TvaEJMX#yi0svv6khNo-sT zJDw2~#yx++)S|9{+v9bSt^r2$$8mk|V0^M+s(PSR9~1`@WvUdaW;@|j9U+3>lnHB) zIjjMa9a~_X)A1SY4IRJK-F_>=T~H0ddYk3^$&GwTTG<_2(BC_@pucy#0%hxDhcc2o z$n*0~Hjq1%L6S}w|>f|2(!2?-EAas&( z-bu!PCyxL9NBrq@1TRfv>l8>3o#dHgCyxJ4fdJ7NotIl16}JIunV%Q8l>p!fEcrSb z;o$h?2NshKI%iWQo!ijfoxTCToW4usN5A1mr=>fW2kC!1<96c4Tm~(Q@bHnHf%)cbH%$g}CkZB=Q5~I_n#daj?VjZ|pwxzEVwfW8eEz`5d8h zV=A_DHyXY384|hySC=Um%iFE^0#LhDMrEpU)@m|Dm0e~LP?sXAvP&BHD9jH1=sXPc z(s7`ox^VLs{%!mi43O90-|`L;#uK%+E|xiWiD~h+c6d1X zoR@Uway-9j90+g1n3akvKaIB`cJzFah5`vr-KRDBUs?&zk}fi$bWH_ls^yg+^w(Cz z@79UvyLG2py5@l2r}e+-JOuaB6&3(pr-1a>4adi& z5eEF3Jo=lmFWwf6z%z-8^R4+Vn7@yUw~pc)ZdWO;VG`p&)e>!ALJ8Qz!mPFJEUTM- zA$qp}@L&`RTbOWJaJa2P!9Gliw{iJe5|4}ku6YtBybwg=g|`~E}o5Ik&jh&Ar2Kq>DEqm{bxIl#^8{&WT) zFHJ5-1tvHGi1{wNyhYqCpP6p?6yUB%G~F#9qo(^gJPDm^)o5yv-xUK?9I` z@iutN2Dm9k6qr3^<=Fia{i!=o|5yTUQNJ67@G0@LlwiPz$Jt8p=nH^#8yatP1EA>k zw}SI^Uxs3EKHE|>RN%!JY)27{3-8G-vqNDKHjzxo4-%) zsNel(#r4VFo$&l3dCeQElJui#Qhh?u^Y zyvEZjidOFND)=|68`Fa$#IFodOz&6xS8zP02ghsrp4GrIrhik!C9_u|-K^(dG)GTP zF~;=KFn!M}-V(jaNr`-vFs7gR1yv>`1h0?jd-BlBm_FoFZ;_tgcue1OJBk?7k4tcy z9%yL#p2xs6rmwu>j`~32WBOkbec5nE=(OkgP^GS2)}y`WjSy1!^B1iy$zJ6oc|+VF zl687SGTn>sKlKnoulRPz!@axQ=ZLqu)x_kgt!aj%I)G5_db~B=5Kw`u51FWYJwj#l zdW6d8ok1Xvz*X}4VY1y5l+ezOXKhOoI*_6l>!&IvVAjnWg2g%{?afIrY>!~b8%b<= z#d(6;}O0DUJQ%;e1!UXdj{2EGdi6MEn| z-4^f``m{Qe1@e?G&=)sp!tW?BUJ7v8m%1sTKQ6Olb>L}T17-_tP+-!Pgn>%^vG!jD zxYSAiJz)?+=wLTyalE`CaJ&W!9J0{uU6)GCn*t^Ft_ynqrIx(&oi|04TNirm6AN`+ z^X|{`mSM-E_h;>AAj$n1dqrvvwS0{o%|0^CD~^YnQ~ z=m`_3YR$-_^I!_83+_2^8=OkG8}|Culn^@K1KD&S0h^jjl~Oa=PC~(1^@G@4BJ$x!d0j70^y=#tySK+^T|5;!i3QVe> za2zYVZk{f_rwUCAPqRQ-!kg%#=D!Ni=ubYA@E#7xKJ;is41n5V^uB-4=zag7(ffY~ zqiaI{?_l(SS8(a?i=c#0?=bpjbS_x>{oI7lO>%5%!WXOn>XN+)U!tvRXmEhr{%@y& z`J32z;4Ok5WTX8D(Ed++;ZKLH2hjdce39@qErklS{{Y$_w9|%$M%?x%_bJW&*2{&9 z!N3FF0|Sn67VGcV36=#0!Kmb21`ozR52wZd0BE`cZy=BT!MlXlJw{R-Z^K&g18boG z9OD}9&VO}hWlogf-2e~!*#D?J76srKzu`Tv9X^#kW*xX31>hKe;)>Ot|17}09~|Q! zbOg=oE+Cg~0YhLQ_kVB<9z0cO|F04`;KDOlej-P%=tbLxu0f|5$n{U21oHmZNa%l! zUNR^MWK9?p6oP(_0`;mDmh2#oLtTA=p3zc_?n8PdOC+Q$A0;=kHKX0cgWlprx_%-nFj~ZnFc*$ zvA=)QU-zTa@Pp2xslXjhOp#SWk(E{em&3)@$vGA82H*tGLl$iea*6QrJDBVLK`zKR z_!xHJ&pVtYka5rv=OWgEj2bzXAA@dUM7-0_xd4Zkpkgw1F63kkc?P?m|KgvRKs$na z@LM#<;FFYRFjrPnp8q%f0Y5sYtiex$baTEFlgohD=%{E%j#b`R>)Z-II^?FwkRKdI zV-60ZJcB=lB!j~s$&h}q(&(U?H9L+TscI%NzG!2Wt*n`(U+8l{$nhW5Ox8JmjCM#J zbWEFEeu*sz)*(YE!;k{XFoXl4Kwkv$(7AN*(sURa{FPw(ikOCsBc`v2=^Le9m>!ha z6dgZtH4&C`gG$?<*j(qhKp678fiVYD>yU3WjPnBH2Z8ZE_%BTNrxS*kreO@3L>NOR z35@H2F=P@vd}ss~a~Y1r!SL;;_t3-hrd#;!iq9DW<}I*q}Urj+F$>Q2Dm$(6vN4bO!i0?EZA3^wKoSp%FwmG=eCHJ^++M zBLHJqFb+=Z1tqlyj3XZ=bs$B!1!X*xa(-6R0ycNo7vF?17P0f$a!_{3b+}--5`7VI7FJ{pkq)!{Gj)C&@Xit^j3_@tS4~ zrT>G$u`-N1;OLK&M&K)N64c#vJ_oKoC}}jJSxa|$cwBxJ&{H=hAy$0uS(oK^G^iLc zUV1zUHvb5do{pj(ri`{>HEFD2HL0uow$PF$X2lCjR4bB=gzZe|vk4Z*D0HF*G*3|ll~s5r>4sw+4b z4kizTwm(I|@jhJO6zz@}09_83Zxs!fZxxLgKy?r2$BIULg7-cuS+)4+y0{JUQ!+7g z?W@%k1xDqy~}A6T&Dhl&bfQGdXTDZ6k~_PpC{1v^B21zF$?3rg98(DFKpK1~f3 zz#(DBf^OiQZJY9_$nPeO(Zt8xL_jY%EX5asbMKmz$K4!bB(2R9tkyrRhO)=qY+X^b zS-|^0Bd%0JytycEgciu%oDo;3(*il37;y#Sje>^Wi*%%R@e_}+Z@`PGK>QDksmJrE zKnWZp83=yqt#w*-d>WVt8%2?VmZ1x|L+8W16+i6F^77wBr z>1~*2H2$x%$4Z+G$A4i0ni>bk&reO8!(Mp!%OKePH5L>HA1x0rM#~rNMsXwj!UTUh zw_+cjPMZtoJY_NSR#w_P1fQ=hsCCp8sgYJpAZSX_ZnYmuD=~SaG=Vp2oJcy3P)A2V z4Wq^Z>zG2kw!6-e-VtGNTK{x7!4S9ZEo6+2{!J4HyNMbUijN7?oFP7(G5a}C$7Fyr z*EKdU5&)#MEN9YeNuDKVyc0ck*KTC}=|*6Y#|;QXfypMEmeND6*fr zXL$vmLT{>ifyJsDF{fKUrq{+0jbjlMl$q<)3H)rwi5|so+ES2bHW@A=9uSzdK{KT=Z`v4UV@~E zSctzE3iF;q{RtQfBd4Ss%D`icDJeKy7}p0$`{yaI&_|%7u{;C){BR0F&IQ@njl8{AZ=WTrpd91Z{cytn(?u>m4c_G zMoZ23%*%i<)XtLh6xq|G8Mk&Q`nW;z5xTJxL<90WZW8n`b^`P;{*qGPIzuv2V7-e~ zGg9%dTiKxv*?>inGnwSW*-NfW+&n>SEFuxfz7 zet!fentwk=?SzVA<_Yp1&;)r8XnZ;FzjgZ4q2KX|p!bf>fF0&fp(MHz))!>vfH^A~ zpB1pwwtq4EgZYCiqaL0d%W(s0N~I~FJFaKc$7=siQ%UtgW6P{*qeiQY23VQ3bJN+$ zc9j34Sq41*E;%K0x5M?{-klkZapP>9yF83I{zK5eb!KFF0pa7+G|B3FGnyKr+awro zV5)Ckuvp-m7c#n{fECQ?oz|LA9;O*DPszqj7xPW0-6kY}JZ?HHHemzy)lIBgymiV& z%LKmvghh*!E3Mv?r&djtVkOE=3)J0&%r33zN9m9g_HzJESO&gXuS{^$ebz9b$wRvd zTL>ZT1py*&I!%x_ohCd7P!p14@T3pMN{w)&TQL|}56gy8cx1uTX1 zjQ^!uBbjc^DJR_;g{CJ~H$ZE>ier@;0(4R`flkUG(21PV5;ptOv5=g|siw-nbZZr+ ztMc*)e!>>i!oR=J5?oII{fDGmcf$vve*7D4_?^_)5+)`}Rih-`nu34!q$HBC> zOt)qLBOIbYE}R%d*?bf>@h!I9Ki$I27@g!?_XpC3-Qif~5ts2{g2>fQo3 zVRO1A6ChgUv%|Z&1SYY}TYOTsJQUgiluHcN?L}~eNi~!Omr1$c8^R8}Mp1MK`(B4I zjPPrtbqKqR|1HB`*k+NZpXRiKdv3@LVxXH(#wF$vGm0xw6{}Saq3=bZK`}h zX%ZFizCLL3(4nkJH1_+)Tw5uRVZ4M|=HKo>3TsuAn@syZvD_s=4dsm7FxKP-;7t0? zwG$2}5k|LygG4gBfZ6ecYd7}j_n9t)UI2RZ53YwX_y)M?Fz)0M(6>I|+Jjs4PrKI* zXpn}V%rn42om_jB`Z$URee1mdUh9FbN3lG>!p_U%w)V5NuE(&Dh%n_vIt)VU9p{zD z6I>rbyMMKCVL`Pdf|fwG)*{y@7$g6!Cj=<-kM;-G*c0@)J2Eu3?>PTOQU|^A+HyB zd6Qfit5tUS^4REj@P0^for4Zv_mN>Lw}4%`xz6L?ZO|TvwrI-L%Ul<*DVBIg9ViT{ zshc_ACd&(hlR9t+PU=9)RDS_fh4AY zub4!b!VK^=nwm!8Y0=ja$*1c#C8o=p64T{PiD~u0AN#3T)I-lhJ-R<90F2iUpOOgx zu^&pAHUu@8sQ`ma7ToRtdgrlG-m&^VU^J7%Rz z1Vy99J7s}8^$4{-^$4{-o$r5W!t``#ea72}(*;qkXE9C??VcmWjIqtBuIKd$sf4>u z$6PO9ln7z^VVdnwEHlFn9U^y~b#>p1(-pYNM~5a4K^jWy8zNwLEW z`Ioq6e9aLz{Uh+dF7T&A`sr7M?m9|wbcLxeabV2|0HZM1^_pQAX`$d5ao`}_oj@#F zXt=cKjHaNU``Psdjym9A`t$%z3a<}%S6_F%iD!<3F}O0^_1NBQ@DL%Prljj$7*=Iy z9if*c)}RDp9QVXbMP$Okc@4)2he zF7FFWZ$z`rI196B!i=+6G0mKT^X%WOczb7THOFVi+wo^qRX!JzYra2jMU|^A1je*@ zdp{OOIyO4q-XH0?Xm4g$V4y`RVN}g-E1I7zcO+-a9m$y^!T)iqKb>nkFAa4OaelXe zW_DZrUja7*$6(pUn}IXuqQC!xOIC|FqP)3ewRk(y=91O5hmbaxtgdZE+FY_)#2Y%Z z!i)^dt6{F!>(gtp4xO9JCBiK3fjpP2z!73ALzGKae#HlZxb?lKtoYM#vBP7C3yl|61Cy>?vo|(qy`=FQ z{n_}9p=`VmVH>6YO>g5zXCNpX0upS5c{21C4!E6c1xoLy$>;C|ox?)LurIs^L>THK zu8_ULa&w)!Fy}$gu~>eyK+|QU)Tt@|+~@3pRPgM9RB++*Q1I-5Q1G0cI7gV|aN2?~ zKpTATv|(qH#3RmX}61Gw;zsMKl07F!0_$TTNEsU;eiQypDW12a~lT8c2TUPJiz&FgOi`(>5h%%%(?Wpxi}%f=$SKCDmiTu4NY^oy!SkZ%l}&^ zowkO!H?~56O1;{a8UW7G`<=EXnB*Rkpl2#N#eQ?~XLrXa=Nv>}Jfg)m&yH14zH=UK zcwEIo8D`xGG3M;VEdSnF=Ulc8Jnw9@5$mbvmi%iP1@53u{wq3^k02;FIGE6UV0a%Dr4 zlk2qQ8mb9OGOy3=PC@2&C+>MR;GWwZGy1&6I6>NF)t(Hr!{L(i0-?=6pQ;VrsWK%0 z{;T#>lWbzIJ&h9(>(liKd+l~MA@Js{pbyPkM;*_b3I6bM{&ectOVbf#o?Mfg7bu_} z1gLp@AEXF=JF$^f`#5w7g4X5H$C260&c8C958B{aVdGyLN9RTIW%qeMfq(RI>D-rI z#7O9+%f$Nm;o2v#62g*1Ppn0)*pBS$vAY(%&D%`9&69O$5x@Vg2}R9d^+i{edb>-A z)5d2H&?kl_`OaW7NUzdSC36~+WK%}K>E}`rwvoOnG2mZgn-45 zgZ@5T0sSw29%+ma+W_?CM7mlRr)?ksB8__twn2P?i*#(X(>BC}4A<9A+bgJh6!azN z_XyH&AYH4E({>c;_mJLN-)Vad>5mZ}itmPY86JjWQM%e49u|LTF$ff|1v45gj=`0; z(?lV|eX%^*$2`L#&!9+)DNfr=JV~u{M-hYq$X(39KRnK9E5W9**rx@s*}`?OTlH|- zmSKPRE@1tji)aY*D}sC9S5Dgsd?jt|zql@NPtJ1MfV>6_dq>?);9h;iXnN z+(dUfe>9kz&N%rl17EgdRWFmv|6LJI+kUHGEhTOL5T6$2%e;FMC(?g0}a*`PH{ zt~H7FQ8*kN#GlFdPqh26R6h@SQxDB?vb+ukNtU%$A(bz;?X;q|KptVx6}S14sS7}79W+}w05dNH_?dvX!k1R zy#MVmr{giKqwi)*K80m6);?Cs&2Q9Fr{g`PzKZlK!+EZhd%!_YI&B{^!lEl7jxxkg z4G~=<6tVas!}-#eL-AzIS;}=FTa&ABW;FaJ6<-499ZVWmO-1yEjX0NvrC)<};h@w0 zEDqFO00`N{c`9R^l;Q?}YTwyu`xzJ6>$qiL-38%P*#xKUAKW}|&yXa|V}y{&rBAIi zCsZbBRy*f`_ta2EdykPWw#+eayKYCBj`y z366TUQ45d!UcaNnQBScl?)cG=qX#}=^WuLO1KzS4C^qSo)3Fx&akr`2$WfvxYi+n& z@pJ-%%jiJ@wmx?*8)-_M#uF5ISZ(ALBuBZuC|hcZT*h7FCyY0ti?pZYM~W~8p1kO^ zcSUHQjaqQKYE#n5+{P40a!(R&uQ34nWl}^ro>=e ztStP`HV&Y~jvdYbxZDSrWtPZ|6|GRy1}8t3fG+mIrI%DRgF(xZ1MU;eo&49Y&kgYP zX&#L(-iSSNeoz+Q-MC&es}rBEV6B2&ol03iJ#{ZuD%CMP?&jhm1ILL4S?yT}$yY09 zbzljM`^CM{30Hl|46RY%3S<2_YK%EIz>Hp-##=eDR)2I6YdyeVOQKMH3|7&&@;>SdopS>+8&EC~y?X zC3`ccBl!lBTkB`eK$0JMs?{eG%cjNeAz7_U<}7+Tl7kOq&Ng@RVxu$RcQfb0N=q>Z zsOyKsdMjGf1PT)iaN_yg(*3+4Avd*QDSYFF_^Ee8A z+mQJnhHG8-BGb+L5xiPynHa{EDpOrQ;iTrrj zw;p+IH17Zw$6bBvvCW}q^Yo_7=P_OG08gea>W#d)n)eEpeQDsy%w=#cdjPyfX_?2d zrYX6z-qV`*4whPt@2vMec&`^{zK?(5W1f46yW9K$yewPh2Y4)nt%weEhPRwi@Pm4p zAHgd(-N{P<@5yH}5yP**gI35HkTw*;4m7+x7X?!HWMVxK@uQEKdr`{eT=*=!C-YY< zzA;beIZfBrGuHBzx&ZxV;AfD>Z4=}d%RF(2lfd%FP;AnvO#V~XYqPz*=&5M=yM_xz zruXp(AkGMotdEtT)Q8HZ-|bsp`eBiKxcfYLG1F?HRBO4pKucj3xhEZr!rqw{%sUDy z@P()&?>$`lumncC4;i>Cj!dY<&d~*`$ zq0`V_nw+LxIL>qDRfq5-=7`U^^NOF)-~Yk6^M)dnH|Ne979#CFcYY3O_qp>yq|LeW zj87rMN^X8A!_CV$1lFI(xORdvtbLR#{gqq5_nteSkR8R!7YtFKJNp)A0Q8O((x+x)Rs#Pse!kjw~e#6s- z0Ps0?UX_nx?sI23Y|V{aTQ6Xcqj)ZQBTde-XB;2Y2K(0#lOG>D1Pk+?`V-+S)7n&0nU zy~Z?tFZ}k#IBERGWHx?dCS7^;BjCHwoqg#C{pgIktMMGloICG51vgs4R{YMLziB9B zgzq&rl-1|XixtbQU7`y&(RBFa=lwKYM#|A+a_+oJzS_A;zS_Bl-~U?0uXe5t$3d;- zNIHb&5nc#@1Fc=cBQLBRQxBl823T{%Mkjp>d;Emt$Ta?&SR(xzT-*@V#&tm@{k8P9 z_e#I=we&SS1W45@N)H{Pt~m<&ZyS=%;YKbZ=l@08w*c5wegB_x?_e;-cn)J;xSliS z&K<-cMrp_^&ytkJ7>ve@7#T5)NRmRS*zxDQ3&~(BzbBK-}8p zIFDzMNUnw@ur;0rV&mHm%qT{5k@=S{Yl5`fRQxeo1UNci0L2RsvfgakdOAA7ssDtf zJ5=)tbeySW(VSB`_S06L#&~kRuQaD>i1P#4oKs%_{)th(aN3F&refWx@|^e-IqqzV z9CtRA=f5k!)VZ|qshfD&_=P;|ILR0yw4Q(mx zBuX2vD^yEY&i00bUj&c?Lc*6ixN@2K6D|X4J4|HOuGwMI?p;N!qEUI)z&tsH#b zbjA#%k56!zF7PaiG+tvdk|@a8Tc!%jNP0o1NyiY8&Uv%u7>HInfgr)NCy*-qb(jc#j-ya-P9h(c_}5{MfNpZEImwozz~NaAGaD12xm19)O!|K! z$Kp(yr9jqkm&u$nfouqAU&dnO+JZSb_!0T4VI)Kr%*h~VOSXeg;r)n)QHwWJ57}M< zva!u!PQ&ica<;6c%;|wZ?shrMw-K|#NBJV7fb6%|VZMi#iN-?-Z(6D{y^TiBfq9Pu za*mZPW8l3F9p(ZU2s)T=&X+WDF&+hf(wegcW=0$YYcFx|OGeY5)a9X9&P-%orf&uI zrAZEc_V8_tD4u2k#f*i#$bdaPUz4qZCqG+|jAQNFWi-WZJesh>TZ}M~F&&&p7Z19< ze>!Y=2&m8fA9U>e1IIbc1Bgb0Ep&>@V2wS5@8kWdvBLfhAm*HLm`Cycb@DygIQ$U{ zPk&k-0)%l>4R3D;B0bk({)Pvx(5${{5DG5)gTj`NCuLzzz7(P@mj4d>0GyL~4Xb_< z8Lo<9!F ze0(nn9NN1%AQ?H>VS5zq|F8_QN}bAP#c&r&aTvvoOQjsPr!b3p*2_!i&;=+Ff<+K$Zdk4d~?DliQUa;H)3X5^r2jbbo@$PIK4p%(muw!wjo!hX~sGs>0 z1K1f812~G|GUH7S^eQROKhNOt8vHQyA2<9QL+kNfLp|L8^)AR4M#;#OsgoF(;Jjy6 z2ei&M#|s*YVG?UTnLh!FQ4$V(#waA`EO)#J+Ac_9EF8(95JkTi16dg(OhR+aRGa@Z z<@Mj0$p#GKz1G10^{zwNg&+_v5%H8l$V>t`1eU2_^7@2_OA`=t7F;vJF$qon0pw9m z7L#Y~RL z%pxE-+t{%HYv%{8nOzEwv1u`u)Cy$e<4WVc;nHA@P+l7!mbJxco)WlIp3zYVv>Pfvi}8PNf9HB^1=OW6&^G78 zwGbB$-27DMW&{}+X7Qw*#!)fzbDZ1pOiHjdj{#Aqji;#HlZOp4oUKrBj()s(wB7j# zM`Ogv+*j#GP4oDg#v7d_SwSSv1UR7%vC@aaou!a;0-Dv=8Hi*ZBtPxzgr|SSvj$Ji_drK&^Pj$@2*x%5rlf zA`6J$zIQSN{PB#JBc84$b{ep|PC3nI5SU6B2d;wn5QwGyoo0W$n=bGAxDlIx_{(&1 z?dbcf)`IeYm~+Nyq8AUBC(w`sqU(b00y}V=lQGs44SmGGxH=~k*kuno%_&&BL!Z)? zp+uR}L?dTo;2M5UR*nLt@O0=5a&jI;(u+vm?tQ$9%~uABK%5mRWJk$9X ze-#TmD49jB41HOPVAO4)dWC-BFRaDFV_f8TzZmcd>VPUAI8DL}=a$F&Z2xNQV~4{E zW|PBXr+K+TbAP|Pnex053y0euZVJ6iFkW<~nU-n9!aB4S^|z%!pYJ^3X_hltF72Sjyh z$4-Ix<)9rqiR8ddDM=NwusVo#H;H}AFar19mx%LTC<1+V!)hNQDb8;ei(8lsqP$6b z>#sbl+iw<&!_Vi#l1^^#jt?#J$)F;g=78L{XS zPew>Kc9Pb|VwYoOeS(`0ieQYEyU0W&@$on6qr3~`jOzt*#`VHeu(Jg`<9Z3Kwf!zL zmLKt=oh{_yRRkgy^7={Vn@}B&wuGH6S}S(80b&ew ztEWp*FFIH3Y%?Oaeg)A`{9+TFVQ1SQI=YQgeCIRBE*E72ix~mH&c4UOV0*Wnbw0y} zTg)yGJKK%$I+&T*raG&Vfkgd41UtiMv8E%?M}&x{ds+*54MecBpRuHhw!RP1XCQ(i za`7~CV;>^1v(6_(CPo0Tv!fX5p97IRiLxY7P^W-`ogGKqEDcFXphpO~q$p6Zvr~p~ zY`t4kOE@J&R&8g97*<2c0gocc6FWPH6()EQ02H0Lu!U#n!WPQm6H6HX>tuT*10Jmc zhUg+?uVQ;?2+ZumkpOg##zBYu!);*Ru?jLTc?E>YWAOJqB1iEL*r`3U$A zM=G0g!?|eeg(-KsL|(F9(wXjb3HLvibcUI)c?=8QKl?>Ii1gAcm`2qJaE*EZad)e@ z`gl(aqw|`_OcQ`Rt>U2O1rx#S4$jHO^r@t4DlGsb0`AUUcTH1wez=Q$ozto{-4NKL zC)5dcVfd2Te8Av`wV5>+_@_kbHAz(2n*QtwAB+G#w9dWo$v$wl(g#Zcs@K+qQFT%a zX(bFEYcQk`w;8xEySQe;XTr=>KtmjQ&hwF$00%z|53(=v{IY}AFfA$yi;ywTFA@|Evp9f3br`d)Z{z?Z3Qtu?Yp zz_GuR5WD2O0YD*%j6m5S+R z1tjOjxdtLxr5*a{AUx4x>*+cP&v~4z>}Yu57!?cf$>_9=;R-A5;(S(3rY@pRppA7C$c}P z2E;2{Y4k>ZBWpHgFAri=;HqcPT$foKyKncBD7tQ-L(PpjQ7-PWzdgVkZmd}bk~Pct zNw|$-1K;S$9-OX!<0n4(tBVOClCi(k>*N`jDg+WPZy_q9Z6FVSV3z zCe93pJp{WeETVcZBwSt@glBW&Ot@mF`kul@XofzmXJZ$)h^D;_>7TNju)pOk0siZ- z%RX1y6m*CvmUI69*WoyM8U%U4ABIiDP=(l_iGkP{Cx~|xVy6{h5sP`(DH2(yQA{_+ zc}cuwUaSu46a&=y;|D=9D$#{Ac4*1+hB13J zyxhmVfjarE%j|*)*DfH%ley&;im{raJ1A=UQk1wZ6bm&)4|uIoz7z;8S;_`b)U4y; z_Y}75q%CPd1bJ8^(Qot1(24|ERra(#{!3=1E)#ur{d#N8o^bNP8kvDP}Q?smD%Nm$iiZbi1>F%&>XE{ZdM$M7h# zG}S=v;GHDZg~VlI_^&BzPHqya_*0H6BLiIKTach!LGI50xive^L<7%!L@NolgRzE; zhXcFol#BamdowI5{B$}HTe4l|Y`h%|H>$>Sx=9xJ6_6N%%>|gkj^dn4D?;%l#m|~z zu^~CsFzpcGbt1A_COmmFkBR10fNs$6v0XalIZ3IIG~KrnxTx7c9*#pwYh+{ zy2rRbW8F!y0rP+%z7)#1Kg-b+o3P7J*Ox*W_h;{e;)S(wwm7WR{OC&ocR@LR&=e2h zh#3qizH*34JbL-;4^7({i?#)5-P1m>ToEi}E^{~h@FicCO7IoUH6w&L<hGwA6}vU< zc?{_oVyv{*s$EuOHb1OjLF>L`Wb`>*ezo>4ytHrB29=eMKUAEph&nUQ1mQwOW zP1Xx%m9H!#v~e~E9*RSc zzo((4x&`fQawu#P3H)*04d0$u%n8AN=g%r9y6EMX&_Xb!qx23-->;j1v6 z0j}l^xDjhhdF$&AM$wnPdKei-FNi~5IRx*`cpci-l|%|L6xnjEXLpl0zWGW=1XP~a8(D8rQCJd zF_#)w%a_a|;HnWq7H5MGKK)-`vVsDxny*{Ji+C9??l4VSq$EseANg@|XqFIhnWzu^E#mL0Ju^L(MS6!064e*=)jFZz-d6!2Tjl7wX%n)s(e zX{o_)+3U%&3Dzhv(pyWm3iu<)roG{^LoF}fVIRPlZKcqz3fwd;cXx{T$Jvt*fmOUetrYK(VKu{k=6V5(Hd}miDc`-C zvp!OcL-D9g;WB92eVP^n>_QAb_oG$Cqpz>Ni44V?xoqdql(1awx21XH0>2HiHMKxi zb&<=4;PugNg_40~{XjYLxp=cUIHuYg???8?WUT+P?cln5Ojg?vOO9}@c*zm2&#oB+ ztOBeVM8MK3L@t#NlC7Bp`bnyo(ONmlMEJMK!S{l zx3JCC$exHD;FDFvs0zj-KP>nLD9$!+#Ip#(zbKG>T@{T?!l3wjVcFL`2#Qa>Xk_b$ z$vQ@3HARGIu!>6|EMQ%4P@J?kvJHe)p0yOML?)rpV3mbr=dz!+ znsy50>^{#BSqKv6hXWup%2*(pomo0A6owdEov&Ff=dBZew#wu(7$MzlBH}o7-?6 z-a0(OP)lugZ8}z9v&*#6Em$|Zo_Y*hhIs1?R`IVkyZ+Nr*Jf=KmMyi}znQh!{~c@d zA5Mq$W|t3~fuR@9U7Y{W3e;v-!V>oa*#4hngX%vkfK!`Y??J&-5}p>jkfKAT;NT0V7&+ITm+W=>aj z>9g6j=@`VZKkMG?`hG2FESp^$d7!1*?2-o;)Ml4FxS%$>I#mS0|Fzk*pKoKS&942| zkW`yposS@?HoH3Wfe5wPwf`29YP0JAkGxl#T?e@Lt2Vn11hzvwg1ryD+4b`;Yy;V8 zIe@W;$WaQ3&936vUZi}d65m%P1)RU$?Air0-@n->JN>`f>>8B^#D z*%)lOE}%BMj?Y%PB4o2`OKBkVX4f@%b}vscSH^@}IQ`X|T_GRbL!#&qs%+t|kKXLc z-yzEa(#-pBfn@(JZY^!e)+7jA7OBlHAvwVJ01nuMgr=`IyAblx<#A886eTtq9D|pT`x^@6IwUBj-kixUniSgTVp`% z+3W&CZe49{EkL!|r3!&z6Y;D<$Yz&99H=1(bO~-S$!3=ufl9d7M3|S)JAgzsyY3|c zu`c5WMJJnGiq7&diV}mWaBp^5NECx@Y=8G=m%@q|olmGT9>$`1U66P-yFjCRHN4!m z_CV>)E=M!ySkAapY*1HHq-%=q*t`7ImjX}JkYcK)K)}6edjX0QLh&&u^k&!QNZOKa zPeFdCk$tf%bq(3&#R~Sfp5XnPceAVRht`}xZuj$UcGWp)%?YWtR|Q+W+4Ut>-ww$8 z-l!SN+|HcdmgU^2HWUR$}`Ct2euDtgtlOcbo+1&92{DxyN%|*Y6er zsW-d6!)Vl{sMfD^5)p~!$b!?dSlwC;DOJeyrL(V+^k0Ktx8p3SZ+ckbbU zYVW9nGW2HG3wzL3_ZasM)?IIQWnJ;5P{zGukfzuKj}L!t$X{~1R@OrcBqu&e3V5#&@PJKv=2x1K)ynCx8uJ(r_ zn(Q&Wf?mLqq(+u%{Sd8b;cGrWYH^*AQdFSK_lK?^)0e$e>jI!yTcO&93)X-k%m3bnRKf zE~hSGELQWeRB(?jm68~1sJiW34CiyMkQalZ`0e6psUiGy%v+>h4M47*n840AE! zyziSq2Uec??#kD+3$a5EXJHk@OKUNk-4%2OM0Gqo>wXz1`R+uamCdfpu!+KHEt2o% zf~Vf>y71ZkGPuJ!matT3z;L@0^P0j-5b>3B-A$|4n_d1V?w3K=oOSrIgwC)PZFlQ_ z8Qj4r`qGbF09Tt`osfsYyxUmZ0TS8v0+rtEvO{ps_y}65pWC`%96!DYTD{pd@)fUF zIS?fRz6-40?BeQQXL(0TkBUF81X6EyeSjA5MRFxvZ+5MQsGTb6Aye3LzNL!aL4YKigHr_SEKv1f=b8SejgIC&p3hM1}g?cuCP zZ+1NosFloGC`Wy@*=00A^kgN|X6`?_Wct0=XoP+Ad?t*$q__@=54ON)SqK&Cgl ze#-MDvk17an2^b4S6R%feYh2{gnb+U^=8+stA)~1z&d|T)2ZsKRnZ-?D!PNKqWgCT zKv4qNzng$f$B5i?Opxb*+;j|=&Np%CQnWO7;G?+Ad-oMykM`%aceE_|9ta^P`2fg9 z`2fg)5c1f#9^ux(96UyNz^{G)Jj0SbTu=k%0DS?M&`M(1KA0m*Xb0s$qJxvUgmz#w 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

aZ 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 literal 0 HcmV?d00001 diff --git a/lib/network/xmlrpc.cpp b/lib/network/xmlrpc.cpp new file mode 100644 index 0000000..a53b50a --- /dev/null +++ b/lib/network/xmlrpc.cpp @@ -0,0 +1,518 @@ +#ifndef DISABLE_NETWORK + +#include + + +static std::map&, ePtrList&)> rpcproc; + +void eXMLRPCVariant::zero() +{ + _struct=0; + _array=0; + _i4=0; + _boolean=0; + _string=0; + _double=0; +// _datetime=0; +// _base64=0; +} + +eXMLRPCVariant::eXMLRPCVariant(std::map *__struct) +{ + zero(); + _struct=__struct; +} + +eXMLRPCVariant::eXMLRPCVariant(std::vector *__array) +{ + zero(); + _array=__array; +} + +eXMLRPCVariant::eXMLRPCVariant(__s32 *__i4) +{ + zero(); + _i4=__i4; +} + +eXMLRPCVariant::eXMLRPCVariant(bool *__boolean) +{ + zero(); + _boolean=__boolean; +} + +eXMLRPCVariant::eXMLRPCVariant(eString *__string) +{ + zero(); + _string=__string; +} + +eXMLRPCVariant::eXMLRPCVariant(double *__double) +{ + zero(); + _double=__double; +} + +/*eXMLRPCVariant::eXMLRPCVariant(QDateTime *__datetime) +{ + zero(); + _datetime=__datetime; +} */ + +/*eXMLRPCVariant::eXMLRPCVariant(QByteArray *__base64) +{ + zero(); + _base64=__base64; +} */ + +eXMLRPCVariant::eXMLRPCVariant(const eXMLRPCVariant &c) +{ + zero(); + if (c._i4) + _i4=new int(*c._i4); + if (c._boolean) + _boolean=new bool(*c._boolean); + if (c._string) + _string=new eString(*c._string); + if (c._double) + _double=new double(*c._double); + // datetime, base64 + if (c._struct) + { + _struct=new std::map; + for (std::map::iterator b(c._struct->begin()); b != c._struct->end(); ++b) + _struct->insert(std::pair(b->first, new eXMLRPCVariant(*b->second))); + } + if (c._array) + _array = new std::vector(*c._array); +} + +eXMLRPCVariant::~eXMLRPCVariant() +{ + if (_struct) + { + for (std::map::iterator i(_struct->begin()); i != _struct->end(); ++i) + delete i->second; + + delete _struct; + } + if (_array) + delete _array; + if (_i4) + delete _i4; + if (_boolean) + delete _boolean; + if (_string) + delete _string; + if (_double) + delete _string; +/* if (_datetime) + delete _datetime;*/ +/* if (_base64) + delete _base64;*/ +} + +std::map *eXMLRPCVariant::getStruct() +{ + return _struct; +} + +std::vector *eXMLRPCVariant::getArray() +{ + return _array; +} + +__s32 *eXMLRPCVariant::getI4() +{ + return _i4; +} + +bool *eXMLRPCVariant::getBoolean() +{ + return _boolean; +} + +eString *eXMLRPCVariant::getString() +{ + return _string; +} + +double *eXMLRPCVariant::getDouble() +{ + return _double; +} + +/*QDateTime *eXMLRPCVariant::getDatetime() +{ + return _datetime; +} */ + +/*QByteArray *eXMLRPCVariant::getBase64() +{ + return _base64; +} */ + +void eXMLRPCVariant::toXML(eString &result) +{ + if (getArray()) + { + static eString s1(""); + result+=s1; + for (unsigned int i=0; isize(); i++) + { + static eString s(" "); + result+=s; + (*getArray())[i].toXML(result); + static eString s1("\n"); + result+=s1; + } + static eString s2("\n"); + result+=s2; + } else if (getStruct()) + { + static eString s1(""); + result+=s1; + for (std::map::iterator i(_struct->begin()); i != _struct->end(); ++i) + { + static eString s1(" "); + result+=s1; + result+=i->first; + static eString s2(""); + result+=s2; + i->second->toXML(result); + static eString s3("\n"); + result+=s3; + } + static eString s2("\n"); + result+=s2; + } else if (getI4()) + { + static eString s1(""); + result+=s1; + result+=eString().setNum(*getI4()); + static eString s2(""); + result+=s2; + } else if (getBoolean()) + { + static eString s0("0"); + static eString s1("1"); + result+=(*getBoolean())?s1:s0; + } else if (getString()) + { + static eString s1(""); + static eString s2(""); + result+=s1; + result+=*getString(); + result+=s2; + } else if (getDouble()) + { + result+=eString().sprintf("%lf", *getDouble()); + } else + eFatal("couldn't append"); +} + +static eXMLRPCVariant *fromXML(XMLTreeNode *n) +{ + if (strcmp(n->GetType(), "value")) + return 0; + n=n->GetChild(); + const char *data=n->GetData(); + if (!data) + data=""; + if ((!strcmp(n->GetType(), "i4")) || (!strcmp(n->GetType(), "int"))) + return new eXMLRPCVariant(new int(atoi(data))); + else if (!strcmp(n->GetType(), "boolean")) + return new eXMLRPCVariant(new bool(atoi(data))); + else if (!strcmp(n->GetType(), "string")) + return new eXMLRPCVariant(new eString(data)); + else if (!strcmp(n->GetType(), "double")) + return new eXMLRPCVariant(new double(atof(data))); + else if (!strcmp(n->GetType(), "struct")) { + std::map *s=new std::map; + for (n=n->GetChild(); n; n=n->GetNext()) + { + if (strcmp(data, "member")) + { + delete s; + return 0; + } + eString name=0; + eXMLRPCVariant *value; + for (XMLTreeNode *v=n->GetChild(); v; v=v->GetNext()) + { + if (!strcmp(v->GetType(), "name")) + name=eString(v->GetData()); + else if (!strcmp(v->GetType(), "value")) + value=fromXML(v); + } + if ((!value) || (!name)) + { + delete s; + return 0; + } + s->INSERT(name,value); + } + return new eXMLRPCVariant(s); + } else if (!strcmp(n->GetType(), "array")) + { + ePtrList l; + l.setAutoDelete(true); + n=n->GetChild(); + if (strcmp(data, "data")) + return 0; + for (n=n->GetChild(); n; n=n->GetNext()) + if (!strcmp(n->GetType(), "value")) + { + eXMLRPCVariant *value=fromXML(n); + if (!value) + return 0; + l.push_back(value); + } + + return new eXMLRPCVariant( l.getVector() ); + } + eDebug("couldn't convert %s", n->GetType()); + return 0; +} + +eXMLRPCResponse::eXMLRPCResponse(eHTTPConnection *c): + eHTTPDataSource(c), parser("ISO-8859-1") +{ + // size etc. setzen aber erst NACH data-phase + connection->localstate=eHTTPConnection::stateWait; +} + +eXMLRPCResponse::~eXMLRPCResponse() +{ +} + +int eXMLRPCResponse::doCall() +{ + eDebug("doing call"); + result=""; + // get method name + eString methodName=0; + + if (connection->remote_header["Content-Type"]!="text/xml") + { + eDebug("remote header failure (%s != text/xml)", (connection->remote_header["Content-Type"]).c_str()); + return -3; + } + + XMLTreeNode *methodCall=parser.RootNode(); + if (!methodCall) + { + eDebug("empty xml"); + return -1; + } + if (strcmp(methodCall->GetType(), "methodCall")) + { + eDebug("no methodCall found"); + return -2; + } + + ePtrList params; + params.setAutoDelete(true); + + for (XMLTreeNode *c=methodCall->GetChild(); c; c=c->GetNext()) + { + if (!strcmp(c->GetType(), "methodName")) + methodName=eString(c->GetData()); + else if (!strcmp(c->GetType(), "params")) + { + for (XMLTreeNode *p=c->GetChild(); p; p=p->GetNext()) + if (!strcmp(p->GetType(), "param")) + params.push_back(fromXML(p->GetChild())); + } else + { + eDebug("unknown stuff found"); + return 0; + } + } + + if (!methodName) + { + eDebug("no methodName found!"); + return -3; + } + + eDebug("methodName: %s", methodName.c_str() ); + + result="\n" + ""; + + ePtrList ret; + ret.setAutoDelete(true); + + int (*proc)(std::vector&, ePtrList &)=rpcproc[methodName]; + int fault; + + std::vector* v = params.getVector(); + + if (!proc) + { + fault=1; + xmlrpc_fault(ret, -1, "called method not present"); + } else + fault=proc( *v , ret); + + delete v; + + eDebug("converting to text..."); + + if (fault) + { + result+="\n"; + ret.current()->toXML(result); + result+="\n"; + } else + { + result+="\n"; + for (ePtrList::iterator i(ret); i != ret.end(); ++i) + { + result+=""; + i->toXML(result); + result+=""; + } + result+=""; + } + result+=""; + char buffer[10]; + snprintf(buffer, 10, "%d", size=result.length()); + wptr=0; + connection->local_header["Content-Type"]="text/xml"; + connection->local_header["Content-Length"]=buffer; + connection->code=200; + connection->code_descr="OK"; + connection->localstate=eHTTPConnection::stateResponse; + return 0; +} + +int eXMLRPCResponse::doWrite(int hm) +{ + int tw=size-wptr; + if (tw>hm) + tw=hm; + if (tw<=0) + return -1; + connection->writeBlock(result.c_str()+wptr, tw); + wptr+=tw; + return size > wptr ? 1 : -1; +} + +void eXMLRPCResponse::haveData(void *data, int len) +{ + if (result) + return; + int err=0; + + if (!parser.Parse((char*)data, len, !len)) + { + char temp[len+1]; + temp[len]=0; + memcpy(temp, data, len); + eDebug("%s: %s", temp, parser.ErrorString(parser.GetErrorCode())); + err=1; + } + + if ((!err) && (!len)) + err=doCall(); + + if (err) + { + eDebug("schade: %d", err); + connection->code=400; + connection->code_descr="Bad request"; + char buffer[10]; + snprintf(buffer, 10, "%d", size=result.length()); + wptr=0; + connection->local_header["Content-Type"]="text/html"; + connection->local_header["Content-Length"]=buffer; + result.sprintf("XMLRPC error %d\n", err); + connection->localstate=eHTTPConnection::stateResponse; + } +} + +void xmlrpc_initialize(eHTTPD *httpd) +{ + httpd->addResolver(new eHTTPXMLRPCResolver); +} + +void xmlrpc_addMethod(eString methodName, int (*proc)(std::vector&, ePtrList&)) +{ + rpcproc[methodName]=proc; +} + +void xmlrpc_fault(ePtrList &res, int faultCode, eString faultString) +{ + std::map *s=new std::map; + s->INSERT("faultCode", new eXMLRPCVariant(new __s32(faultCode))); + s->INSERT("faultString", new eXMLRPCVariant(new eString(faultString))); + res.push_back(new eXMLRPCVariant(s)); +} + +int xmlrpc_checkArgs(eString args, std::vector &parm, ePtrList &res) +{ + if (parm.size() != args.length()) + { + xmlrpc_fault(res, -500, eString().sprintf("parameter count mismatch (found %d, expected %d)", parm.size(), args.length())); + return 1; + } + + for (unsigned int i=0; i +#include +#include +#include + +#include +#include +#include + +#define INSERT(KEY,VALUE) insert(std::pair(KEY,VALUE)) + +class eXMLRPCVariant +{ + std::map *_struct; + std::vector *_array; + __s32 *_i4; + bool *_boolean; + eString *_string; + double *_double; +// QDateTime *_datetime; +// QByteArray *_base64; + void zero(); +public: + eXMLRPCVariant(std::map *_struct); + eXMLRPCVariant(std::vector *_array); + eXMLRPCVariant(__s32 *_i4); + eXMLRPCVariant(bool *_boolean); + eXMLRPCVariant(eString *_string); + eXMLRPCVariant(double *_double); +// eXMLRPCVariant(QDateTime *_datetime); +// eXMLRPCVariant(QByteArray *_base64); + eXMLRPCVariant(const eXMLRPCVariant &c); + ~eXMLRPCVariant(); + + std::map *getStruct(); + std::vector *getArray(); + __s32 *getI4(); + bool *getBoolean(); + eString *getString(); + double *getDouble(); +// QDateTime *getDatetime(); +// QByteArray *getBase64(); + + void toXML(eString &); +}; + +class eXMLRPCResponse: public eHTTPDataSource +{ + XMLTreeParser parser; + eString result; + int size; + int wptr; + int doCall(); +public: + eXMLRPCResponse(eHTTPConnection *c); + ~eXMLRPCResponse(); + + int doWrite(int); + void haveData(void *data, int len); +}; + +void xmlrpc_initialize(eHTTPD *httpd); +void xmlrpc_addMethod(eString methodName, int (*)(std::vector&, ePtrList&)); +void xmlrpc_fault(ePtrList &res, int faultCode, eString faultString); +int xmlrpc_checkArgs(eString args, std::vector&, ePtrList &res); + +class eHTTPXMLRPCResolver: public eHTTPPathResolver +{ +public: + eHTTPXMLRPCResolver(); + eHTTPDataSource *getDataSource(eString request, eString path, eHTTPConnection *conn); +}; + +#endif + +#endif //DISABLE_NETWORK diff --git a/lib/service/Makefile.am b/lib/service/Makefile.am new file mode 100644 index 0000000..09ba4ce --- /dev/null +++ b/lib/service/Makefile.am @@ -0,0 +1,8 @@ +INCLUDES = \ + -I$(top_srcdir)/include + +noinst_LIBRARIES = libenigma_service.a + +libenigma_service_a_SOURCES = \ + service.cpp servicemp3.cpp servicedvb.cpp servicefs.cpp + diff --git a/lib/service/Makefile.in b/lib/service/Makefile.in new file mode 100644 index 0000000..e69de29 diff --git a/lib/service/iservice.h b/lib/service/iservice.h new file mode 100644 index 0000000..bedb0d6 --- /dev/null +++ b/lib/service/iservice.h @@ -0,0 +1,173 @@ +#ifndef __lib_dvb_iservice_h +#define __lib_dvb_iservice_h + +#include +#include +#include + +class eServiceReference +{ +public: + enum + { + idInvalid=-1, + idStructure, // service_id == 0 is root + idDVB, + idFile, + idUser=0x1000 + }; + int type; + + int flags; // flags will NOT be compared. + enum + { + isDirectory=1, // SHOULD enter (implies mustDescent) + mustDescent=2, // cannot be played directly - often used with "isDirectory" (implies canDescent) + /* + for example: + normal services have none of them - they can be fed directly into the "play"-handler. + normal directories have both of them set - you cannot play a directory directly and the UI should descent into it. + playlists have "mustDescent", but not "isDirectory" - you don't want the user to browse inside the playlist (unless he really wants) + services with sub-services have none of them, instead the have the "canDecsent" flag (as all of the above) + */ + canDescent=4, // supports enterDirectory/leaveDirectory + flagDirectory=isDirectory|mustDescent|canDescent, + shouldSort=8, // should be ASCII-sorted according to service_name. great for directories. + hasSortKey=16, // has a sort key in data[3]. not having a sort key implies 0. + sort1=32 // sort key is 1 instead of 0 + }; + + inline int getSortKey() const { return (flags & hasSortKey) ? data[3] : ((flags & sort1) ? 1 : 0); } + + int data[8]; + eString path; + + eServiceReference() + : type(idInvalid), flags(0) + { + } + + eServiceReference(int type, int flags) + : type(type), flags(flags) + { + memset(data, 0, sizeof(data)); + } + eServiceReference(int type, int flags, int data0) + : type(type), flags(flags) + { + memset(data, 0, sizeof(data)); + data[0]=data0; + } + eServiceReference(int type, int flags, int data0, int data1) + : type(type), flags(flags) + { + memset(data, 0, sizeof(data)); + data[0]=data0; + data[1]=data1; + } + eServiceReference(int type, int flags, int data0, int data1, int data2) + : type(type), flags(flags) + { + memset(data, 0, sizeof(data)); + data[0]=data0; + data[1]=data1; + data[2]=data2; + } + eServiceReference(int type, int flags, int data0, int data1, int data2, int data3) + : type(type), flags(flags) + { + memset(data, 0, sizeof(data)); + data[0]=data0; + data[1]=data1; + data[2]=data2; + data[3]=data3; + } + eServiceReference(int type, int flags, int data0, int data1, int data2, int data3, int data4) + : type(type), flags(flags) + { + memset(data, 0, sizeof(data)); + data[0]=data0; + data[1]=data1; + data[2]=data2; + data[3]=data3; + data[4]=data4; + } + eServiceReference(int type, int flags, const eString &path) + : type(type), flags(flags), path(path) + { + memset(data, 0, sizeof(data)); + } + eServiceReference(const eString &string); + eString toString() const; + bool operator==(const eServiceReference &c) const + { + if (type != c.type) + return 0; + return /* (flags == c.flags) && */ (memcmp(data, c.data, sizeof(int)*8)==0) && (path == c.path); + } + bool operator!=(const eServiceReference &c) const + { + return !(*this == c); + } + bool operator<(const eServiceReference &c) const + { + if (type < c.type) + return 1; + + if (type > c.type) + return 0; + +/* if (flags < c.flags) + return 1; + if (flags > c.flags) + return 0; */ + + int r=memcmp(data, c.data, sizeof(int)*8); + if (r) + return r < 0; + return path < c.path; + } + operator bool() const + { + return type != idInvalid; + } +}; + +class iPauseableService: public virtual iObject +{ +public: + virtual RESULT pause()=0; + virtual RESULT unpause()=0; +}; + +class iPlayableService: public virtual iObject +{ + friend class iServiceHandler; +public: + // it's PRIVATE to the class factory + virtual RESULT start()=0; + virtual RESULT getIPausableService(ePtr &ptr)=0; +}; + +class iRecordableService: public virtual iObject +{ +public: + virtual RESULT start()=0; + virtual RESULT stop()=0; +}; + +class iListableService: public virtual iObject +{ +public: + virtual RESULT getContent(std::list &list)=0; +}; + +class iServiceHandler: public virtual iObject +{ +public: + virtual RESULT play(const eServiceReference &, ePtr &ptr)=0; + virtual RESULT record(const eServiceReference &, ePtr &ptr)=0; + virtual RESULT list(const eServiceReference &, ePtr &ptr)=0; +}; + +#endif diff --git a/lib/service/service.cpp b/lib/service/service.cpp new file mode 100644 index 0000000..99199aa --- /dev/null +++ b/lib/service/service.cpp @@ -0,0 +1,94 @@ +#include +#include +#include + +eServiceReference::eServiceReference(const eString &string) +{ + const char *c=string.c_str(); + int pathl=-1; + + if ( sscanf(c, "%d:%d:%x:%x:%x:%x:%x:%x:%x:%x:%n", &type, &flags, &data[0], &data[1], &data[2], &data[3], &data[4], &data[5], &data[6], &data[7], &pathl) < 8 ) + { + memset( data, 0, sizeof(data) ); + eDebug("find old format eServiceReference string"); + sscanf(c, "%d:%d:%x:%x:%x:%x:%n", &type, &flags, &data[0], &data[1], &data[2], &data[3], &pathl); + } + + if (pathl) + path=c+pathl; +} + +eString eServiceReference::toString() const +{ + eString ret; + ret+=eString().sprintf("%d:", type); + ret+=eString().sprintf("%d", flags); + for (unsigned int i=0; i &ptr) +{ + std::map >::iterator i = handler.find(ref.type); + if (i == handler.end()) + { + ptr = 0; + return -1; + } + return i->second->play(ref, ptr); +} + +RESULT eServiceCenter::record(const eServiceReference &ref, ePtr &ptr) +{ + std::map >::iterator i = handler.find(ref.type); + if (i == handler.end()) + { + ptr = 0; + return -1; + } + return i->second->record(ref, ptr); +} + +RESULT eServiceCenter::list(const eServiceReference &ref, ePtr &ptr) +{ + std::map >::iterator i = handler.find(ref.type); + if (i == handler.end()) + { + ptr = 0; + return -1; + } + return i->second->list(ref, ptr); +} + +RESULT eServiceCenter::addServiceFactory(int id, iServiceHandler *hnd) +{ + handler.insert(std::pair >(id, hnd)); + return 0; +} + +RESULT eServiceCenter::removeServiceFactory(int id) +{ + handler.erase(id); + return 0; +} + +eAutoInitP0 init_eServiceCenter(eAutoInitNumbers::service, "eServiceCenter"); diff --git a/lib/service/service.h b/lib/service/service.h new file mode 100644 index 0000000..f32e23d --- /dev/null +++ b/lib/service/service.h @@ -0,0 +1,29 @@ +#ifndef __service_h +#define __service_h + +#include +#include +#include + +class eServiceCenter: public virtual iServiceHandler, public virtual iObject +{ +DECLARE_REF; +private: + std::map > handler; + static eServiceCenter *instance; +public: + eServiceCenter(); + virtual ~eServiceCenter(); + + // iServiceHandler + RESULT play(const eServiceReference &, ePtr &ptr); + RESULT record(const eServiceReference &, ePtr &ptr); + RESULT list(const eServiceReference &, ePtr &ptr); + + // eServiceCenter + static RESULT getInstance(ePtr &ptr) { ptr = instance; return 0; } + RESULT addServiceFactory(int id, iServiceHandler *hnd); + RESULT removeServiceFactory(int id); +}; + +#endif diff --git a/lib/service/servicedvb.cpp b/lib/service/servicedvb.cpp new file mode 100644 index 0000000..fc48fa6 --- /dev/null +++ b/lib/service/servicedvb.cpp @@ -0,0 +1,154 @@ +#include +#include +#include +#include +#include +#include +#include + +DEFINE_REF(eServiceFactoryDVB) + +eServiceFactoryDVB::eServiceFactoryDVB(): ref(0) +{ + ePtr sc; + + eServiceCenter::getInstance(sc); + if (sc) + sc->addServiceFactory(eServiceFactoryDVB::id, this); +} + +eServiceFactoryDVB::~eServiceFactoryDVB() +{ + ePtr sc; + + eServiceCenter::getInstance(sc); + if (sc) + sc->removeServiceFactory(eServiceFactoryDVB::id); +} + +RESULT eServiceFactoryDVB::play(const eServiceReference &ref, ePtr &ptr) +{ + RESULT res; + // check resources... + ptr = new eDVBServicePlay(ref); + res = ptr->start(); + if (res) + { + ptr = 0; + return res; + } + return 0; +} + +RESULT eServiceFactoryDVB::record(const eServiceReference &, ePtr &ptr) +{ + ptr = 0; + return -1; +} + +RESULT eServiceFactoryDVB::list(const eServiceReference &, ePtr &ptr) +{ + ptr = 0; + return -1; +} + +eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref): + ref(0), m_reference(ref) +{ + CONNECT(m_serviceHandler.serviceEvent, eDVBServicePlay::serviceEvent); + eDebug("DVB start (play)"); +} + +eDVBServicePlay::~eDVBServicePlay() +{ + eDebug("DVB stop (play)"); +} + +void eDVBServicePlay::serviceEvent(int event) +{ + eDebug("service event %d", event); + switch (event) + { + case eDVBServicePMTHandler::eventNewProgramInfo: + { + int vpid = -1, apid = -1, pcrpid = -1; + eDVBServicePMTHandler::program program; + if (m_serviceHandler.getProgramInfo(program)) + eDebug("getting program info failed."); + else + { + eDebugNoNewLine("have %d video stream(s)", program.videoStreams.size()); + if (!program.videoStreams.empty()) + { + eDebugNoNewLine(" ("); + for (std::vector::const_iterator + i(program.videoStreams.begin()); + i != program.videoStreams.end(); ++i) + { + if (vpid == -1) + vpid = i->pid; + if (i != program.videoStreams.begin()) + eDebugNoNewLine(", "); + eDebugNoNewLine("%04x", i->pid); + } + eDebugNoNewLine(")"); + } + eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size()); + if (!program.audioStreams.empty()) + { + eDebugNoNewLine(" ("); + for (std::vector::const_iterator + i(program.audioStreams.begin()); + i != program.audioStreams.end(); ++i) + { + if (apid == -1) + apid = i->pid; + if (i != program.audioStreams.begin()) + eDebugNoNewLine(", "); + eDebugNoNewLine("%04x", i->pid); + } + eDebugNoNewLine(")"); + } + eDebug(", and the pcr pid is %04x", program.pcrPid); + if (program.pcrPid != 0x1fff) + pcrpid = program.pcrPid; + } + + if (!m_decoder) + { + ePtr demux; + m_serviceHandler.getDemux(demux); + if (demux) + demux->getMPEGDecoder(m_decoder); + } + + if (m_decoder) + { + m_decoder->setVideoPID(vpid); + m_decoder->setAudioPID(apid, 0); + m_decoder->setSyncPCR(pcrpid); + m_decoder->start(); + } + + break; + } + } +} + +RESULT eDVBServicePlay::start() +{ + eDebug("starting DVB service"); + m_serviceHandler.tune((eServiceReferenceDVB&)m_reference); + return 0; +} + +RESULT eDVBServicePlay::getIPausableService(ePtr &ptr) +{ + // not yet possible, maybe later... + ptr = 0; + return -1; +} + +DEFINE_REF(eDVBServicePlay) + +eAutoInitP0 init_eServiceFactoryDVB(eAutoInitNumbers::service+1, "eServiceFactoryDVB"); diff --git a/lib/service/servicedvb.h b/lib/service/servicedvb.h new file mode 100644 index 0000000..fac9edf --- /dev/null +++ b/lib/service/servicedvb.h @@ -0,0 +1,45 @@ +#ifndef __servicemp3_h +#define __servicemp3_h + +#include +#include + +#include + +class eServiceFactoryDVB: public virtual iServiceHandler, public virtual iObject +{ +DECLARE_REF; +public: + eServiceFactoryDVB(); + virtual ~eServiceFactoryDVB(); + enum { id = 0x1 }; + + // iServiceHandler + RESULT play(const eServiceReference &, ePtr &ptr); + RESULT record(const eServiceReference &, ePtr &ptr); + RESULT list(const eServiceReference &, ePtr &ptr); +}; + +class eDVBServicePlay: public virtual iPlayableService, public virtual iObject, public Object +{ +DECLARE_REF; +private: + friend class eServiceFactoryDVB; + eServiceReference m_reference; + + ePtr m_decoder; + + eDVBServicePMTHandler m_serviceHandler; + + eDVBServicePlay(const eServiceReference &ref); + + void serviceEvent(int event); +public: + virtual ~eDVBServicePlay(); + + // iPlayableService + RESULT start(); + RESULT getIPausableService(ePtr &ptr); +}; + +#endif diff --git a/lib/service/servicefs.cpp b/lib/service/servicefs.cpp new file mode 100644 index 0000000..ad40f0a --- /dev/null +++ b/lib/service/servicefs.cpp @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// eServiceFactoryFS + +eServiceFactoryFS::eServiceFactoryFS(): ref(0) +{ + ePtr sc; + + eServiceCenter::getInstance(sc); + if (sc) + sc->addServiceFactory(eServiceFactoryFS::id, this); +} + +eServiceFactoryFS::~eServiceFactoryFS() +{ + ePtr sc; + + eServiceCenter::getInstance(sc); + if (sc) + sc->removeServiceFactory(eServiceFactoryFS::id); +} + +DEFINE_REF(eServiceFactoryFS) + + // iServiceHandler +RESULT eServiceFactoryFS::play(const eServiceReference &ref, ePtr &ptr) +{ + ptr=0; + return -1; +} + +RESULT eServiceFactoryFS::record(const eServiceReference &ref, ePtr &ptr) +{ + ptr=0; + return -1; +} + +RESULT eServiceFactoryFS::list(const eServiceReference &ref, ePtr &ptr) +{ + ptr = new eServiceFS(ref.path.c_str()); + return 0; +} + +// eServiceFS + +DEFINE_REF(eServiceFS); + +eServiceFS::eServiceFS(const char *path): ref(0), path(path) +{ +} + +eServiceFS::~eServiceFS() +{ +} + +RESULT eServiceFS::getContent(std::list &list) +{ + DIR *d=opendir(path.c_str()); + if (!d) + return -errno; + while (dirent *e=readdir(d)) + { + if (!(strcmp(e->d_name, ".") && strcmp(e->d_name, ".."))) + continue; + + eString filename; + + filename = path; + filename += e->d_name; + + struct stat s; + if (::stat(filename.c_str(), &s) < 0) + continue; + + if (S_ISDIR(s.st_mode)) + filename += "/"; + + if (S_ISDIR(s.st_mode)) + { + eServiceReference service(eServiceFactoryFS::id, + eServiceReference::isDirectory| + eServiceReference::canDescent|eServiceReference::mustDescent| + eServiceReference::shouldSort|eServiceReference::sort1, + filename); + service.data[0] = 1; + list.push_back(service); + } else + { + eServiceReference service(eServiceFactoryFS::id, + eServiceReference::isDirectory| + eServiceReference::canDescent|eServiceReference::mustDescent| + eServiceReference::shouldSort|eServiceReference::sort1, + filename); + service.data[0] = 0; + list.push_back(service); + } + } + return 0; +} + +eAutoInitP0 init_eServiceFactoryFS(eAutoInitNumbers::service+1, "eServiceFactoryFS"); diff --git a/lib/service/servicefs.h b/lib/service/servicefs.h new file mode 100644 index 0000000..9d49b42 --- /dev/null +++ b/lib/service/servicefs.h @@ -0,0 +1,33 @@ +#ifndef __servicefs_h +#define __servicefs_h + +#include + +class eServiceFactoryFS: public virtual iServiceHandler, public virtual iObject +{ +DECLARE_REF; +public: + eServiceFactoryFS(); + virtual ~eServiceFactoryFS(); + enum { id = 0x2 }; + + // iServiceHandler + RESULT play(const eServiceReference &, ePtr &ptr); + RESULT record(const eServiceReference &, ePtr &ptr); + RESULT list(const eServiceReference &, ePtr &ptr); +}; + +class eServiceFS: public virtual iListableService, public virtual iObject +{ +DECLARE_REF; +private: + eString path; + friend class eServiceFactoryFS; + eServiceFS(const char *path); +public: + virtual ~eServiceFS(); + + RESULT getContent(std::list &list); +}; + +#endif diff --git a/lib/service/servicemp3.cpp b/lib/service/servicemp3.cpp new file mode 100644 index 0000000..fb5993e --- /dev/null +++ b/lib/service/servicemp3.cpp @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include +#include + +// eServiceFactoryMP3 + +eServiceFactoryMP3::eServiceFactoryMP3(): ref(0) +{ + ePtr sc; + + eServiceCenter::getInstance(sc); + if (sc) + sc->addServiceFactory(eServiceFactoryMP3::id, this); +} + +eServiceFactoryMP3::~eServiceFactoryMP3() +{ + ePtr sc; + + eServiceCenter::getInstance(sc); + if (sc) + sc->removeServiceFactory(eServiceFactoryMP3::id); +} + +DEFINE_REF(eServiceFactoryMP3) + + // iServiceHandler +RESULT eServiceFactoryMP3::play(const eServiceReference &ref, ePtr &ptr) +{ + RESULT res; + // check resources... + ptr = new eServiceMP3(ref.path.c_str()); + res = ptr->start(); + if (res) + { + ptr = 0; + return res; + } + return 0; +} + +RESULT eServiceFactoryMP3::record(const eServiceReference &ref, ePtr &ptr) +{ + ptr=0; + return -1; +} + +RESULT eServiceFactoryMP3::list(const eServiceReference &, ePtr &ptr) +{ + ptr=0; + return -1; +} + +// eServiceMP3 + +eServiceMP3::eServiceMP3(const char *filename): filename(filename), ref(0) +{ + printf("MP3: %s start\n", filename); +} + +eServiceMP3::~eServiceMP3() +{ + printf("MP3: %s stop\n", filename.c_str()); +} + +void eServiceMP3::AddRef() +{ + ++ref; +} + +void eServiceMP3::Release() +{ + if (!--ref) + delete this; +} + +RESULT eServiceMP3::start() { printf("mp3 starts\n"); return 0; } +RESULT eServiceMP3::getIPausableService(ePtr &ptr) { ptr=this; return 0; } + + // iPausableService +RESULT eServiceMP3::pause() { printf("mp3 pauses!\n"); return 0; } +RESULT eServiceMP3::unpause() { printf("mp3 unpauses!\n"); return 0; } + + +eAutoInitP0 init_eServiceFactoryMP3(eAutoInitNumbers::service+1, "eServiceFactoryMP3"); diff --git a/lib/service/servicemp3.h b/lib/service/servicemp3.h new file mode 100644 index 0000000..0f2c074 --- /dev/null +++ b/lib/service/servicemp3.h @@ -0,0 +1,42 @@ +#ifndef __servicemp3_h +#define __servicemp3_h + +#include + +class eServiceFactoryMP3: public virtual iServiceHandler, public virtual iObject +{ +DECLARE_REF; +public: + eServiceFactoryMP3(); + virtual ~eServiceFactoryMP3(); + enum { id = 0x1001 }; + + // iServiceHandler + RESULT play(const eServiceReference &, ePtr &ptr); + RESULT record(const eServiceReference &, ePtr &ptr); + RESULT list(const eServiceReference &, ePtr &ptr); +}; + +class eServiceMP3: public virtual iPlayableService, public virtual iPauseableService, public virtual iObject +{ + friend class eServiceFactoryMP3; + std::string filename; + eServiceMP3(const char *filename); + int ref; +public: + virtual ~eServiceMP3(); + + // iObject + void AddRef(); + void Release(); + + // iPlayableService + RESULT start(); + RESULT getIPausableService(ePtr &ptr); + + // iPausableService + RESULT pause(); + RESULT unpause(); +}; + +#endif diff --git a/main/Makefile.am b/main/Makefile.am new file mode 100644 index 0000000..2f63dff --- /dev/null +++ b/main/Makefile.am @@ -0,0 +1,31 @@ +INCLUDES = \ + -I$(top_srcdir)/include + +bin_PROGRAMS = enigma2 + +enigma2_SOURCES = \ + enigma.cpp + +enigma2_LDADD_WHOLE = \ + $(top_builddir)/lib/base/libenigma_base.a \ + $(top_builddir)/lib/driver/libenigma_driver.a \ + $(top_builddir)/lib/dvb/libenigma_dvb.a \ + $(top_builddir)/lib/dvb_si/libenigma_dvb_si.a \ + $(top_builddir)/lib/gui/libenigma_gui.a \ + $(top_builddir)/lib/gdi/libenigma_gdi.a \ + $(top_builddir)/lib/network/libenigma_network.a \ + $(top_builddir)/lib/service/libenigma_service.a + +enigma2_LDADD = \ + @FREETYPE_LIBS@ \ + @ID3TAG_LIBS@ \ + @MAD_LIBS@ \ + @MD5SUM_LIBS@ \ + @PNG_LIBS@ \ + @SIGC_LIBS@ \ + @XMLTREE_LIBS@ \ + -ldl -lpthread -lcrypt -lresolv + +enigma2$(EXEEXT): $(enigma2_OBJECTS) $(enigma2_DEPENDENCIES) $(enigma2_LDADD_WHOLE) + @rm -f enigma2$(EXEEXT) + $(CXXLINK) $(enigma2_LDFLAGS) $(enigma2_OBJECTS) -Wl,--export-dynamic -Wl,--whole-archive $(enigma2_LDADD_WHOLE) -Wl,--no-whole-archive $(enigma2_LDADD) $(LIBS) diff --git a/main/Makefile.in b/main/Makefile.in new file mode 100644 index 0000000..e69de29 diff --git a/main/enigma-dvbtest.cpp b/main/enigma-dvbtest.cpp new file mode 100644 index 0000000..4fdb698 --- /dev/null +++ b/main/enigma-dvbtest.cpp @@ -0,0 +1,125 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +class eMain: public eApplication, public Object +{ + ePtr m_mgr; + ePtr m_channel; + ePtr m_demux; + eAUTable > m_table; + + ePtr m_dvbdb; + + ePtr m_state_change_connection; + int m_last_channel_state; +public: + eMain() + { + eDebug("mich gibts nu!"); + + m_mgr = new eDVBResourceManager(); + + m_dvbdb = new eDVBDB(); + m_mgr->setChannelList(m_dvbdb); + + eDVBChannelID chid(1,2,3); + + eDVBFrontendParametersSatellite fesat; + + fesat.frequency = 12070000; + fesat.symbol_rate = 27500000; + fesat.polarisation = eDVBFrontendParametersSatellite::Polarisation::Horizontal; + fesat.fec = eDVBFrontendParametersSatellite::FEC::f3_4; + fesat.inversion = eDVBFrontendParametersSatellite::Inversion::Off; + fesat.orbital_position = 192; + + eDVBFrontendParameters *fe = new eDVBFrontendParameters(); + + fe->setDVBS(fesat); + + m_dvbdb->addChannelToList(chid, fe); + + if (m_mgr->allocateChannel(chid, m_channel)) + eDebug("shit it failed!"); + + if (m_channel) + { + m_channel->connectStateChange(slot(*this, &eMain::channelStateChanged), m_state_change_connection); + channelStateChanged(m_channel); + } + } + + void channelStateChanged(iDVBChannel *channel) + { + int state; + channel->getState(state); + eDebug("channel state is now %d", state); + + if ((m_last_channel_state != iDVBChannel::state_ok) + && (state == iDVBChannel::state_ok) && (!m_demux)) + { + eDebug("we'll start tuning!"); + if (m_channel) + if (m_channel->getDemux(m_demux)) + eDebug("shit it failed.. again."); + + if (m_demux) + { + CONNECT(m_table.tableReady, eMain::tableReady); + m_table.begin(this, eDVBPMTSpec(0x20, 0x33f6), m_demux); + } + } + + m_last_channel_state = state; + } + + void tableReady(int) + { + ePtr > ptr; + if (!m_table.getCurrent(ptr)) + { + ProgramMapTableConstIterator i; + for (i = ptr->getSections().begin(); i != ptr->getSections().end(); ++i) + { + const ProgramMapTable &pmt = **i; + eDebug("pcr pid: %x", pmt.getPcrPid()); + } + eDebug("program map ..."); + quit(0); + } + eDebug("table ready."); + } + + ~eMain() + { + eDebug("... nicht mehr."); + } +}; + +#ifdef OBJECT_DEBUG +int object_total_remaining; + +void object_dump() +{ + printf("%d items left\n", object_total_remaining); +} +#endif + +int main() +{ +#ifdef OBJECT_DEBUG + atexit(object_dump); +#endif + eMain app; + return app.exec(); +} diff --git a/main/enigma-scan.cpp b/main/enigma-scan.cpp new file mode 100644 index 0000000..af50548 --- /dev/null +++ b/main/enigma-scan.cpp @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +class eMain: public eApplication, public Object +{ + eInit init; + + eDVBScan *m_scan; + + ePtr m_mgr; + ePtr m_channel; + ePtr m_dvbdb; + + void scanEvent(int evt) + { + eDebug("scan event %d!", evt); + if (evt == eDVBScan::evtFinish) + { + m_scan->insertInto(m_dvbdb); + quit(0); + } + } + ePtr m_scan_event_connection; +public: + eMain() + { + m_dvbdb = new eDVBDB(); + m_mgr = new eDVBResourceManager(); + + eDVBFrontendParametersSatellite fesat; + + fesat.frequency = 11817000; // 12070000; + fesat.symbol_rate = 27500000; + fesat.polarisation = eDVBFrontendParametersSatellite::Polarisation::Vertical; + fesat.fec = eDVBFrontendParametersSatellite::FEC::f3_4; + fesat.inversion = eDVBFrontendParametersSatellite::Inversion::Off; + fesat.orbital_position = 192; + + eDVBFrontendParameters *fe = new eDVBFrontendParameters(); + + fe->setDVBS(fesat); + + if (m_mgr->allocateRawChannel(m_channel)) + eDebug("shit it failed!"); + +// init.setRunlevel(eAutoInitNumbers::main); + eDebug("starting scan..."); + + std::list > list; + + list.push_back(fe); + + m_scan = new eDVBScan(m_channel); + m_scan->start(list); + + m_scan->connectEvent(slot(*this, &eMain::scanEvent), m_scan_event_connection); + } + + ~eMain() + { + delete m_scan; + eDebug("... nicht mehr."); + } +}; + +#ifdef OBJECT_DEBUG +int object_total_remaining; + +void object_dump() +{ + printf("%d items left\n", object_total_remaining); +} +#endif + +int main() +{ +#ifdef OBJECT_DEBUG + atexit(object_dump); +#endif + eMain app; + return app.exec(); +} diff --git a/main/enigma.cpp b/main/enigma.cpp new file mode 100644 index 0000000..7543e45 --- /dev/null +++ b/main/enigma.cpp @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +class eMain: public eApplication, public Object +{ + eInit init; + + ePtr m_mgr; + ePtr m_channel; + ePtr m_dvbdb; + + ePtr m_playservice; +public: + eMain() + { + init.setRunlevel(eAutoInitNumbers::main); + m_dvbdb = new eDVBDB(); + m_mgr = new eDVBResourceManager(); + m_mgr->setChannelList(m_dvbdb); + + ePtr service_center; + eServiceCenter::getInstance(service_center); + + if (service_center) + { + eServiceReference ref("2:0:1:0:0:0:0:0:0:0:/"); + ePtr lst; + if (service_center->list(ref, lst)) + eDebug("no list available!"); + else + { + std::list list; + if (lst->getContent(list)) + eDebug("list itself SUCKED AROUND!!!"); + else + for (std::list::const_iterator i(list.begin()); + i != list.end(); ++i) + eDebug("%s", i->toString().c_str()); + } + } + + eServiceReference ref("1:0:1:6de2:44d:1:c00000:0:0:0:"); + + if (service_center) + { + if (service_center->play(ref, m_playservice)) + eDebug("play sucked around!"); + else + eDebug("play r00lz!"); + } else + eDebug("no service center: no play."); + } + + ~eMain() + { + + } +}; + +#ifdef OBJECT_DEBUG +int object_total_remaining; + +void object_dump() +{ + printf("%d items left\n", object_total_remaining); +} +#endif + +int main() +{ +#ifdef OBJECT_DEBUG + atexit(object_dump); +#endif + eMain app; + return app.exec(); +} diff --git a/stamp-h.in b/stamp-h.in new file mode 100644 index 0000000..9788f70 --- /dev/null +++ b/stamp-h.in @@ -0,0 +1 @@ +timestamp -- 2.7.4

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�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%0DKdvVOTu%e%j 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